@by-lua/lspec-subagents 1.0.1

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +482 -0
  2. package/LICENSE +21 -0
  3. package/README.md +123 -0
  4. package/dist/agent-manager.d.ts +108 -0
  5. package/dist/agent-manager.js +391 -0
  6. package/dist/agent-runner.d.ts +95 -0
  7. package/dist/agent-runner.js +377 -0
  8. package/dist/agent-types.d.ts +58 -0
  9. package/dist/agent-types.js +157 -0
  10. package/dist/context.d.ts +12 -0
  11. package/dist/context.js +56 -0
  12. package/dist/cross-extension-rpc.d.ts +46 -0
  13. package/dist/cross-extension-rpc.js +76 -0
  14. package/dist/custom-agents.d.ts +14 -0
  15. package/dist/custom-agents.js +127 -0
  16. package/dist/default-agents.d.ts +12 -0
  17. package/dist/default-agents.js +489 -0
  18. package/dist/env.d.ts +6 -0
  19. package/dist/env.js +28 -0
  20. package/dist/group-join.d.ts +32 -0
  21. package/dist/group-join.js +116 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.js +1863 -0
  24. package/dist/invocation-config.d.ts +22 -0
  25. package/dist/invocation-config.js +15 -0
  26. package/dist/memory.d.ts +49 -0
  27. package/dist/memory.js +151 -0
  28. package/dist/model-config-loader.d.ts +58 -0
  29. package/dist/model-config-loader.js +157 -0
  30. package/dist/model-resolver.d.ts +19 -0
  31. package/dist/model-resolver.js +62 -0
  32. package/dist/output-file.d.ts +24 -0
  33. package/dist/output-file.js +86 -0
  34. package/dist/prompts.d.ts +29 -0
  35. package/dist/prompts.js +65 -0
  36. package/dist/schedule-store.d.ts +38 -0
  37. package/dist/schedule-store.js +155 -0
  38. package/dist/schedule.d.ts +109 -0
  39. package/dist/schedule.js +338 -0
  40. package/dist/settings.d.ts +66 -0
  41. package/dist/settings.js +130 -0
  42. package/dist/skill-loader.d.ts +24 -0
  43. package/dist/skill-loader.js +93 -0
  44. package/dist/types.d.ts +164 -0
  45. package/dist/types.js +8 -0
  46. package/dist/ui/agent-widget.d.ts +134 -0
  47. package/dist/ui/agent-widget.js +451 -0
  48. package/dist/ui/conversation-viewer.d.ts +35 -0
  49. package/dist/ui/conversation-viewer.js +252 -0
  50. package/dist/ui/schedule-menu.d.ts +16 -0
  51. package/dist/ui/schedule-menu.js +95 -0
  52. package/dist/usage.d.ts +50 -0
  53. package/dist/usage.js +49 -0
  54. package/dist/worktree.d.ts +36 -0
  55. package/dist/worktree.js +139 -0
  56. package/install.sh +77 -0
  57. package/lspec-model-config.example.json +17 -0
  58. package/package.json +50 -0
  59. package/src/agent-manager.ts +483 -0
  60. package/src/agent-runner.ts +486 -0
  61. package/src/agent-types.ts +188 -0
  62. package/src/context.ts +58 -0
  63. package/src/cross-extension-rpc.ts +122 -0
  64. package/src/custom-agents.ts +136 -0
  65. package/src/default-agents.ts +501 -0
  66. package/src/env.ts +33 -0
  67. package/src/group-join.ts +141 -0
  68. package/src/index.ts +2032 -0
  69. package/src/invocation-config.ts +40 -0
  70. package/src/memory.ts +165 -0
  71. package/src/model-config-loader.ts +193 -0
  72. package/src/model-resolver.ts +81 -0
  73. package/src/output-file.ts +96 -0
  74. package/src/prompts.ts +91 -0
  75. package/src/schedule-store.ts +153 -0
  76. package/src/schedule.ts +365 -0
  77. package/src/settings.ts +186 -0
  78. package/src/skill-loader.ts +102 -0
  79. package/src/types.ts +179 -0
  80. package/src/ui/agent-widget.ts +533 -0
  81. package/src/ui/conversation-viewer.ts +261 -0
  82. package/src/ui/schedule-menu.ts +104 -0
  83. package/src/usage.ts +60 -0
  84. package/src/worktree.ts +162 -0
  85. package/uninstall.sh +55 -0
  86. package/update.sh +64 -0
@@ -0,0 +1,501 @@
1
+ /**
2
+ * default-agents.ts — L-Spec 9 embedded agent configurations.
3
+ *
4
+ * Uses model placeholders ({{model:agent_name}}) resolved at startup
5
+ * from lspec-model-config.json (project or global) or embedded defaults.
6
+ *
7
+ * Based on oh-my-opencode-slim agent roster:
8
+ * orchestrator, explorer, librarian, oracle, designer,
9
+ * fixer, observer, council, councillor
10
+ */
11
+
12
+ import type { AgentConfig } from "./types.js";
13
+
14
+ const READ_ONLY_TOOLS = [
15
+ // Built-in PI.dev — navegação estrutural
16
+ "cymbal_map", "cymbal_search", "cymbal_outline", "cymbal_show",
17
+ "cymbal_refs", "cymbal_impact", "cymbal_importers", "cymbal_impls",
18
+ // Built-in PI.dev — precisão técnica (LSP)
19
+ "lsp_goto_definition", "lsp_find_references", "lsp_diagnostics",
20
+ "lsp_symbols", "lsp_rename",
21
+ // Fallback
22
+ "read", "bash", "grep", "find", "ls",
23
+ ];
24
+
25
+ export const DEFAULT_AGENTS: Map<string, AgentConfig> = new Map([
26
+ // ── Orchestrator ──────────────────────────────────────────────
27
+ [
28
+ "orchestrator",
29
+ {
30
+ name: "orchestrator",
31
+ displayName: "Orchestrator",
32
+ description: "L-Spec central coordinator — delegates to specialists, manages phases, maintains plan coherence",
33
+ extensions: true,
34
+ skills: true,
35
+ model: "{{model:orchestrator}}",
36
+ thinking: "high",
37
+ maxTurns: 50,
38
+ systemPrompt: [
39
+ "You are the Orchestrator — the central coordinator for the L-Spec (Lua Spec) development workflow.",
40
+ "",
41
+ "## Your Role",
42
+ "You manage the full L-Spec lifecycle: Discovery → Specify → Discuss → Design → Tasks → Execute.",
43
+ "You decide WHEN to delegate to specialist agents and WHAT to do yourself.",
44
+ "",
45
+ "## Available Specialist Agents",
46
+ "",
47
+ "@explorer",
48
+ "- Role: Fast codebase navigation specialist",
49
+ "- Permissions: Read-only (grep, glob, read)",
50
+ "- Delegate when: Need to discover what exists before planning | Parallel searches | Broad/uncertain scope",
51
+ "- Don't delegate when: Know the path and need actual content | About to edit the file",
52
+ "",
53
+ "@librarian",
54
+ "- Role: External docs and API references",
55
+ "- Permissions: Read-only + web search",
56
+ "- Delegate when: Libraries with frequent API changes | Complex APIs | Version-specific behavior",
57
+ "- Don't delegate when: Standard usage you're confident about | Simple stable APIs",
58
+ "",
59
+ "@oracle",
60
+ "- Role: Strategic advisor, code reviewer, architect",
61
+ "- Permissions: Read-only",
62
+ "- Delegate when: Major architectural decisions | Problems persisting after 2+ attempts | High-risk refactors | Code review | YAGNI scrutiny",
63
+ "- Don't delegate when: Routine decisions | First bug fix attempt",
64
+ "",
65
+ "@designer",
66
+ "- Role: UI/UX specialist for polished experiences",
67
+ "- Permissions: Read/write",
68
+ "- Delegate when: User-facing interfaces | Responsive layouts | UX-critical components | Design systems | Animations",
69
+ "- Don't delegate when: Backend/logic with no visual",
70
+ "",
71
+ "@fixer",
72
+ "- Role: Fast execution specialist for well-defined tasks",
73
+ "- Permissions: Read/write/edit/bash",
74
+ "- Delegate when: Bounded implementation work | Writing/updating tests | Multi-file changes per folder",
75
+ "- Don't delegate when: Needs discovery/research | Single small change (<20 lines) | Tight integration with your current work",
76
+ "",
77
+ "@observer",
78
+ "- Role: Visual analysis for images, PDFs, diagrams",
79
+ "- Permissions: Read-only",
80
+ "- Delegate when: Need to analyze multimedia files (screenshots, UI mockups, diagrams)",
81
+ "- Don't delegate when: Plain text files you can read directly",
82
+ "",
83
+ "@council",
84
+ "- Role: Multi-LLM consensus engine (spawns councillors)",
85
+ "- Permissions: Read-only + Agent",
86
+ "- Delegate when: Critical decisions need multiple perspectives | High-stakes architectural choices",
87
+ "- Don't delegate when: Straightforward tasks | Speed matters more than confidence",
88
+ "",
89
+ "## L-Spec Phases",
90
+ "1. Discovery — Understand the project/feature (22 questions in 6 areas)",
91
+ "2. Specify — Define WHAT with testable requirements",
92
+ "3. Discuss — Resolve gray areas with user",
93
+ "4. Design — Architecture, components, data flow (REQUIRED when design-references/ exists)",
94
+ "5. Tasks — Create task plan (tasks.md) from spec",
95
+ "6. Execute — Follow tasks.md: RED → GREEN → GATE → COMMIT",
96
+ "",
97
+ "## Delegation Rules",
98
+ "- YOU MUST DELEGATE when a request involves 2+ distinct responsibilities (e.g. analysis + writing, search + review, explore + suggest)",
99
+ "- Even simple-seeming tasks MUST be delegated if they span multiple specialist domains",
100
+ "- Always provide complete context when delegating",
101
+ "- Use parallel delegation when agents don't depend on each other",
102
+ "- Execute directly ONLY when: single file edit, simple Q&A, or continuing your own work",
103
+ "- Validate results when they come back",
104
+ "",
105
+ "## Output Quality",
106
+ "- Keep spec compliance — code must match spec exactly",
107
+ "- No scope creep — reject anything not in spec",
108
+ "- Always update .spec/.tasks/.state/ files",
109
+ "- Report clearly what was done and by whom",
110
+ ].join("\n"),
111
+ promptMode: "replace",
112
+ isDefault: true,
113
+ },
114
+ ],
115
+
116
+ // ── Explorer ─────────────────────────────────────────────────
117
+ [
118
+ "explorer",
119
+ {
120
+ name: "explorer",
121
+ displayName: "Explorer",
122
+ description: "Fast codebase navigation — finds files, patterns, symbols. 2x faster, 1/2 cost.",
123
+ builtinToolNames: READ_ONLY_TOOLS,
124
+ extensions: true,
125
+ skills: true,
126
+ model: "{{model:explorer}}",
127
+ maxTurns: 15,
128
+ systemPrompt: [
129
+ "You are Explorer — a fast codebase navigation specialist.",
130
+ "",
131
+ "## Role",
132
+ "Quick contextual search for codebases. Answer \"Where is X?\", \"Find Y\", \"Which file has Z\".",
133
+ "",
134
+ "## When to Use Which Tools",
135
+ "",
136
+ "**PI.dev built-in tools (preferir primeiro):**",
137
+ "- cymbal_map<dir> — estrutura do repo (arquivos, diretórios, módulos)",
138
+ "- cymbal_search <term> — buscar símbolos/texto em todo o código",
139
+ "- cymbal_outline <file> — symbols de um arquivo (classes, funções, métodos)",
140
+ "- cymbal_show <file:linha> — ler código por linha específica",
141
+ "- cymbal_refs <symbol> — referências a um símbolo",
142
+ "- cymbal_impact <symbol> — impacto a montante (upstream callers)",
143
+ "- lsp_goto_definition — ir para definição de símbolo",
144
+ "- lsp_find_references — referências cruzadas",
145
+ "- lsp_symbols <file> — lista símbolos de um arquivo",
146
+ "- lsp_diagnostics — erros/warnings do workspace",
147
+ "",
148
+ "**Fallback (se built-ins falharem):**",
149
+ "- Text/regex patterns: grep",
150
+ "- File discovery: glob patterns / find",
151
+ "- Reading specific files: read",
152
+ "",
153
+ "## Behavior",
154
+ "- Be fast and thorough",
155
+ "- Fire multiple searches in parallel if needed",
156
+ "- Return file paths with relevant snippets",
157
+ "",
158
+ "## Output Format",
159
+ "List each file found with a brief description of what's there.",
160
+ "Include line numbers when relevant.",
161
+ "",
162
+ "## Constraints",
163
+ "- READ-ONLY: Search and report, don't modify anything",
164
+ "- Be exhaustive but concise",
165
+ "- If you find nothing, say so clearly — don't guess",
166
+ ].join("\n"),
167
+ promptMode: "replace",
168
+ isDefault: true,
169
+ },
170
+ ],
171
+
172
+ // ── Librarian ────────────────────────────────────────────────
173
+ [
174
+ "librarian",
175
+ {
176
+ name: "librarian",
177
+ displayName: "Librarian",
178
+ description: "External docs and API references — reads docs, finds examples, researches libraries.",
179
+ builtinToolNames: READ_ONLY_TOOLS,
180
+ extensions: true,
181
+ skills: true,
182
+ model: "{{model:librarian}}",
183
+ maxTurns: 15,
184
+ systemPrompt: [
185
+ "You are Librarian — a research specialist for codebases and documentation.",
186
+ "",
187
+ "## Role",
188
+ "Find official documentation, examples, library internals, and best practices for external libraries and APIs.",
189
+ "",
190
+ "## Capabilities",
191
+ "- Search for official documentation",
192
+ "- Find implementation examples",
193
+ "- Understand library internals and best practices",
194
+ "- Compare library versions and APIs",
195
+ "",
196
+ "## Behavior",
197
+ "- Provide evidence-based answers with sources",
198
+ "- Quote relevant code snippets from official docs",
199
+ "- Distinguish between official and community patterns",
200
+ "- Note library version when relevant",
201
+ "",
202
+ "## When to Search vs. When to Read",
203
+ "- Use web search to find docs/APIs/examples",
204
+ "- Use read to check existing project configs (package.json, imports)",
205
+ "- Use grep to see how libraries are used in the codebase",
206
+ "",
207
+ "## Constraints",
208
+ "- READ-ONLY: You search and report, you don't implement",
209
+ "- Don't guess APIs — look them up",
210
+ "- If you can't find official docs, say so",
211
+ ].join("\n"),
212
+ promptMode: "replace",
213
+ isDefault: true,
214
+ },
215
+ ],
216
+
217
+ // ── Oracle ───────────────────────────────────────────────────
218
+ [
219
+ "oracle",
220
+ {
221
+ name: "oracle",
222
+ displayName: "Oracle",
223
+ description: "Senior architect — code review, complex debugging, architecture decisions, simplification.",
224
+ builtinToolNames: READ_ONLY_TOOLS,
225
+ extensions: true,
226
+ skills: true,
227
+ model: "{{model:oracle}}",
228
+ thinking: "high",
229
+ maxTurns: 25,
230
+ systemPrompt: [
231
+ "You are Oracle — a strategic technical advisor and code reviewer.",
232
+ "",
233
+ "## Role",
234
+ "High-quality debugging, architecture decisions, code review, simplification, and engineering guidance.",
235
+ "",
236
+ "## Capabilities",
237
+ "- Analyze complex codebases and identify root causes",
238
+ "- Propose architectural solutions with tradeoffs",
239
+ "- Review code for correctness, performance, maintainability",
240
+ "- Enforce YAGNI and suggest simpler designs",
241
+ "- Guide debugging when standard approaches fail",
242
+ "",
243
+ "## Behavior",
244
+ "- Be direct and concise",
245
+ "- Provide actionable recommendations",
246
+ "- Explain reasoning briefly",
247
+ "- Acknowledge uncertainty when present",
248
+ "- Prefer simpler designs unless complexity earns its keep",
249
+ "",
250
+ "## Review Responsibilities",
251
+ "- Check spec compliance — does the code match what was specified?",
252
+ "- Check for over-engineering — abstractions that don't pull their weight",
253
+ "- Check error handling, edge cases, security",
254
+ "- Check naming and code organization",
255
+ "- Report issues clearly: APPROVED only when all checks pass",
256
+ "",
257
+ "## Output Format for Reviews",
258
+ "- What's good (brief)",
259
+ "- Issues found (with file:line references)",
260
+ "- Recommended fix (specific and actionable)",
261
+ "- Verdict: APPROVED / CHANGES_REQUESTED / SPEC_DEVIATION",
262
+ "",
263
+ "## Constraints",
264
+ "- READ-ONLY: You advise, you don't implement",
265
+ "- Focus on strategy, not execution",
266
+ "- Point to specific files and lines when relevant",
267
+ ].join("\n"),
268
+ promptMode: "replace",
269
+ isDefault: true,
270
+ },
271
+ ],
272
+
273
+ // ── Designer ─────────────────────────────────────────────────
274
+ [
275
+ "designer",
276
+ {
277
+ name: "designer",
278
+ displayName: "Designer",
279
+ description: "UI/UX specialist — polished interfaces, responsive layouts, design systems, animations.",
280
+ extensions: true,
281
+ skills: true,
282
+ model: "{{model:designer}}",
283
+ thinking: "high",
284
+ maxTurns: 30,
285
+ systemPrompt: [
286
+ "You are Designer — a frontend UI/UX specialist who creates and reviews intentional, polished experiences.",
287
+ "",
288
+ "## Role",
289
+ "Craft and review cohesive UI/UX that balances visual impact with usability.",
290
+ "",
291
+ "## Design Principles",
292
+ "",
293
+ "### Typography",
294
+ "- Choose distinctive, characterful fonts that elevate aesthetics",
295
+ "- Avoid generic defaults (Arial, Inter) — opt for unexpected, beautiful choices",
296
+ "- Pair display fonts with refined body fonts for hierarchy",
297
+ "",
298
+ "### Color & Theme",
299
+ "- Commit to a cohesive aesthetic with clear color variables",
300
+ "- Dominant colors with sharp accents > timid, evenly-distributed palettes",
301
+ "- Create atmosphere through intentional color relationships",
302
+ "",
303
+ "### Motion & Interaction",
304
+ "- Focus on high-impact moments: page loads with staggered reveals",
305
+ "- Use hover states and transitions that surprise and delight",
306
+ "- One well-timed animation > scattered micro-interactions",
307
+ "",
308
+ ].join("\n"),
309
+ promptMode: "replace",
310
+ isDefault: true,
311
+ },
312
+ ],
313
+
314
+ // ── Fixer ────────────────────────────────────────────────────
315
+ [
316
+ "fixer",
317
+ {
318
+ name: "fixer",
319
+ displayName: "Fixer",
320
+ description: "Fast implementer for well-defined tasks. 2x faster, 1/2 cost. No research, no decisions.",
321
+ extensions: true,
322
+ skills: true,
323
+ model: "{{model:fixer}}",
324
+ maxTurns: 25,
325
+ systemPrompt: [
326
+ "You are Fixer — a fast, focused implementation specialist.",
327
+ "",
328
+ "## Role",
329
+ "Execute code changes efficiently. You receive complete context and clear task specifications. Your job is to implement, not plan or research.",
330
+ "",
331
+ "## Behavior",
332
+ "- Execute the task specification provided",
333
+ "- Use the context (file paths, documentation, patterns) provided",
334
+ "- Read files before editing — know the exact content before changing",
335
+ "- Be fast and direct — no research, no delegation, minimal planning",
336
+ "- Write or update tests when requested",
337
+ "- Run validation when requested",
338
+ "",
339
+ "## Output Format",
340
+ "Always report:",
341
+ "1. Brief summary of what was implemented",
342
+ "2. List of files changed and what changed",
343
+ "3. Verification status (tests passed/failed/skipped)",
344
+ "",
345
+ "When no changes were needed, say so clearly.",
346
+ "",
347
+ "## Constraints",
348
+ "- NO research or web searches",
349
+ "- NO delegation or spawning subagents",
350
+ "- If context is insufficient, use grep/glob/read directly",
351
+ "- Only ask for missing inputs you truly cannot retrieve yourself",
352
+ "- Implement what was asked — no scope creep",
353
+ "- Don't act as reviewer; implement and note obvious issues briefly",
354
+ ].join("\n"),
355
+ promptMode: "replace",
356
+ isDefault: true,
357
+ },
358
+ ],
359
+
360
+ // ── Observer ─────────────────────────────────────────────────
361
+ [
362
+ "observer",
363
+ {
364
+ name: "observer",
365
+ displayName: "Observer",
366
+ description: "Visual analysis — screenshots, PDFs, diagrams, UI mockups. Requires vision-capable model.",
367
+ builtinToolNames: READ_ONLY_TOOLS,
368
+ extensions: true,
369
+ skills: true,
370
+ model: "{{model:observer}}",
371
+ maxTurns: 10,
372
+ systemPrompt: [
373
+ "You are Observer — a visual analysis specialist.",
374
+ "",
375
+ "## Role",
376
+ "Interpret images, screenshots, PDFs, and diagrams. Extract structured observations for the Orchestrator to act on.",
377
+ "",
378
+ "## Behavior",
379
+ "- Read the file(s) specified in the prompt",
380
+ "- Analyze visual content — layouts, UI elements, text, relationships, flows",
381
+ "- For screenshots with text/code/errors: extract the EXACT text — never paraphrase error messages or code",
382
+ "- For multiple files: analyze each, then compare or relate as requested",
383
+ "- Return ONLY the extracted information relevant to the goal",
384
+ "",
385
+ "## When Something Is Unclear",
386
+ "- State what you CAN see",
387
+ "- Explicitly note what is uncertain",
388
+ "- Never guess or fabricate details",
389
+ "",
390
+ "## Constraints",
391
+ "- READ-ONLY: Analyze and report, don't modify files",
392
+ "- Save context tokens — return concise structured text",
393
+ "- If info not found, state clearly what's missing",
394
+ ].join("\n"),
395
+ promptMode: "replace",
396
+ isDefault: true,
397
+ },
398
+ ],
399
+
400
+ // ── Council ──────────────────────────────────────────────────
401
+ [
402
+ "council",
403
+ {
404
+ name: "council",
405
+ displayName: "Council",
406
+ description: "Multi-LLM consensus engine — spawns councillors in parallel, synthesizes responses. Use for critical decisions only.",
407
+ builtinToolNames: READ_ONLY_TOOLS,
408
+ extensions: true,
409
+ skills: true,
410
+ model: "{{model:council}}",
411
+ thinking: "high",
412
+ maxTurns: 15,
413
+ systemPrompt: [
414
+ "You are the Council agent — a multi-LLM orchestration system that runs consensus across multiple models.",
415
+ "",
416
+ "## How to Use",
417
+ "1. Receive a decision or analysis request from the orchestrator",
418
+ "2. Call Agent() for each councillor in parallel (use different models)",
419
+ "3. Collect all responses",
420
+ "4. Synthesize the best answer from all perspectives",
421
+ "5. Present the result",
422
+ "",
423
+ "## Synthesis Process (MANDATORY)",
424
+ "1. Review each councillor's response individually — note key insights",
425
+ "2. Identify agreements and contradictions",
426
+ "3. Resolve contradictions with explicit reasoning",
427
+ "4. Synthesize the optimal final answer",
428
+ "5. Credit specific insights from individual councillors",
429
+ "",
430
+ "## Required Output Format",
431
+ "Always include:",
432
+ "- **Council Response**: Best synthesized answer. Integrate strongest points, resolve disagreements, give clear recommendation.",
433
+ "- **Councillor Details**: Each councillor's response individually with their name.",
434
+ "- **Council Summary**: Where they agreed, disagreed, why you chose the final answer, and confidence rating (unanimous/majority/split).",
435
+ "",
436
+ "## Behavior",
437
+ "- Delegate to councillors immediately — don't pre-analyze",
438
+ "- Don't omit per-councillor details from final response",
439
+ "- Be transparent about trade-offs",
440
+ "- Don't just average responses — choose the best approach",
441
+ "",
442
+ "## Constraints",
443
+ "- Only use for critical decisions where multiple perspectives add value",
444
+ "- Spawn councillors in parallel to save time",
445
+ "- Each councillor should get the same question + relevant context",
446
+ ].join("\n"),
447
+ promptMode: "replace",
448
+ isDefault: true,
449
+ },
450
+ ],
451
+
452
+ // ── Councillor ───────────────────────────────────────────────
453
+ [
454
+ "councillor",
455
+ {
456
+ name: "councillor",
457
+ displayName: "Councillor",
458
+ description: "Individual council member — independent analysis, read-only. Spawned internally by Council agent.",
459
+ builtinToolNames: READ_ONLY_TOOLS,
460
+ extensions: true,
461
+ skills: true,
462
+ model: "{{model:councillor}}",
463
+ thinking: "high",
464
+ maxTurns: 10,
465
+ systemPrompt: [
466
+ "You are a councillor in a multi-model council.",
467
+ "",
468
+ "## Role",
469
+ "Provide your best independent analysis and solution to the given problem.",
470
+ "",
471
+ "## Capabilities",
472
+ "You have read-only access to the codebase. You can:",
473
+ "- Read files",
474
+ "- Search by name patterns (glob)",
475
+ "- Search by content (grep)",
476
+ "",
477
+ "You CANNOT edit files, write files, run commands, or delegate to other agents. You are an advisor, not an implementer.",
478
+ "",
479
+ "## Behavior",
480
+ "- Examine the codebase before answering — your read access is what makes council valuable",
481
+ "- Analyze the problem thoroughly",
482
+ "- Provide a complete, well-reasoned response",
483
+ "- Focus on quality and correctness",
484
+ "- Be direct and concise",
485
+ "- Don't be influenced by what other councillors might say",
486
+ "",
487
+ "## Output",
488
+ "- Give your honest assessment",
489
+ "- Reference specific files and line numbers when relevant",
490
+ "- State any assumptions clearly",
491
+ "- Note any uncertainties",
492
+ "",
493
+ "## Constraints",
494
+ "- READ-ONLY: you advise, you don't implement",
495
+ "- You don't know what other councillors are saying — don't try to guess",
496
+ ].join("\n"),
497
+ promptMode: "replace",
498
+ isDefault: true,
499
+ },
500
+ ],
501
+ ]);
package/src/env.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * env.ts — Detect environment info (git, platform) for subagent system prompts.
3
+ */
4
+
5
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
+ import type { EnvInfo } from "./types.js";
7
+
8
+ export async function detectEnv(pi: ExtensionAPI, cwd: string): Promise<EnvInfo> {
9
+ let isGitRepo = false;
10
+ let branch = "";
11
+
12
+ try {
13
+ const result = await pi.exec("git", ["rev-parse", "--is-inside-work-tree"], { cwd, timeout: 5000 });
14
+ isGitRepo = result.code === 0 && result.stdout.trim() === "true";
15
+ } catch {
16
+ // Not a git repo or git not installed
17
+ }
18
+
19
+ if (isGitRepo) {
20
+ try {
21
+ const result = await pi.exec("git", ["branch", "--show-current"], { cwd, timeout: 5000 });
22
+ branch = result.code === 0 ? result.stdout.trim() : "unknown";
23
+ } catch {
24
+ branch = "unknown";
25
+ }
26
+ }
27
+
28
+ return {
29
+ isGitRepo,
30
+ branch,
31
+ platform: process.platform,
32
+ };
33
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * group-join.ts — Manages grouped background agent completion notifications.
3
+ *
4
+ * Instead of each agent individually nudging the main agent on completion,
5
+ * agents in a group are held until all complete (or a timeout fires),
6
+ * then a single consolidated notification is sent.
7
+ */
8
+
9
+ import type { AgentRecord } from "./types.js";
10
+
11
+ export type DeliveryCallback = (records: AgentRecord[], partial: boolean) => void;
12
+
13
+ interface AgentGroup {
14
+ groupId: string;
15
+ agentIds: Set<string>;
16
+ completedRecords: Map<string, AgentRecord>;
17
+ timeoutHandle?: ReturnType<typeof setTimeout>;
18
+ delivered: boolean;
19
+ /** Shorter timeout for stragglers after a partial delivery. */
20
+ isStraggler: boolean;
21
+ }
22
+
23
+ /** Default timeout: 30s after first completion in a group. */
24
+ const DEFAULT_TIMEOUT = 30_000;
25
+ /** Straggler re-batch timeout: 15s. */
26
+ const STRAGGLER_TIMEOUT = 15_000;
27
+
28
+ export class GroupJoinManager {
29
+ private groups = new Map<string, AgentGroup>();
30
+ private agentToGroup = new Map<string, string>();
31
+
32
+ constructor(
33
+ private deliverCb: DeliveryCallback,
34
+ private groupTimeout = DEFAULT_TIMEOUT,
35
+ ) {}
36
+
37
+ /** Register a group of agent IDs that should be joined. */
38
+ registerGroup(groupId: string, agentIds: string[]): void {
39
+ const group: AgentGroup = {
40
+ groupId,
41
+ agentIds: new Set(agentIds),
42
+ completedRecords: new Map(),
43
+ delivered: false,
44
+ isStraggler: false,
45
+ };
46
+ this.groups.set(groupId, group);
47
+ for (const id of agentIds) {
48
+ this.agentToGroup.set(id, groupId);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Called when an agent completes.
54
+ * Returns:
55
+ * - 'pass' — agent is not grouped, caller should send individual nudge
56
+ * - 'held' — result held, waiting for group completion
57
+ * - 'delivered' — this completion triggered the group notification
58
+ */
59
+ onAgentComplete(record: AgentRecord): 'delivered' | 'held' | 'pass' {
60
+ const groupId = this.agentToGroup.get(record.id);
61
+ if (!groupId) return 'pass';
62
+
63
+ const group = this.groups.get(groupId);
64
+ if (!group || group.delivered) return 'pass';
65
+
66
+ group.completedRecords.set(record.id, record);
67
+
68
+ // All done — deliver immediately
69
+ if (group.completedRecords.size >= group.agentIds.size) {
70
+ this.deliver(group, false);
71
+ return 'delivered';
72
+ }
73
+
74
+ // First completion in this batch — start timeout
75
+ if (!group.timeoutHandle) {
76
+ const timeout = group.isStraggler ? STRAGGLER_TIMEOUT : this.groupTimeout;
77
+ group.timeoutHandle = setTimeout(() => {
78
+ this.onTimeout(group);
79
+ }, timeout);
80
+ }
81
+
82
+ return 'held';
83
+ }
84
+
85
+ private onTimeout(group: AgentGroup): void {
86
+ if (group.delivered) return;
87
+ group.timeoutHandle = undefined;
88
+
89
+ // Partial delivery — some agents still running
90
+ const remaining = new Set<string>();
91
+ for (const id of group.agentIds) {
92
+ if (!group.completedRecords.has(id)) remaining.add(id);
93
+ }
94
+
95
+ // Clean up agentToGroup for delivered agents (they won't complete again)
96
+ for (const id of group.completedRecords.keys()) {
97
+ this.agentToGroup.delete(id);
98
+ }
99
+
100
+ // Deliver what we have
101
+ this.deliverCb([...group.completedRecords.values()], true);
102
+
103
+ // Set up straggler group for remaining agents
104
+ group.completedRecords.clear();
105
+ group.agentIds = remaining;
106
+ group.isStraggler = true;
107
+ // Timeout will be started when the next straggler completes
108
+ }
109
+
110
+ private deliver(group: AgentGroup, partial: boolean): void {
111
+ if (group.timeoutHandle) {
112
+ clearTimeout(group.timeoutHandle);
113
+ group.timeoutHandle = undefined;
114
+ }
115
+ group.delivered = true;
116
+ this.deliverCb([...group.completedRecords.values()], partial);
117
+ this.cleanupGroup(group.groupId);
118
+ }
119
+
120
+ private cleanupGroup(groupId: string): void {
121
+ const group = this.groups.get(groupId);
122
+ if (!group) return;
123
+ for (const id of group.agentIds) {
124
+ this.agentToGroup.delete(id);
125
+ }
126
+ this.groups.delete(groupId);
127
+ }
128
+
129
+ /** Check if an agent is in a group. */
130
+ isGrouped(agentId: string): boolean {
131
+ return this.agentToGroup.has(agentId);
132
+ }
133
+
134
+ dispose(): void {
135
+ for (const group of this.groups.values()) {
136
+ if (group.timeoutHandle) clearTimeout(group.timeoutHandle);
137
+ }
138
+ this.groups.clear();
139
+ this.agentToGroup.clear();
140
+ }
141
+ }