diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 1c68471..7ddb703 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -13,6 +13,6 @@ "anti-slop" ], "license": "MIT", - "version": "4.8.3", + "version": "4.8.4", "repository": "https://github.com/agent-kit-startup/agent-kit" } diff --git a/.cursor/agent-kit.json b/.cursor/agent-kit.json index 3040aae..4725044 100644 --- a/.cursor/agent-kit.json +++ b/.cursor/agent-kit.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "version": "4.8.3", + "version": "4.8.4", "protected": [ ".cursor/HANDOFF.md", ".cursor/agents/test-suites.md", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c64fa35..6d7e347 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,14 +57,17 @@ jobs: # Secret PUBLIC_REPO_TOKEN: fine-grained token with public repo Contents, # Pull requests, and merge (or auto-merge) permission. Repo must Allow auto-merge # when required checks exist. Opt out: PUBLIC_SYNC_AUTO_MERGE=false. + # Private-origin only: skip when this workflow runs on the public storefront + # slug (Path C mirrors ci.yml; public has no PUBLIC_REPO_TOKEN by design). # The secrets context is not allowed in job-level `if`; the guard step - # below fails loudly when the secret is unset on tag/manual sync paths. + # below fails loudly when the secret is unset on private tag/manual sync paths. sync-public: needs: build if: >- - startsWith(github.ref, 'refs/tags/v') - || (github.event_name == 'workflow_dispatch' - && github.event.inputs.sync_public == 'true') + github.repository != 'agent-kit-startup/agent-kit' + && (startsWith(github.ref, 'refs/tags/v') + || (github.event_name == 'workflow_dispatch' + && github.event.inputs.sync_public == 'true')) runs-on: ubuntu-latest steps: - name: Checkout @@ -105,11 +108,15 @@ jobs: GIT_AUTHOR_EMAIL: sync@agent-kit.dev # ── Publish @dadado/agent-kit-cli to npm (tag only) ── + # Private-origin only: skip on the public storefront slug (same Path C + # mirror as sync-public; npm publish belongs on agent-kit-dev). # Secret NPM_TOKEN: the publish step skips gracefully if not configured # (secrets context is not allowed in job-level `if`). publish-npm: needs: build - if: startsWith(github.ref, 'refs/tags/v') + if: >- + github.repository != 'agent-kit-startup/agent-kit' + && startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 09770ca..f535a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and ## [Unreleased] +## [4.8.4] - 2026-07-29 + +### Fixed + +- Public storefront tag CI: skip `sync-public` and `publish-npm` when `github.repository` is `agent-kit-startup/agent-kit` so mirrored workflows do not fail on missing private-only secrets +- Mission Control Flight Log: hover/focus border+outline follow typed kind palette tokens (ok green, advice/prompt blue, residual yellow, warning orange); Earlier stays muted; remove unset `--accent` yellow double ring + +### Changed + +- Mission Control Health (More → Health): Healthcenter UI for the same seven checks with live severity, expand/detail, and copy-only Autofix CTAs on both Interface Skins; snapshot error distinct from per-check fail +- Mission Control chrome icons: align `spaceIconSvg` shell to refresh/`nowMetaIconSvg` (stroke 1.5); redraw Home house as one coherent glyph; add Skins palette affordance on More menu group label; leave logo assets untouched +- Mission Control: main nav, mobile stack, and desktop 2x2 grid order is Current mission → Flight Log → Checklist → Crew Monitor (first nav label matches Current mission) +- Docs: public-launch + repository-boundaries state mirror skip and private-only `PUBLIC_REPO_TOKEN`; verify checklist for next public `v*` tag + ## [4.8.3] - 2026-07-29 ### Fixed diff --git a/README.md b/README.md index 1c79b3c..a348927 100644 --- a/README.md +++ b/README.md @@ -104,11 +104,11 @@ The Cockpit reads as one page in four sections, each reachable from the primary | Section | What it answers | |---------|------------------| | Current mission | The plan in flight: status, progress, friendly Mode labels, and previous/current/next todo | -| Crew Monitor | Live agent/crew feed: ticks, handoffs, deliveries, and denser `agent_step` rows for active-plan to-dos (cap 20) | | Flight Log | HANDOFF Gaps log (**Live** / **Earlier**, wipe on new flight; cap 15 within a flight) plus operator Warnings (Quota pause, Heads up); palette-by-type notification chrome (`ok` / `advice` / `prompt` / `residual` / `warning`); clipboard icon; clickable copy text/path; **All clear** when idle (no literal `none` as a yellow Live debit) | | Checklist | What remains: recent plan cards, parked and incomplete plans, and readiness notes | +| Crew Monitor | Live agent/crew feed: ticks, handoffs, deliveries, and denser `agent_step` rows for active-plan to-dos (cap 20) | -Plans, Activity, Agents, Skills, Commands, Health, Git, Memory, Terminals, Processes, and Config live in the More sections menu next to those links, with their counts. +Plans, Activity, Agents, Skills, Commands, Health, Git, Memory, Terminals, Processes, and Config live in the More sections menu next to those links, with their counts. **Health** is a Healthcenter for the same seven workspace checks (`plans`, `handoff`, `agents`, `commands`, `memory`, `git`, `config`): live severity, expand/detail per check, and Autofix/Fix controls that only copy a command or path and name where to paste it (chat, terminal, or file picker). Snapshot/serve errors are distinct from per-check fails. Health does not reorder main cockpit tabs and does not duplicate Checklist readiness. Every action copies text and names where to paste it: repo-relative paths go to the file picker, slash commands to the chat input, chat references to the past-chat picker, and shell commands, PIDs, and commit shas to the terminal. The panel cannot open a file or a chat, and no label claims it can. diff --git a/autogit/gitupdate.md b/autogit/gitupdate.md index 0681244..3ef50a9 100644 --- a/autogit/gitupdate.md +++ b/autogit/gitupdate.md @@ -422,6 +422,7 @@ This section contains the detailed prompts that should be followed when commands | Check | How | |-------|-----| | Private tag CI | `gh run list` for the `vX.Y.Z` tag: `build`, `publish-npm`, and `sync-public` all green | + | Public storefront tag CI (advisory) | On `agent-kit-startup/agent-kit`, tag/Release runs should show `build` green with `sync-public` / `publish-npm` **skipped** (not failed). Do not treat a skipped public sync job as a private sync failure. | | npm | `npm view @dadado/agent-kit-cli version` matches the release | | Public sync PR **merged** | `sync-public` may open a PR; do **not** pass this row on CI-green alone. Confirm the public sync PR is **merged** (`gh pr view` / `gh pr list -R --state merged`) before claiming public `main` is current | | Public `main` | Latest commit message like `chore: sync private vX.Y.Z (...)` on the public default branch **after** that merge | diff --git a/dashboard/dashboard.html b/dashboard/dashboard.html index 7ebb7ab..92a2c69 100644 --- a/dashboard/dashboard.html +++ b/dashboard/dashboard.html @@ -503,6 +503,9 @@ background: var(--border); } .nav-more-group-label { + display: flex; + align-items: center; + gap: var(--mc-chrome-label-gap); padding: 6px 10px 2px; font-size: var(--mc-chrome-subtitle-size); font-weight: var(--mc-chrome-subtitle-weight); @@ -800,25 +803,122 @@ } .terminal-item:hover { background: var(--bg-card-hover); } -/* ===== Health ===== */ +/* ===== Healthcenter ===== */ +.healthcenter { + display: flex; + flex-direction: column; + gap: 10px; +} +.healthcenter-presence { + display: flex; + align-items: center; + gap: 8px; + font-size: 11px; + color: var(--text-muted); +} +.healthcenter-presence .dot-pulse { + animation: pulse 1.6s ease-in-out infinite; +} .health-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; } +.health-card { + display: flex; + flex-direction: column; + gap: 0; + border-radius: var(--mc-radius-sm, 6px); + background: var(--bg-card); + border: 1px solid var(--border); + overflow: hidden; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} +.health-card:hover, +.health-card.is-expanded { + border-color: var(--border-active); +} +.health-card[data-severity="ok"] { border-left: 3px solid var(--green, #3fb950); } +.health-card[data-severity="warning"] { border-left: 3px solid var(--yellow, #d29922); } +.health-card[data-severity="degraded"] { border-left: 3px solid var(--orange, #db6d28); } +.health-card[data-severity="error"] { border-left: 3px solid var(--red, #f85149); } .health-item { display: flex; align-items: center; gap: 8px; - padding: 8px 10px; - border-radius: 6px; - background: var(--bg-card); - border: 1px solid var(--border); + padding: 10px 12px; font-size: 12px; color: var(--text-secondary); - transition: opacity 0.3s ease, transform 0.3s ease, border-color 0.2s; + cursor: pointer; + background: transparent; + border: none; + width: 100%; + text-align: left; + font-family: inherit; + transition: opacity 0.3s ease, transform 0.3s ease, background 0.15s ease; +} +.health-item:hover { background: var(--bg-card-hover); } +.health-item-label { + flex: 1; + min-width: 0; + color: var(--text-primary); +} +.health-item-sev { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--text-muted); + flex-shrink: 0; +} +.health-item-chevron { + color: var(--text-muted); + font-size: 10px; + transition: transform 0.15s ease; + flex-shrink: 0; +} +.health-card.is-expanded .health-item-chevron { + transform: rotate(180deg); +} +.health-detail { + display: none; + padding: 0 12px 12px; + border-top: 1px solid var(--border); + background: var(--bg-secondary); +} +.health-card.is-expanded .health-detail { + display: block; + animation: fadeIn 0.2s ease; +} +.health-detail-copy { + font-size: 11px; + color: var(--text-secondary); + margin: 10px 0 8px; + line-height: 1.4; +} +.health-detail-actions { + display: flex; + flex-wrap: wrap; + gap: 6px; +} +.health-autofix-btn { + font-size: 11px; + padding: 4px 10px; + border-radius: 4px; + border: 1px solid var(--border-active); + background: var(--bg-card); + color: var(--text-primary); + cursor: pointer; + font-family: inherit; +} +.health-autofix-btn:hover { + background: var(--bg-card-hover); +} +html[data-dashboard-skin="cursor"] .health-card { + border-radius: 8px; +} +html[data-dashboard-skin="legacy"] .health-card { + border-radius: 4px; } -.health-item:hover { border-color: var(--border-active); } /* ===== Git ===== */ .git-info { @@ -1448,7 +1548,7 @@ vertical-align: middle; } -/* ===== Health Investigation Message ===== */ +/* ===== Health Investigation Message (legacy; Healthcenter uses .health-detail) ===== */ .health-message { font-size: 11px; color: var(--text-secondary); @@ -2031,8 +2131,8 @@ grid-template-columns: 1fr 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); grid-template-areas: - "now monitor" - "attention checklist"; + "now attention" + "checklist monitor"; } #section-overview.active #now-execution-panel, @@ -2662,10 +2762,10 @@ font: inherit; line-height: 1.4; } +/* Base hover/focus: keyboard-visible ring; kind rules below set colors (no unset --accent → yellow). */ .flight-log-card:hover, .flight-log-card:focus-visible { - border-color: var(--accent, var(--yellow)); - outline: 2px solid var(--accent, var(--yellow)); + outline: 2px solid var(--border); outline-offset: 1px; } .flight-log-card-current { @@ -2679,6 +2779,11 @@ .flight-log-card-current.flight-log-kind-residual .flight-log-card-label { color: var(--yellow); } +.flight-log-card-current.flight-log-kind-residual:hover, +.flight-log-card-current.flight-log-kind-residual:focus-visible { + border-color: var(--yellow); + outline-color: var(--yellow); +} .flight-log-card-current.flight-log-kind-advice { border-color: var(--blue); background: var(--blue-bg); @@ -2686,6 +2791,11 @@ .flight-log-card-current.flight-log-kind-advice .flight-log-card-label { color: var(--blue); } +.flight-log-card-current.flight-log-kind-advice:hover, +.flight-log-card-current.flight-log-kind-advice:focus-visible { + border-color: var(--blue); + outline-color: var(--blue); +} .flight-log-card-current.flight-log-kind-ok { border-color: color-mix(in srgb, var(--green) 55%, var(--border)); background: color-mix(in srgb, var(--green-bg) 40%, var(--bg-elevated, var(--bg-card))); @@ -2693,6 +2803,11 @@ .flight-log-card-current.flight-log-kind-ok .flight-log-card-label { color: var(--green); } +.flight-log-card-current.flight-log-kind-ok:hover, +.flight-log-card-current.flight-log-kind-ok:focus-visible { + border-color: var(--green); + outline-color: var(--green); +} .flight-log-card-current.flight-log-kind-warning { border-color: color-mix(in srgb, var(--orange, var(--yellow)) 70%, var(--border)); background: color-mix(in srgb, var(--orange-bg, var(--yellow-bg)) 55%, var(--bg-elevated, var(--bg-card))); @@ -2700,6 +2815,11 @@ .flight-log-card-current.flight-log-kind-warning .flight-log-card-label { color: var(--orange, var(--yellow)); } +.flight-log-card-current.flight-log-kind-warning:hover, +.flight-log-card-current.flight-log-kind-warning:focus-visible { + border-color: var(--orange, var(--yellow)); + outline-color: var(--orange, var(--yellow)); +} .flight-log-card-past { padding: 8px 10px; font-size: 11px; @@ -2709,15 +2829,35 @@ .flight-log-card-past.flight-log-kind-residual { border-color: color-mix(in srgb, var(--yellow) 35%, var(--border)); } +.flight-log-card-past.flight-log-kind-residual:hover, +.flight-log-card-past.flight-log-kind-residual:focus-visible { + border-color: color-mix(in srgb, var(--yellow) 55%, var(--border)); + outline-color: color-mix(in srgb, var(--yellow) 55%, var(--border)); +} .flight-log-card-past.flight-log-kind-advice { border-color: color-mix(in srgb, var(--blue) 35%, var(--border)); } +.flight-log-card-past.flight-log-kind-advice:hover, +.flight-log-card-past.flight-log-kind-advice:focus-visible { + border-color: color-mix(in srgb, var(--blue) 55%, var(--border)); + outline-color: color-mix(in srgb, var(--blue) 55%, var(--border)); +} .flight-log-card-past.flight-log-kind-ok { border-color: color-mix(in srgb, var(--green) 30%, var(--border)); } +.flight-log-card-past.flight-log-kind-ok:hover, +.flight-log-card-past.flight-log-kind-ok:focus-visible { + border-color: color-mix(in srgb, var(--green) 50%, var(--border)); + outline-color: color-mix(in srgb, var(--green) 50%, var(--border)); +} .flight-log-card-past.flight-log-kind-warning { border-color: color-mix(in srgb, var(--orange, var(--yellow)) 30%, var(--border)); } +.flight-log-card-past.flight-log-kind-warning:hover, +.flight-log-card-past.flight-log-kind-warning:focus-visible { + border-color: color-mix(in srgb, var(--orange, var(--yellow)) 50%, var(--border)); + outline-color: color-mix(in srgb, var(--orange, var(--yellow)) 50%, var(--border)); +} .flight-log-card-warning { padding: 10px 12px; border-color: color-mix(in srgb, var(--orange, var(--yellow)) 70%, var(--border)); @@ -2728,6 +2868,11 @@ .flight-log-card-warning .flight-log-card-label { color: var(--orange, var(--yellow)); } +.flight-log-card-warning:hover, +.flight-log-card-warning:focus-visible { + border-color: var(--orange, var(--yellow)); + outline-color: var(--orange, var(--yellow)); +} .flight-log-card-label { display: block; font-weight: 600; @@ -2894,7 +3039,7 @@ `); @@ -6394,30 +6625,108 @@ `); - // ===== Health ===== + // ===== Healthcenter (More → Health) ===== const checks = health.checks || []; + const healthStatus = health.status || 'ok'; + const passing = checks.filter(c => c.ok).length; + const isSnapshotError = healthStatus === 'error' && checks.length === 0; + const isOffline = !isSnapshotError && checks.length === 0; + const presenceTone = healthStatus === 'ok' ? 'green' : healthStatus === 'warning' ? 'yellow' : 'red'; + const presencePulse = healthStatus !== 'ok' ? ' dot-pulse' : ''; + const presenceLabel = isSnapshotError + ? 'Snapshot error' + : isOffline + ? 'Health offline' + : healthStatus === 'ok' + ? 'Live' + : healthStatus === 'warning' + ? 'Warning' + : healthStatus === 'degraded' + ? 'Degraded' + : 'Error'; + const subtitle = isSnapshotError + ? 'Snapshot unavailable' + : isOffline + ? 'No health data' + : `${passing}/${checks.length} checks passing`; + + let healthBody = ''; + if (isSnapshotError) { + healthBody = renderEmptyStateCta({ + headline: 'Health snapshot error', + support: (typeof d.error === 'string' && d.error) + || 'The dashboard could not load health checks (serve/snapshot failure). This is not a per-check fail.', + iconKind: 'health', + cta: { + text: 'agent-kit doctor', + subject: 'agent-kit doctor', + destination: 'terminal', + label: 'Autofix: copy agent-kit doctor', + focusKey: 'cta-health-snapshot-error', + }, + }); + } else if (isOffline) { + healthBody = renderEmptyStateCta({ + headline: 'Health offline', + support: 'No health data available yet. Refresh after the snapshot loads, or run doctor if the panel stays empty.', + iconKind: 'health', + cta: { + text: 'agent-kit doctor', + subject: 'agent-kit doctor', + destination: 'terminal', + label: 'Autofix: copy agent-kit doctor', + focusKey: 'cta-health-offline', + }, + }); + } else { + healthBody = ` +
+ ${checks.map(c => { + const id = c.id || c.label || 'check'; + const meta = HEALTH_CHECK_META[id] || { + okDetail: 'Check is passing.', + failDetail: 'Check is failing. Review configuration for this item.', + autofix: null, + }; + const sev = healthCheckSeverity(c, healthStatus); + const detail = c.ok ? meta.okDetail : meta.failDetail; + const autofix = !c.ok && meta.autofix ? meta.autofix : null; + const autofixBtn = autofix + ? `` + : (c.ok + ? `No Autofix needed.` + : `No Autofix mapped for this check.`); + return ` +
+ +
+

${escapeHtml(detail)}

+
${autofixBtn}
+
+
`; + }).join('')} +
`; + } + parts.push(`
- + Health - ${checks.filter(c => c.ok).length}/${checks.length} checks passing + ${escapeHtml(subtitle)}
- ${checks.length === 0 - ? renderEmptyStateCta({ - headline: 'Health offline', - support: 'No health data available.', - }) - : ''} -
- ${checks.map(c => ` -
-
- - ${escapeHtml(c.label)} -
-
- `).join('')} +
+
+ + ${escapeHtml(presenceLabel)} + · same seven checks · copy-only Autofix +
+ ${healthBody}
`); diff --git a/docs/public-launch.md b/docs/public-launch.md index 243291a..88aa040 100644 --- a/docs/public-launch.md +++ b/docs/public-launch.md @@ -24,7 +24,7 @@ Decision recorded in the maintainers' decision log (private repo memory). | Dogfood on this repo (plan, HANDOFF, staging→prod, memory) | ✅ | | Public sync opens an append-only PR by default | ✅ `scripts/sync-public.mjs` | | GitHub About description and topics | ✅ aligned with [github-about.md](github-about.md) | -| `PUBLIC_REPO_TOKEN` secret on private CI | ⏳ ops - requires Contents + Pull requests + Workflows write on the public repo | +| `PUBLIC_REPO_TOKEN` secret on private CI | ⏳ ops - requires Contents + Pull requests + Workflows write on the public repo; **private repo only** (never configure on the public storefront) | | Public `main` ruleset | ✅ active (`Protect main` - PR + 1 approval + Admin bypass + check `build`) | | Fase 7 registry-on-public topology | ✅ spec [topology-private-public.md](topology-private-public.md); cutover ops still open | | Final plan review (`review-camadas`) | ✅ [review-camadas.md](review-camadas.md) | @@ -90,12 +90,23 @@ node scripts/sync-public.mjs --dry-run After `git prod` on the private repo, the pipeline automatically: -1. **Creates annotated vX.Y.Z tag** (triggers npm publish + sync-public jobs) +1. **Creates annotated vX.Y.Z tag** (triggers npm publish + sync-public jobs on **private** `agent-kit-dev`) 2. **Opens sync PR** with semantic body: Summary + CHANGELOG release notes + source SHA 3. **Auto-merges PR** after required checks pass (`gh pr merge --auto`) Opt-out: Set `PUBLIC_SYNC_AUTO_MERGE=false` to require manual merge. +**Public storefront tag CI:** Path C mirrors `.github/workflows/ci.yml` to `agent-kit-startup/agent-kit`. When a Release or tag push runs on that public slug, `sync-public` and `publish-npm` are **skipped** (`github.repository != 'agent-kit-startup/agent-kit'`). Only the `build` job should run. Do **not** add `PUBLIC_REPO_TOKEN` or `NPM_TOKEN` to the public repo; those secrets stay on private. Private tag/manual sync still fails loud when `PUBLIC_REPO_TOKEN` is unset. + +### Verify next public `v*` tag + +After the next storefront tag (from private sync + public Release): + +1. Public Actions for that tag: `build` green. +2. `sync-public` and `publish-npm` do **not** run (skipped by repository slug guard). +3. No `PUBLIC_REPO_TOKEN` / `NPM_TOKEN` on the public repo. +4. Do **not** force-move an already-pushed `v*` tag; use a new patch tag if retry is needed. + Manual fallback: ```bash # workflow_dispatch when tag-based trigger is insufficient diff --git a/docs/repository-boundaries.md b/docs/repository-boundaries.md index 151e06b..1bbf0d8 100644 --- a/docs/repository-boundaries.md +++ b/docs/repository-boundaries.md @@ -38,13 +38,13 @@ Do not `git add -f` session paths. Contributions after Phase B: registry PRs go 1. **`git staging`** - PR to `staging` on the **private** repo (`agent-kit-dev`). Everything committed goes to private. 2. **`git prod`** - `staging` → `main` on **private**; push `origin main`; create/push annotated vX.Y.Z tag when absent. -3. **Automatic triggers** - Annotated `v*` tags trigger both `publish-npm` (when `NPM_TOKEN` configured) and `sync-public` (when `PUBLIC_REPO_TOKEN` configured) CI jobs. +3. **Automatic triggers** - Annotated `v*` tags on the **private** repo trigger both `publish-npm` (when `NPM_TOKEN` configured) and `sync-public` (when `PUBLIC_REPO_TOKEN` configured) CI jobs. The same workflow file is mirrored to the public storefront; on `agent-kit-startup/agent-kit`, those two jobs are **skipped** by repository slug so public tag CI does not fail on missing private-only secrets. 4. **Public sync PR** - Creates semantic PR body (Summary + CHANGELOG release notes + source SHA) against public `main`. 5. **Auto-merge** - PRs auto-merge after required checks pass (`gh pr merge --auto`). Set `PUBLIC_SYNC_AUTO_MERGE=false` to require manual merge. 6. **Public GitHub Release** - After the sync PR merges, sync creates/updates a public GitHub Release `vX.Y.Z` (CHANGELOG notes; Latest badge). Opt out with `PUBLIC_SYNC_CREATE_RELEASE=false`. Git tags alone do not move the Releases sidebar. 7. **Fallback** (manual dispatch): `pnpm git:trigger-public-sync` or *workflow_dispatch* in Actions when tag-based trigger is insufficient. -Without `PUBLIC_REPO_TOKEN` configured on the **private** GitHub repo, the sync job pushes nothing; the rest of CI still runs normally. +Without `PUBLIC_REPO_TOKEN` configured on the **private** GitHub repo, tag and manual public-sync jobs **fail loudly** (visible Actions error). Do not configure that secret on the **public** storefront; public tag runs skip `sync-public` / `publish-npm` entirely. ### Public launch checklist @@ -81,8 +81,8 @@ Before the first publish or any new registry version, follow [npm-publish-checkl | Secret | Purpose | |--------|---------| -| `PUBLIC_REPO_TOKEN` | Fine-grained token limited to the public repo, with `Contents: write`, `Pull requests: write`, and `Workflows: write` (required when the sync tree includes `.github/workflows/`) | -| `NPM_TOKEN` | Token to publish to the npm registry | +| `PUBLIC_REPO_TOKEN` | Fine-grained token limited to the public repo, with `Contents: write`, `Pull requests: write`, and `Workflows: write` (required when the sync tree includes `.github/workflows/`). **Private repo only**; never set on the public storefront. | +| `NPM_TOKEN` | Token to publish to the npm registry (**private** tag CI only) | Optional repository variable `PUBLIC_REPO_URL` overrides the default public Git URL without embedding credentials. diff --git a/package.json b/package.json index 7fc0d41..6d0e476 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-kit", - "version": "4.8.3", + "version": "4.8.4", "description": "HITL framework for AI-assisted IDEs: plan, handoff, staging-to-prod, memory loop; project-aware setup for Cursor, VS Code, and Windsurf.", "private": true, "license": "MIT", diff --git a/packages/cli/package.json b/packages/cli/package.json index 4478c59..5741675 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@dadado/agent-kit-cli", - "version": "4.8.3", + "version": "4.8.4", "description": "Agent Kit CLI: HITL framework install and tooling for AI-assisted IDEs (rules, skills, plan/handoff, context).", "type": "module", "bin": { diff --git a/packages/cli/src/dashboard/plugin-ux-validation.test.ts b/packages/cli/src/dashboard/plugin-ux-validation.test.ts index abae95a..e0b9781 100644 --- a/packages/cli/src/dashboard/plugin-ux-validation.test.ts +++ b/packages/cli/src/dashboard/plugin-ux-validation.test.ts @@ -161,7 +161,7 @@ describe("plugin-ux-validation: narrow shell + a11y chrome", () => { /\.btn-refresh \.btn-refresh-icon\s*\{[^}]*width:\s*var\(--mc-chrome-icon-size\)[^}]*height:\s*var\(--mc-chrome-icon-size\)/, ); expect(dashboardHtml).toMatch( - /function spaceIconSvg[\s\S]*?viewBox="0 0 16 16"[\s\S]*?stroke-width="0\.5"/, + /function spaceIconSvg[\s\S]*?viewBox="0 0 16 16"[\s\S]*?stroke-width="1\.5"/, ); expect(dashboardHtml).toMatch( /function nowMetaIconSvg[\s\S]*?viewBox="0 0 16 16"[\s\S]*?stroke-width="1\.5"/, @@ -328,6 +328,8 @@ describe("plugin-ux-validation: narrow shell + a11y chrome", () => { // Interface Skins group lives in the More menu, separate from Agent Persona packs. expect(dashboardHtml).toContain('id="navSkinsLabel"'); expect(dashboardHtml).toMatch(/navSkinsLabel[\s\S]*?>Skins { } }); + it("pins Flight Log kind-aware hover/focus chrome (not yellow-only via unset --accent)", () => { + // Regression: shared hover must not fall back to yellow via unset --accent. + expect(dashboardHtml).not.toContain("border-color: var(--accent, var(--yellow));"); + expect(dashboardHtml).not.toContain("outline: 2px solid var(--accent, var(--yellow));"); + + const kindHoverTokens: Array<{ kind: string; token: string }> = [ + { kind: "ok", token: "var(--green)" }, + { kind: "advice", token: "var(--blue)" }, + { kind: "residual", token: "var(--yellow)" }, + { kind: "warning", token: "var(--orange, var(--yellow))" }, + ]; + for (const { kind, token } of kindHoverTokens) { + expect(dashboardHtml).toContain(`.flight-log-card-current.flight-log-kind-${kind}:hover`); + expect(dashboardHtml).toContain( + `.flight-log-card-current.flight-log-kind-${kind}:focus-visible`, + ); + expect(dashboardHtml).toContain(`.flight-log-card-past.flight-log-kind-${kind}:hover`); + expect(dashboardHtml).toContain( + `.flight-log-card-past.flight-log-kind-${kind}:focus-visible`, + ); + // Live hover/focus outline follows the kind palette token. + const liveHoverBlock = dashboardHtml.match( + new RegExp( + `\\.flight-log-card-current\\.flight-log-kind-${kind}:hover,\\s*` + + `\\.flight-log-card-current\\.flight-log-kind-${kind}:focus-visible\\s*\\{[^}]+\\}`, + ), + ); + expect(liveHoverBlock?.[0]).toContain(`outline-color: ${token}`); + } + expect(dashboardHtml).toContain(".flight-log-card-warning:hover"); + expect(dashboardHtml).toContain(".flight-log-card-warning:focus-visible"); + }); + it("renders Flight Log operator Warnings lane without cadence Review/Resolve CTAs", () => { expect(dashboardHtml).toContain("function renderFlightLogWarningCard(warning, idx)"); expect(dashboardHtml).toContain("flight-log-warnings"); @@ -1267,18 +1302,18 @@ describe("plugin-ux-validation: SSE + overview model wiring", () => { expect(formatModeDisplayLabel("")).toBe(""); }); - it("orders overview as current mission → monitor → field report → checklist", () => { + it("orders overview as current mission → flight log → checklist → crew monitor", () => { const overviewStart = dashboardHtml.indexOf('id="section-overview"'); expect(overviewStart).toBeGreaterThan(-1); const overviewSlice = dashboardHtml.slice(overviewStart, overviewStart + 4000); const nowIdx = overviewSlice.indexOf("renderNowExecutionPanel"); - const activityIdx = overviewSlice.indexOf('id="hero-activity"'); const attentionIdx = overviewSlice.indexOf("renderAttentionPanel"); const recentIdx = overviewSlice.indexOf("renderRecentPlanCards"); + const activityIdx = overviewSlice.indexOf('id="hero-activity"'); expect(nowIdx).toBeGreaterThan(-1); - expect(activityIdx).toBeGreaterThan(nowIdx); - expect(attentionIdx).toBeGreaterThan(activityIdx); + expect(attentionIdx).toBeGreaterThan(nowIdx); expect(recentIdx).toBeGreaterThan(attentionIdx); + expect(activityIdx).toBeGreaterThan(recentIdx); }); it("locks desktop one-fold at min-width 1024px with a 2x2 overview grid", () => { @@ -1296,8 +1331,8 @@ describe("plugin-ux-validation: SSE + overview model wiring", () => { /#section-overview\.active \.overview-stack\s*\{[^}]*overflow:\s*hidden/, ); expect(dashboardHtml).toContain("grid-template-areas:"); - expect(dashboardHtml).toContain('"now monitor"'); - expect(dashboardHtml).toContain('"attention checklist"'); + expect(dashboardHtml).toContain('"now attention"'); + expect(dashboardHtml).toContain('"checklist monitor"'); expect(dashboardHtml).toMatch( /#section-overview\.active \.overview-stack\s*\{[^}]*grid-template-rows:\s*minmax\(0,\s*1fr\)\s+minmax\(0,\s*1fr\)/, ); @@ -1458,8 +1493,22 @@ describe("plugin-ux-validation: SSE + overview model wiring", () => { ]) { expect(dashboardHtml).toContain(heading); } - // Primary nav names the page; no redundant page-level Cockpit/Overview title. - expect(dashboardHtml).toContain('class="top-nav-anchor-label">Cockpit'); + // Primary nav first label matches Current mission; order: mission → Flight Log → Checklist → Crew. + expect(dashboardHtml).toContain('class="top-nav-anchor-label">Current mission'); + const navBlock = dashboardHtml.match( + /class="cockpit-anchors"[\s\S]*?<\/div>\s*<\/div>\s*<\/nav>/, + ); + expect(navBlock).not.toBeNull(); + const navHtml = navBlock?.[0] ?? ""; + const missionNav = navHtml.indexOf(">Current mission"); + const flightNav = navHtml.indexOf(">Flight Log"); + const checklistNav = navHtml.indexOf(">Checklist"); + const crewNav = navHtml.indexOf(">Crew Monitor"); + expect(missionNav).toBeGreaterThan(-1); + expect(flightNav).toBeGreaterThan(missionNav); + expect(checklistNav).toBeGreaterThan(flightNav); + expect(crewNav).toBeGreaterThan(checklistNav); + expect(dashboardHtml).not.toContain('class="top-nav-anchor-label">Cockpit'); expect(dashboardHtml).not.toMatch(/<\/span>\s*Cockpit/); expect(dashboardHtml).not.toMatch(/<\/span>\s*Overview/); // No prose pointing at navigation the panel no longer ships. @@ -1691,12 +1740,13 @@ describe("cockpit-validation: icons, assets, and accessible names", () => { "field-report": "Flight Log", checklist: "Checklist", "more-sections": "More sections", - // More-menu section icons (12 kinds) + // More-menu section icons (12 kinds) + skins affordance overview: "Home", plans: "Plans", activity: "Activity", agents: "Agents", skills: "Skills", + skins: "Skins", commands: "Commands", health: "Health", git: "Git", @@ -1710,12 +1760,25 @@ describe("cockpit-validation: icons, assets, and accessible names", () => { expect(decorative, `${kind} must render`).toContain(' { expect(dashboardHtml).toContain(".queue-role-pill-queued"); }); }); + +describe("plugin-ux-validation: Healthcenter (More → Health)", () => { + it("renders Healthcenter shell with presence and fixed seven-check Autofix map", () => { + expect(dashboardHtml).toContain('id="section-health"'); + expect(dashboardHtml).toContain('class="healthcenter"'); + expect(dashboardHtml).toContain("healthcenter-presence"); + expect(dashboardHtml).toContain("HEALTH_CHECK_META"); + expect(dashboardHtml).toContain("function toggleHealthCheck(checkId)"); + expect(dashboardHtml).toContain("function healthCheckSeverity(check, aggregateStatus)"); + for (const id of ["plans", "handoff", "agents", "commands", "memory", "git", "config"]) { + expect(dashboardHtml).toContain(`${id}:`); + } + expect(dashboardHtml).toContain("same seven checks · copy-only Autofix"); + }); + + it("maps Autofix to copyForPaste destinations (no Open/protocol)", () => { + expect(dashboardHtml).toContain("health-autofix-btn"); + expect(dashboardHtml).toMatch( + /autofix:\s*\{\s*text:\s*'\/start-project'[\s\S]*?destination:\s*'chatInput'/, + ); + expect(dashboardHtml).toMatch( + /autofix:\s*\{\s*text:\s*'agent-kit doctor'[\s\S]*?destination:\s*'terminal'/, + ); + expect(dashboardHtml).toMatch( + /autofix:\s*\{\s*text:\s*'\.cursor\/commands\/'[\s\S]*?destination:\s*'filePicker'/, + ); + const healthBlock = dashboardHtml.match( + /\/\/ ===== Healthcenter \(More → Health\) =====[\s\S]*?(?=\n {2}\/\/ ===== Git =====)/, + )?.[0]; + expect(healthBlock).toBeTruthy(); + expect(healthBlock).toContain("copyForPasteHandler"); + expect(healthBlock).not.toMatch(/vscode:\/\/|cursor:\/\/|Open file|protocol/i); + expect(healthBlock).toContain("Health snapshot error"); + expect(healthBlock).toContain("Health offline"); + }); + + it("keeps dual-skin Healthcard radius and reduced-motion press on .health-item", () => { + expect(dashboardHtml).toMatch( + /html\[data-dashboard-skin="cursor"\] \.health-card\s*\{[^}]*border-radius:\s*8px/, + ); + expect(dashboardHtml).toMatch( + /html\[data-dashboard-skin="legacy"\] \.health-card\s*\{[^}]*border-radius:\s*4px/, + ); + expect(dashboardHtml).toMatch(/\.health-item:active\s*\{[^}]*transform:\s*scale\(0\.98\)/); + }); +});