work-kit-cli 0.2.7 → 0.2.8

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.
@@ -171,10 +171,10 @@ function subStageIndicator(status: string, tick: number): string {
171
171
 
172
172
  function phaseName(name: string, status: string, tick: number): string {
173
173
  switch (status) {
174
- case "completed": return green(name);
175
- case "in-progress": return tick % 2 === 0 ? boldCyan(name) : cyan(name);
176
- case "waiting": return yellow(name);
177
- case "failed": return red(name);
174
+ case "completed": return boldGreen(name);
175
+ case "in-progress": return tick % 2 === 0 ? boldCyan(name) : bold(cyan(name));
176
+ case "waiting": return bold(yellow(name));
177
+ case "failed": return bold(red(name));
178
178
  default: return dim(name);
179
179
  }
180
180
  }
@@ -201,17 +201,79 @@ function renderGatedBadge(): string {
201
201
 
202
202
  // ── Phase Pipeline ──────────────────────────────────────────────────
203
203
 
204
+ function phaseDuration(p: { status: string; startedAt?: string; completedAt?: string }): string {
205
+ if (p.status === "completed" && p.startedAt && p.completedAt) {
206
+ const ms = new Date(p.completedAt).getTime() - new Date(p.startedAt).getTime();
207
+ const sec = Math.floor(ms / 1000);
208
+ if (sec < 60) return `${sec}s`;
209
+ const min = Math.floor(sec / 60);
210
+ if (min < 60) return `${min}m`;
211
+ const hr = Math.floor(min / 60);
212
+ const remMin = min % 60;
213
+ return remMin > 0 ? `${hr}h${remMin}m` : `${hr}h`;
214
+ }
215
+ if (p.status === "in-progress" && p.startedAt) {
216
+ return formatDuration(p.startedAt);
217
+ }
218
+ if (p.status === "waiting" && p.startedAt) {
219
+ return formatDuration(p.startedAt);
220
+ }
221
+ return "";
222
+ }
223
+
204
224
  function renderPhasePipeline(
205
225
  phases: WorkItemView["phases"],
206
226
  tick: number
207
- ): string {
208
- const segments: string[] = [];
209
- for (const p of phases) {
227
+ ): string[] {
228
+ const connector = dim(" ── ");
229
+ const connectorLen = 4; // " ── "
230
+
231
+ // Build top line (icons + names) and bottom line (timing, aligned)
232
+ const topParts: string[] = [];
233
+ const bottomParts: string[] = [];
234
+
235
+ for (let i = 0; i < phases.length; i++) {
236
+ const p = phases[i];
210
237
  const icon = phaseIndicator(p.status, tick);
211
238
  const name = phaseName(p.name, p.status, tick);
212
- segments.push(`${icon} ${name}`);
239
+ const segment = `${icon} ${name}`;
240
+ topParts.push(segment);
241
+
242
+ // Calculate plain width of this segment for alignment
243
+ const segPlainLen = stripAnsi(segment).length;
244
+ const dur = phaseDuration(p);
245
+
246
+ if (dur) {
247
+ // Color the duration based on status
248
+ let durStyled: string;
249
+ if (p.status === "completed") durStyled = dim(dur);
250
+ else if (p.status === "in-progress") durStyled = cyan(dur);
251
+ else if (p.status === "waiting") durStyled = yellow(dur);
252
+ else durStyled = dim(dur);
253
+
254
+ // Center the duration under the segment
255
+ const durPlainLen = stripAnsi(durStyled).length;
256
+ const pad = Math.max(0, Math.floor((segPlainLen - durPlainLen) / 2));
257
+ bottomParts.push(" ".repeat(pad) + durStyled + " ".repeat(Math.max(0, segPlainLen - durPlainLen - pad)));
258
+ } else {
259
+ bottomParts.push(" ".repeat(segPlainLen));
260
+ }
261
+
262
+ // Add connector spacing to bottom line too
263
+ if (i < phases.length - 1) {
264
+ bottomParts.push(" ".repeat(connectorLen));
265
+ }
213
266
  }
214
- return segments.join(dim(" ── "));
267
+
268
+ const topLine = topParts.join(connector);
269
+ const bottomLine = bottomParts.join("");
270
+
271
+ // Only show bottom line if there's at least one duration
272
+ const hasAnyDuration = phases.some(p => phaseDuration(p) !== "");
273
+ if (hasAnyDuration) {
274
+ return [topLine, bottomLine];
275
+ }
276
+ return [topLine];
215
277
  }
216
278
 
217
279
  // ── Sub-Stage Detail Box ────────────────────────────────────────────
@@ -304,7 +366,7 @@ function renderWorkItem(item: WorkItemView, innerWidth: number, tick: number): s
304
366
  lines.push(slugText + " ".repeat(gap1) + elapsedText);
305
367
 
306
368
  // Line 2: branch + mode badge + gated badge + classification
307
- const branchText = dim(item.branch);
369
+ const branchText = dim("⎇ " + item.branch);
308
370
  let badges = " " + renderModeBadge(item.mode);
309
371
  if (item.gated) badges += " " + renderGatedBadge();
310
372
  if (item.status === "paused") badges += " " + bgYellow(" PAUSED ");
@@ -334,8 +396,11 @@ function renderWorkItem(item: WorkItemView, innerWidth: number, tick: number): s
334
396
  tick
335
397
  ));
336
398
 
337
- // Line 5: phase pipeline with connectors and spinner
338
- lines.push(" " + renderPhasePipeline(item.phases, tick));
399
+ // Line 5-6: phase pipeline with connectors, spinner, and timing row
400
+ const pipelineLines = renderPhasePipeline(item.phases, tick);
401
+ for (const pl of pipelineLines) {
402
+ lines.push(" " + pl);
403
+ }
339
404
 
340
405
  // Line 6: sub-stage detail box (all sub-stages of current phase)
341
406
  const subStageBox = renderSubStageBox(item, innerWidth, tick);
@@ -361,11 +426,11 @@ function renderWorkItem(item: WorkItemView, innerWidth: number, tick: number): s
361
426
  // Worktree path
362
427
  if (item.worktreePath) {
363
428
  let displayPath = item.worktreePath;
364
- const maxPathLen = innerWidth - 4;
429
+ const maxPathLen = innerWidth - 8;
365
430
  if (displayPath.length > maxPathLen) {
366
431
  displayPath = "…" + displayPath.slice(displayPath.length - maxPathLen + 1);
367
432
  }
368
- lines.push(" " + dim(displayPath));
433
+ lines.push(" " + dim("⌂ " + displayPath));
369
434
  }
370
435
 
371
436
  return lines;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "work-kit-cli",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Structured development workflow for Claude Code. Two modes, 6 phases, 27 sub-stages.",
5
5
  "type": "module",
6
6
  "bin": {