pi-crew 0.1.37 → 0.1.39

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 (162) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +5 -0
  4. package/agents/analyst.md +11 -11
  5. package/agents/critic.md +11 -11
  6. package/agents/executor.md +11 -11
  7. package/agents/explorer.md +11 -11
  8. package/agents/planner.md +11 -11
  9. package/agents/reviewer.md +11 -11
  10. package/agents/security-reviewer.md +11 -11
  11. package/agents/test-engineer.md +11 -11
  12. package/agents/verifier.md +11 -11
  13. package/agents/writer.md +11 -11
  14. package/docs/refactor-tasks-phase3.md +394 -394
  15. package/docs/refactor-tasks-phase4.md +564 -564
  16. package/docs/refactor-tasks-phase5.md +402 -402
  17. package/docs/refactor-tasks-phase6.md +662 -662
  18. package/docs/research-extension-examples.md +297 -297
  19. package/docs/research-extension-system.md +324 -324
  20. package/docs/research-optimization-plan.md +548 -548
  21. package/docs/research-pi-coding-agent.md +357 -357
  22. package/docs/research-source-pi-crew-reference.md +174 -174
  23. package/docs/resource-formats.md +10 -8
  24. package/docs/runtime-flow.md +148 -148
  25. package/docs/source-runtime-refactor-map.md +83 -83
  26. package/docs/usage.md +6 -0
  27. package/index.ts +6 -6
  28. package/package.json +3 -3
  29. package/schema.json +2 -2
  30. package/src/agents/agent-serializer.ts +34 -34
  31. package/src/config/config.ts +8 -4
  32. package/src/extension/cross-extension-rpc.ts +82 -82
  33. package/src/extension/import-index.ts +18 -2
  34. package/src/extension/register.ts +11 -1
  35. package/src/extension/registration/compaction-guard.ts +125 -125
  36. package/src/extension/registration/subagent-helpers.ts +30 -6
  37. package/src/extension/registration/subagent-tools.ts +8 -3
  38. package/src/extension/result-watcher.ts +98 -98
  39. package/src/extension/run-import.ts +12 -2
  40. package/src/extension/run-index.ts +12 -2
  41. package/src/extension/run-maintenance.ts +24 -24
  42. package/src/extension/team-tool/api.ts +54 -14
  43. package/src/extension/team-tool/cancel.ts +31 -31
  44. package/src/extension/team-tool/doctor.ts +179 -179
  45. package/src/extension/team-tool/inspect.ts +41 -41
  46. package/src/extension/team-tool/lifecycle-actions.ts +79 -79
  47. package/src/extension/team-tool/plan.ts +19 -19
  48. package/src/extension/team-tool/status.ts +73 -73
  49. package/src/observability/correlation.ts +35 -35
  50. package/src/observability/event-to-metric.ts +54 -54
  51. package/src/observability/exporters/adapter.ts +24 -24
  52. package/src/observability/exporters/otlp-exporter.ts +65 -65
  53. package/src/observability/exporters/prometheus-exporter.ts +47 -47
  54. package/src/observability/metric-registry.ts +72 -72
  55. package/src/observability/metric-retention.ts +46 -46
  56. package/src/observability/metric-sink.ts +51 -51
  57. package/src/observability/metrics-primitives.ts +166 -166
  58. package/src/prompt/prompt-runtime.ts +68 -68
  59. package/src/runtime/agent-control.ts +64 -64
  60. package/src/runtime/agent-memory.ts +72 -72
  61. package/src/runtime/agent-observability.ts +114 -113
  62. package/src/runtime/async-marker.ts +26 -26
  63. package/src/runtime/background-runner.ts +53 -53
  64. package/src/runtime/crash-recovery.ts +56 -56
  65. package/src/runtime/crew-agent-records.ts +54 -9
  66. package/src/runtime/crew-agent-runtime.ts +58 -58
  67. package/src/runtime/deadletter.ts +36 -36
  68. package/src/runtime/direct-run.ts +35 -35
  69. package/src/runtime/foreground-control.ts +82 -82
  70. package/src/runtime/green-contract.ts +46 -46
  71. package/src/runtime/group-join.ts +88 -88
  72. package/src/runtime/heartbeat-gradient.ts +28 -28
  73. package/src/runtime/heartbeat-watcher.ts +80 -80
  74. package/src/runtime/live-agent-control.ts +87 -78
  75. package/src/runtime/live-agent-manager.ts +85 -85
  76. package/src/runtime/live-control-realtime.ts +36 -36
  77. package/src/runtime/live-session-runtime.ts +299 -299
  78. package/src/runtime/manifest-cache.ts +248 -212
  79. package/src/runtime/model-fallback.ts +261 -261
  80. package/src/runtime/parallel-research.ts +44 -44
  81. package/src/runtime/parallel-utils.ts +99 -99
  82. package/src/runtime/pi-json-output.ts +111 -111
  83. package/src/runtime/policy-engine.ts +78 -78
  84. package/src/runtime/post-exit-stdio-guard.ts +86 -86
  85. package/src/runtime/process-status.ts +56 -56
  86. package/src/runtime/progress-event-coalescer.ts +43 -43
  87. package/src/runtime/recovery-recipes.ts +74 -74
  88. package/src/runtime/retry-executor.ts +59 -59
  89. package/src/runtime/role-permission.ts +39 -39
  90. package/src/runtime/session-usage.ts +79 -79
  91. package/src/runtime/sidechain-output.ts +28 -28
  92. package/src/runtime/subagent-manager.ts +80 -12
  93. package/src/runtime/task-display.ts +38 -38
  94. package/src/runtime/task-output-context.ts +127 -106
  95. package/src/runtime/task-runner/live-executor.ts +98 -98
  96. package/src/runtime/task-runner/progress.ts +111 -111
  97. package/src/runtime/task-runner/result-utils.ts +14 -14
  98. package/src/runtime/task-runner/state-helpers.ts +22 -22
  99. package/src/runtime/team-runner.ts +1 -1
  100. package/src/runtime/worker-heartbeat.ts +21 -21
  101. package/src/runtime/worker-startup.ts +57 -57
  102. package/src/schema/config-schema.ts +21 -21
  103. package/src/schema/team-tool-schema.ts +100 -100
  104. package/src/state/artifact-store.ts +122 -108
  105. package/src/state/contracts.ts +105 -105
  106. package/src/state/jsonl-writer.ts +77 -77
  107. package/src/state/mailbox.ts +67 -22
  108. package/src/state/state-store.ts +36 -5
  109. package/src/state/task-claims.ts +42 -42
  110. package/src/state/usage.ts +29 -29
  111. package/src/subagents/async-entry.ts +1 -1
  112. package/src/subagents/index.ts +3 -3
  113. package/src/subagents/live/control.ts +1 -1
  114. package/src/subagents/live/manager.ts +1 -1
  115. package/src/subagents/live/realtime.ts +1 -1
  116. package/src/subagents/live/session-runtime.ts +1 -1
  117. package/src/subagents/manager.ts +1 -1
  118. package/src/subagents/spawn.ts +1 -1
  119. package/src/teams/discover-teams.ts +27 -5
  120. package/src/teams/team-serializer.ts +38 -36
  121. package/src/types/diff.d.ts +18 -18
  122. package/src/ui/crew-footer.ts +101 -101
  123. package/src/ui/crew-select-list.ts +111 -111
  124. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  125. package/src/ui/dynamic-border.ts +25 -25
  126. package/src/ui/layout-primitives.ts +106 -106
  127. package/src/ui/loaders.ts +158 -158
  128. package/src/ui/mascot.ts +441 -441
  129. package/src/ui/render-diff.ts +119 -119
  130. package/src/ui/run-dashboard.ts +5 -2
  131. package/src/ui/run-snapshot-cache.ts +19 -8
  132. package/src/ui/spinner.ts +17 -17
  133. package/src/ui/status-colors.ts +54 -54
  134. package/src/ui/syntax-highlight.ts +116 -116
  135. package/src/ui/transcript-viewer.ts +15 -1
  136. package/src/utils/completion-dedupe.ts +63 -63
  137. package/src/utils/file-coalescer.ts +84 -84
  138. package/src/utils/frontmatter.ts +36 -36
  139. package/src/utils/fs-watch.ts +31 -31
  140. package/src/utils/git.ts +262 -262
  141. package/src/utils/ids.ts +12 -12
  142. package/src/utils/names.ts +26 -26
  143. package/src/utils/paths.ts +3 -2
  144. package/src/utils/safe-paths.ts +34 -0
  145. package/src/utils/sleep.ts +32 -32
  146. package/src/utils/timings.ts +31 -31
  147. package/src/utils/visual.ts +159 -159
  148. package/src/workflows/discover-workflows.ts +30 -3
  149. package/src/workflows/validate-workflow.ts +40 -40
  150. package/src/worktree/branch-freshness.ts +45 -45
  151. package/teams/default.team.md +12 -12
  152. package/teams/fast-fix.team.md +11 -11
  153. package/teams/implementation.team.md +18 -18
  154. package/teams/parallel-research.team.md +14 -14
  155. package/teams/research.team.md +11 -11
  156. package/teams/review.team.md +12 -12
  157. package/workflows/default.workflow.md +29 -29
  158. package/workflows/fast-fix.workflow.md +22 -22
  159. package/workflows/implementation.workflow.md +38 -38
  160. package/workflows/parallel-research.workflow.md +46 -46
  161. package/workflows/research.workflow.md +22 -22
  162. package/workflows/review.workflow.md +30 -30
@@ -1,119 +1,119 @@
1
- import * as Diff from "diff";
2
- import type { CrewTheme } from "./theme-adapter.ts";
3
- import { asCrewTheme } from "./theme-adapter.ts";
4
-
5
- interface ParsedDiffLine {
6
- prefix: string;
7
- lineNum: string; content: string;
8
- }
9
-
10
- interface DiffLineContent {
11
- lineNum: string;
12
- content: string;
13
- }
14
-
15
- function parseDiffLine(line: string): ParsedDiffLine | null {
16
- const match = line.match(/^([+-\s])(\s*\d*)\s(.*)$/);
17
- if (!match) return null;
18
- return { prefix: match[1], lineNum: match[2], content: match[3] };
19
- }
20
-
21
- function replaceTabs(text: string): string {
22
- return text.replace(/\t/g, " ");
23
- }
24
-
25
- function renderIntraLineDiff(theme: CrewTheme, oldContent: string, newContent: string): { removedLine: string; addedLine: string } {
26
- const wordDiff = Diff.diffWords(oldContent, newContent);
27
- let removedLine = "";
28
- let addedLine = "";
29
- let isFirstRemoved = true;
30
- let isFirstAdded = true;
31
-
32
- for (const part of wordDiff) {
33
- if (part.removed) {
34
- let value = part.value;
35
- if (isFirstRemoved) {
36
- const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
37
- value = value.slice(leadingWs.length);
38
- removedLine += leadingWs;
39
- isFirstRemoved = false;
40
- }
41
- if (value) removedLine += theme.inverse?.(value) ?? value;
42
- } else if (part.added) {
43
- let value = part.value;
44
- if (isFirstAdded) {
45
- const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
46
- value = value.slice(leadingWs.length);
47
- addedLine += leadingWs;
48
- isFirstAdded = false;
49
- }
50
- if (value) addedLine += theme.inverse?.(value) ?? value;
51
- } else {
52
- removedLine += part.value;
53
- addedLine += part.value;
54
- }
55
- }
56
-
57
- return { removedLine, addedLine };
58
- }
59
-
60
- export interface RenderDiffOptions {
61
- filePath?: string;
62
- theme?: unknown;
63
- }
64
-
65
- export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
66
- const theme = asCrewTheme(options.theme);
67
- const lines = diffText.split("\n");
68
- const result: string[] = [];
69
- let i = 0;
70
-
71
- while (i < lines.length) {
72
- const line = lines[i] ?? "";
73
- const parsed = parseDiffLine(line);
74
- if (!parsed) {
75
- result.push(theme.fg("toolDiffContext", line));
76
- i++;
77
- continue;
78
- }
79
-
80
- if (parsed.prefix === "-") {
81
- const removedLines: DiffLineContent[] = [];
82
- while (i < lines.length) {
83
- const nextParsed = parseDiffLine(lines[i] ?? "");
84
- if (!nextParsed || nextParsed.prefix !== "-") break;
85
- removedLines.push({ lineNum: nextParsed.lineNum, content: nextParsed.content });
86
- i++;
87
- }
88
-
89
- const addedLines: DiffLineContent[] = [];
90
- while (i < lines.length) {
91
- const nextParsed = parseDiffLine(lines[i] ?? "");
92
- if (!nextParsed || nextParsed.prefix !== "+") break;
93
- addedLines.push({ lineNum: nextParsed.lineNum, content: nextParsed.content });
94
- i++;
95
- }
96
-
97
- if (removedLines.length === 1 && addedLines.length === 1) {
98
- const { removedLine, addedLine } = renderIntraLineDiff(theme, replaceTabs(removedLines[0]!.content), replaceTabs(addedLines[0]!.content));
99
- result.push(theme.fg("toolDiffRemoved", `-${removedLines[0]!.lineNum} ${removedLine}`));
100
- result.push(theme.fg("toolDiffAdded", `+${addedLines[0]!.lineNum} ${addedLine}`));
101
- } else {
102
- for (const removed of removedLines) {
103
- result.push(theme.fg("toolDiffRemoved", `-${removed.lineNum} ${replaceTabs(removed.content)}`));
104
- }
105
- for (const added of addedLines) {
106
- result.push(theme.fg("toolDiffAdded", `+${added.lineNum} ${replaceTabs(added.content)}`));
107
- }
108
- }
109
- } else if (parsed.prefix === "+") {
110
- result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`));
111
- i++;
112
- } else {
113
- result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`));
114
- i++;
115
- }
116
- }
117
-
118
- return result.join("\n");
119
- }
1
+ import * as Diff from "diff";
2
+ import type { CrewTheme } from "./theme-adapter.ts";
3
+ import { asCrewTheme } from "./theme-adapter.ts";
4
+
5
+ interface ParsedDiffLine {
6
+ prefix: string;
7
+ lineNum: string; content: string;
8
+ }
9
+
10
+ interface DiffLineContent {
11
+ lineNum: string;
12
+ content: string;
13
+ }
14
+
15
+ function parseDiffLine(line: string): ParsedDiffLine | null {
16
+ const match = line.match(/^([+-\s])(\s*\d*)\s(.*)$/);
17
+ if (!match) return null;
18
+ return { prefix: match[1], lineNum: match[2], content: match[3] };
19
+ }
20
+
21
+ function replaceTabs(text: string): string {
22
+ return text.replace(/\t/g, " ");
23
+ }
24
+
25
+ function renderIntraLineDiff(theme: CrewTheme, oldContent: string, newContent: string): { removedLine: string; addedLine: string } {
26
+ const wordDiff = Diff.diffWords(oldContent, newContent);
27
+ let removedLine = "";
28
+ let addedLine = "";
29
+ let isFirstRemoved = true;
30
+ let isFirstAdded = true;
31
+
32
+ for (const part of wordDiff) {
33
+ if (part.removed) {
34
+ let value = part.value;
35
+ if (isFirstRemoved) {
36
+ const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
37
+ value = value.slice(leadingWs.length);
38
+ removedLine += leadingWs;
39
+ isFirstRemoved = false;
40
+ }
41
+ if (value) removedLine += theme.inverse?.(value) ?? value;
42
+ } else if (part.added) {
43
+ let value = part.value;
44
+ if (isFirstAdded) {
45
+ const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
46
+ value = value.slice(leadingWs.length);
47
+ addedLine += leadingWs;
48
+ isFirstAdded = false;
49
+ }
50
+ if (value) addedLine += theme.inverse?.(value) ?? value;
51
+ } else {
52
+ removedLine += part.value;
53
+ addedLine += part.value;
54
+ }
55
+ }
56
+
57
+ return { removedLine, addedLine };
58
+ }
59
+
60
+ export interface RenderDiffOptions {
61
+ filePath?: string;
62
+ theme?: unknown;
63
+ }
64
+
65
+ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
66
+ const theme = asCrewTheme(options.theme);
67
+ const lines = diffText.split("\n");
68
+ const result: string[] = [];
69
+ let i = 0;
70
+
71
+ while (i < lines.length) {
72
+ const line = lines[i] ?? "";
73
+ const parsed = parseDiffLine(line);
74
+ if (!parsed) {
75
+ result.push(theme.fg("toolDiffContext", line));
76
+ i++;
77
+ continue;
78
+ }
79
+
80
+ if (parsed.prefix === "-") {
81
+ const removedLines: DiffLineContent[] = [];
82
+ while (i < lines.length) {
83
+ const nextParsed = parseDiffLine(lines[i] ?? "");
84
+ if (!nextParsed || nextParsed.prefix !== "-") break;
85
+ removedLines.push({ lineNum: nextParsed.lineNum, content: nextParsed.content });
86
+ i++;
87
+ }
88
+
89
+ const addedLines: DiffLineContent[] = [];
90
+ while (i < lines.length) {
91
+ const nextParsed = parseDiffLine(lines[i] ?? "");
92
+ if (!nextParsed || nextParsed.prefix !== "+") break;
93
+ addedLines.push({ lineNum: nextParsed.lineNum, content: nextParsed.content });
94
+ i++;
95
+ }
96
+
97
+ if (removedLines.length === 1 && addedLines.length === 1) {
98
+ const { removedLine, addedLine } = renderIntraLineDiff(theme, replaceTabs(removedLines[0]!.content), replaceTabs(addedLines[0]!.content));
99
+ result.push(theme.fg("toolDiffRemoved", `-${removedLines[0]!.lineNum} ${removedLine}`));
100
+ result.push(theme.fg("toolDiffAdded", `+${addedLines[0]!.lineNum} ${addedLine}`));
101
+ } else {
102
+ for (const removed of removedLines) {
103
+ result.push(theme.fg("toolDiffRemoved", `-${removed.lineNum} ${replaceTabs(removed.content)}`));
104
+ }
105
+ for (const added of addedLines) {
106
+ result.push(theme.fg("toolDiffAdded", `+${added.lineNum} ${replaceTabs(added.content)}`));
107
+ }
108
+ }
109
+ } else if (parsed.prefix === "+") {
110
+ result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`));
111
+ i++;
112
+ } else {
113
+ result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`));
114
+ i++;
115
+ }
116
+ }
117
+
118
+ return result.join("\n");
119
+ }
@@ -22,6 +22,7 @@ import { dashboardActionForKey } from "./keybinding-map.ts";
22
22
  import type { RunSnapshotCache, RunUiSnapshot } from "./snapshot-types.ts";
23
23
  import { spinnerBucket, spinnerFrame } from "./spinner.ts";
24
24
  import type { MetricRegistry } from "../observability/metric-registry.ts";
25
+ import { resolveRealContainedPath } from "../utils/safe-paths.ts";
25
26
 
26
27
  interface DashboardComponent {
27
28
  invalidate(): void;
@@ -67,9 +68,11 @@ function renderLines(lines: string[], width: number): string[] {
67
68
 
68
69
  function readProgressPreview(run: TeamRunManifest, maxLines = 5): string[] {
69
70
  const progress = [...run.artifacts].reverse().find((artifact) => artifact.kind === "progress");
70
- if (!progress || !fs.existsSync(progress.path)) return ["Progress: (none)"];
71
+ if (!progress) return ["Progress: (none)"];
71
72
  try {
72
- return ["Progress:", ...fs.readFileSync(progress.path, "utf-8").split(/\r?\n/).filter(Boolean).slice(0, maxLines)];
73
+ const progressPath = resolveRealContainedPath(run.artifactsRoot, progress.path);
74
+ if (!fs.existsSync(progressPath)) return ["Progress: (none)"];
75
+ return ["Progress:", ...fs.readFileSync(progressPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(0, maxLines)];
73
76
  } catch (error) {
74
77
  const message = error instanceof Error ? error.message : String(error);
75
78
  return [`Progress: failed to read (${message})`];
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { readCrewAgents, agentsPath } from "../runtime/crew-agent-records.ts";
4
+ import { readCrewAgents, agentsPath, agentOutputPath } from "../runtime/crew-agent-records.ts";
5
5
  import type { CrewAgentRecord } from "../runtime/crew-agent-runtime.ts";
6
6
  import { isActiveRunStatus } from "../runtime/process-status.ts";
7
7
  import { readEvents, type TeamEvent } from "../state/event-log.ts";
@@ -82,8 +82,16 @@ function mailboxStamp(manifest: TeamRunManifest): FileStamp {
82
82
  return combineStamps(stamps);
83
83
  }
84
84
 
85
- function outputStamp(agents: CrewAgentRecord[]): FileStamp {
86
- return combineStamps(agents.map((agent) => stampFile(agent.outputPath)));
85
+ function safeAgentOutputPath(manifest: TeamRunManifest, agent: CrewAgentRecord): string | undefined {
86
+ try {
87
+ return agentOutputPath(manifest, agent.taskId);
88
+ } catch {
89
+ return undefined;
90
+ }
91
+ }
92
+
93
+ function outputStamp(manifest: TeamRunManifest, agents: CrewAgentRecord[]): FileStamp {
94
+ return combineStamps(agents.map((agent) => stampFile(safeAgentOutputPath(manifest, agent))));
87
95
  }
88
96
 
89
97
  function sameStamp(a: FileStamp, b: FileStamp): boolean {
@@ -134,9 +142,12 @@ function tailLines(filePath: string, limit: number): string[] {
134
142
  }
135
143
  }
136
144
 
137
- function recentOutputLines(agents: CrewAgentRecord[], limit: number): string[] {
145
+ function recentOutputLines(manifest: TeamRunManifest, agents: CrewAgentRecord[], limit: number): string[] {
138
146
  const fromProgress = agents.flatMap((agent) => agent.progress?.recentOutput ?? []);
139
- const fromFiles = agents.flatMap((agent) => agent.outputPath ? tailLines(agent.outputPath, limit) : []);
147
+ const fromFiles = agents.flatMap((agent) => {
148
+ const outputPath = safeAgentOutputPath(manifest, agent);
149
+ return outputPath ? tailLines(outputPath, limit) : [];
150
+ });
140
151
  return [...fromProgress, ...fromFiles].map((line) => line.replace(/\s+/g, " ").trim()).filter(Boolean).slice(-limit);
141
152
  }
142
153
 
@@ -244,7 +255,7 @@ function stampsFor(manifest: TeamRunManifest, agents: CrewAgentRecord[]): Snapsh
244
255
  agents: stampFile(agentsPath(manifest)),
245
256
  events: stampFile(manifest.eventsPath),
246
257
  mailbox: mailboxStamp(manifest),
247
- output: outputStamp(agents),
258
+ output: outputStamp(manifest, agents),
248
259
  };
249
260
  }
250
261
 
@@ -305,7 +316,7 @@ export function createRunSnapshotCache(cwd: string, options: RunSnapshotCacheOpt
305
316
  usage: usageFrom(tasks, agents),
306
317
  mailbox,
307
318
  recentEvents: safeRecentEvents(loaded.manifest.eventsPath, recentEventsLimit),
308
- recentOutputLines: recentOutputLines(agents, recentOutputLimit),
319
+ recentOutputLines: recentOutputLines(loaded.manifest, agents, recentOutputLimit),
309
320
  };
310
321
  const stamps = stampsFor(loaded.manifest, agents);
311
322
  const snapshot: RunUiSnapshot = { ...base, fetchedAt: Date.now(), signature: signatureFor(base, stamps) };
@@ -320,7 +331,7 @@ export function createRunSnapshotCache(cwd: string, options: RunSnapshotCacheOpt
320
331
  agents: stampFile(agentsPath(manifest)),
321
332
  events: stampFile(manifest.eventsPath),
322
333
  mailbox: mailboxStamp(manifest),
323
- output: outputStamp(previous.snapshot.agents),
334
+ output: outputStamp(previous.snapshot.manifest, previous.snapshot.agents),
324
335
  };
325
336
  }
326
337
 
package/src/ui/spinner.ts CHANGED
@@ -1,17 +1,17 @@
1
- export const SUBAGENT_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
2
- export const SUBAGENT_SPINNER_FRAME_MS = 160;
3
-
4
- export function spinnerBucket(now = Date.now(), frameMs = SUBAGENT_SPINNER_FRAME_MS): number {
5
- return Math.floor(now / Math.max(1, frameMs));
6
- }
7
-
8
- function hashKey(key: string): number {
9
- let hash = 0;
10
- for (let index = 0; index < key.length; index += 1) hash = (hash * 31 + key.charCodeAt(index)) >>> 0;
11
- return hash;
12
- }
13
-
14
- export function spinnerFrame(key = "", now = Date.now()): string {
15
- const offset = key ? hashKey(key) % SUBAGENT_SPINNER_FRAMES.length : 0;
16
- return SUBAGENT_SPINNER_FRAMES[(spinnerBucket(now) + offset) % SUBAGENT_SPINNER_FRAMES.length] ?? SUBAGENT_SPINNER_FRAMES[0];
17
- }
1
+ export const SUBAGENT_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
2
+ export const SUBAGENT_SPINNER_FRAME_MS = 160;
3
+
4
+ export function spinnerBucket(now = Date.now(), frameMs = SUBAGENT_SPINNER_FRAME_MS): number {
5
+ return Math.floor(now / Math.max(1, frameMs));
6
+ }
7
+
8
+ function hashKey(key: string): number {
9
+ let hash = 0;
10
+ for (let index = 0; index < key.length; index += 1) hash = (hash * 31 + key.charCodeAt(index)) >>> 0;
11
+ return hash;
12
+ }
13
+
14
+ export function spinnerFrame(key = "", now = Date.now()): string {
15
+ const offset = key ? hashKey(key) % SUBAGENT_SPINNER_FRAMES.length : 0;
16
+ return SUBAGENT_SPINNER_FRAMES[(spinnerBucket(now) + offset) % SUBAGENT_SPINNER_FRAMES.length] ?? SUBAGENT_SPINNER_FRAMES[0];
17
+ }
@@ -1,54 +1,54 @@
1
- import type { CrewTheme, CrewThemeColor } from "./theme-adapter.ts";
2
-
3
- export type RunStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "stopped" | "blocked" | (string & {});
4
-
5
- export function colorForStatus(status: RunStatus): CrewThemeColor {
6
- switch (status) {
7
- case "running":
8
- return "accent";
9
- case "completed":
10
- return "success";
11
- case "failed":
12
- case "stale":
13
- return "error";
14
- case "cancelled":
15
- case "blocked":
16
- case "stopped":
17
- return "warning";
18
- case "queued":
19
- default:
20
- return "dim";
21
- }
22
- }
23
-
24
- export function iconForStatus(status: RunStatus, options?: { runningGlyph?: string }): string {
25
- const glyph = options?.runningGlyph ?? "▶";
26
- switch (status) {
27
- case "completed":
28
- return "✓";
29
- case "failed":
30
- case "stale":
31
- return "✗";
32
- case "cancelled":
33
- case "stopped":
34
- return "■";
35
- case "running":
36
- return glyph;
37
- case "queued":
38
- return "◦";
39
- case "blocked":
40
- return "⏸";
41
- default:
42
- return "·";
43
- }
44
- }
45
-
46
- export function colorForActivity(activityState: string | undefined): CrewThemeColor {
47
- if (activityState === "needs_attention") return "warning";
48
- if (activityState === "stale") return "error";
49
- return "dim";
50
- }
51
-
52
- export function applyStatusColor(theme: CrewTheme, status: RunStatus, text: string): string {
53
- return theme.fg(colorForStatus(status), text);
54
- }
1
+ import type { CrewTheme, CrewThemeColor } from "./theme-adapter.ts";
2
+
3
+ export type RunStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "stopped" | "blocked" | (string & {});
4
+
5
+ export function colorForStatus(status: RunStatus): CrewThemeColor {
6
+ switch (status) {
7
+ case "running":
8
+ return "accent";
9
+ case "completed":
10
+ return "success";
11
+ case "failed":
12
+ case "stale":
13
+ return "error";
14
+ case "cancelled":
15
+ case "blocked":
16
+ case "stopped":
17
+ return "warning";
18
+ case "queued":
19
+ default:
20
+ return "dim";
21
+ }
22
+ }
23
+
24
+ export function iconForStatus(status: RunStatus, options?: { runningGlyph?: string }): string {
25
+ const glyph = options?.runningGlyph ?? "▶";
26
+ switch (status) {
27
+ case "completed":
28
+ return "✓";
29
+ case "failed":
30
+ case "stale":
31
+ return "✗";
32
+ case "cancelled":
33
+ case "stopped":
34
+ return "■";
35
+ case "running":
36
+ return glyph;
37
+ case "queued":
38
+ return "◦";
39
+ case "blocked":
40
+ return "⏸";
41
+ default:
42
+ return "·";
43
+ }
44
+ }
45
+
46
+ export function colorForActivity(activityState: string | undefined): CrewThemeColor {
47
+ if (activityState === "needs_attention") return "warning";
48
+ if (activityState === "stale") return "error";
49
+ return "dim";
50
+ }
51
+
52
+ export function applyStatusColor(theme: CrewTheme, status: RunStatus, text: string): string {
53
+ return theme.fg(colorForStatus(status), text);
54
+ }