Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e5ee1d6
fix(workflow): post-merge agent panel polish (0.2.5-agent-panel.1)
cursoragent Oct 9, 2026
ff0734f
fix(workflow): QA agent-panel regressions and task progress (0.2.5-ag…
cursoragent Oct 9, 2026
512646a
fix(release): typecheck gate, zh copy, 0.2.5-agent-panel.3
cursoragent Oct 9, 2026
21c345f
fix(release): typecheck gate hardening and zh copy (0.2.5-agent-panel.4)
cursoragent Oct 9, 2026
74b2bd7
Release 0.2.5-agent-panel.5: tasks.md perf, agentAutoSubmit UI, zh Da…
cursoragent Oct 9, 2026
736c352
test: assert typecheck gate uses pretty output and ANSI strip
cursoragent Oct 9, 2026
1feee4b
Release 0.2.5-agent-panel.6: detail task patches by root, lifecycle r…
cursoragent Oct 9, 2026
2f84710
fix: loosen task progress patch row type for project sidebar
cursoragent Oct 9, 2026
e2df292
Release 0.2.5-agent-panel.7: fix ESLint no-useless-assignment in data…
cursoragent Oct 9, 2026
4ae6bdb
fix: reconcile sidebar changes, dashboard clicks, and relative times
cursoragent Oct 10, 2026
e5fd2d3
fix: satisfy eslint for changes panel scroll scheduling
cursoragent Oct 10, 2026
073891c
fix: localize calendar-relative dates and clamp clock skew (0.2.4-pre.2)
cursoragent Oct 10, 2026
6bb91c4
fix: restore project sidebar auto-refresh on external change churn
cursoragent Oct 10, 2026
fc72f96
feat: remove All Changes tile and add Changes list open-in-editor action
cursoragent Oct 10, 2026
8def704
feat(sidebar): vertical project nav list for 0.2.4-pre.4
cursoragent Oct 10, 2026
661e544
fix: webview codicon fonts, sidebar cards, and Changes explorer refre…
cursoragent Oct 10, 2026
628bc60
fix: cast explorer snapshot changes for typecheck baseline
cursoragent Oct 10, 2026
35fd1fa
fix: wrap priority card CTA at 260px without truncating labels (0.2.4…
cursoragent Oct 10, 2026
46ff2d6
chore(release): prepare 0.2.4 version and changelog
cursoragent Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,92 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.4] - 2026-10-10

### Added

- Project sidebar **vertical navigation** (`data-project-nav-list`): **Project Dashboard** (open in editor), **Specs** (with count badge when > 0), **Workset Projects**; tooltips and i18n (`navProjectDashboard`, `navWorksetProjects`).
- **Changes (N)** section title action (**Open in editor**, `data-open-changes-in-editor`) to open the bound Changes explorer from the sidebar.
- **Changes explorer** manual **refresh** control; explorer panel titles localized (`explorer.changesPanelTitle`, specs panel).
- Relative **timestamps** on Project Dashboard (calendar-relative dates, clock-skew clamp); zh-CN calendar wording.
- VSIX packaging check: `scripts/verify-vsix-webview.js` validates webview markers, `./index.ttf` in CSS, and font file inside the package.

### Changed

- Removed the **All Changes** tile from the project sidebar header; navigation and open-in-editor entry points replace it.
- **Project Dashboard** recent updates and **priority change** cards: names and CTAs navigate to change detail; priority row layout tuned for narrow sidebar (260px).
- **Priority card CTA** at 260px: drop `max-w-[55%]` / `truncate`; use `flex-wrap`, `min-w-[7rem]` on the name, and `shrink-0` on the button so full CTA labels wrap instead of clipping.
- Rebuild project sidebar change list from dashboard refresh payloads; wire dashboard click targets (recent updates, priority names) to change detail.
- zh-CN: **项目仪表盘** for project dashboard title; explorer and sidebar copy polish.

### Fixed

- **Auto-refresh** when changes are added/removed or updated externally: sidebar list reconciles from refresh payloads; publish explorer snapshots on sidebar merge/reload so open **Changes** panels update without stale data.
- **Codicon** icons in the webview: Vite `base: './'`, relative `url(./index.ttf)` in CSS, CSP `font-src` and font preload in webview HTML; fixes square glyph boxes in sidebar and dashboard.
- **ChangeCard** **APPLYING** lifecycle badge contrast in light theme (uses button foreground/background tokens).
- Typecheck: cast explorer snapshot changes at `publishChangesExplorerSnapshots` to stay within CI baseline.

## [0.2.5-agent-panel.7] - 2026-10-09

### Fixed

- CI: resolve ESLint `no-useless-assignment` in tasks.md reconcile helpers (`dataManager.ts`).

## [0.2.5-agent-panel.6] - 2026-10-09

### Fixed

- Detail task progress: match `changeTaskProgressPatch` by OpenSpec root + change name (project-bound `scopeId` no longer drops watcher patches); tasks-only watcher events send background `artifactInvalidated` safety net.
- Lifecycle after tasks.md: debounced single-change `getChangeStatus` when file counts cross a lifecycle boundary; empty/deleted `tasks.md` immediately publishes 0/0; ordinary checkbox edits stay CLI-free.

## [0.2.5-agent-panel.5] - 2026-10-09

### Fixed

- Detail page: `tasks.md` edits push `changeTaskProgressPatch` (counts + content) without `artifactInvalidated` / loading flash; tasks artifact reads skip CLI list/status; tasks-only watcher events no longer trigger full dashboard CLI refresh.
- V&A hints: `agentAutoSubmit` included in executor launch presentation and refreshed on settings change; labels use host-effective mode (`never` / `readOnly` / `always`).
- Optional missing artifacts log at debug instead of error.
- zh-CN: keep **Dashboard** product label; lowercase **change** consistently.
- Typecheck gate: fail on abnormal tsc exit (≥128) or missing `Found N error(s)` summary even when partial `error TS` lines exist.

## [0.2.5-agent-panel.4] - 2026-10-09

### Fixed

- Typecheck gate: fail when `tsc` exits non-zero but emits zero `error TS` lines; baseline lowered to **12**.
- zh-CN: Needs Attention filter/badge, lifecycle empty states, archive verify dialog, Proposal Why, V&A intro wording, and remaining English UI fragments.

## [0.2.5-agent-panel.3] - 2026-10-09

### Fixed

- Typecheck: narrow `changeTaskProgressPatch` messages in Change Detail; accept `readonly` change lists in task-progress patch helper.
- CI typecheck gate: `check-typecheck-baseline.sh` counts errors with `grep` (no `rg` dependency); fails loudly on missing tools or unparseable output.
- zh-CN: localized sidebar card action summaries, lifecycle labels, New Change, and Verify & Archive intro copy (slash commands unchanged).

## [0.2.5-agent-panel.2] - 2026-10-09

### Fixed

- **BLOCKER:** Removed post–`composer.createNew` `composer.openComposer` / focus calls that broke Cursor 3.21.16 Agent tabs; restored 0.2.4-agent-panel.18 launch behavior.
- Task progress: OpenSpec CLI–aligned checkbox counting (`ws2-zeta-markers` 3/8); immediate `changeTaskProgressPatch` to sidebar and detail on `tasks.md` edits; project-first sidebar no longer CLI-reloads on every dashboard refresh.
- Prefill hint at most once per activation; Verify/Archive hints follow `agentAutoSubmit`; prefill launches receipt status `prefilled` / 「已预填」.

## [0.2.5-agent-panel.1] - 2026-10-09

### Changed

- `openspec.agentAutoSubmit` now applies to VS Code Chat (`workbench.action.chat.open` with `isPartialQuery: false` to submit) as well as Cursor; prefill-only launches show a one-shot hint with a link to the setting.
- Dashboard task counts are derived from `tasks.md` parsing (CLI-aligned) for faster updates after edits; sidebar receives immediate patches on file watch.
- Localized workflow action labels in zh-CN; copy/command hints use the current change name instead of `my-change`.
- Dedupe toast uses “just launched” semantics; Agent panel button wording clarified.

### Fixed

- Cursor: best-effort focus on new composer after `composer.createNew` (see `docs/CURSOR_COMPOSER_FOCUS.md`).

## [0.2.5-agent-panel.0] - 2026-10-08

### Changed

- Workflow actions (Apply, Verify, Archive, Continue, etc.) now launch through a unified IDE Agent panel path on Cursor and VS Code instead of opening an editor-area `agent` terminal by default.
Expand Down
21 changes: 21 additions & 0 deletions docs/CURSOR_COMPOSER_FOCUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Cursor composer focus after restart

## Symptom

After Cursor restarts, the first workflow launch via `composer.createNew` can leave a **restored previous chat** visible while the new Agent run opens in another tab.

## Investigation (2026-10-09 QA)

- Post–PR #25 `tryFocusComposerById` called `composer.openComposer` with `{ composerId }` ~100ms after `composer.createNew`. On Cursor 3.21.16 this **regressed** launch: tabs collapsed, blank “Loading Chat”, prefill lost, and errors such as `e.path.replace is not a function` inside `computeTabLabels` → `openEditor`. The object form was treated as an editor input.
- **Decision:** remove all post–`createNew` composer focus / `openComposer` calls. Launch behavior matches pre-focus builds (e.g. 0.2.4-agent-panel.18 / `bdeab81`).
- There is still no stable, public Cursor API to focus a composer by id without risking editor corruption.

## Mitigation in the extension

1. **Launch:** only `composer.createNew` (or chat/deeplink fallbacks) — no follow-up focus commands.
2. **First launch per activation:** one non-modal toast (`agentLaunch.cursorFirstPanelTabHint`) on the first successful Cursor agent-panel launch after the extension activates in that window.
3. **Background pane check:** unchanged warning if pane count does not increase within 1.5s (`agentLaunch.panelNotConfirmed`).

## Reliable fix

None without Cursor-supported focus API. Track Cursor releases for a documented “focus composer” command that accepts an id safely.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "openspec-workflow",
"displayName": "OpenSpec Workflow",
"description": "Project-first OpenSpec dashboard for changes, specs, stores, worksets, and safe workflow actions",
"version": "0.2.4-agent-panel.18",
"version": "0.2.4",
"publisher": "randysss",
"license": "MIT",
"engines": {
Expand Down Expand Up @@ -109,11 +109,11 @@
"always"
],
"default": "readOnly",
"description": "When launching in the Cursor Agent panel via composer.createNew, auto-send the workflow command. readOnly auto-sends explore and verify only; other layers always prefill.",
"description": "%configuration.openspec.agentAutoSubmit.description%",
"enumDescriptions": [
"Never auto-send; always prefill and wait for Enter.",
"Auto-send read-only workflow commands (explore, verify).",
"Auto-send every workflow command (including apply and archive)."
"%configuration.openspec.agentAutoSubmit.enum.never%",
"%configuration.openspec.agentAutoSubmit.enum.readOnly%",
"%configuration.openspec.agentAutoSubmit.enum.always%"
]
},
"openspec.cursorLaunchMode": {
Expand Down
6 changes: 6 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"configuration.openspec.agentAutoSubmit.description": "When launching workflow commands into the IDE Agent panel (VS Code Chat/Copilot or Cursor), control whether the command is sent automatically or only prefilled. Applies to both VS Code and Cursor. never: always prefill; readOnly (default): auto-send explore and verify only; always: auto-send every workflow action including apply and archive. If auto-send is unsupported in your editor version, the extension prefills instead.",
"configuration.openspec.agentAutoSubmit.enum.never": "Never auto-send — always prefill the Agent input and wait for Enter.",
"configuration.openspec.agentAutoSubmit.enum.readOnly": "Auto-send read-only workflow commands (explore, verify) in VS Code and Cursor.",
"configuration.openspec.agentAutoSubmit.enum.always": "Auto-send every workflow command (apply, archive, etc.) in VS Code and Cursor."
}
6 changes: 6 additions & 0 deletions package.nls.zh-cn.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"configuration.openspec.agentAutoSubmit.description": "在 VS Code Chat/Copilot 或 Cursor 的 Agent 面板中启动工作流命令时,控制是自动发送还是仅预填。适用于 VS Code 与 Cursor。never:始终预填;readOnly(默认):仅对 explore、verify 自动发送;always:对所有工作流动作(含 apply、archive)自动发送。若当前编辑器版本不支持自动发送,扩展会回退为预填。",
"configuration.openspec.agentAutoSubmit.enum.never": "从不自动发送 — 始终预填 Agent 输入框,按 Enter 发送。",
"configuration.openspec.agentAutoSubmit.enum.readOnly": "在 VS Code 与 Cursor 中仅对只读工作流(explore、verify)自动发送。",
"configuration.openspec.agentAutoSubmit.enum.always": "在 VS Code 与 Cursor 中对所有工作流命令(含 apply、archive)自动发送。"
}
66 changes: 60 additions & 6 deletions scripts/check-typecheck-baseline.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,68 @@
#!/usr/bin/env bash
set -euo pipefail

BASELINE="${TSC_BASELINE_ERROR_COUNT:-13}"
BASELINE="${TSC_BASELINE_ERROR_COUNT:-12}"
LOG="$(mktemp)"
trap 'rm -f "$LOG"' EXIT

# Must match local: npx tsc --noEmit -p tsconfig.typecheck.json
npx tsc --noEmit -p tsconfig.typecheck.json >"$LOG" 2>&1 || true
COUNT="$(rg -c 'error TS' "$LOG" || true)"
COUNT="${COUNT:-0}"
if ! command -v npx >/dev/null 2>&1; then
echo "check-typecheck-baseline: required command 'npx' not found in PATH" >&2
exit 1
fi

if ! command -v grep >/dev/null 2>&1; then
echo "check-typecheck-baseline: required command 'grep' not found in PATH" >&2
exit 1
fi

strip_ansi() {
sed 's/\x1b\[[0-9;]*m//g'
}

run_tsc() {
if [ -n "${CHECK_TYPECHECK_TSC_INVOKE:-}" ]; then
bash -c "${CHECK_TYPECHECK_TSC_INVOKE}"
else
npx tsc --noEmit -p tsconfig.typecheck.json --pretty true
fi
}

# Must match local: npx tsc --noEmit -p tsconfig.typecheck.json --pretty true
TSC_EXIT=0
run_tsc >"$LOG" 2>&1 || TSC_EXIT=$?
PLAIN_LOG="$(mktemp)"
trap 'rm -f "$LOG" "$PLAIN_LOG"' EXIT
strip_ansi <"$LOG" >"$PLAIN_LOG"

if [ ! -s "$LOG" ] && [ "$TSC_EXIT" -ne 0 ]; then
echo "check-typecheck-baseline: tsc failed (exit $TSC_EXIT) but produced no output" >&2
exit 1
fi

COUNT="$(grep -c 'error TS' "$PLAIN_LOG" 2>/dev/null || true)"
if ! [[ "${COUNT:-}" =~ ^[0-9]+$ ]]; then
echo "check-typecheck-baseline: could not parse TypeScript error count from tsc output" >&2
cat "$LOG" >&2
exit 1
fi

if [ "$TSC_EXIT" -ne 0 ] && [ "$COUNT" -eq 0 ]; then
echo "check-typecheck-baseline: tsc exited $TSC_EXIT but no 'error TS' lines were counted" >&2
cat "$LOG" >&2
exit 1
fi

if [ "$TSC_EXIT" -ne 0 ]; then
if [ "$TSC_EXIT" -ge 128 ]; then
echo "check-typecheck-baseline: tsc exited with abnormal signal/code $TSC_EXIT" >&2
cat "$LOG" >&2
exit 1
fi
if ! grep -qE 'Found [0-9]+ error' "$PLAIN_LOG"; then
echo "check-typecheck-baseline: tsc exited $TSC_EXIT without a 'Found N error(s)' summary (possible crash)" >&2
cat "$LOG" >&2
exit 1
fi
fi

if [ "$COUNT" -gt "$BASELINE" ]; then
echo "TypeScript error count $COUNT exceeds baseline $BASELINE"
Expand Down
19 changes: 19 additions & 0 deletions scripts/verify-vsix-webview.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ if (!fs.existsSync(inputPath)) {
}

let contents;
let packagedCss;
let label = path.basename(inputPath);
if (inputPath.endsWith('.js')) {
contents = fs.readFileSync(inputPath, 'utf8');
Expand All @@ -45,6 +46,15 @@ if (inputPath.endsWith('.js')) {
fail('extension/dist/webview/index.js missing in VSIX');
}
contents = fs.readFileSync(webviewJs, 'utf8');
const cssPath = path.join(tmpDir, 'extension', 'dist', 'webview', 'index.css');
const fontPath = path.join(tmpDir, 'extension', 'dist', 'webview', 'index.ttf');
if (!fs.existsSync(cssPath)) {
fail('extension/dist/webview/index.css missing in VSIX');
}
if (!fs.existsSync(fontPath)) {
fail('extension/dist/webview/index.ttf missing in VSIX');
}
packagedCss = fs.readFileSync(cssPath, 'utf8');
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
Expand All @@ -70,6 +80,15 @@ try {
fail(`gate pattern "${GATE_PATTERN}" matched 0 times in webview/index.js`);
}

if (packagedCss) {
if (/url\(\/index\.ttf/.test(packagedCss) || /url\(['"]?\//.test(packagedCss)) {
fail('webview CSS must not use root-absolute font urls');
}
if (!/url\(\.\/index\.ttf\?/.test(packagedCss)) {
fail('webview CSS must reference ./index.ttf for codicon');
}
}

console.log(`verify-vsix-webview: PASS (${label})`);
} catch (error) {
fail(error instanceof Error ? error.message : String(error));
Expand Down
12 changes: 11 additions & 1 deletion src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { OpenSpecCacheService } from './services/openSpecCacheService';
import { createProjectContext, ProjectDataGateway } from './services/projectDataGateway';
import { setLocale, t } from '../i18n';
import { AgentUnavailableViewProvider } from './providers/agentUnavailableViewProvider';
import { configureAgentLaunchPrefillNotify } from './services/agentLaunchPrefillNotify';

let dataManager: DataManager | null = null;

Expand Down Expand Up @@ -89,6 +90,7 @@ export async function activate(context: vscode.ExtensionContext) {
extensionVersion: context.extension.packageJSON.version ?? '0.0.0',
});
dataManager = new DataManager(workspaceRoot, { cacheService, projectRoots });
configureAgentLaunchPrefillNotify(context);
await dataManager.initialize();

let dashboardViewProviderRef: DashboardViewProvider | null = null;
Expand Down Expand Up @@ -136,13 +138,21 @@ export async function activate(context: vscode.ExtensionContext) {
})
);

context.subscriptions.push(
dataManager.onTaskProgressPatch((patch) => {
changeDetailPanelManager.broadcastTaskProgressPatch(patch);
dashboardViewProvider.applyTaskProgressPatch(patch);
})
);

context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((event) => {
const launchConfigChanged =
event.affectsConfiguration('openspec.workflowLaunchMode') ||
event.affectsConfiguration('openspec.preferredAgentAdapter') ||
event.affectsConfiguration('openspec.cursorLaunchMode') ||
event.affectsConfiguration('openspec.cursorAgentModel');
event.affectsConfiguration('openspec.cursorAgentModel') ||
event.affectsConfiguration('openspec.agentAutoSubmit');
if (!launchConfigChanged) return;
logger.info('[workflow] launch configuration changed; updating webviews');
dashboardViewProvider.postWorkflowLaunchConfig();
Expand Down
22 changes: 22 additions & 0 deletions src/extension/providers/changeDetailPanelManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
isWorkflowSnapshotBoundTo,
type ChangeWorkflowSnapshot,
} from '../../shared/changeWorkflow';
import type { ChangeTaskProgressPatch } from '../../shared/changeTaskProgressPatch';
import { changeTaskProgressPatchAppliesToPanel } from '../../shared/changeTaskProgressScope';
import type { OpenSpecScope } from '../services/openspecScope';
import type { OpenSpecRootBinding, ProjectContext } from '../services/types';

Expand Down Expand Up @@ -428,6 +430,26 @@ export class ChangeDetailPanelManager {
* Notify an open panel that certain artifact caches should be invalidated.
* Called by DataManager's onArtifactChanged subscriber when upstream files change.
*/
public broadcastTaskProgressPatch(patch: ChangeTaskProgressPatch): void {
for (const [key, panel] of this.panels) {
if (!this.keyMatchesChange(key, patch.changeName)) continue;
const panelRootPath = this.panelRootPaths.get(key);
const panelScope = this.panelScopes.get(panel.webview);
if (!changeTaskProgressPatchAppliesToPanel(patch, {
changeName: patch.changeName,
scopeId: panelScope?.id,
planningRoot: panelRootPath,
})) {
continue;
}
try {
panel.webview.postMessage(patch);
} catch (err) {
logger.warn(`broadcastTaskProgressPatch: panel ${patch.changeName} may be disposed`, err as Error);
}
}
}

public notifyArtifactChanged(changeName: string, artifactTypes: string[], rootPath?: string): void {
for (const [key, panel] of this.panels) {
if (!this.keyMatchesChange(key, changeName)) continue;
Expand Down
Loading
Loading