oh-pi 0.1.53 → 0.1.54

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-pi",
3
- "version": "0.1.53",
3
+ "version": "0.1.54",
4
4
  "description": "One-click setup for pi-coding-agent. Like oh-my-zsh for pi.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -72,7 +72,7 @@ function makeInitialScoutTask(goal: string): Task {
72
72
 
73
73
  function childTaskFromParsed(
74
74
  parentId: string,
75
- parsed: { title: string; description: string; files: string[]; caste: AntCaste; priority: TaskPriority },
75
+ parsed: { title: string; description: string; files: string[]; caste: AntCaste; priority: TaskPriority; context?: string },
76
76
  ): Task {
77
77
  return {
78
78
  id: makeTaskId(),
@@ -83,6 +83,7 @@ function childTaskFromParsed(
83
83
  status: "pending",
84
84
  priority: parsed.priority,
85
85
  files: parsed.files,
86
+ context: parsed.context || undefined,
86
87
  claimedBy: null,
87
88
  result: null,
88
89
  error: null,
@@ -398,9 +399,18 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
398
399
  await runAntWave({ ...waveBase, caste: "worker" });
399
400
  }
400
401
 
402
+ // ═══ Auto-check: run tsc before soldier review ═══
403
+ let tscPassed = true;
404
+ try {
405
+ const { execSync } = await import("node:child_process");
406
+ execSync("npx tsc --noEmit", { cwd: opts.cwd, timeout: 30000, stdio: "pipe" });
407
+ } catch {
408
+ tscPassed = false;
409
+ }
410
+
401
411
  // ═══ Phase 3: 审查 ═══
402
412
  const completedWorkerTasks = nest.getAllTasks().filter(t => t.caste === "worker" && t.status === "done");
403
- if (completedWorkerTasks.length > 0) {
413
+ if (completedWorkerTasks.length > 0 && (!tscPassed || completedWorkerTasks.length > 3)) {
404
414
  nest.updateState({ status: "reviewing" });
405
415
  callbacks.onPhase("reviewing", "Dispatching soldier ants to review changes...");
406
416
  const reviewTask = makeReviewTask(completedWorkerTasks);
@@ -57,6 +57,7 @@ export interface ParsedSubTask {
57
57
  files: string[];
58
58
  caste: AntCaste;
59
59
  priority: 1 | 2 | 3 | 4 | 5;
60
+ context?: string;
60
61
  }
61
62
 
62
63
  const CASTE_PROMPTS: Record<AntCaste, string> = {
@@ -67,6 +68,7 @@ Behavior:
67
68
  - Identify files, functions, dependencies related to the goal
68
69
  - IMPORTANT: After EACH tool call, summarize what you found so far. Do NOT wait until the end.
69
70
  - Report findings as structured intelligence for Worker Ants
71
+ - For each recommended task, include the KEY code snippets (with file:line) the worker will need — this saves workers from re-reading files
70
72
 
71
73
  Output format (MUST follow exactly):
72
74
  ## Discoveries
@@ -79,6 +81,7 @@ For each task the colony should do next:
79
81
  - files: <comma-separated file paths>
80
82
  - caste: worker
81
83
  - priority: <1-5, 1=highest>
84
+ - context: <relevant code snippets that the worker will need, with file:line references>
82
85
 
83
86
  ## Warnings
84
87
  Any risks, blockers, or conflicts detected.`,
@@ -142,6 +145,9 @@ function buildPrompt(task: Task, pheromoneContext: string, castePrompt: string,
142
145
  if (task.files.length > 0) {
143
146
  prompt += `**Files scope:** ${task.files.join(", ")}\n`;
144
147
  }
148
+ if (task.context) {
149
+ prompt += `\n## Pre-loaded Context (from scout)\n${task.context}\n`;
150
+ }
145
151
  if (/[\u4e00-\u9fff]/.test(task.description)) {
146
152
  prompt += '\nIMPORTANT: Follow the language requirements specified in the task description. If the task says to write in Chinese, write in Chinese.\n';
147
153
  }
@@ -152,13 +158,18 @@ function buildPrompt(task: Task, pheromoneContext: string, castePrompt: string,
152
158
  function parseSubTasks(output: string): ParsedSubTask[] {
153
159
  const tasks: ParsedSubTask[] = [];
154
160
  const regex = /### TASK:\s*(.+)\n(?:- description:\s*(.+)\n)?(?:- files:\s*(.+)\n)?(?:- caste:\s*(\w+)\n)?(?:- priority:\s*(\d))?/g;
161
+ const taskBlocks = output.split(/(?=### TASK:)/);
155
162
  for (const m of output.matchAll(regex)) {
163
+ const block = taskBlocks.find(b => b.includes(`### TASK: ${m[1]?.trim()}`)) || "";
164
+ const ctxMatch = block.match(/- context:\s*([\s\S]*?)(?=### TASK:|## |\n\n|$)/);
165
+ const context = ctxMatch?.[1]?.trim() || undefined;
156
166
  tasks.push({
157
167
  title: m[1]?.trim() || "Untitled",
158
168
  description: m[2]?.trim() || m[1]?.trim() || "",
159
169
  files: (m[3]?.trim() || "").split(",").map(f => f.trim()).filter(Boolean),
160
170
  caste: (m[4]?.trim() as AntCaste) || "worker",
161
171
  priority: (parseInt(m[5] || "3") as 1 | 2 | 3 | 4 | 5) || 3,
172
+ context,
162
173
  });
163
174
  }
164
175
  return tasks;
@@ -35,6 +35,7 @@ export interface Task {
35
35
  status: TaskStatus;
36
36
  priority: TaskPriority;
37
37
  files: string[]; // 锁定的文件
38
+ context?: string; // Scout 预加载的代码片段
38
39
  claimedBy: string | null; // ant id
39
40
  result: string | null;
40
41
  error: string | null;