@phi-code-admin/phi-code 0.74.0 → 0.74.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.
@@ -16,12 +16,24 @@
16
16
  * /plans — List plans and their execution status
17
17
  */
18
18
 
19
+ import { Type } from "@sinclair/typebox";
19
20
  import type { ExtensionAPI } from "phi-code";
20
21
  import { writeFile, mkdir, readdir, readFile } from "node:fs/promises";
21
22
  import { join } from "node:path";
22
23
  import { existsSync, readFileSync } from "node:fs";
23
24
  import { homedir } from "node:os";
24
25
 
26
+ // ─── Types ───────────────────────────────────────────────────────────────
27
+
28
+ interface TaskDef {
29
+ title: string;
30
+ description: string;
31
+ agent?: string;
32
+ priority?: string;
33
+ dependencies?: number[];
34
+ subtasks?: string[];
35
+ }
36
+
25
37
  // ─── Extension ───────────────────────────────────────────────────────────
26
38
 
27
39
  export default function orchestratorExtension(pi: ExtensionAPI) {
@@ -35,6 +47,161 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
35
47
  return new Date().toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
36
48
  }
37
49
 
50
+ // ─── Plan File Generators ────────────────────────────────────────
51
+
52
+ function generateSpec(p: {
53
+ title: string; description: string; goals: string[]; requirements: string[];
54
+ architecture?: string[]; constraints?: string[]; successCriteria?: string[]; tasks: TaskDef[];
55
+ }): string {
56
+ let spec = `# ${p.title}\n\n`;
57
+ spec += `**Created:** ${new Date().toLocaleString()}\n\n`;
58
+ spec += `## Description\n\n${p.description}\n\n`;
59
+ spec += `## Goals\n\n`;
60
+ p.goals.forEach((g, i) => { spec += `${i + 1}. ${g}\n`; });
61
+ spec += "\n## Requirements\n\n";
62
+ p.requirements.forEach(r => { spec += `- ${r}\n`; });
63
+ spec += "\n";
64
+ if (p.architecture?.length) {
65
+ spec += `## Architecture\n\n`;
66
+ p.architecture.forEach(a => { spec += `- ${a}\n`; });
67
+ spec += "\n";
68
+ }
69
+ if (p.constraints?.length) {
70
+ spec += `## Constraints\n\n`;
71
+ p.constraints.forEach(c => { spec += `- ${c}\n`; });
72
+ spec += "\n";
73
+ }
74
+ if (p.successCriteria?.length) {
75
+ spec += `## Success Criteria\n\n`;
76
+ p.successCriteria.forEach(s => { spec += `- [ ] ${s}\n`; });
77
+ spec += "\n";
78
+ }
79
+ spec += `## Task Overview\n\n| # | Task | Agent | Priority | Dependencies |\n|---|------|-------|----------|-------------|\n`;
80
+ p.tasks.forEach((t, i) => {
81
+ const deps = t.dependencies?.map(d => `#${d}`).join(", ") || "—";
82
+ spec += `| ${i + 1} | ${t.title} | ${t.agent || "code"} | ${t.priority || "medium"} | ${deps} |\n`;
83
+ });
84
+ spec += `\n---\n*Generated by Phi Code Orchestrator*\n`;
85
+ return spec;
86
+ }
87
+
88
+ function generateTodo(title: string, tasks: TaskDef[]): string {
89
+ let todo = `# TODO: ${title}\n\n`;
90
+ todo += `**Created:** ${new Date().toLocaleString()}\n`;
91
+ todo += `**Tasks:** ${tasks.length}\n**Status:** executing\n\n`;
92
+ tasks.forEach((t, i) => {
93
+ const agentTag = t.agent ? ` [${t.agent}]` : "";
94
+ const prioTag = t.priority === "high" ? " 🔴" : t.priority === "low" ? " 🟢" : " 🟡";
95
+ const depsTag = t.dependencies?.length ? ` (after #${t.dependencies.join(", #")})` : "";
96
+ todo += `## Task ${i + 1}: ${t.title}${prioTag}${agentTag}${depsTag}\n\n- [ ] ${t.description}\n`;
97
+ if (t.subtasks) t.subtasks.forEach(st => { todo += ` - [ ] ${st}\n`; });
98
+ todo += "\n";
99
+ });
100
+ todo += `---\n\n## Progress\n\n- Total: ${tasks.length} tasks\n`;
101
+ todo += `- High priority: ${tasks.filter(t => t.priority === "high").length}\n`;
102
+ todo += `- Agents: ${[...new Set(tasks.map(t => t.agent || "code"))].join(", ")}\n`;
103
+ return todo;
104
+ }
105
+
106
+ // ─── Orchestrate Tool — Structured planning with prompt-architect pattern ───
107
+
108
+ pi.registerTool({
109
+ name: "orchestrate",
110
+ label: "Project Orchestrator",
111
+ description: "Create a structured project plan with spec, todo, and task breakdown. Forces the model to decompose the project into goals, requirements, architecture, constraints, and success criteria before execution.",
112
+ promptSnippet: "Plan + structure projects using the prompt-architect pattern. Each task gets [CONTEXT] → [TASK] → [FORMAT] → [CONSTRAINTS].",
113
+ promptGuidelines: [
114
+ // NOTE: The old guideline "When asked to plan or build a project, call the orchestrate tool"
115
+ // was REMOVED because it hijacked the /plan command flow. The orchestrate tool is now
116
+ // only called explicitly or when the model decides structured planning is needed outside of /plan.
117
+ "IMPORTANT: Do NOT call orchestrate during /plan orchestration phases. The /plan command handles its own workflow.",
118
+ "Structure each task description using the prompt-architect pattern: [CONTEXT] what exists and why → [TASK] what to do specifically → [FORMAT] expected output → [CONSTRAINTS] rules and limitations.",
119
+ "Assign agent types strategically: 'explore' (read-only analysis), 'plan' (architecture), 'code' (implementation), 'test' (write+run tests), 'review' (quality audit).",
120
+ "Set dependencies to maximize parallelism: tasks without dependencies run simultaneously.",
121
+ "Set priority=high for critical-path tasks, medium for standard work, low for nice-to-haves.",
122
+ ],
123
+ parameters: Type.Object({
124
+ title: Type.String({ description: "Concise project title" }),
125
+ description: Type.String({ description: "Full project description: what to build, why, and any relevant context" }),
126
+ goals: Type.Union([Type.Array(Type.String()), Type.String()], { description: "Measurable project goals (what success looks like)" }),
127
+ requirements: Type.Union([Type.Array(Type.String()), Type.String()], { description: "Technical and functional requirements" }),
128
+ architecture: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Architecture decisions, tech stack choices, trade-offs" })),
129
+ tasks: Type.Array(
130
+ Type.Object({
131
+ title: Type.String({ description: "Clear, action-oriented task title" }),
132
+ description: Type.String({ description: "SELF-CONTAINED task description. Include ALL context the sub-agent needs." }),
133
+ agent: Type.Optional(Type.String({ description: "Agent type: explore, plan, code, test, review" })),
134
+ priority: Type.Optional(Type.String({ description: "high, medium, low" })),
135
+ dependencies: Type.Optional(Type.Array(Type.Number(), { description: "Task numbers this depends on (1-indexed)" })),
136
+ subtasks: Type.Optional(Type.Array(Type.String(), { description: "Specific sub-steps within this task" })),
137
+ }),
138
+ { description: "Ordered list of tasks" }
139
+ ),
140
+ constraints: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Hard constraints: things to avoid, rules" })),
141
+ successCriteria: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "How to verify the project is complete" })),
142
+ }),
143
+
144
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
145
+ // Block if /plan orchestration is active — prevent hijacking
146
+ if (orchestrationActive) {
147
+ return {
148
+ content: [{ type: "text", text: "⚠️ Orchestration is already active via /plan. Do NOT call orchestrate during /plan phases. Follow the phase instructions instead." }],
149
+ };
150
+ }
151
+
152
+ const raw = params as any;
153
+
154
+ // Normalize string fields to arrays
155
+ const toArray = (v: any): string[] => {
156
+ if (!v) return [];
157
+ if (Array.isArray(v)) return v;
158
+ if (typeof v === "string") return v.split("\n").map((s: string) => s.replace(/^[-•*]\s*/, "").trim()).filter(Boolean);
159
+ return [];
160
+ };
161
+
162
+ const p = {
163
+ title: raw.title as string,
164
+ description: raw.description as string,
165
+ goals: toArray(raw.goals),
166
+ requirements: toArray(raw.requirements),
167
+ architecture: raw.architecture ? toArray(raw.architecture) : undefined,
168
+ tasks: raw.tasks as TaskDef[],
169
+ constraints: raw.constraints ? toArray(raw.constraints) : undefined,
170
+ successCriteria: raw.successCriteria ? toArray(raw.successCriteria) : undefined,
171
+ };
172
+
173
+ try {
174
+ await ensurePlansDir();
175
+ const ts = timestamp();
176
+ const specFile = `spec-${ts}.md`;
177
+ const todoFile = `todo-${ts}.md`;
178
+
179
+ // Generate and write structured plan files
180
+ const spec = generateSpec(p);
181
+ const todo = generateTodo(p.title, p.tasks);
182
+ await writeFile(join(plansDir, specFile), spec, "utf-8");
183
+ await writeFile(join(plansDir, todoFile), todo, "utf-8");
184
+
185
+ const summary = `📋 **Project "${p.title}" — Structured plan created!**\n\n` +
186
+ `**Spec:** \`${specFile}\` | **Todo:** \`${todoFile}\`\n\n` +
187
+ `**Goals:** ${p.goals.length} | **Requirements:** ${p.requirements.length} | **Tasks:** ${p.tasks.length}\n\n` +
188
+ `The spec and todo files are ready in \`.phi/plans/\`. ` +
189
+ `Use \`/plan ${p.description.slice(0, 100)}\` to execute with the 5-phase orchestrator, ` +
190
+ `or work through the tasks manually.`;
191
+
192
+ return {
193
+ content: [{ type: "text", text: summary }],
194
+ details: { specFile, todoFile, taskCount: p.tasks.length },
195
+ };
196
+ } catch (error) {
197
+ return {
198
+ content: [{ type: "text", text: `Orchestration failed: ${error}` }],
199
+ details: { error: String(error) },
200
+ };
201
+ }
202
+ },
203
+ });
204
+
38
205
  // ─── Orchestration State ─────────────────────────────────────────
39
206
 
40
207
  interface AgentDef {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.74.0",
3
+ "version": "0.74.1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {