pi-crew 0.5.0 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
1
  # Changelog
2
2
 
3
- ## [0.5.0] — Understand-Anything Patterns & New Features (2026-05-26)
3
+ ## [0.5.1] — Integration + End-to-End Tests (2026-05-26)
4
+
5
+ ### Integration
6
+ - **team-tool.ts**: Wire P1-P6 into switch statement
7
+ - `action='graph'` — load/save/list run graphs
8
+ - `action='onboard'` — team onboarding generator
9
+ - `action='explain'` — task explain context
10
+ - `action='cache'` — run result caching lookup
11
+ - `action='checkpoint'` — checkpoint retrieval
12
+ - `action='search'` — BM25 ranked agent/team search
13
+ - **team-tool-schema.ts**: Add 6 new actions to schema
14
+ - **Type fixes**: run-graph.ts, run-cache.ts, checkpoint.ts, team-onboard.ts
15
+ - **P0 .gitignore**: ensureCrewDirectory auto-updates .gitignore
16
+
17
+ ### Tests
18
+ - 8/8 new action tests pass
19
+ - 10/10 end-to-end feature tests pass
20
+ - All 1796 unit + 45 integration passing
21
+ - CI: Ubuntu/macOS/Windows all passing
22
+
23
+ ---
24
+
25
+ ## [0.5.0]
4
26
 
5
27
  ### New Features: P0-P6 from Understand-Anything Research
6
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-crew",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
5
5
  "author": "baphuongna",
6
6
  "license": "MIT",
@@ -64,9 +64,7 @@ function loadRunSummaries(cwd: string, options: OnboardingOptions = {}): RunSumm
64
64
  team: raw.team,
65
65
  createdAt: raw.createdAt,
66
66
  completedAt: raw.completedAt ?? raw.updatedAt,
67
- taskCount: (raw as Record<string, unknown>).tasks != null
68
- ? ((raw as Record<string, unknown>).tasks as unknown[]).length
69
- : 0,
67
+ taskCount: 0, // tasks stored separately, not in manifest
70
68
  });
71
69
  } catch {
72
70
  continue;
@@ -129,12 +129,15 @@ async function handleRun(
129
129
  import { waitForRun } from "../runtime/run-tracker.ts";
130
130
  import { normalizeSkillOverride } from "../runtime/skill-instructions.ts";
131
131
  import { logInternalError } from "../utils/internal-error.ts";
132
+ import { searchAgents, searchTeams } from "../utils/bm25-search.ts";
133
+ import { projectCrewRoot } from "../utils/paths.ts";
132
134
  import {
133
135
  type CacheControlDeps,
134
136
  invalidateSnapshot,
135
137
  } from "./team-tool/cache-control.ts";
136
138
  import { handleCancel, handleRetry } from "./team-tool/cancel.ts";
137
139
  import { handleDoctor } from "./team-tool/doctor.ts";
140
+ import { handleExplain } from "./team-tool/explain.ts";
138
141
  import { handleHealthMonitor } from "./team-tool/health-monitor.ts";
139
142
  import {
140
143
  handleArtifacts,
@@ -150,6 +153,17 @@ import {
150
153
  handlePrune,
151
154
  handleWorktrees,
152
155
  } from "./team-tool/lifecycle-actions.ts";
156
+ import {
157
+ getCachedRun,
158
+ computeRunCacheKey,
159
+ getCacheStats,
160
+ } from "../state/run-cache.ts";
161
+ import {
162
+ loadRunGraph,
163
+ listRunGraphs,
164
+ } from "../state/run-graph.ts";
165
+ import { FileCheckpointStore } from "../runtime/checkpoint.ts";
166
+ import { buildTeamOnboarding } from "./team-onboard.ts";
153
167
  import { handleParallel } from "./team-tool/parallel-dispatch.ts";
154
168
  import { handlePlan } from "./team-tool/plan.ts";
155
169
  import { handleRespond } from "./team-tool/respond.ts";
@@ -1089,6 +1103,97 @@ export async function handleTeamTool(
1089
1103
  return handleHealthMonitor(ctx, params);
1090
1104
  case "wait":
1091
1105
  return handleWait(params, ctx);
1106
+ case "graph": {
1107
+ if (params.runId) {
1108
+ const graph = loadRunGraph(ctx.cwd, params.runId);
1109
+ return result(
1110
+ graph ? JSON.stringify(graph, null, 2) : "No graph found for this run.",
1111
+ { action: "graph", status: graph ? "ok" : "error" },
1112
+ !graph,
1113
+ );
1114
+ }
1115
+ const graphs = listRunGraphs(ctx.cwd);
1116
+ return result(
1117
+ graphs.length ? `Available graphs:\n${graphs.join("\n")}` : "No graphs available.",
1118
+ { action: "graph", status: "ok" },
1119
+ );
1120
+ }
1121
+ case "search": {
1122
+ const query = params.goal ?? params.task ?? "";
1123
+ if (!query) {
1124
+ return result("Search requires goal or task query.", { action: "search", status: "error" }, true);
1125
+ }
1126
+ try {
1127
+ const [agentResults, teamResults] = await Promise.all([
1128
+ searchAgents(query, { limit: 5 }),
1129
+ searchTeams(query, { limit: 3 }),
1130
+ ]);
1131
+ const lines: string[] = [];
1132
+ if (teamResults.length) {
1133
+ lines.push("## Teams");
1134
+ for (const r of teamResults) {
1135
+ lines.push(`- [${r.team.name}] score=${r.score.toFixed(2)}: ${r.team.description ?? "(no description)"}`);
1136
+ }
1137
+ }
1138
+ if (agentResults.length) {
1139
+ lines.push("## Agents");
1140
+ for (const r of agentResults) {
1141
+ lines.push(`- [${r.agent.name}] score=${r.score.toFixed(2)}: ${r.agent.description ?? "(no description)"}`);
1142
+ }
1143
+ }
1144
+ return result(lines.length ? lines.join("\n") : "No results found.", { action: "search", status: "ok" });
1145
+ } catch (err) {
1146
+ const msg = err instanceof Error ? err.message : String(err);
1147
+ return result(`Search failed: ${msg}`, { action: "search", status: "error" }, true);
1148
+ }
1149
+ }
1150
+ case "onboard": {
1151
+ const team = params.team ?? "default";
1152
+ const onboarding = buildTeamOnboarding(team, ctx.cwd);
1153
+ return result(onboarding, { action: "onboard", status: "ok" });
1154
+ }
1155
+ case "explain": {
1156
+ const explainResult = handleExplain(params, ctx.cwd);
1157
+ return result(explainResult.text, { action: "explain", status: explainResult.isError ? "error" : "ok" }, explainResult.isError);
1158
+ }
1159
+ case "cache": {
1160
+ if (params.goal) {
1161
+ const key = computeRunCacheKey(
1162
+ params.goal,
1163
+ params.team ?? "default",
1164
+ params.workflow ?? "default",
1165
+ ctx.cwd,
1166
+ );
1167
+ const cached = getCachedRun(ctx.cwd, key);
1168
+ if (cached) {
1169
+ return result(
1170
+ `Cached run found (${new Date(cached.cachedAt).toISOString()}): runId=${cached.runId}, status=${cached.status}, ${cached.tasks.length} tasks`,
1171
+ { action: "cache", status: "ok", data: { cacheKey: key, cacheHit: true, runId: cached.runId, status: cached.status, taskCount: cached.tasks.length } },
1172
+ );
1173
+ }
1174
+ return result(`No cached result for key: ${key}`, { action: "cache", status: "ok", data: { cacheKey: key, cacheHit: false } });
1175
+ }
1176
+ const stats = getCacheStats(ctx.cwd);
1177
+ return result(
1178
+ `Cache stats: ${stats.entries} entries, ${stats.sizeBytes} bytes`,
1179
+ { action: "cache", status: "ok" },
1180
+ );
1181
+ }
1182
+ case "checkpoint": {
1183
+ if (!params.runId || !params.taskId) {
1184
+ return result("Checkpoint requires runId and taskId.", { action: "checkpoint", status: "error" }, true);
1185
+ }
1186
+ const stateRoot = path.join(projectCrewRoot(ctx.cwd), "state", "runs", params.runId);
1187
+ const store = new FileCheckpointStore(stateRoot);
1188
+ const checkpoint = store.load(params.runId, params.taskId);
1189
+ if (!checkpoint) {
1190
+ return result("No checkpoint found.", { action: "checkpoint", status: "error" }, true);
1191
+ }
1192
+ return result(
1193
+ `Checkpoint: step=${checkpoint.step}, progress=${checkpoint.progress}, savedAt=${new Date(checkpoint.savedAt).toISOString()}`,
1194
+ { action: "checkpoint", status: "ok", data: { checkpoint } },
1195
+ );
1196
+ }
1092
1197
  default:
1093
1198
  return result(
1094
1199
  `Unknown action: ${action}`,
@@ -59,6 +59,12 @@ export const TeamToolParams = Type.Object({
59
59
  Type.Literal("settings"),
60
60
  Type.Literal("steer"),
61
61
  Type.Literal("health"),
62
+ Type.Literal("graph"),
63
+ Type.Literal("onboard"),
64
+ Type.Literal("explain"),
65
+ Type.Literal("cache"),
66
+ Type.Literal("checkpoint"),
67
+ Type.Literal("search"),
62
68
  ],
63
69
  { description: "Team action. Defaults to 'list' when omitted." },
64
70
  ),
@@ -222,7 +228,13 @@ export interface TeamToolParamsValue {
222
228
  | "settings"
223
229
  | "steer"
224
230
  | "invalidate"
225
- | "health";
231
+ | "health"
232
+ | "graph"
233
+ | "onboard"
234
+ | "explain"
235
+ | "cache"
236
+ | "checkpoint"
237
+ | "search";
226
238
  resource?: "agent" | "team" | "workflow";
227
239
  team?: string;
228
240
  workflow?: string;
@@ -57,7 +57,6 @@ export function buildRunGraph(
57
57
  workflow: manifest.workflow,
58
58
  status: manifest.status,
59
59
  createdAt: manifest.createdAt,
60
- completedAt: (manifest as Record<string, unknown>).completedAt,
61
60
  },
62
61
  });
63
62
  nodeIds.add(`run:${runId}`);
@@ -73,10 +72,7 @@ export function buildRunGraph(
73
72
  type: "task",
74
73
  name: task.role,
75
74
  metadata: {
76
- phase: (task as Record<string, unknown>).phase,
77
75
  status: task.status,
78
- agentModel: (task as Record<string, unknown>).agentModel,
79
- usage: (task as Record<string, unknown>).usage,
80
76
  startedAt: task.startedAt,
81
77
  finishedAt: task.finishedAt,
82
78
  },
@@ -99,29 +95,14 @@ export function buildRunGraph(
99
95
  });
100
96
  }
101
97
 
102
- // Edge from task to agent (if we have agent model info)
103
- const agentModel = (task as Record<string, unknown>).agentModel as string | undefined;
104
- if (agentModel) {
105
- const agentId = `agent:${agentModel.replace(/[^a-zA-Z0-9-_]/g, "_")}`;
106
- if (!nodeIds.has(agentId)) {
107
- nodeIds.add(agentId);
108
- nodes.push({ id: agentId, type: "agent", name: agentModel });
109
- }
110
- edges.push({
111
- source: agentId,
112
- target: taskId,
113
- type: "runs",
114
- weight: 0.9,
115
- });
116
- }
117
98
  }
118
99
 
119
- // Group by layer (based on phase)
100
+ // Group by layer (based on phase or role)
120
101
  const layerMap = new Map<string, string[]>();
121
102
  for (const task of tasks) {
122
- const phase = ((task as Record<string, unknown>).phase as string) ?? "unknown";
123
- if (!layerMap.has(phase)) layerMap.set(phase, []);
124
- layerMap.get(phase)!.push(`task:${task.id}`);
103
+ const layerName = task.adaptive?.phase ?? task.role;
104
+ if (!layerMap.has(layerName)) layerMap.set(layerName, []);
105
+ layerMap.get(layerName)!.push(`task:${task.id}`);
125
106
  }
126
107
 
127
108
  const layers: RunGraphLayer[] = [...layerMap.entries()].map(([name, nodeIdList]) => ({
@@ -135,7 +116,7 @@ export function buildRunGraph(
135
116
  team: manifest.team ?? "unknown",
136
117
  workflow: manifest.workflow ?? "unknown",
137
118
  createdAt: manifest.createdAt,
138
- completedAt: (manifest as Record<string, unknown>).completedAt as string | undefined,
119
+ completedAt: manifest.updatedAt,
139
120
  status: manifest.status,
140
121
  nodes,
141
122
  edges,
@@ -140,7 +140,6 @@ export async function searchAgents(query: string, options?: { limit?: number }):
140
140
  name: agent.name,
141
141
  description: agent.description ?? "",
142
142
  skills: (agent.skills ?? []).join(" "),
143
- tags: (agent.tags ?? []).join(" "),
144
143
  },
145
144
  agent,
146
145
  }));
@@ -149,7 +148,6 @@ export async function searchAgents(query: string, options?: { limit?: number }):
149
148
  name: 3.0,
150
149
  description: 1.5,
151
150
  skills: 1.0,
152
- tags: 1.0,
153
151
  });
154
152
 
155
153
  const results = engine.search(query, {