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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +27 -0
- package/README.md +5 -0
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +11 -11
- package/agents/writer.md +11 -11
- package/docs/refactor-tasks-phase3.md +394 -394
- package/docs/refactor-tasks-phase4.md +564 -564
- package/docs/refactor-tasks-phase5.md +402 -402
- package/docs/refactor-tasks-phase6.md +662 -662
- package/docs/research-extension-examples.md +297 -297
- package/docs/research-extension-system.md +324 -324
- package/docs/research-optimization-plan.md +548 -548
- package/docs/research-pi-coding-agent.md +357 -357
- package/docs/research-source-pi-crew-reference.md +174 -174
- package/docs/resource-formats.md +10 -8
- package/docs/runtime-flow.md +148 -148
- package/docs/source-runtime-refactor-map.md +83 -83
- package/docs/usage.md +6 -0
- package/index.ts +6 -6
- package/package.json +3 -3
- package/schema.json +2 -2
- package/src/agents/agent-serializer.ts +34 -34
- package/src/config/config.ts +8 -4
- package/src/extension/cross-extension-rpc.ts +82 -82
- package/src/extension/import-index.ts +18 -2
- package/src/extension/register.ts +11 -1
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +30 -6
- package/src/extension/registration/subagent-tools.ts +8 -3
- package/src/extension/result-watcher.ts +98 -98
- package/src/extension/run-import.ts +12 -2
- package/src/extension/run-index.ts +12 -2
- package/src/extension/run-maintenance.ts +24 -24
- package/src/extension/team-tool/api.ts +54 -14
- package/src/extension/team-tool/cancel.ts +31 -31
- package/src/extension/team-tool/doctor.ts +179 -179
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +79 -79
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/status.ts +73 -73
- package/src/observability/correlation.ts +35 -35
- package/src/observability/event-to-metric.ts +54 -54
- package/src/observability/exporters/adapter.ts +24 -24
- package/src/observability/exporters/otlp-exporter.ts +65 -65
- package/src/observability/exporters/prometheus-exporter.ts +47 -47
- package/src/observability/metric-registry.ts +72 -72
- package/src/observability/metric-retention.ts +46 -46
- package/src/observability/metric-sink.ts +51 -51
- package/src/observability/metrics-primitives.ts +166 -166
- package/src/prompt/prompt-runtime.ts +68 -68
- package/src/runtime/agent-control.ts +64 -64
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -113
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/background-runner.ts +53 -53
- package/src/runtime/crash-recovery.ts +56 -56
- package/src/runtime/crew-agent-records.ts +54 -9
- package/src/runtime/crew-agent-runtime.ts +58 -58
- package/src/runtime/deadletter.ts +36 -36
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +88 -88
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +80 -80
- package/src/runtime/live-agent-control.ts +87 -78
- package/src/runtime/live-agent-manager.ts +85 -85
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +299 -299
- package/src/runtime/manifest-cache.ts +248 -212
- package/src/runtime/model-fallback.ts +261 -261
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +99 -99
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/policy-engine.ts +78 -78
- package/src/runtime/post-exit-stdio-guard.ts +86 -86
- package/src/runtime/process-status.ts +56 -56
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +59 -59
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +28 -28
- package/src/runtime/subagent-manager.ts +80 -12
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-output-context.ts +127 -106
- package/src/runtime/task-runner/live-executor.ts +98 -98
- package/src/runtime/task-runner/progress.ts +111 -111
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/team-runner.ts +1 -1
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/schema/config-schema.ts +21 -21
- package/src/schema/team-tool-schema.ts +100 -100
- package/src/state/artifact-store.ts +122 -108
- package/src/state/contracts.ts +105 -105
- package/src/state/jsonl-writer.ts +77 -77
- package/src/state/mailbox.ts +67 -22
- package/src/state/state-store.ts +36 -5
- package/src/state/task-claims.ts +42 -42
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/discover-teams.ts +27 -5
- package/src/teams/team-serializer.ts +38 -36
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/loaders.ts +158 -158
- package/src/ui/mascot.ts +441 -441
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/run-dashboard.ts +5 -2
- package/src/ui/run-snapshot-cache.ts +19 -8
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +54 -54
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/transcript-viewer.ts +15 -1
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/file-coalescer.ts +84 -84
- package/src/utils/frontmatter.ts +36 -36
- package/src/utils/fs-watch.ts +31 -31
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +12 -12
- package/src/utils/names.ts +26 -26
- package/src/utils/paths.ts +3 -2
- package/src/utils/safe-paths.ts +34 -0
- package/src/utils/sleep.ts +32 -32
- package/src/utils/timings.ts +31 -31
- package/src/utils/visual.ts +159 -159
- package/src/workflows/discover-workflows.ts +30 -3
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +29 -29
- package/workflows/fast-fix.workflow.md +22 -22
- package/workflows/implementation.workflow.md +38 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
package/src/ui/render-diff.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/ui/run-dashboard.ts
CHANGED
|
@@ -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
|
|
71
|
+
if (!progress) return ["Progress: (none)"];
|
|
71
72
|
try {
|
|
72
|
-
|
|
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
|
|
86
|
-
|
|
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) =>
|
|
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
|
+
}
|
package/src/ui/status-colors.ts
CHANGED
|
@@ -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
|
+
}
|