pipeline-worker 0.1.26 → 0.1.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 (54) hide show
  1. package/README.md +6 -2
  2. package/dist/agent/claude.js +32 -1
  3. package/dist/agent/claude.js.map +1 -1
  4. package/dist/agent/types.d.ts +18 -0
  5. package/dist/cli.js +30 -4
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config/loader.js +25 -3
  8. package/dist/config/loader.js.map +1 -1
  9. package/dist/forge/github.js +7 -0
  10. package/dist/forge/github.js.map +1 -1
  11. package/dist/forge/gitlab.js +7 -0
  12. package/dist/forge/gitlab.js.map +1 -1
  13. package/dist/forge/types.d.ts +9 -0
  14. package/dist/state/runState.d.ts +10 -1
  15. package/dist/state/runState.js +20 -2
  16. package/dist/state/runState.js.map +1 -1
  17. package/dist/types.d.ts +4 -0
  18. package/dist/ui/format.d.ts +8 -0
  19. package/dist/ui/format.js +17 -0
  20. package/dist/ui/format.js.map +1 -0
  21. package/dist/ui/renderer.d.ts +41 -0
  22. package/dist/ui/renderer.js +93 -0
  23. package/dist/ui/renderer.js.map +1 -0
  24. package/dist/ui/runTree.d.ts +106 -0
  25. package/dist/ui/runTree.js +133 -0
  26. package/dist/ui/runTree.js.map +1 -0
  27. package/dist/ui/sessions.js +11 -4
  28. package/dist/ui/sessions.js.map +1 -1
  29. package/dist/ui/steps.d.ts +82 -35
  30. package/dist/ui/steps.js +158 -97
  31. package/dist/ui/steps.js.map +1 -1
  32. package/dist/ui/treeRenderer.d.ts +89 -0
  33. package/dist/ui/treeRenderer.js +271 -0
  34. package/dist/ui/treeRenderer.js.map +1 -0
  35. package/dist/workflow/adoptBranch.js +16 -10
  36. package/dist/workflow/adoptBranch.js.map +1 -1
  37. package/dist/workflow/captureIntent.d.ts +7 -2
  38. package/dist/workflow/captureIntent.js +1 -25
  39. package/dist/workflow/captureIntent.js.map +1 -1
  40. package/dist/workflow/openMergeRequest.d.ts +1 -1
  41. package/dist/workflow/openMergeRequest.js +8 -7
  42. package/dist/workflow/openMergeRequest.js.map +1 -1
  43. package/dist/workflow/orchestrate.d.ts +1 -1
  44. package/dist/workflow/orchestrate.js +61 -39
  45. package/dist/workflow/orchestrate.js.map +1 -1
  46. package/dist/workflow/runPlan.d.ts +18 -0
  47. package/dist/workflow/runPlan.js +53 -0
  48. package/dist/workflow/runPlan.js.map +1 -0
  49. package/dist/workflow/syncTargetBranch.d.ts +37 -0
  50. package/dist/workflow/syncTargetBranch.js +83 -0
  51. package/dist/workflow/syncTargetBranch.js.map +1 -0
  52. package/dist/workflow/watchPipeline.js +61 -23
  53. package/dist/workflow/watchPipeline.js.map +1 -1
  54. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Rendering strategies for the run tree (ui/runTree.ts). Two implementations:
3
+ *
4
+ * - LineRenderer (here): append-only scrolled lines — used when stdout is not
5
+ * a TTY (CI logs, piped output) or when PIPELINE_WORKER_PLAIN_OUTPUT=true.
6
+ * Zero cursor movement; colors are applied via node:util's styleText, which
7
+ * already disables itself on non-color streams.
8
+ * - TreeRenderer (ui/treeRenderer.ts): the live TTY dashboard with a pinned
9
+ * bottom region.
10
+ *
11
+ * Renderers receive fine-grained TreeEvents from the facade (ui/steps.ts) and
12
+ * may ignore the granularity (TreeRenderer just repaints). log() is the one
13
+ * channel for freeform text — notes, agent output, warnings — so a renderer
14
+ * that owns the screen can route it above its pinned region.
15
+ */
16
+ import { styleText } from 'node:util';
17
+ import { formatTokens } from './format.js';
18
+ export function formatElapsed(ms) {
19
+ return `${(ms / 1000).toFixed(1)}s`;
20
+ }
21
+ /** The parenthesized tail of a finished step: '(3.2s · 1.9k tok)', or '' when neither figure is known. */
22
+ export function formatStepFigures(node) {
23
+ const parts = [];
24
+ if (node.durationMs !== undefined)
25
+ parts.push(formatElapsed(node.durationMs));
26
+ if (node.tokens !== undefined)
27
+ parts.push(formatTokens(node.tokens));
28
+ return parts.length > 0 ? ` (${parts.join(' · ')})` : '';
29
+ }
30
+ /** 'attempt N/M' when both sides are known, else ''. */
31
+ export function formatAttempt(node) {
32
+ return node.attempt !== undefined && node.maxAttempts !== undefined ? `attempt ${node.attempt}/${node.maxAttempts}` : '';
33
+ }
34
+ const FINISH_GLYPH = { done: '✓', failed: '✗', skipped: '–' };
35
+ const FINISH_COLOR = { done: 'green', failed: 'red', skipped: 'dim' };
36
+ const FINAL_LINE = {
37
+ done: '🎉 Done',
38
+ failed: '✗ Run failed',
39
+ escalated: '🚨 Stopped for human review',
40
+ interrupted: '⏹ Interrupted',
41
+ };
42
+ /**
43
+ * Append-only narration, one block per step: a bold start line with the
44
+ * detail beneath it, phase changes as indented arrows, and a colored
45
+ * ✓/✗/– settle line with duration and token figures. Mirrors the shape of
46
+ * the old numbered-stage output so non-TTY logs stay diffable and greppable.
47
+ */
48
+ export class LineRenderer {
49
+ out;
50
+ // The default defers the console.log lookup to call time (not construction),
51
+ // so a test — or the TreeRenderer's console interception — that swaps
52
+ // console.log after this renderer exists still receives the output.
53
+ constructor(out = (line) => console.log(line)) {
54
+ this.out = out;
55
+ }
56
+ onEvent(event) {
57
+ if (event.kind === 'start') {
58
+ const attempt = formatAttempt(event.node);
59
+ this.out('');
60
+ this.out(styleText(['bold', 'cyan'], `▶ ${event.node.label}${attempt ? ` — ${attempt}` : ''}`));
61
+ if (event.node.detail)
62
+ this.out(styleText('dim', ` ${event.node.detail}`));
63
+ return;
64
+ }
65
+ if (event.kind === 'update' && event.node.status === 'running' && event.node.detail) {
66
+ this.out(styleText('dim', ` → ${event.node.detail}`));
67
+ return;
68
+ }
69
+ if (event.kind === 'finish') {
70
+ const status = event.node.status;
71
+ const glyph = FINISH_GLYPH[status] ?? '?';
72
+ const skipTail = status === 'skipped' ? ' (skipped)' : '';
73
+ const detail = event.node.detail ? ` — ${event.node.detail}` : '';
74
+ this.out(styleText(FINISH_COLOR[status] ?? 'dim', `${glyph} ${event.node.label}${skipTail}${detail}${formatStepFigures(event.node)}`));
75
+ }
76
+ // 'add', 'tokens', and 'header' need no line of their own: tokens show on
77
+ // the finish line, and header changes only matter to the live dashboard.
78
+ }
79
+ log(text) {
80
+ this.out(text);
81
+ }
82
+ stop(status, detail, tree) {
83
+ if (status === 'running')
84
+ return;
85
+ const total = tree.totalTokens();
86
+ const tokensPart = total > 0 ? ` (${formatTokens(total)})` : '';
87
+ this.out('');
88
+ this.out(styleText('bold', `${FINAL_LINE[status]}${tokensPart}`));
89
+ if (detail)
90
+ this.out(styleText('dim', ` ${detail}`));
91
+ }
92
+ }
93
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/ui/renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The run's step tree: a pure in-memory data model of every workflow step,
3
+ * its status, timing, attempt counters, and best-effort token spend. This is
4
+ * what the renderers (ui/renderer.ts, ui/treeRenderer.ts) draw from — it
5
+ * contains no ANSI, no console access, and never throws: an unknown step id
6
+ * is auto-added as a top-level node rather than crashing the workflow (the
7
+ * UI must never kill a run — see CLAUDE.md's never-throw contracts).
8
+ *
9
+ * Step identity is a string id ('capture', 'ci-watch', 'ci-watch/fix-2').
10
+ * Ids replaced the old hard-coded stage numbers ([7/14]) because the watch
11
+ * loop grows children dynamically — fix attempts, rebases — and stable ids
12
+ * don't need renumbering when a step is inserted.
13
+ */
14
+ export type StepStatus = 'pending' | 'running' | 'done' | 'failed' | 'skipped';
15
+ /** The whole run's terminal status, shown in the header line. */
16
+ export type RunStatus = 'running' | 'done' | 'failed' | 'escalated' | 'interrupted';
17
+ export interface StepNode {
18
+ /** Unique within the tree; children conventionally use 'parent/child' ids. */
19
+ id: string;
20
+ /** Short name in the left column: 'capture', 'ci-watch', 'fix 2'. */
21
+ label: string;
22
+ /** Right-hand description; mutable while the step runs (phase updates). */
23
+ detail: string;
24
+ status: StepStatus;
25
+ /** Epoch ms when the step entered 'running'. */
26
+ startedAt?: number;
27
+ /** Set when the step finishes ('done'/'failed'). */
28
+ durationMs?: number;
29
+ /** Best-effort agent tokens attributed to this step; absent means unknown, never zero. */
30
+ tokens?: number;
31
+ /** Rendered as 'attempt N/M' when both are present. */
32
+ attempt?: number;
33
+ maxAttempts?: number;
34
+ children: StepNode[];
35
+ }
36
+ /** What a skeleton declares per step up front — everything else starts pending/empty. */
37
+ export interface StepSeed {
38
+ id: string;
39
+ label: string;
40
+ detail: string;
41
+ }
42
+ export interface RunHeader {
43
+ /** The run's name: initially generic, updated once intent names the branch. */
44
+ title: string;
45
+ /** Short worktree identifier (last hex chars of the temp-branch uuid). */
46
+ worktreeShortId?: string;
47
+ status: RunStatus;
48
+ }
49
+ export type TreeEvent = {
50
+ kind: 'add';
51
+ node: StepNode;
52
+ } | {
53
+ kind: 'start';
54
+ node: StepNode;
55
+ } | {
56
+ kind: 'finish';
57
+ node: StepNode;
58
+ } | {
59
+ kind: 'update';
60
+ node: StepNode;
61
+ } | {
62
+ kind: 'tokens';
63
+ node: StepNode;
64
+ } | {
65
+ kind: 'header';
66
+ };
67
+ /** One renderable row of the flattened tree: the node, its depth, and whether it is the last sibling at each ancestor level (for branch glyphs). */
68
+ export interface TreeRow {
69
+ node: StepNode;
70
+ depth: number;
71
+ /** isLast[d] — whether the chain through this row is the final sibling at depth d. Drives '├─' vs '└─' and '│' vs ' ' continuation. */
72
+ isLast: boolean[];
73
+ }
74
+ export declare class RunTree {
75
+ private readonly onChange;
76
+ readonly roots: StepNode[];
77
+ readonly header: RunHeader;
78
+ private readonly index;
79
+ /** Tokens spent before this tree existed (a resumed run's persisted total) — folded into totalTokens(). */
80
+ private seededTokens;
81
+ constructor(skeleton: StepSeed[], header: {
82
+ title: string;
83
+ worktreeShortId?: string;
84
+ }, onChange: (event: TreeEvent) => void);
85
+ private insert;
86
+ get(id: string): StepNode | undefined;
87
+ /**
88
+ * The never-throw guarantee: any operation on an id nobody declared falls
89
+ * back to materializing that id as a fresh top-level node, so the workflow
90
+ * keeps narrating instead of dying on a UI bookkeeping mismatch.
91
+ */
92
+ private getOrAdd;
93
+ /** Adds a child under parentId (or top-level when parentId is undefined). Adding an id that already exists is a no-op returning the existing node. */
94
+ add(parentId: string | undefined, seed: StepSeed, extras?: Partial<StepNode>): StepNode;
95
+ start(id: string, patch?: Partial<Pick<StepNode, 'detail' | 'attempt' | 'maxAttempts'>>): StepNode;
96
+ finish(id: string, status: 'done' | 'failed' | 'skipped', patch?: Partial<Pick<StepNode, 'detail'>>): StepNode;
97
+ update(id: string, patch: Partial<Pick<StepNode, 'detail' | 'attempt' | 'maxAttempts'>>): StepNode;
98
+ addTokens(id: string, tokens: number): void;
99
+ /** Registers tokens spent before this tree existed (a resumed run's persisted total). */
100
+ seedTokens(tokens: number): void;
101
+ setHeader(patch: Partial<RunHeader>): void;
102
+ /** The run's total spend: every node's tokens plus any seeded pre-resume total. */
103
+ totalTokens(): number;
104
+ /** Depth-first rows for rendering, with last-sibling flags per ancestor level for branch glyphs. */
105
+ flatten(): TreeRow[];
106
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * The run's step tree: a pure in-memory data model of every workflow step,
3
+ * its status, timing, attempt counters, and best-effort token spend. This is
4
+ * what the renderers (ui/renderer.ts, ui/treeRenderer.ts) draw from — it
5
+ * contains no ANSI, no console access, and never throws: an unknown step id
6
+ * is auto-added as a top-level node rather than crashing the workflow (the
7
+ * UI must never kill a run — see CLAUDE.md's never-throw contracts).
8
+ *
9
+ * Step identity is a string id ('capture', 'ci-watch', 'ci-watch/fix-2').
10
+ * Ids replaced the old hard-coded stage numbers ([7/14]) because the watch
11
+ * loop grows children dynamically — fix attempts, rebases — and stable ids
12
+ * don't need renumbering when a step is inserted.
13
+ */
14
+ export class RunTree {
15
+ onChange;
16
+ roots = [];
17
+ header;
18
+ index = new Map();
19
+ /** Tokens spent before this tree existed (a resumed run's persisted total) — folded into totalTokens(). */
20
+ seededTokens = 0;
21
+ constructor(skeleton, header, onChange) {
22
+ this.onChange = onChange;
23
+ this.header = { ...header, status: 'running' };
24
+ for (const seed of skeleton)
25
+ this.insert(undefined, seed);
26
+ }
27
+ insert(parent, seed, extras = {}) {
28
+ const node = { ...seed, status: 'pending', children: [], ...extras };
29
+ (parent ? parent.children : this.roots).push(node);
30
+ this.index.set(node.id, node);
31
+ return node;
32
+ }
33
+ get(id) {
34
+ return this.index.get(id);
35
+ }
36
+ /**
37
+ * The never-throw guarantee: any operation on an id nobody declared falls
38
+ * back to materializing that id as a fresh top-level node, so the workflow
39
+ * keeps narrating instead of dying on a UI bookkeeping mismatch.
40
+ */
41
+ getOrAdd(id) {
42
+ const existing = this.index.get(id);
43
+ if (existing)
44
+ return existing;
45
+ const node = this.insert(undefined, { id, label: id, detail: '' });
46
+ this.onChange({ kind: 'add', node });
47
+ return node;
48
+ }
49
+ /** Adds a child under parentId (or top-level when parentId is undefined). Adding an id that already exists is a no-op returning the existing node. */
50
+ add(parentId, seed, extras = {}) {
51
+ const existing = this.index.get(seed.id);
52
+ if (existing)
53
+ return existing;
54
+ const parent = parentId === undefined ? undefined : this.getOrAdd(parentId);
55
+ const node = this.insert(parent, seed, extras);
56
+ this.onChange({ kind: 'add', node });
57
+ return node;
58
+ }
59
+ start(id, patch = {}) {
60
+ const node = this.getOrAdd(id);
61
+ node.status = 'running';
62
+ node.startedAt = Date.now();
63
+ node.durationMs = undefined;
64
+ if (patch.detail !== undefined)
65
+ node.detail = patch.detail;
66
+ if (patch.attempt !== undefined)
67
+ node.attempt = patch.attempt;
68
+ if (patch.maxAttempts !== undefined)
69
+ node.maxAttempts = patch.maxAttempts;
70
+ this.onChange({ kind: 'start', node });
71
+ return node;
72
+ }
73
+ finish(id, status, patch = {}) {
74
+ const node = this.getOrAdd(id);
75
+ node.status = status;
76
+ if (node.startedAt !== undefined && status !== 'skipped')
77
+ node.durationMs = Date.now() - node.startedAt;
78
+ if (patch.detail !== undefined)
79
+ node.detail = patch.detail;
80
+ this.onChange({ kind: 'finish', node });
81
+ return node;
82
+ }
83
+ update(id, patch) {
84
+ const node = this.getOrAdd(id);
85
+ if (patch.detail !== undefined)
86
+ node.detail = patch.detail;
87
+ if (patch.attempt !== undefined)
88
+ node.attempt = patch.attempt;
89
+ if (patch.maxAttempts !== undefined)
90
+ node.maxAttempts = patch.maxAttempts;
91
+ this.onChange({ kind: 'update', node });
92
+ return node;
93
+ }
94
+ addTokens(id, tokens) {
95
+ if (!Number.isFinite(tokens) || tokens <= 0)
96
+ return;
97
+ const node = this.getOrAdd(id);
98
+ node.tokens = (node.tokens ?? 0) + tokens;
99
+ this.onChange({ kind: 'tokens', node });
100
+ }
101
+ /** Registers tokens spent before this tree existed (a resumed run's persisted total). */
102
+ seedTokens(tokens) {
103
+ if (!Number.isFinite(tokens) || tokens <= 0)
104
+ return;
105
+ this.seededTokens += tokens;
106
+ this.onChange({ kind: 'header' });
107
+ }
108
+ setHeader(patch) {
109
+ Object.assign(this.header, patch);
110
+ this.onChange({ kind: 'header' });
111
+ }
112
+ /** The run's total spend: every node's tokens plus any seeded pre-resume total. */
113
+ totalTokens() {
114
+ let total = this.seededTokens;
115
+ for (const node of this.index.values())
116
+ total += node.tokens ?? 0;
117
+ return total;
118
+ }
119
+ /** Depth-first rows for rendering, with last-sibling flags per ancestor level for branch glyphs. */
120
+ flatten() {
121
+ const rows = [];
122
+ const walk = (nodes, depth, ancestors) => {
123
+ nodes.forEach((node, i) => {
124
+ const isLast = [...ancestors, i === nodes.length - 1];
125
+ rows.push({ node, depth, isLast });
126
+ walk(node.children, depth + 1, isLast);
127
+ });
128
+ };
129
+ walk(this.roots, 0, []);
130
+ return rows;
131
+ }
132
+ }
133
+ //# sourceMappingURL=runTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTree.js","sourceRoot":"","sources":["../../src/ui/runTree.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  * narration style ui/steps.ts uses for a live run.
5
5
  */
6
6
  import { styleText } from 'node:util';
7
+ import { formatTokens } from './format.js';
7
8
  const PHASE_COLOR = {
8
9
  diff: 'dim',
9
10
  intent: 'dim',
@@ -25,12 +26,16 @@ function formatBranchColumn(branch) {
25
26
  function formatMrColumn(mrIid) {
26
27
  return mrIid !== undefined ? `#${mrIid}` : '-';
27
28
  }
29
+ /** '-' when the run's adapter never reported usage (pi/copilot, or a pre-token-accounting state file) — absence means unknown, never zero. */
30
+ function formatTokensColumn(totalTokens) {
31
+ return (totalTokens !== undefined ? formatTokens(totalTokens) : '-').padEnd(9);
32
+ }
28
33
  function formatSessionRow(session) {
29
34
  const { state } = session;
30
35
  const branch = formatBranchColumn(state.branch);
31
36
  const mr = formatMrColumn(state.mrIid);
32
37
  const attempts = `${String(state.ciFixAttempt).padEnd(6)} ${String(state.conflictAttempt).padEnd(8)}`;
33
- return `${branch} ${formatPhase(state.phase)} ${attempts} ${mr.padEnd(7)} ${formatTimestamp(state.updatedAt)}`;
38
+ return `${branch} ${formatPhase(state.phase)} ${attempts} ${mr.padEnd(7)} ${formatTokensColumn(state.totalTokens)} ${formatTimestamp(state.updatedAt)}`;
34
39
  }
35
40
  /** `pipeline-worker sessions` with no --branch: one line per persisted run, most recently updated first. */
36
41
  export function printSessionList(sessions) {
@@ -38,7 +43,7 @@ export function printSessionList(sessions) {
38
43
  console.log('pipeline-worker: no sessions found in this repo (.pipeline-worker/state/ is empty).');
39
44
  return;
40
45
  }
41
- console.log(styleText('bold', `${'BRANCH'.padEnd(40)} ${'PHASE'.padEnd(9)} CI-FIX CONFLICT MR/PR UPDATED`));
46
+ console.log(styleText('bold', `${'BRANCH'.padEnd(40)} ${'PHASE'.padEnd(9)} CI-FIX CONFLICT MR/PR ${'TOKENS'.padEnd(9)} UPDATED`));
42
47
  for (const session of sessions) {
43
48
  console.log(formatSessionRow(session));
44
49
  }
@@ -54,7 +59,8 @@ function formatOptionalMetaParts(state) {
54
59
  }
55
60
  function formatHistoryEntry(entry) {
56
61
  const message = entry.level === 'error' ? styleText('red', entry.message) : entry.message;
57
- return ` ${LEVEL_ICON[entry.level]} ${styleText('dim', formatTimestamp(entry.at))} ${styleText('dim', `[${entry.phase}]`)} ${message}`;
62
+ const tokensPart = entry.tokens !== undefined ? ` ${styleText('dim', ${formatTokens(entry.tokens)}`)}` : '';
63
+ return ` ${LEVEL_ICON[entry.level]} ${styleText('dim', formatTimestamp(entry.at))} ${styleText('dim', `[${entry.phase}]`)} ${message}${tokensPart}`;
58
64
  }
59
65
  /** `pipeline-worker sessions --branch <name>`: one run's metadata plus its full step-by-step history. */
60
66
  export function printSessionDetail(session) {
@@ -63,7 +69,8 @@ export function printSessionDetail(session) {
63
69
  console.log(styleText('bold', `Session: ${state.branch}`));
64
70
  console.log(styleText('dim', ` target: ${state.targetBranch}`));
65
71
  console.log(styleText('dim', ` worktree: ${state.worktreePath}`));
66
- console.log(styleText('dim', ` phase: ${state.phase} ci-fix: ${state.ciFixAttempt} conflict: ${state.conflictAttempt}${mrPart}${pipelinePart}`));
72
+ const tokensPart = state.totalTokens !== undefined ? ` tokens: ${formatTokens(state.totalTokens)}` : '';
73
+ console.log(styleText('dim', ` phase: ${state.phase} ci-fix: ${state.ciFixAttempt} conflict: ${state.conflictAttempt}${mrPart}${pipelinePart}${tokensPart}`));
67
74
  console.log(styleText('dim', ` started: ${formatTimestamp(state.startedAt)} updated: ${formatTimestamp(state.updatedAt)}`));
68
75
  const history = state.history ?? [];
69
76
  if (history.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/ui/sessions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAItC,MAAM,WAAW,GAAsD;IACrE,IAAI,EAAE,KAAK;IACX,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,MAAM;IACd,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,KAAK;CACjB,CAAC;AAEF,SAAS,WAAW,CAAC,KAAe;IAClC,OAAO,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,eAAe,CAAC,GAAuB;IAC9C,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1D,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc;IACxC,OAAO,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAmB;IAC3C,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACtG,OAAO,GAAG,MAAM,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;AACjH,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAC;QACnG,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAC9G,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,2EAA2E,CAAC,CAAC,CAAC;AAC7G,CAAC;AAED,MAAM,UAAU,GAAqC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AAEjF,SAAS,uBAAuB,CAAC,KAA0B;IACzD,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;QACnE,YAAY,EAAE,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE;KACtF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,KAA0D;IACpF,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAC1F,OAAO,KAAK,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,OAAO,EAAE,CAAC;AAC1I,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,kBAAkB,CAAC,OAAmB;IACpD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAEhE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,eAAe,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CACT,SAAS,CAAC,KAAK,EAAE,YAAY,KAAK,CAAC,KAAK,aAAa,KAAK,CAAC,YAAY,eAAe,KAAK,CAAC,eAAe,GAAG,MAAM,GAAG,YAAY,EAAE,CAAC,CACvI,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,cAAc,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9H,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,mGAAmG,CAAC,CAAC,CAAC;QACnI,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/ui/sessions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,GAAsD;IACrE,IAAI,EAAE,KAAK;IACX,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,MAAM;IACd,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,KAAK;CACjB,CAAC;AAEF,SAAS,WAAW,CAAC,KAAe;IAClC,OAAO,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,eAAe,CAAC,GAAuB;IAC9C,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1D,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc;IACxC,OAAO,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,8IAA8I;AAC9I,SAAS,kBAAkB,CAAC,WAA+B;IACzD,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAmB;IAC3C,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACtG,OAAO,GAAG,MAAM,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;AAC1J,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAC;QACnG,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,4BAA4B,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACpI,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,2EAA2E,CAAC,CAAC,CAAC;AAC7G,CAAC;AAED,MAAM,UAAU,GAAqC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AAEjF,SAAS,uBAAuB,CAAC,KAA0B;IACzD,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;QACnE,YAAY,EAAE,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE;KACtF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,KAA0D;IACpF,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAC1F,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,KAAK,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/G,OAAO,KAAK,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;AACvJ,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,kBAAkB,CAAC,OAAmB;IACpD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAEhE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,eAAe,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzG,OAAO,CAAC,GAAG,CACT,SAAS,CAAC,KAAK,EAAE,YAAY,KAAK,CAAC,KAAK,aAAa,KAAK,CAAC,YAAY,eAAe,KAAK,CAAC,eAAe,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,EAAE,CAAC,CACpJ,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,cAAc,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9H,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,mGAAmG,CAAC,CAAC,CAAC;QACnI,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
@@ -1,22 +1,84 @@
1
1
  /**
2
- * Friendly progress narration for `pipeline-worker run`: a bold, colored,
3
- * numbered headline per workflow step (with an icon and a "[n/TOTAL_STAGES]"
4
- * counter) and a dim technical detail line underneath — mirrors how Claude
5
- * Code narrates its own tool calls, so a non-technical user can follow along
6
- * without reading source.
2
+ * The workflow's one door to the terminal: module-level functions the
3
+ * workflow code calls by step id, backed by a RunTree (the data model) and a
4
+ * Renderer (LineRenderer for non-TTY/plain output, TreeRenderer for the live
5
+ * TTY dashboard). Workflow code never touches process.stdout itself see
6
+ * CLAUDE.md's terminal-output discipline.
7
+ *
8
+ * The facade is safe to use without beginRun() (unit tests exercise workflow
9
+ * helpers directly): the first call lazily creates an empty tree with a
10
+ * LineRenderer, and unknown step ids materialize as top-level nodes instead
11
+ * of throwing — the UI must never kill the run.
7
12
  */
13
+ import { type RunStatus, type StepSeed } from './runTree.js';
8
14
  import type { RiskLevel } from '../types.js';
9
15
  import type { AgentInvokeResult } from '../agent/types.js';
10
16
  /**
11
- * Keeps a spinner redraw to a single physical row. Without this, a line
12
- * longer than the terminal's column count auto-wraps, and the `\r\x1b[K`
13
- * redraw in runStep() below only rewinds/clears the row the cursor is on
14
- * not the wrapped continuation from the previous frame so each tick
15
- * appends a new line instead of animating in place.
17
+ * Keeps a spinner/tree redraw to a single physical row. Without this, a line
18
+ * longer than the terminal's column count auto-wraps, and cursor-based
19
+ * erasing only rewinds the rows it knows about not the wrapped
20
+ * continuation so each frame appends garbage instead of animating in place.
16
21
  */
17
22
  export declare function truncateToWidth(text: string, width: number): string;
18
- /** A one-off status line with no associated work to wait on (e.g. a final summary). */
19
- export declare function step(icon: string, title: string, detail?: string): void;
23
+ /**
24
+ * Pure selection logic, kept separate from createRenderer() so it's testable
25
+ * without touching the real process.stdout (flipping process.stdout.isTTY in
26
+ * a test would make a real TreeRenderer hide the cursor on the terminal
27
+ * actually running the test suite). The live tree dashboard needs a real
28
+ * terminal to redraw in place; CI logs, piped output, and
29
+ * PIPELINE_WORKER_PLAIN_OUTPUT (an explicit escape hatch — useful when filing
30
+ * a bug report, or any tool that greps run output) all fall back to the
31
+ * append-only LineRenderer instead.
32
+ */
33
+ export declare function selectRendererMode(isTTY: boolean, plainOutputEnv: string | undefined): 'tree' | 'line';
34
+ /** Starts a new run display. Replaces any previous run's tree (each CLI invocation begins at most one). */
35
+ export declare function beginRun(skeleton: StepSeed[], header: {
36
+ title: string;
37
+ worktreeShortId?: string;
38
+ }): void;
39
+ /**
40
+ * Settles the run display into its terminal status. Idempotent: only the
41
+ * first call paints, so error paths can call endRun('failed') defensively
42
+ * without stomping an earlier, more specific verdict.
43
+ */
44
+ export declare function endRun(status: Exclude<RunStatus, 'running'>, detail?: string): void;
45
+ /** Adds a step that wasn't in the skeleton — the watch loop's fix/rebase attempts, conflict resolution, squash. */
46
+ export declare function addDynamicStep(parentId: string | undefined, id: string, label: string, detail?: string): void;
47
+ /** Marks a step running without tying it to a single task closure — for steps whose phases span several calls (see runPhase). */
48
+ export declare function startStep(id: string, patch?: {
49
+ detail?: string;
50
+ attempt?: number;
51
+ maxAttempts?: number;
52
+ }): void;
53
+ export declare function finishStep(id: string, status?: 'done' | 'failed', patch?: {
54
+ detail?: string;
55
+ }): void;
56
+ export declare function updateStep(id: string, patch: {
57
+ detail?: string;
58
+ attempt?: number;
59
+ maxAttempts?: number;
60
+ }): void;
61
+ /** Announces a step that did not run this time, and why — a skipped stage stays visible instead of silently vanishing (which reads as a bug, not a choice). */
62
+ export declare function skipStep(id: string, reason: string): void;
63
+ /** Runs one step to completion: running → done, or running → failed + rethrow. */
64
+ export declare function runStep<T>(id: string, detail: string, task: () => Promise<T>): Promise<T>;
65
+ /**
66
+ * Runs one phase of an already-running step (a fix attempt's agent turn,
67
+ * local verify, push, ...), mutating the step's detail rather than creating
68
+ * grandchildren — the tree stays one row per attempt, as the dashboard
69
+ * renders it. Failure marks the whole step failed and rethrows; success
70
+ * leaves it running for the next phase (the caller finishes it).
71
+ */
72
+ export declare function runPhase<T>(id: string, detail: string, task: () => Promise<T>): Promise<T>;
73
+ /** Updates the header line: the run's display name (once intent names the branch) and the worktree short id. */
74
+ export declare function setRunHeader(patch: {
75
+ title?: string;
76
+ worktreeShortId?: string;
77
+ }): void;
78
+ /** Folds a resumed run's persisted token total into the header figure, so the dashboard shows the whole run's spend, not just this process's share. */
79
+ export declare function seedRunTokens(tokens: number): void;
80
+ /** A bold one-off announcement outside any step (adopting a branch, pipeline failed, ...). */
81
+ export declare function announce(text: string, detail?: string): void;
20
82
  /**
21
83
  * An indented supplementary detail line under the current step (e.g. an
22
84
  * agent's summary, a result). Input is freeform (agent output, forge data)
@@ -26,36 +88,21 @@ export declare function step(icon: string, title: string, detail?: string): void
26
88
  export declare function note(text: string): void;
27
89
  /** Like note(), but colors the text by risk level (green/yellow/red for low/medium/high). */
28
90
  export declare function noteRisk(risk: RiskLevel, reason: string): void;
29
- /**
30
- * Announces a numbered stage that did not run this time, and why, so the
31
- * printed sequence stays gap-free instead of a conditional stage silently
32
- * vanishing from between its neighbors (which reads as a bug, not a choice).
33
- */
34
- export declare function skipStep(stage: number | string, icon: string, title: string, reason: string): void;
35
91
  /**
36
92
  * Reports which agent CLI session handled a turn, how long it took, and the
37
93
  * worktree it ran in, so a user who wants to see how a conflict was resolved
38
94
  * or a CI failure was fixed can look it up afterwards (`claude --resume <id>`
39
95
  * or, for Copilot, `copilot --resume <id>` — see agent/copilot.ts on why that
40
96
  * id is one we assigned rather than one the CLI reported). Session history is
41
- * scoped to the working directory the CLI ran in (see agent/claude.ts:
42
- * `cwd: opts.cwd` is always the worktree, never the caller's own repo), so
43
- * `--resume` only finds the session when run from that same worktree path —
44
- * printing the path lets the user `cd` there first. This function has no way
45
- * to know which adapter produced `result` (`AgentInvokeResult` doesn't carry
46
- * one), so it names the path rather than assembling a `claude`/`copilot`
47
- * command that would guess wrong half the time. A no-op when the adapter
48
- * didn't return a sessionId, which keeps this safe to call unconditionally.
97
+ * scoped to the working directory the CLI ran in, so `--resume` only finds
98
+ * the session when run from that same worktree path printing the path lets
99
+ * the user `cd` there first. A no-op when the adapter didn't return a
100
+ * sessionId, which keeps this safe to call unconditionally.
101
+ *
102
+ * Also attributes the turn's token spend (when the adapter reported any) to
103
+ * the step that ran it, which is what feeds the per-step and header token
104
+ * figures on the dashboard.
49
105
  */
50
106
  export declare function noteSession(result: AgentInvokeResult, worktreePath: string): void;
51
107
  /** Prints an agent's response (truncated) followed by its resumable session info — the shared tail of every conflict/CI-fix agent invocation. */
52
108
  export declare function reportAgentInvocation(result: AgentInvokeResult, worktreePath: string): void;
53
- /**
54
- * Runs one numbered workflow stage with a colored, iconed title, then a live
55
- * status line beneath it while `task` runs: a spinner plus a counting-up
56
- * elapsed timer in a TTY, settling into a green checkmark or red cross with
57
- * the total duration on completion. Falls back to a single static print when
58
- * stdout isn't a TTY (CI logs, redirected files) since carriage-return
59
- * spinners would just garble those.
60
- */
61
- export declare function runStep<T>(stage: number | string, icon: string, title: string, detail: string, task: () => Promise<T>): Promise<T>;