feat(pi-workflow): add human-readable run labels
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
executeHostDecision,
|
||||
type ContextTransitionRuntime,
|
||||
} from "./context-transition.ts";
|
||||
import { deriveRunLabels } from "./run-label.ts";
|
||||
import {
|
||||
readVerifiedPlanArtifact,
|
||||
readVerifiedReportArtifact,
|
||||
@@ -88,6 +89,7 @@ export class FocusedWorkflowDashboard implements Component {
|
||||
private readonly runs: WorkflowRun[];
|
||||
private readonly theme: DashboardTheme;
|
||||
private readonly done: (result: string | null) => void;
|
||||
private readonly labels: string[];
|
||||
private selected = 0;
|
||||
private detail = false;
|
||||
|
||||
@@ -100,6 +102,7 @@ export class FocusedWorkflowDashboard implements Component {
|
||||
this.runs = runs;
|
||||
this.theme = theme;
|
||||
this.done = done;
|
||||
this.labels = deriveRunLabels(runs);
|
||||
const initial = initialRunId
|
||||
? runs.findIndex((run) => run.runId === initialRunId)
|
||||
: -1;
|
||||
@@ -150,7 +153,7 @@ export class FocusedWorkflowDashboard implements Component {
|
||||
for (const [visibleIndex, run] of visibleRuns.entries()) {
|
||||
const index = windowStart + visibleIndex;
|
||||
const prefix = index === this.selected ? "> " : " ";
|
||||
const name = run.featureName ?? run.request.slice(0, 24);
|
||||
const name = this.labels[index]!;
|
||||
const status = this.theme.fg(
|
||||
statusColor(run.status),
|
||||
run.status.toUpperCase().padEnd(9),
|
||||
@@ -171,7 +174,7 @@ export class FocusedWorkflowDashboard implements Component {
|
||||
const run = this.runs[this.selected]!;
|
||||
const gate = selectedGate(run);
|
||||
lines.push("", this.theme.fg("dim", "─".repeat(Math.max(1, width))));
|
||||
lines.push(this.theme.bold(run.featureName ?? run.request));
|
||||
lines.push(this.theme.bold(this.labels[this.selected]!));
|
||||
if (run.workspace.branch) lines.push(this.theme.fg("muted", run.workspace.branch));
|
||||
lines.push("", `${this.theme.bold("Progress")} ${stageLine(run, this.theme)}`);
|
||||
if (gate) {
|
||||
@@ -202,10 +205,10 @@ export class FocusedWorkflowDashboard implements Component {
|
||||
invalidate() {}
|
||||
}
|
||||
|
||||
function runSummary(run: WorkflowRun) {
|
||||
function runSummary(run: WorkflowRun, label: string) {
|
||||
const gate = selectedGate(run);
|
||||
return [
|
||||
`${run.runId} · ${run.status} · revision ${run.revision}`,
|
||||
`${label} · ${run.status} · revision ${run.revision}`,
|
||||
`Stage: ${activeStage(run)}`,
|
||||
...(gate ? [`Gate: ${gate.kind} (${gate.id})`] : []),
|
||||
...(run.workspace.branch ? [`Branch: ${run.workspace.branch}`] : []),
|
||||
@@ -443,6 +446,7 @@ async function actionSheet(
|
||||
runtime: ContextTransitionRuntime,
|
||||
ctx: ExtensionCommandContext,
|
||||
run: WorkflowRun,
|
||||
label: string,
|
||||
) {
|
||||
const actions = [
|
||||
"Inspect run",
|
||||
@@ -461,8 +465,8 @@ async function actionSheet(
|
||||
: []),
|
||||
...(run.workspace.root ? ["Show worktree path"] : []),
|
||||
];
|
||||
const action = await ctx.ui.select(run.featureName ?? run.runId, actions);
|
||||
if (action === "Inspect run") ctx.ui.notify(runSummary(run), "info");
|
||||
const action = await ctx.ui.select(label, actions);
|
||||
if (action === "Inspect run") ctx.ui.notify(runSummary(run, label), "info");
|
||||
else if (action === "Open plan") {
|
||||
const plan = await readVerifiedPlanArtifact(run.planArtifact!);
|
||||
await ctx.ui.editor("Workflow plan", plan.slice(0, 32 * 1024));
|
||||
@@ -495,11 +499,11 @@ async function actionSheet(
|
||||
decision.strategy === "smart-compact"
|
||||
) {
|
||||
ctx.ui.notify(
|
||||
`Workflow ${run.runId} is ready for smart_compact. Ask the assistant to invoke it and report the Gate-bound outcome through pi_workflow.`,
|
||||
`Workflow ${label} is ready for smart_compact. Ask the assistant to invoke it and report the Gate-bound outcome through pi_workflow.`,
|
||||
"info",
|
||||
);
|
||||
} else {
|
||||
ctx.ui.notify(`Workflow ${run.runId}: ${result.status}`, "info");
|
||||
ctx.ui.notify(`Workflow ${label}: ${result.status}`, "info");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,12 +524,20 @@ export async function openWorkflowDashboard(
|
||||
const runs = (await runtime.store.listRuns())
|
||||
.filter((run) => !sessionId || run.parentSessionIds.includes(sessionId))
|
||||
.sort((left, right) => right.revision - left.revision);
|
||||
if (initialRunId && !runs.some((run) => run.runId === initialRunId)) {
|
||||
ctx.ui.notify(
|
||||
`Workflow Run ${initialRunId} was not found for this session.`,
|
||||
"error",
|
||||
);
|
||||
return {};
|
||||
}
|
||||
const labels = deriveRunLabels(runs);
|
||||
if (!ctx.hasUI || ctx.mode !== "tui") {
|
||||
const visibleRuns = runs.slice(0, 20);
|
||||
const omitted = runs.length - visibleRuns.length;
|
||||
ctx.ui.notify(
|
||||
runs.length > 0
|
||||
? `${visibleRuns.map(runSummary).join("\n\n")}${
|
||||
? `${visibleRuns.map((run, index) => runSummary(run, labels[index]!)).join("\n\n")}${
|
||||
omitted > 0 ? `\n\n[${omitted} older runs omitted]` : ""
|
||||
}`
|
||||
: "No Workflow Runs.",
|
||||
@@ -552,6 +564,7 @@ export async function openWorkflowDashboard(
|
||||
},
|
||||
);
|
||||
if (!selected) return {};
|
||||
const run = runs.find((candidate) => candidate.runId === selected);
|
||||
return run ? actionSheet(pi, runtime, ctx, run) : {};
|
||||
const index = runs.findIndex((candidate) => candidate.runId === selected);
|
||||
const run = runs[index];
|
||||
return run ? actionSheet(pi, runtime, ctx, run, labels[index]!) : {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user