From 09653767b59acdf0a839b90be487d3e9aa2bd962 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Sat, 21 Feb 2026 18:26:26 +1000 Subject: [PATCH] feat(extension): show elapsed time during step execution Add a live elapsed time counter to the step streaming header. When a step is executing, the header shows 'Executing... (12s)' and updates every second. When done, the final duration is preserved in the completed/failed header. Changes: - Add stepTimers map and formatElapsed helper - Start interval timer in showStepStreaming() - Clear timer and show final duration in finalizeStepStreaming() - Support mm:ss format for executions over 60 seconds Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 18 +++++++++ extension/package-lock.json | 4 +- extension/src/providers/storyPanelProvider.ts | 39 ++++++++++++++++++- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5e84920c..2add2cd3 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -173,6 +173,24 @@ The pre-commit hook will reject files with CRLF line endings. | New feature | `.project/features/upcoming/` | Create spec first | | Complete feature | `.project/features/completed/` | **Follow ceremony** (see below) | +## Interactive Story Workflow + +You can work on an Aura story interactively from any Copilot CLI session. Use the `/work-on-story` prompt to bootstrap, or follow this flow manually: + +1. **Find your story**: `aura_workflow(operation: "list")` or `aura_workflow(operation: "get", storyId: "...")` +2. **Get next step**: `aura_workflow(operation: "next_step", storyId: "...")` — returns the next actionable step with full context +3. **Start the step**: `aura_workflow(operation: "start_step", storyId: "...", stepId: "...")` — marks it Running +4. **Do the work**: Edit files in the worktree using **absolute paths** from the step context +5. **Complete the step**: `aura_workflow(operation: "update_step", storyId: "...", stepId: "...", status: "completed", output: "...")` +6. **Repeat** until all steps done, then `aura_workflow(operation: "complete", storyId: "...")` + +**Critical conventions when working on a story:** +- Your cwd is the main workspace, but **file edits must target the worktree** (absolute paths) +- Use `worktreeSolutionPath` from step context for `aura_validate` calls +- Use `repositoryPath` for `aura_search` — RAG indexes the main repo, not the worktree +- Run git commands with `cd && git ...` +- Load the full pattern: `aura_pattern(operation: "get", name: "interactive-story")` + ## Blast Radius Protocol (Renames, Extract, Large Changes) `aura_refactor` defaults to **analyze mode** (`analyze: true`). Before executing any refactoring: diff --git a/extension/package-lock.json b/extension/package-lock.json index b7e071d2..ffa06eb7 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "aura", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aura", - "version": "1.4.0", + "version": "1.5.0", "dependencies": { "axios": "^1.6.2" }, diff --git a/extension/src/providers/storyPanelProvider.ts b/extension/src/providers/storyPanelProvider.ts index 1c925d18..04140154 100644 --- a/extension/src/providers/storyPanelProvider.ts +++ b/extension/src/providers/storyPanelProvider.ts @@ -3325,6 +3325,16 @@ export class StoryPanelProvider { } } + // Track elapsed time timers per step + const stepTimers = {}; + + function formatElapsed(seconds) { + if (seconds < 60) return seconds + 's'; + const m = Math.floor(seconds / 60); + const s = seconds % 60; + return m + 'm ' + s + 's'; + } + function showStepStreaming(stepId, stepName) { const stepCard = document.querySelector('[data-step-id="' + stepId + '"]'); if (!stepCard) return; @@ -3333,16 +3343,31 @@ export class StoryPanelProvider { const existing = stepCard.querySelector('.step-streaming'); if (existing) existing.remove(); + // Clear any existing timer for this step + if (stepTimers[stepId]) { + clearInterval(stepTimers[stepId]); + } + // Create streaming output section const streaming = document.createElement('div'); streaming.className = 'step-streaming'; streaming.id = 'streaming-' + stepId; - streaming.innerHTML = '
Executing...
'; + streaming.innerHTML = '
Executing... (0s)
'; // Insert after the header (and progress bar if present) const header = stepCard.querySelector('.step-header'); const progress = stepCard.querySelector('.step-progress'); (progress || header).after(streaming); + + // Start elapsed time counter + const startTime = Date.now(); + stepTimers[stepId] = setInterval(() => { + const elapsed = Math.floor((Date.now() - startTime) / 1000); + const el = document.getElementById('elapsed-' + stepId); + if (el) { + el.textContent = 'Executing... (' + formatElapsed(elapsed) + ')'; + } + }, 1000); } function appendStepOutput(stepId, text, type) { @@ -3357,12 +3382,22 @@ export class StoryPanelProvider { } function finalizeStepStreaming(stepId, success) { + // Stop the elapsed timer + if (stepTimers[stepId]) { + clearInterval(stepTimers[stepId]); + delete stepTimers[stepId]; + } + const streaming = document.getElementById('streaming-' + stepId); if (!streaming) return; const header = streaming.querySelector('.step-streaming-header'); if (header) { - header.innerHTML = success ? '✓ Completed' : '✗ Failed'; + // Calculate final duration + const elapsedEl = document.getElementById('elapsed-' + stepId); + const durationText = elapsedEl ? elapsedEl.textContent.replace('Executing... ', '') : ''; + const statusText = success ? '✓ Completed' : '✗ Failed'; + header.innerHTML = statusText + (durationText ? ' ' + durationText : ''); header.style.color = success ? 'var(--vscode-terminal-ansiBrightGreen)' : 'var(--vscode-terminal-ansiBrightRed)'; } }