pi-crew 0.9.26 → 0.9.27

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 (59) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/NOTICE.md +14 -0
  3. package/assets/crew-vibes.ttf +0 -0
  4. package/assets/runner-spritesheet.png +0 -0
  5. package/dist/build-meta.json +255 -40
  6. package/dist/index.mjs +1564 -241
  7. package/dist/index.mjs.map +4 -4
  8. package/docs/perf/optimization-plan-2026-07-verified.md +396 -0
  9. package/package.json +9 -2
  10. package/scripts/bench-check.mjs +96 -0
  11. package/scripts/bench-cold-start.mjs +216 -0
  12. package/scripts/build-bundle.mjs +84 -0
  13. package/scripts/build-crew-vibes-font.py +328 -0
  14. package/scripts/check-all-skills.ts +294 -0
  15. package/scripts/check-bundle-staleness.mjs +97 -0
  16. package/scripts/check-conflict-markers.mjs +91 -0
  17. package/scripts/check-lazy-imports.mjs +28 -0
  18. package/scripts/install-crew-vibes-font.mjs +91 -0
  19. package/scripts/postinstall.mjs +47 -0
  20. package/scripts/profile-startup.mjs +125 -0
  21. package/scripts/release-smoke.mjs +74 -0
  22. package/scripts/run-bench.mjs +47 -0
  23. package/scripts/run-real-chain.ts +41 -0
  24. package/scripts/test-issue-29-crash.ts +111 -0
  25. package/scripts/test-issue-29-e2e.ts +183 -0
  26. package/scripts/test-issue-29-real-runtime.ts +330 -0
  27. package/scripts/test-issue-29-real-tasks.ts +387 -0
  28. package/scripts/test-issue-29-team-tool.ts +105 -0
  29. package/scripts/test-runner.mjs +74 -0
  30. package/scripts/verify-flicker-fix.ts +109 -0
  31. package/scripts/verify-skill.ts +550 -0
  32. package/scripts/watch-bundle.mjs +259 -0
  33. package/scripts/watchdog-harness.ts +119 -0
  34. package/src/agents/discover-agents.ts +6 -1
  35. package/src/config/defaults.ts +6 -0
  36. package/src/extension/crew-vibes/cat-frames.ts +18 -0
  37. package/src/extension/crew-vibes/config.ts +195 -0
  38. package/src/extension/crew-vibes/figures.ts +94 -0
  39. package/src/extension/crew-vibes/font-detect.ts +58 -0
  40. package/src/extension/crew-vibes/index.ts +396 -0
  41. package/src/extension/crew-vibes/provider-usage.ts +330 -0
  42. package/src/extension/crew-vibes/render.ts +206 -0
  43. package/src/extension/crew-vibes/speed.ts +286 -0
  44. package/src/extension/knowledge-injection.ts +12 -3
  45. package/src/extension/management.ts +23 -3
  46. package/src/extension/register.ts +7 -0
  47. package/src/runtime/child-pi.ts +117 -10
  48. package/src/runtime/crew-agent-records.ts +10 -3
  49. package/src/runtime/retry-executor.ts +4 -1
  50. package/src/runtime/task-runner/state-helpers.ts +9 -30
  51. package/src/runtime/team-runner.ts +5 -3
  52. package/src/state/atomic-write.ts +153 -49
  53. package/src/state/event-log.ts +16 -10
  54. package/src/state/locks.ts +7 -8
  55. package/src/state/mailbox.ts +15 -4
  56. package/src/state/state-store.ts +39 -10
  57. package/src/teams/discover-teams.ts +56 -1
  58. package/src/utils/safe-paths.ts +2 -1
  59. package/src/workflows/discover-workflows.ts +72 -1
@@ -198,15 +198,86 @@ function parseDynamicWorkflowFile(filePath: string, source: ResourceSource): Wor
198
198
  }
199
199
  }
200
200
 
201
+ // ─── Workflow Discovery Cache (F17) ──────────────────────────────────────
202
+ // Mirrors the Agent Discovery Cache pattern (see discover-agents.ts:493-556).
203
+ // `discoverWorkflows(cwd)` is called on the powerbar hot path (≤200 ms coalesce =
204
+ // up to ~5 Hz when a run is active + emitting events). Without caching, each call
205
+ // walks 3 roots, each requiring readdirSync × 2 + readFileSync + regex-parse per
206
+ // `.workflow.md`. We use TTL + dir-stamp invalidation so the cache survives
207
+ // steady-state rendering yet still picks up file changes within a few seconds.
208
+ //
209
+ // SECURITY: ResourceIdentity preserved — same precedence + same parsers as before.
210
+ // Reviewers: there is no path-component concern; this only memoises a pure function
211
+ // of the filesystem state into a process-local Map.
212
+ const WORKFLOW_DISCOVERY_TTL_MS = 5000;
213
+ const WORKFLOW_DISCOVERY_MAX_ENTRIES = 32;
214
+ interface CachedWorkflowEntry {
215
+ result: WorkflowDiscoveryResult;
216
+ expiresAt: number;
217
+ /** mtime tuple of the 3 workflow source dirs; change ⇒ invalidate early. */
218
+ dirStamp: string;
219
+ }
220
+ const workflowCache = new Map<string, CachedWorkflowEntry>();
221
+
222
+ /** Compact mtime signature for the 3 workflow source dirs. Cheap (3 statSync). */
223
+ function workflowDirStamp(cwd: string): string {
224
+ const dirs = [
225
+ path.join(packageRoot(), "workflows"),
226
+ path.join(userPiRoot(), "workflows"),
227
+ path.join(projectCrewRoot(cwd), "workflows"),
228
+ ];
229
+ let out = "";
230
+ for (const d of dirs) {
231
+ try {
232
+ const st = fs.statSync(d);
233
+ out += `${st.mtimeMs}|`;
234
+ } catch {
235
+ out += "0|";
236
+ }
237
+ }
238
+ return out;
239
+ }
240
+
241
+ /** Drop one or all cached entries. Called from management actions and tests. */
242
+ export function invalidateWorkflowDiscoveryCache(cwd?: string): void {
243
+ if (cwd) {
244
+ workflowCache.delete(cwd);
245
+ } else {
246
+ workflowCache.clear();
247
+ }
248
+ }
249
+
201
250
  export function discoverWorkflows(cwd: string): WorkflowDiscoveryResult {
202
251
  if (!cwd || typeof cwd !== "string") {
203
252
  return { builtin: [], user: [], project: [] };
204
253
  }
205
- return {
254
+ // F17: serve from cache when both TTL is fresh AND dir-stamp is unchanged.
255
+ // `dirStamp` is a 3-stat pulse we run on every cached call — it is much
256
+ // cheaper than the disk scan it protects against (3 statSync vs 2 readdirSync
257
+ // + N readFileSync + regex parse per `.workflow.md`).
258
+ const now = Date.now();
259
+ const stamp = workflowDirStamp(cwd);
260
+ const cached = workflowCache.get(cwd);
261
+ if (cached && cached.expiresAt > now && cached.dirStamp === stamp) {
262
+ return cached.result;
263
+ }
264
+ const result: WorkflowDiscoveryResult = {
206
265
  builtin: readWorkflowDir(path.join(packageRoot(), "workflows"), "builtin"),
207
266
  user: readWorkflowDir(path.join(userPiRoot(), "workflows"), "user"),
208
267
  project: readWorkflowDir(path.join(projectCrewRoot(cwd), "workflows"), "project"),
209
268
  };
269
+ workflowCache.set(cwd, {
270
+ result,
271
+ expiresAt: now + WORKFLOW_DISCOVERY_TTL_MS,
272
+ dirStamp: stamp,
273
+ });
274
+ // Bounded LRU-ish eviction by insertion order (Map iteration order).
275
+ while (workflowCache.size > WORKFLOW_DISCOVERY_MAX_ENTRIES) {
276
+ const oldest = workflowCache.keys().next().value;
277
+ if (oldest === undefined) break;
278
+ workflowCache.delete(oldest);
279
+ }
280
+ return result;
210
281
  }
211
282
 
212
283
  export function allWorkflows(discovery: WorkflowDiscoveryResult | undefined): WorkflowConfig[] {