pi-messenger 0.14.0 → 0.15.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +81 -6
  3. package/config-overlay.ts +11 -12
  4. package/config.ts +11 -5
  5. package/crew/agents/crew-worker.md +3 -3
  6. package/crew/agents.ts +39 -22
  7. package/crew/handlers/coordination.ts +72 -15
  8. package/crew/handlers/plan.ts +104 -38
  9. package/crew/handlers/review.ts +20 -11
  10. package/crew/handlers/revise.ts +15 -13
  11. package/crew/handlers/status.ts +10 -10
  12. package/crew/handlers/sync.ts +11 -10
  13. package/crew/handlers/task.ts +173 -26
  14. package/crew/handlers/team.ts +235 -0
  15. package/crew/handlers/work.ts +71 -29
  16. package/crew/index.ts +44 -29
  17. package/crew/live-progress.ts +1 -1
  18. package/crew/lobby.ts +18 -15
  19. package/crew/prompt.ts +115 -19
  20. package/crew/registry.ts +1 -1
  21. package/crew/spawn.ts +21 -16
  22. package/crew/state-autonomous.ts +1 -1
  23. package/crew/state-planning.ts +1 -1
  24. package/crew/state.ts +2 -2
  25. package/crew/store.ts +39 -10
  26. package/crew/task-actions.ts +13 -5
  27. package/crew/team/store.ts +479 -0
  28. package/crew/team/subagent-roles.ts +87 -0
  29. package/crew/team/types.ts +50 -0
  30. package/crew/types.ts +25 -5
  31. package/crew/utils/artifacts.ts +11 -1
  32. package/crew/utils/config.ts +1 -1
  33. package/crew/utils/discover.ts +1 -1
  34. package/crew/utils/progress.ts +73 -6
  35. package/crew/utils/risk-labels.ts +7 -0
  36. package/crew/utils/task-format.ts +14 -0
  37. package/crew/utils/team-roles.ts +28 -0
  38. package/feed.ts +52 -11
  39. package/handlers.ts +34 -30
  40. package/index.ts +183 -114
  41. package/install.mjs +30 -2
  42. package/overlay-actions.ts +12 -12
  43. package/overlay-coordinator.ts +1 -1
  44. package/overlay-render.ts +54 -30
  45. package/overlay.ts +76 -43
  46. package/package.json +14 -3
  47. package/skills/pi-messenger-crew/SKILL.md +38 -4
  48. package/store.ts +4 -4
@@ -7,14 +7,14 @@
7
7
 
8
8
  import * as fs from "node:fs";
9
9
  import * as path from "node:path";
10
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
11
- import type { CrewParams } from "../types.js";
12
- import { result } from "../utils/result.js";
13
- import { spawnAgents } from "../agents.js";
14
- import { discoverCrewAgents, discoverCrewSkills, type CrewSkillInfo } from "../utils/discover.js";
15
- import { loadCrewConfig } from "../utils/config.js";
16
- import { parseVerdict, type ParsedReview } from "../utils/verdict.js";
17
- import { logFeedEvent } from "../../feed.js";
10
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
11
+ import type { CrewParams } from "../types.ts";
12
+ import { result } from "../utils/result.ts";
13
+ import { spawnAgents } from "../agents.ts";
14
+ import { discoverCrewAgents, discoverCrewSkills, type CrewSkillInfo } from "../utils/discover.ts";
15
+ import { loadCrewConfig } from "../utils/config.ts";
16
+ import { parseVerdict, type ParsedReview } from "../utils/verdict.ts";
17
+ import { logFeedEvent } from "../../feed.ts";
18
18
  import {
19
19
  finishPlanningRun,
20
20
  isPlanningCancelled,
@@ -24,9 +24,12 @@ import {
24
24
  setPlanningPhase,
25
25
  startPlanningRun,
26
26
  type PlanningPhase,
27
- } from "../state.js";
28
- import { getLiveWorkers } from "../live-progress.js";
29
- import * as store from "../store.js";
27
+ } from "../state.ts";
28
+ import { getLiveWorkers } from "../live-progress.ts";
29
+ import * as store from "../store.ts";
30
+ import * as teamStore from "../team/store.ts";
31
+ import type { TeamProfile, TeamRoleDefinition } from "../team/types.ts";
32
+ import { taskMetadataMarkers } from "../utils/task-format.ts";
30
33
 
31
34
  const PRD_PATTERNS = [
32
35
  "PRD.md", "prd.md",
@@ -202,8 +205,9 @@ export async function execute(
202
205
  ctx: ExtensionContext,
203
206
  agentName: string,
204
207
  onProgress?: () => void,
208
+ sessionModel?: string,
205
209
  ) {
206
- const cwd = ctx.cwd ?? process.cwd();
210
+ const cwd = ctx.cwd;
207
211
  const { prd, prompt } = params;
208
212
  const reportProgress = () => onProgress?.();
209
213
  resetPlanningCancellation();
@@ -299,6 +303,10 @@ export async function execute(
299
303
  const maxPasses = Math.max(1, config.planning.maxPasses);
300
304
  const hasReviewer = availableAgents.some(a => a.name === "crew-reviewer");
301
305
  const skills = discoverCrewSkills(cwd);
306
+ const activeTeam = teamStore.getActiveTeam(cwd);
307
+ const activeProfile = activeTeam ? teamStore.loadActiveProfile(cwd) : null;
308
+ const teamRoles = activeTeam ? teamStore.resolveRoles(cwd) : {};
309
+ const approvalLabels = activeTeam ? teamStore.activeApprovalLabels(cwd) : [];
302
310
 
303
311
  const existingProgress = readProgressForPrompt(cwd);
304
312
 
@@ -328,13 +336,13 @@ export async function execute(
328
336
  notify(ctx, `Planning pass ${pass}/${maxPasses} in progress`, "info");
329
337
 
330
338
  const plannerPrompt = pass === 1
331
- ? buildFirstPassPrompt(prdPath, prdContent, existingProgress, isPromptBased, skills)
332
- : buildRefinementPrompt(prdPath, prdContent, readProgressForPrompt(cwd), isPromptBased, skills);
339
+ ? buildFirstPassPrompt(prdPath, prdContent, existingProgress, isPromptBased, skills, activeTeam?.name, activeProfile, teamRoles, approvalLabels)
340
+ : buildRefinementPrompt(prdPath, prdContent, readProgressForPrompt(cwd), isPromptBased, skills, activeTeam?.name, activeProfile, teamRoles, approvalLabels);
333
341
 
334
342
  const [plannerResult] = await spawnAgents([{
335
343
  agent: PLANNER_AGENT,
336
344
  task: plannerPrompt,
337
- modelOverride: config.models?.planner,
345
+ modelOverride: config.models?.planner ?? sessionModel,
338
346
  taskId: "__planner__",
339
347
  }], cwd);
340
348
 
@@ -387,7 +395,7 @@ export async function execute(
387
395
  const [reviewResult] = await spawnAgents([{
388
396
  agent: "crew-reviewer",
389
397
  task: reviewPrompt,
390
- modelOverride: config.models?.reviewer,
398
+ modelOverride: config.models?.reviewer ?? sessionModel,
391
399
  taskId: "__reviewer__",
392
400
  }], cwd);
393
401
 
@@ -420,7 +428,7 @@ export async function execute(
420
428
  const outlineContent = sections
421
429
  ? `# Planning Outline\n\n## 1. PRD Understanding Summary\n${sections.prdSummary}\n\n## 2. Relevant Code/Docs/Resources Reviewed\n${sections.resourcesReviewed}\n\n## 3. Sequential Implementation Steps\n${sections.sequentialSteps}\n\n## 4. Parallelized Task Graph\n${sections.parallelTaskGraph}\n`
422
430
  : `# Planning Outline\n\nStructured sections were not detected. Full planner output is included below.\n\n${lastPlannerOutput}`;
423
- try { setPlanningOutline(cwd, outlineContent); } catch {}
431
+ setPlanningOutline(cwd, outlineContent);
424
432
 
425
433
  if (tasks.length === 0) {
426
434
  store.setPlanSpec(cwd, lastPlannerOutput);
@@ -436,13 +444,21 @@ export async function execute(
436
444
  });
437
445
  }
438
446
 
439
- const createdTasks: { id: string; title: string; dependsOn: string[]; skills?: string[] }[] = [];
447
+ const createdTasks: { id: string; title: string; dependsOn: string[] }[] = [];
440
448
  const titleToId = new Map<string, string>();
441
449
 
442
450
  for (let i = 0; i < tasks.length; i++) {
443
451
  const task = tasks[i];
444
- const created = store.createTask(cwd, task.title, task.description);
445
- createdTasks.push({ id: created.id, title: task.title, dependsOn: task.dependsOn, skills: task.skills });
452
+ const role = teamStore.canonicalRoleForTask(cwd, task.role);
453
+ const riskLabels = teamStore.normalizeRiskLabels(task.riskLabels);
454
+ const approval = teamStore.approvalForTask(cwd, role, riskLabels);
455
+ const created = store.createTask(cwd, task.title, task.description, undefined, {
456
+ ...(role ? { role } : {}),
457
+ ...(riskLabels && riskLabels.length > 0 ? { risk_labels: riskLabels } : {}),
458
+ ...(approval ? { approval } : {}),
459
+ ...(task.skills && task.skills.length > 0 ? { skills: task.skills } : {}),
460
+ });
461
+ createdTasks.push({ id: created.id, title: task.title, dependsOn: task.dependsOn });
446
462
  titleToId.set(task.title.toLowerCase(), created.id);
447
463
  titleToId.set(`task ${i + 1}`, created.id);
448
464
  titleToId.set(`task-${i + 1}`, created.id);
@@ -462,9 +478,6 @@ export async function execute(
462
478
  }
463
479
  }
464
480
 
465
- if (task.skills && task.skills.length > 0) {
466
- store.updateTask(cwd, task.id, { skills: task.skills });
467
- }
468
481
  }
469
482
 
470
483
  pruneTransitiveDeps(cwd, createdTasks.map(t => t.id));
@@ -474,7 +487,8 @@ export async function execute(
474
487
  const taskList = createdTasks.map(t => {
475
488
  const task = store.getTask(cwd, t.id);
476
489
  const deps = task?.depends_on.length ? ` → deps: ${task.depends_on.join(", ")}` : "";
477
- return ` - ${t.id}: ${t.title}${deps}`;
490
+ const markers = task ? taskMetadataMarkers(task) : "";
491
+ return ` - ${t.id}: ${t.title}${markers}${deps}`;
478
492
  }).join("\n");
479
493
 
480
494
  const passLabel = passesCompleted === 1 ? "pass" : "passes";
@@ -503,12 +517,20 @@ export async function execute(
503
517
 
504
518
  const successLabel = isPromptBased ? `"${runLabel}"` : `**${prdPath}**`;
505
519
  const shouldAutoWork = params.autoWork !== false;
506
- const nextSteps = shouldAutoWork
507
- ? `Workers will start automatically.`
508
- : `**Next steps:**
520
+ let nextSteps = `**Next steps:**
509
521
  - Review tasks: \`pi_messenger({ action: "task.list" })\`
510
522
  - Start work: \`pi_messenger({ action: "work" })\`
511
523
  - Autonomous: \`pi_messenger({ action: "work", autonomous: true })\``;
524
+ if (shouldAutoWork) {
525
+ const ready = store.getReadyTasks(cwd, { advisory: config.dependencies === "advisory" });
526
+ const startable = ready.filter(t => !teamStore.taskNeedsApproval(t));
527
+ const needsApproval = ready.filter(teamStore.taskNeedsApproval);
528
+ nextSteps = startable.length > 0
529
+ ? `Workers will start automatically.`
530
+ : needsApproval.length > 0
531
+ ? `Ready tasks need lead approval before workers can start:\n${needsApproval.map(t => `- \`pi_messenger({ action: "task.approve", id: "${t.id}" })\` — ${t.title}`).join("\n")}`
532
+ : `No tasks are startable yet. Review dependencies with \`pi_messenger({ action: "task.ready" })\`.`;
533
+ }
512
534
 
513
535
  const text = `✅ Plan created from ${successLabel}
514
536
 
@@ -558,26 +580,60 @@ ${lines.join("\n")}
558
580
  `;
559
581
  }
560
582
 
561
- function tasksJsonFormatHint(skills: CrewSkillInfo[]): string {
562
- return skills.length > 0
563
- ? "title, description, dependsOn, and optionally skills (array of skill names from the Available Skills list that are relevant to the task)"
564
- : "title, description, and dependsOn";
583
+ function formatTeamForPlanner(teamName: string | undefined, profile: TeamProfile | null, roles: Record<string, TeamRoleDefinition>, approvalLabels: string[]): string {
584
+ if (!teamName) return "";
585
+ const roleLines = Object.values(roles)
586
+ .sort((a, b) => a.name.localeCompare(b.name))
587
+ .map(role => ` ${role.name}${role.description ? ` — ${role.description}` : ""}${role.model ? ` (model: ${role.model})` : ""}`);
588
+ const approvalLine = approvalLabels.length > 0
589
+ ? `\nHigh-risk labels requiring lead approval for editing roles: ${approvalLabels.join(", ")}\n`
590
+ : "";
591
+
592
+ return `
593
+ ## Active Team
594
+
595
+ Team: ${teamName}${profile?.name ? `\nProfile: ${profile.name}` : ""}
596
+
597
+ Available roles for task assignment:
598
+ ${roleLines.join("\n")}${approvalLine}
599
+ These are Team role labels and prompt context; Crew still runs tasks through Crew agents and does not launch pi-subagents.
600
+ When creating tasks, include optional \`role\` and \`riskLabels\` fields where useful. Use non-editing roles such as scout, researcher, reviewer, planner, oracle, and context-builder for investigation, planning, decision, or review tasks.
601
+
602
+ `;
603
+ }
604
+
605
+ function tasksJsonFormatHint(skills: CrewSkillInfo[], teamActive: boolean): string {
606
+ const fields = ["title", "description", "dependsOn"];
607
+ if (skills.length > 0) fields.push("optionally skills (array of skill names from the Available Skills list that are relevant to the task)");
608
+ if (teamActive) fields.push("role", "riskLabels (array of high-risk labels such as auth or api-contract)");
609
+ return `${fields.slice(0, -1).join(", ")}${fields.length > 1 ? ", and " : ""}${fields[fields.length - 1]}`;
565
610
  }
566
611
 
567
- function buildFirstPassPrompt(prdPath: string, prdContent: string, existingProgress: string, isPromptBased: boolean, skills: CrewSkillInfo[]): string {
612
+ function buildFirstPassPrompt(
613
+ prdPath: string,
614
+ prdContent: string,
615
+ existingProgress: string,
616
+ isPromptBased: boolean,
617
+ skills: CrewSkillInfo[],
618
+ teamName?: string,
619
+ profile: TeamProfile | null = null,
620
+ roles: Record<string, TeamRoleDefinition> = {},
621
+ approvalLabels: string[] = [],
622
+ ): string {
568
623
  const specType = isPromptBased ? "request" : "PRD";
569
624
  const specLabel = isPromptBased ? "Request" : `PRD: ${prdPath}`;
570
625
  const progressSection = existingProgress
571
626
  ? `\n## Previous Planning Context\n${existingProgress}\n`
572
627
  : "";
573
628
  const skillsSection = formatSkillsForPlanner(skills);
629
+ const teamSection = formatTeamForPlanner(teamName, profile, roles, approvalLabels);
574
630
 
575
631
  return `Create a task breakdown for implementing this ${specType}.
576
632
 
577
633
  ## ${specLabel}
578
634
 
579
635
  ${prdContent}
580
- ${progressSection}${skillsSection}
636
+ ${progressSection}${skillsSection}${teamSection}
581
637
  You must follow this sequence strictly:
582
638
  1) Understand the ${specType}
583
639
  2) Review relevant code/docs/reference resources
@@ -592,7 +648,7 @@ Return output in this exact section order and headings:
592
648
 
593
649
  In section 4, include both:
594
650
  - markdown task breakdown
595
- - a \`tasks-json\` fenced block with task objects containing ${tasksJsonFormatHint(skills)}.`;
651
+ - a \`tasks-json\` fenced block with task objects containing ${tasksJsonFormatHint(skills, !!teamName)}.`;
596
652
  }
597
653
 
598
654
  function buildRefinementPrompt(
@@ -601,14 +657,19 @@ function buildRefinementPrompt(
601
657
  progressFileContent: string,
602
658
  isPromptBased: boolean,
603
659
  skills: CrewSkillInfo[],
660
+ teamName?: string,
661
+ profile: TeamProfile | null = null,
662
+ roles: Record<string, TeamRoleDefinition> = {},
663
+ approvalLabels: string[] = [],
604
664
  ): string {
605
665
  const specLabel = isPromptBased ? "Request" : `PRD: ${prdPath}`;
606
666
  const skillsSection = formatSkillsForPlanner(skills);
667
+ const teamSection = formatTeamForPlanner(teamName, profile, roles, approvalLabels);
607
668
  return `Refine your task breakdown based on review feedback.
608
669
 
609
670
  ## ${specLabel}
610
671
  ${prdContent}
611
- ${skillsSection}
672
+ ${skillsSection}${teamSection}
612
673
  ## Planning Progress
613
674
  ${progressFileContent}
614
675
 
@@ -624,7 +685,7 @@ Return output in this exact section order and headings:
624
685
 
625
686
  In section 4, include both:
626
687
  - markdown task breakdown
627
- - a \`tasks-json\` fenced block with task objects containing ${tasksJsonFormatHint(skills)}.`;
688
+ - a \`tasks-json\` fenced block with task objects containing ${tasksJsonFormatHint(skills, !!teamName)}.`;
628
689
  }
629
690
 
630
691
  function buildPlanReviewPrompt(
@@ -681,6 +742,8 @@ interface ParsedTask {
681
742
  description: string;
682
743
  dependsOn: string[];
683
744
  skills?: string[];
745
+ role?: string;
746
+ riskLabels?: string[];
684
747
  }
685
748
 
686
749
  function extractPlanSections(output: string): PlanSections | null {
@@ -726,12 +789,15 @@ function parseJsonTaskBlock(output: string): ParsedTask[] | null {
726
789
  const parsed = JSON.parse(match[1]);
727
790
  if (!Array.isArray(parsed)) return null;
728
791
  const tasks = parsed
729
- .filter((t: Record<string, unknown>) => typeof t.title === "string" && t.title.trim().length > 0)
730
- .map((t: Record<string, unknown>) => ({
792
+ .filter((t: unknown): t is Record<string, unknown> => !!t && typeof t === "object" && !Array.isArray(t))
793
+ .filter(t => typeof t.title === "string" && t.title.trim().length > 0)
794
+ .map(t => ({
731
795
  title: (t.title as string).trim(),
732
796
  description: typeof t.description === "string" ? t.description : "",
733
797
  dependsOn: Array.isArray(t.dependsOn) ? t.dependsOn.filter((d: unknown) => typeof d === "string") : [],
734
798
  skills: Array.isArray(t.skills) ? t.skills.filter((s: unknown) => typeof s === "string") : undefined,
799
+ role: typeof t.role === "string" && t.role.trim().length > 0 ? t.role.trim() : undefined,
800
+ riskLabels: Array.isArray(t.riskLabels) ? t.riskLabels.filter((s: unknown) => typeof s === "string") : undefined,
735
801
  }));
736
802
  return tasks.length > 0 ? tasks : null;
737
803
  } catch {
@@ -6,23 +6,24 @@
6
6
  */
7
7
 
8
8
  import { execSync } from "node:child_process";
9
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
10
- import type { CrewParams } from "../types.js";
11
- import { result } from "../utils/result.js";
12
- import { spawnAgents } from "../agents.js";
13
- import { discoverCrewAgents } from "../utils/discover.js";
14
- import { loadCrewConfig } from "../utils/config.js";
15
- import { parseVerdict, type ParsedReview } from "../utils/verdict.js";
16
- import * as store from "../store.js";
9
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
10
+ import type { CrewParams } from "../types.ts";
11
+ import { result } from "../utils/result.ts";
12
+ import { spawnAgents } from "../agents.ts";
13
+ import { discoverCrewAgents } from "../utils/discover.ts";
14
+ import { loadCrewConfig } from "../utils/config.ts";
15
+ import { parseVerdict, type ParsedReview } from "../utils/verdict.ts";
16
+ import * as store from "../store.ts";
17
17
 
18
18
  export async function execute(
19
19
  params: CrewParams,
20
- ctx: ExtensionContext
20
+ ctx: ExtensionContext,
21
+ sessionModel?: string,
21
22
  ) {
22
- const cwd = ctx.cwd ?? process.cwd();
23
+ const cwd = ctx.cwd;
23
24
  const { target, type } = params;
24
25
  const config = loadCrewConfig(store.getCrewDir(cwd));
25
- const reviewerModel = config.models?.reviewer;
26
+ const reviewerModel = config.models?.reviewer ?? sessionModel;
26
27
 
27
28
  if (!target) {
28
29
  return result("Error: target (task ID) required for review action.\n\nUsage: pi_messenger({ action: \"review\", target: \"task-1\" })", {
@@ -41,6 +42,14 @@ export async function execute(
41
42
  });
42
43
  }
43
44
 
45
+ if (type && type !== "impl" && type !== "plan") {
46
+ return result("Error: review type must be 'impl' or 'plan'.", {
47
+ mode: "review",
48
+ error: "invalid_type",
49
+ type,
50
+ });
51
+ }
52
+
44
53
  // Determine review type: "impl" for task, "plan" for plan review
45
54
  const reviewType = type ?? (target.startsWith("task-") ? "impl" : "plan");
46
55
 
@@ -1,14 +1,14 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { MessengerState } from "../../lib.js";
4
- import type { CrewParams, Task } from "../types.js";
5
- import { result } from "../utils/result.js";
6
- import * as store from "../store.js";
7
- import { logFeedEvent } from "../../feed.js";
8
- import { spawnAgents } from "../agents.js";
9
- import { getLiveWorkers } from "../live-progress.js";
10
- import { isAutonomousForCwd, isPlanningForCwd } from "../state.js";
11
- import { loadCrewConfig } from "../utils/config.js";
3
+ import type { MessengerState } from "../../lib.ts";
4
+ import type { CrewParams, Task } from "../types.ts";
5
+ import { result } from "../utils/result.ts";
6
+ import * as store from "../store.ts";
7
+ import { logFeedEvent } from "../../feed.ts";
8
+ import { spawnAgents } from "../agents.ts";
9
+ import { getLiveWorkers } from "../live-progress.ts";
10
+ import { isAutonomousForCwd, isPlanningForCwd } from "../state.ts";
11
+ import { loadCrewConfig } from "../utils/config.ts";
12
12
 
13
13
  export interface ReviseResult {
14
14
  success: boolean;
@@ -24,6 +24,7 @@ export async function executeRevise(
24
24
  taskId: string,
25
25
  prompt: string | undefined,
26
26
  agentName: string,
27
+ sessionModel?: string,
27
28
  ): Promise<ReviseResult> {
28
29
  const task = store.getTask(cwd, taskId);
29
30
  if (!task) return { success: false, message: `Task ${taskId} not found` };
@@ -50,7 +51,7 @@ export async function executeRevise(
50
51
  agent: "crew-planner",
51
52
  task: revisePrompt,
52
53
  taskId: "__reviser__",
53
- modelOverride: config.models?.planner,
54
+ modelOverride: config.models?.planner ?? sessionModel,
54
55
  }], cwd);
55
56
 
56
57
  if (agentResult.exitCode !== 0) {
@@ -84,7 +85,7 @@ export async function taskRevise(cwd: string, params: CrewParams, state: Messeng
84
85
  const { id, prompt } = params;
85
86
  if (!id) return result("Error: id required for task.revise", { mode: "task.revise", error: "missing_id" });
86
87
 
87
- const r = await executeRevise(cwd, id, prompt ?? undefined, state.agentName || "unknown");
88
+ const r = await executeRevise(cwd, id, prompt ?? undefined, state.agentName || "unknown", state.model || undefined);
88
89
  if (!r.success) {
89
90
  return result(`Error: ${r.message}`, { mode: "task.revise", error: "revision_failed", id });
90
91
  }
@@ -100,6 +101,7 @@ export async function executeReviseTree(
100
101
  taskId: string,
101
102
  prompt: string | undefined,
102
103
  agentName: string,
104
+ sessionModel?: string,
103
105
  ): Promise<ReviseResult> {
104
106
  const target = store.getTask(cwd, taskId);
105
107
  if (!target) return { success: false, message: `Task ${taskId} not found` };
@@ -134,7 +136,7 @@ export async function executeReviseTree(
134
136
  agent: "crew-planner",
135
137
  task: revisePrompt,
136
138
  taskId: "__reviser__",
137
- modelOverride: config.models?.planner,
139
+ modelOverride: config.models?.planner ?? sessionModel,
138
140
  }], cwd);
139
141
 
140
142
  if (agentResult.exitCode !== 0) {
@@ -227,7 +229,7 @@ export async function taskReviseTree(cwd: string, params: CrewParams, state: Mes
227
229
  const { id, prompt } = params;
228
230
  if (!id) return result("Error: id required for task.revise-tree", { mode: "task.revise-tree", error: "missing_id" });
229
231
 
230
- const r = await executeReviseTree(cwd, id, prompt ?? undefined, state.agentName || "unknown");
232
+ const r = await executeReviseTree(cwd, id, prompt ?? undefined, state.agentName || "unknown", state.model || undefined);
231
233
  if (!r.success) {
232
234
  return result(`Error: ${r.message}`, { mode: "task.revise-tree", error: "revision_failed", id });
233
235
  }
@@ -4,20 +4,20 @@
4
4
  * Shows plan progress and task status.
5
5
  */
6
6
 
7
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
8
- import { result } from "../utils/result.js";
9
- import { discoverCrewAgents } from "../utils/discover.js";
10
- import { uninstallAgents } from "../utils/install.js";
11
- import { loadCrewConfig } from "../utils/config.js";
12
- import { formatDuration } from "../../lib.js";
13
- import * as store from "../store.js";
14
- import { autonomousState, getPlanningUpdateAgeMs, isAutonomousForCwd, isPlanningForCwd, isPlanningStalled, planningState, PLANNING_STALE_TIMEOUT_MS } from "../state.js";
7
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
8
+ import { result } from "../utils/result.ts";
9
+ import { discoverCrewAgents } from "../utils/discover.ts";
10
+ import { uninstallAgents } from "../utils/install.ts";
11
+ import { loadCrewConfig } from "../utils/config.ts";
12
+ import { formatDuration } from "../../lib.ts";
13
+ import * as store from "../store.ts";
14
+ import { autonomousState, getPlanningUpdateAgeMs, isAutonomousForCwd, isPlanningForCwd, isPlanningStalled, planningState, PLANNING_STALE_TIMEOUT_MS } from "../state.ts";
15
15
 
16
16
  /**
17
17
  * Execute status action - shows plan progress.
18
18
  */
19
19
  export async function execute(ctx: ExtensionContext) {
20
- const cwd = ctx.cwd ?? process.cwd();
20
+ const cwd = ctx.cwd;
21
21
  const plan = store.getPlan(cwd);
22
22
 
23
23
  if (!plan) {
@@ -197,7 +197,7 @@ export async function executeCrew(
197
197
  op: string,
198
198
  ctx: ExtensionContext
199
199
  ) {
200
- const cwd = ctx.cwd ?? process.cwd();
200
+ const cwd = ctx.cwd;
201
201
 
202
202
  switch (op) {
203
203
  case "status": {
@@ -5,19 +5,20 @@
5
5
  * Works with current plan's tasks.
6
6
  */
7
7
 
8
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
9
- import type { CrewParams } from "../types.js";
10
- import { result } from "../utils/result.js";
11
- import { spawnAgents } from "../agents.js";
12
- import { discoverCrewAgents } from "../utils/discover.js";
13
- import { loadCrewConfig } from "../utils/config.js";
14
- import * as store from "../store.js";
8
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
9
+ import type { CrewParams } from "../types.ts";
10
+ import { result } from "../utils/result.ts";
11
+ import { spawnAgents } from "../agents.ts";
12
+ import { discoverCrewAgents } from "../utils/discover.ts";
13
+ import { loadCrewConfig } from "../utils/config.ts";
14
+ import * as store from "../store.ts";
15
15
 
16
16
  export async function execute(
17
17
  params: CrewParams,
18
- ctx: ExtensionContext
18
+ ctx: ExtensionContext,
19
+ sessionModel?: string,
19
20
  ) {
20
- const cwd = ctx.cwd ?? process.cwd();
21
+ const cwd = ctx.cwd;
21
22
  const { target } = params;
22
23
 
23
24
  if (!target) {
@@ -126,7 +127,7 @@ Follow the output format in your instructions.`;
126
127
  const [syncResult] = await spawnAgents([{
127
128
  agent: "crew-plan-sync",
128
129
  task: prompt,
129
- modelOverride: config.models?.analyst,
130
+ modelOverride: config.models?.analyst ?? sessionModel,
130
131
  }], cwd);
131
132
 
132
133
  if (syncResult.exitCode !== 0) {