@phi-code-admin/phi-code 0.62.2 → 0.63.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.
@@ -2,21 +2,18 @@
2
2
  * Orchestrator Extension - Full-cycle project planning and execution
3
3
  *
4
4
  * WORKFLOW (single command):
5
- * /plan <description> → LLM analyzes orchestrate tool spec + todo auto-execute → progress
6
- * Everything happens in one shot. No manual steps.
5
+ * /plan <description> → 5 sequential agent phaseseach with its own model
6
+ *
7
+ * The orchestrator uses event-driven phase chaining:
8
+ * 1. Send phase 1 message with model A
9
+ * 2. Detect when agent goes idle (output event + polling)
10
+ * 3. Switch to model B, send phase 2
11
+ * 4. Repeat until all 5 phases complete
7
12
  *
8
13
  * Commands:
9
- * /plan — Full workflow: plan + execute with sub-agents
10
- * /run — Re-execute an existing plan (e.g. after fixes)
14
+ * /plan — Full workflow: plan + execute with agents
15
+ * /run — Re-execute an existing plan
11
16
  * /plans — List plans and their execution status
12
- *
13
- * Sub-agent execution:
14
- * Each task spawns a separate `phi` CLI process with:
15
- * - Its own system prompt (from the agent .md file)
16
- * - Its own model (from routing.json)
17
- * - Its own context (isolated, no shared history)
18
- * - Its own tool access (read, write, edit, bash, etc.)
19
- * Results are collected into progress.md and reported to the user.
20
17
  */
21
18
 
22
19
  import { Type } from "@sinclair/typebox";
@@ -451,10 +448,141 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
451
448
  },
452
449
  });
453
450
 
451
+ // ─── Orchestration State ─────────────────────────────────────────
452
+
453
+ interface OrchestratorPhase {
454
+ key: string;
455
+ label: string;
456
+ model: string;
457
+ fallback: string;
458
+ instruction: string;
459
+ }
460
+
461
+ let phaseQueue: OrchestratorPhase[] = [];
462
+ let orchestrationActive = false;
463
+ let idlePollTimer: ReturnType<typeof setInterval> | null = null;
464
+
465
+ /**
466
+ * Load routing config and build phase queue with model assignments.
467
+ */
468
+ function buildPhases(description: string): OrchestratorPhase[] {
469
+ // Read routing.json for model assignments
470
+ const routingPath = join(homedir(), ".phi", "agent", "routing.json");
471
+ let routing: any = { routes: {}, default: { model: "default" } };
472
+ try {
473
+ routing = JSON.parse(readFileSync(routingPath, "utf-8"));
474
+ } catch { /* no routing config */ }
475
+
476
+ function getModel(routeKey: string): { preferred: string; fallback: string } {
477
+ const route = routing.routes?.[routeKey];
478
+ return {
479
+ preferred: route?.preferredModel || routing.default?.model || "default",
480
+ fallback: route?.fallback || routing.default?.model || "default",
481
+ };
482
+ }
483
+
484
+ const explore = getModel("explore");
485
+ const plan = getModel("plan");
486
+ const code = getModel("code");
487
+ const test = getModel("test");
488
+ const review = getModel("review");
489
+
490
+ return [
491
+ {
492
+ key: "explore", label: "🔍 Phase 1 — EXPLORE", model: explore.preferred, fallback: explore.fallback,
493
+ instruction: `Analyze the project requirements and existing codebase. Identify what exists, what's needed, and any constraints.\n\n**Project:** ${description}\n\nList files, read key ones, check dependencies. Return a structured summary.`,
494
+ },
495
+ {
496
+ key: "plan", label: "📐 Phase 2 — PLAN", model: plan.preferred, fallback: plan.fallback,
497
+ instruction: `Design the architecture for this project. Define file structure, tech choices, and implementation approach.\n\n**Project:** ${description}\n\nBe specific: list every file to create with its purpose.`,
498
+ },
499
+ {
500
+ key: "code", label: "💻 Phase 3 — CODE", model: code.preferred, fallback: code.fallback,
501
+ instruction: `Implement the COMPLETE project. Create ALL files with production-quality code.\n\n**Project:** ${description}\n\n**Rules:**\n- Create every file needed\n- No placeholders, no TODOs, no stubs\n- Every function must be fully implemented\n- Follow the architecture from the previous planning phase`,
502
+ },
503
+ {
504
+ key: "test", label: "🧪 Phase 4 — TEST", model: test.preferred, fallback: test.fallback,
505
+ instruction: `Test the implementation. Run the code, check for errors, verify it works.\n\n**Project:** ${description}\n\nFix any errors you find. Ensure the project runs correctly.`,
506
+ },
507
+ {
508
+ key: "review", label: "🔍 Phase 5 — REVIEW", model: review.preferred, fallback: review.fallback,
509
+ instruction: `Review the code quality, security, and performance. Fix any issues.\n\n**Project:** ${description}\n\nCheck: error handling, edge cases, code style, documentation.`,
510
+ },
511
+ ];
512
+ }
513
+
514
+ /**
515
+ * Switch model for the current phase.
516
+ * Tries preferred model first, then fallback.
517
+ */
518
+ async function switchModelForPhase(phase: OrchestratorPhase, ctx: any): Promise<string> {
519
+ const available = ctx.modelRegistry?.getAvailable?.() || [];
520
+ const preferred = available.find((m: any) => m.id === phase.model);
521
+ const fallback = available.find((m: any) => m.id === phase.fallback);
522
+ const target = preferred || fallback;
523
+
524
+ if (target && target.id !== ctx.model?.id) {
525
+ const switched = await pi.setModel(target);
526
+ if (switched) return target.id;
527
+ }
528
+ return ctx.model?.id || phase.model;
529
+ }
530
+
531
+ /**
532
+ * Send the next phase in the queue.
533
+ * Called after the agent goes idle (previous phase complete).
534
+ */
535
+ function sendNextPhase(ctx: any) {
536
+ if (phaseQueue.length === 0) {
537
+ // All phases done
538
+ orchestrationActive = false;
539
+ ctx.ui.notify(`\n✅ **All 5 phases complete!**`, "info");
540
+ return;
541
+ }
542
+
543
+ const phase = phaseQueue.shift()!;
544
+
545
+ // Switch model and send
546
+ switchModelForPhase(phase, ctx).then((modelId) => {
547
+ ctx.ui.notify(`\n${phase.label} → \`${modelId}\``, "info");
548
+ setTimeout(() => pi.sendUserMessage(phase.instruction), 200);
549
+ });
550
+ }
551
+
552
+ // ─── Output Event — Phase Chaining ───────────────────────────────
553
+
554
+ pi.on("output", async (_event, ctx) => {
555
+ if (!orchestrationActive || phaseQueue.length === 0) return;
556
+
557
+ // Debounce: clear any existing poll timer
558
+ if (idlePollTimer) {
559
+ clearInterval(idlePollTimer);
560
+ idlePollTimer = null;
561
+ }
562
+
563
+ // Poll for idle state (agent finishes tool calls + response)
564
+ let attempts = 0;
565
+ idlePollTimer = setInterval(() => {
566
+ attempts++;
567
+ if (attempts > 120) { // 60 seconds max
568
+ clearInterval(idlePollTimer!);
569
+ idlePollTimer = null;
570
+ ctx.ui.notify("⚠️ Orchestrator timeout — phase took too long.", "warning");
571
+ orchestrationActive = false;
572
+ return;
573
+ }
574
+ if (ctx.isIdle()) {
575
+ clearInterval(idlePollTimer!);
576
+ idlePollTimer = null;
577
+ sendNextPhase(ctx);
578
+ }
579
+ }, 500);
580
+ });
581
+
454
582
  // ─── /plan Command — Full workflow ───────────────────────────────
455
583
 
456
584
  pi.registerCommand("plan", {
457
- description: "Plan AND execute a project with agents describe what to build",
585
+ description: "Plan AND execute a project — 5 phases, each with its own model from routing.json",
458
586
  handler: async (args, ctx) => {
459
587
  const description = args.trim();
460
588
 
@@ -477,36 +605,24 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
477
605
  const specFile = `spec-${ts}.md`;
478
606
  await writeFile(join(plansDir, specFile), `# ${description}\n\n**Created:** ${new Date().toLocaleString()}\n`, "utf-8");
479
607
 
480
- ctx.ui.notify(`📋 Launching orchestrator...`, "info");
481
-
482
- // Build a single comprehensive prompt with all agent phases
483
- const prompt = `# Project: ${description}
484
-
485
- ## Your workflow (follow these phases in order):
608
+ // Build phases with model assignments from routing.json
609
+ const phases = buildPhases(description);
610
+ phaseQueue = phases.slice(1); // Queue phases 2-5
611
+ orchestrationActive = true;
612
+ const firstPhase = phases[0];
486
613
 
487
- ### Phase 1🔍 EXPLORE
488
- Analyze the project requirements and any existing codebase. List what exists, what's needed, and constraints.
614
+ ctx.ui.notify(`📋 **Orchestrator started**5 phases with model routing\n`, "info");
489
615
 
490
- ### Phase 2 — 📐 PLAN
491
- Design the architecture, file structure, and tech choices. Be specific about file names and structure.
492
-
493
- ### Phase 3 — 💻 CODE
494
- Implement EVERYTHING. Create ALL files with complete, production-quality code. No placeholders, no TODOs, no "implement later". Every file must be fully functional.
495
-
496
- ### Phase 4 — 🧪 TEST
497
- Run the code. Verify it works. Fix any errors you find.
498
-
499
- ### Phase 5 — 🔍 REVIEW
500
- Check code quality. Fix any issues. Ensure everything is polished.
501
-
502
- ## Rules
503
- - Complete ALL 5 phases in this turn
504
- - Create every file needed for a working project
505
- - Announce each phase as you start it (e.g. "## Phase 1 — Exploring...")
506
- - Do NOT stop after planning — implement everything`;
616
+ // Show the plan
617
+ for (const p of phases) {
618
+ ctx.ui.notify(` ${p.label} → \`${p.model}\``, "info");
619
+ }
620
+ ctx.ui.notify("", "info");
507
621
 
508
- // Send after handler returns (agent goes idle after command completes)
509
- setTimeout(() => pi.sendUserMessage(prompt), 200);
622
+ // Switch model and send first phase after handler returns
623
+ const modelId = await switchModelForPhase(firstPhase, ctx);
624
+ ctx.ui.notify(`${firstPhase.label} → \`${modelId}\``, "info");
625
+ setTimeout(() => pi.sendUserMessage(firstPhase.instruction), 200);
510
626
  },
511
627
  });
512
628
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.62.2",
3
+ "version": "0.63.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {