UI: Center align coverage columns and standardize naming - #53
Open
execsumo wants to merge 115 commits into
Open
Conversation
- Add hooks router, schemas, container setup and business logic - Add comprehensive integration and unit tests for the hooks capability Co-Authored-By: agy <antigravity-cli>
… registry - Create hooks feature with components, sheets, API queries, models and localizations - Add routes and wire hooks into App.tsx and capability registries (overview, sidebar, invalidation) - Update README with Antigravity (agy) integration details Co-Authored-By: agy <antigravity-cli>
Add Hooks column to the harness capability matrix (Claude Code: Yes, others: Not Yet), a How-it-works Hooks section, the hooks manifest paths, the local-first action, and check off Hook support on the roadmap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… category terms - Update HookSpec to utilize canonical event and match properties - Add data migration path in store from-dict loader for raw Claude Code strings - Update FastAPI endpoints and request/response validation schemas Co-Authored-By: agy <antigravity-cli>
…sses - Add specific hook codecs and representation filters for Codex, Cursor, OpenCode, and Antigravity - Generalize FileBackedHooksAdapter to delegate layout formatting to codec mappers - Register hook binding profiles to supported harness definitions in catalog Co-Authored-By: agy <antigravity-cli>
…sheet views - Update forms and state keys to submit and read canonical match categories and events - Re-run OpenAPI type generator to sync typescript clients - Adjust localizations and controllers to map new schemas Co-Authored-By: agy <antigravity-cli>
…ecks - Update HookStoreTests and FileBackedHooksAdapterTests to verify canonical parameters - Add comprehensive HookRoutesTests verifying config updates for all 5 harnesses - Add unit mappers tests covering round-tripping and format layout checks Co-Authored-By: agy <antigravity-cli>
Map canonical user_prompt_submit to Antigravity PreInvocation (flat handler list, no matcher), surface representability caveat, and add round-trip + caveat + foreign-preservation tests. Co-Authored-By: agy <antigravity-cli>
Hooks now sync across all feasible harnesses, not just Claude Code: Codex and Cursor are full, OpenCode and Antigravity are partial. Update the capability matrix and rewrite the How-it-works Hooks section to describe the canonical event/category model and the representability matrix (including the Antigravity PreInvocation caveat). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…to-global
The permissions Needs-Review page was a stub and the inventory discarded the
parsed spec for unmanaged entries (spec=null, displayName=hash), so existing
harness permission rules were invisible and unpromotable.
- inventory: populate spec + a readable display_name ("allow · shell: git *")
for unmanaged entries from the harness-observed parsed_spec
- mutations/router: add promote_permission + POST /permissions/{id}/promote to
adopt an observed rule into the managed manifest (reclassifies the existing
binding without rewriting harness config)
- frontend: real PermissionsNeedsReviewPage listing unmanaged rules with a
Promote action; filterPermissionsNeedsReview selector + promote mutation
- tests: integration round-trip (readable + promotable) and selector coverage
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the permissions fix for the hooks family: the Needs-Review page was a
stub and the inventory discarded the parsed spec for unmanaged entries, so
existing harness hooks were invisible and unpromotable.
- inventory: populate spec + readable display_name ("pre_tool_use · shell: cmd")
for unmanaged entries from the harness-observed parsed_spec
- mutations/router: add promote_hook + POST /hooks/{id}/promote
- adapters: exclude bookkeeping `id` from hook drift comparison so a promoted
external hook reads back as managed rather than drifted (neutral for hooks
skill-manager itself wrote, which already match by id)
- frontend: real HooksNeedsReviewPage listing unmanaged hooks with a Promote
action; filterHooksNeedsReview selector + promote mutation
- tests: integration round-trip (readable + promotable) and selector coverage
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…Hooks
Hooks was reusing MCP's iconKey ("mcp"), making the two indistinguishable
in the sidebar. Give Hooks its own iconKey and a Webhook icon.
Also bring MCP Servers and Hooks to parity with Skills/Slash Commands by
adding a Board view (Disabled everywhere / Selective / Enabled everywhere
drag columns), scoped to single-card drag only - no multi-drag, no
optimistic pinning, since MCP's enable path can pop a config dialog
mid-drag that a pin can't safely anticipate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Hermes Agent is now a catalog harness (CLI probe `hermes`, root ~/.hermes) with skills, MCP (~/.hermes/mcp.json, `mcpServers`), and slash-command (~/.hermes/commands, frontmatter) bindings, so it flows app-wide. Closing the slash allowlist gap: SlashTargetId + TARGET_ORDER now include hermes (backend models/schema, frontend types), openapi regenerated. Hooks and permissions are intentionally unbound (see handoff.md). Settings "Harness roots" drop the trailing /skills and show the harness root the app writes into. All home paths render as ~/… via a frontend display-only formatter (lib/paths: formatHomePath + useFormatPath + HomeDirProvider) fed by a new homeDir field on /api/health; API values stay absolute so keys and the MCP config-choice round-trip are unaffected. Also: remove README.zh-CN.md from this fork; add handoff.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…with fork Adopts the product-accurate Hermes Agent implementation from upstream mode-io/skill-manager PR mode-io#51 (commit 4f085f8), replacing our earlier speculative one whose formats were unverified guesses: - MCP: ~/.hermes/config.yaml (YAML) under `mcp_servers`, standalone HermesMapper with no `type` field (stdio=command/args/env, http/sse=url/transport/headers). Adds ruamel.yaml for round-trip-safe YAML I/O and refactors FileBackedMcpAdapter to mutate the document in place (_ensure_subtree) so YAML comments/quoting survive — this changes the write path for every config-subtree MCP harness. - Skills: categorized layout ~/.hermes/skills/<category>/<skill>/; shared skills link under the `skill-manager` category. - Hub-awareness: reads Hermes' .hub/lock.json + .bundled_manifest to exclude official/builtin/optional and self-learned skills, adopting only external-hub skills; threads `origin_harness` provenance through the skill store manifest. - Home override: SKILL_MANAGER_HERMES_HOME → HERMES_HOME → ~/.hermes. Reconciled with fork-only work that upstream lacks: kept our Hermes slash-command binding (still provisional — upstream deliberately omits it) and its SlashTargetId/TARGET_ORDER allowlist entries, the `agy` harness, and our theme-neutral hermes-logo.svg (dropped upstream's png). Independently verified: backend 309+133, typecheck, npm 269, build, openapi (no drift) all green; CommentedMap read-back path confirmed against a live YAML config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The PR mode-io#51 cherry-pick merge left two Hermes Agent cells in the harness logo table: upstream's (referenced the dropped hermes-logo.png — a now broken image link) and ours (hermes-logo.svg). Collapse to one cell using the existing svg and upstream's real product docs link (hermes-agent.nousresearch.com), placed last to match the frontend HarnessLogoKey order. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The detail endpoint returned only name/description/prompt/tools, which is not enough to render a Skills-style detail view. It now also returns the raw document, the store path, and a per-harness row carrying state, install path, and installMethod (symlink vs rendered vs none) — so the view can show where each copy actually lives and why Codex differs. Also adds a regression test that PUT preserves fields the client omits; the current edit dialog blanks them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…arse 'About' was sourced from the `description` frontmatter key alone, and the writer re-rendered files from only name/description/tools — so every other key was destroyed on the first save. The 11 real Claude agents in this workspace carry model, permissionMode, maxTurns, disallowedTools, skills, mcpServers, effort, and hooks. All of it was collateral damage. The parser now keeps the frontmatter mapping verbatim and the writer merges edits into it, so keys we do not interpret survive untouched. The one exception is our own retired compile keys (capabilities, harnesses), which are still dropped since nothing reads them. The detail view gains a Configuration section listing every remaining key/value as-is — no per-harness whitelist to maintain, so Cursor's readonly/is_background and Codex's sandbox_mode will display without further work. Replaces the tool chips, whose hardcoded white border broke in light mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`effort: ""` round-tripped to `effort:`, which reparses as null rather than the empty string it was. Quote empty strings on write. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
README now states what Skill Manager actually does with agent frontmatter: it interprets name/description/tools and leaves every other key alone, rather than "shrinking" files to those three. plan-agents-simplify.md decision 6 said the opposite of the shipped behavior, so it is marked amended with the corrected model appended — including the lesson, since the same bug shape (rewriting a whole artifact from the subset of fields you understand) shipped twice in this rebuild. handoff.md gains the detail-view and frontmatter work, the two frontend wire-contract failures that passed their own mocked tests, and the final suite counts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The last column's value ("0 / 5", "Active", "Action") rendered offset from
its header and past the row's right edge, on every matrix table.
MatrixTable emitted a <colgroup> that assumed a fixed cell layout: checkbox,
identity, N harness, compact, coverage. Real views don't match it — the agents
and permissions/hooks review pages render no compact cell and end in Action
instead of coverage — and the compact/harness cells are toggled per breakpoint
with `display: none`, which generates no box and so occupies no column at all.
Every mismatch shifted the trailing cells one column left, parking the last
value in the zero-width compact column (its content overflowing, uncentered)
and leaving the real 96px column empty at the right edge.
Drop the colgroup: with `table-layout: fixed` the widths already come from the
header cells' own `matrix-table__th--*` classes, which track exactly the cells
each view renders at each breakpoint. That leaves `harnessColumnCount` and
`hasCheckbox` with nothing to do, so they're gone from the props and callers.
Tests can't catch this class of bug — jsdom has no layout — so they now assert
the invariant that broke instead: header and body agree cell-for-cell, and
MatrixTable declares no columns of its own. Verified visually in the app.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…argets One sequence everywhere: Claude, Codex, Antigravity, Cursor, OpenCode, Hermes, OpenClaw. Every family (Settings, Skills, MCP, Hooks, Permissions, Agents) already derives column order from SUPPORTED_HARNESS_DEFINITIONS via bindings_for_family, so reordering the catalog reorders all of them at once. Slash commands was the one family with its own curated TARGET_ORDER, which is why OpenCode led that matrix. Drop it and resolve targets the way Agents does: catalog order, minus the harnesses disabled in Settings. A disabled harness now leaves the slash matrix entirely instead of showing as a dead column. Filtering also drops that harness's tracked and unmanaged review rows. The command files and sync records are untouched on disk — re-enabling the harness brings the rows back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The review matrix had a fallback that synthesized its columns from the review rows when the payload's target list came back empty. That fallback ordered columns by whichever row was seen first and hardcoded enabled: true — the two things this change is meant to fix, reintroduced in the one view the report was about. It was also unreachable: a review row can only exist for a target the payload lists, so rows are empty whenever targets are. Drop it and read the payload, which is already canonically ordered and already excludes disabled harnesses. Also aligns the hand-written SlashTargetId union with the backend Literal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…loop _manage_entry called require_enabled_adapter at the top of its binding loop, so whether it rejected a missing install *before* creating any binding depended entirely on which harness the loop reached first. That happened to be Codex, which is why test_manage_rejects_missing_harness_install_before_creating_bindings passed: Codex was first in the catalog, so its check fired before Claude's copy was adopted. Reordering the catalog put Claude first and exposed it — Claude's copy was adopted, then Codex raised, leaving a half-applied mutation. The test asserts no binding is created, which is the intended guarantee; it was just being met by accident. Hoist the validation into a pre-flight pass so it holds regardless of catalog order. The paired assertion changes are behavioral, not cosmetic: the managed package directory is ingested from harness_sightings[0], so with Claude first the store now names it after Claude's copy (trace-lens-copy) instead of Codex's (trace-lens). Existing managed skills are unaffected; this only applies to new manage operations on a skill found under different names in different harnesses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ly at once resolve_slash_targets ran once at container build and its result was frozen into the read-model and mutation services, so disabling a harness in Settings did not leave the slash matrix until the process restarted. Agents already solved this at container.py:295; use the same shape — a plain callable, resolved once per public method into a local. migrate_legacy_slash_commands keeps its one-shot build-time resolve. It is a migration, not a read path. Adds a regression test that disables a harness through the settings route and asserts the very next GET /api/slash-commands omits it, with no rebuild. Regenerates the OpenAPI schema for the reordered SlashTargetId enum. Implementation by agy; reviewed, and validation re-run independently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Now that resolved targets exclude harnesses disabled in Settings, the `not target.enabled` branch in _require_target was dead and every disabled target fell through to "unknown slash command target: opencode" — which is wrong twice over: the target is known, and the reason is recoverable by flipping it back on in Settings. Route it to "harness support is disabled", the phrasing MCP, hooks, permissions, and skills already use for this case. The accompanying test documents a two-layer behavior worth knowing: an id that is not a slash-command harness at all never reaches the mutation layer, because the request schema's Literal rejects it with a 422 first. The "unknown slash command target" message is reachable only from internal callers. Docs: README states the canonical harness order and where it is declared, so the next person adding a harness edits the catalog tuple and nothing else, plus the no-restart and no-data-loss behavior of disabling a harness. New handoff entry records the ordering rule and the two latent bugs the reorder exposed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The repo root had accumulated four one-off codemod scripts (fix_slash.py, patch_mcp.py, patch_slash.py, update_reviews.py) from the review-matrix work. Their edits are all present in committed source, so the scripts were spent — removed. Ignore what should never have been a candidate for commit in the first place: .DS_Store, the 28M .codegraph index, .rtk state, and the per-agent instruction files local tooling generates (.clinerules, .agents/). Nothing previously tracked is affected. Also drops the RTK instruction block that had been appended to CLAUDE.md. It duplicates the user's global ~/.claude config verbatim, and this file is a shared working agreement in a fork that can be contributed upstream — machine-local tooling guidance does not belong in it. `rtk init` regenerates it if wanted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d UI Salvaged from the stale permissions-ux-redesign branch (c2d2770), which was 87 commits behind main. Its second commit — the Tier 2 page unification — is deliberately left behind: it merges In Use and Needs Review into one page, which contradicts the 2026-07-24 decision to standardize every family on those two views. Permissions carry a decision (allow/ask/deny) AND a per-harness applied state. The UI reused "Enabled/Disabled" for the latter on rows that already carry a decision, producing nonsense like "Enable all" on a deny rule. The applied-state axis is now Applied / Not applied / Differs / Untracked, and the decision chip owns allow/ask/deny. Drops the bulk-select column from the In Use matrix. It was decorative: PermissionsInUsePage passed `checkedIds={new Set()}` and `onToggleChecked={() => {}}`, so it could never select anything. The Needs Review page keeps its checkboxes — those are wired to real state. Drift resolution reads "Keep central" / "Take harness's" rather than "Use central spec" / "Adopt harness spec", and promotion is "Adopt" throughout. Resolved against main rather than replayed: - MatrixTable keeps no <colgroup> (b967504) and does not regain the `hasCheckboxColumn` prop. That prop existed only to keep the colgroup aligned; with widths coming from header-cell classes it has no purpose. - The matrix identity cell keeps main's OverflowTooltipText inside the branch's <button>, so it stays keyboard-accessible without losing overflow handling. - Needs Review keeps main's matrix layout and "Adopt all eligible" action; the branch's older card layout is discarded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The branch survives at fork/permissions-ux-redesign, so without this note the next person finds two unmerged commits and no reason they were split. States which half landed, and that the page-unification half is a cross-family design decision rather than a branch to merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The SPA catch-all guarded traversal with str.startswith(dist_root), which a sibling directory whose name extends "dist" (e.g. dist-backup/) satisfies. Use Path.is_relative_to and add a regression test with a decoy sibling.
The local API is unauthenticated by design, which is only safe if browsers cannot be driven to it. Add an ASGI guard that (a) rejects non-loopback Host headers, closing DNS-rebinding, and (b) rejects mutations whose Origin is non-loopback, closing cross-site simple-request CSRF. Local non-browser clients send no Origin and are unaffected. Binding a non-loopback --host now requires an explicit --allow-remote flag with a loud warning; start(1) passes it through to the spawned serve process.
… package The LLM scan feature was removed earlier but left its optional deps (azure-identity, google-genai, litellm), its prompt payloads (still shipped as package-data and in the PyInstaller spec), and stale test artifacts. The skill_manager.db package was fully dead: Database was constructed and stored on the container but no router, service, or test ever used it, while every launch created skill-manager.db and ran four migrations. Settings live in settings.json. Also dedupe the double certifi pin.
The three marketplace clients hardcoded skill-manager/0.1 while the app is at 0.3.1.
…dits npm audit flagged two high-severity advisories in the production tree; the in-range bump clears all but GHSA-qwww-vcr4-c8h2, which only affects unstable RSC/server-action code paths this client-only SPA never executes (patched in 8.3.0; allowlisted with justification). Add scripts/audit_gate.cjs (npm audit minus a reviewed allowlist) as npm run audit:check, wire it into frontend-validate, and add pip-audit to backend-compat.
…root fix, dead-code pass
npm audit attributes advisories inline on the owning package and as plain strings on dependents (react-router-dom via ["react-router"]). The first version treated the string as an unattributed finding, so the allowlist could never match it. Resolve strings through the vulnerabilities map and dedupe; add AUDIT_GATE_JSON_FILE to replay recorded audit payloads so the parsing is testable offline.
The file now tracks open items only; the Tier 1 batch that landed in 98c3417 is recorded in handoff.md. Former Tiers 2-4 become 1-3, and cross-references (including the now-shipped audit gate and the deleted db package) are updated.
Tier-1 recommendations batch (RECOMMENDATIONS.md): - supply-chain: .github/dependabot.yml (npm+pip+github-actions, weekly) and SHA-pin every action in ci.yml/release.yml with # vN comments - lint: ruff gate enforcing I (import sorting, applied tree-wide) + F (pyflakes), baselined green via [tool.ruff] in pyproject.toml; requirements-dev.txt + 'Backend lint' CI step - tests: tests/unit/test_writer_round_trip.py pins idempotency, owned-field preservation, and loss-surface characterization for the slash frontmatter codec and every MCP transport mapper - harness: provisional support_note on the Hermes slash binding (surfaces via SlashTarget.supportNote) isort-only reorg applied across the tree (mechanical; full suite green). A blanket ruff --fix was attempted and reverted (it broke test collection), so the baseline cleanup is deferred to a verified per-module pass. pyright/ESLint and writer field-preservation hardening remain (tracked in RECOMMENDATIONS.md).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Centers matrix coverage columns, standardizes last column names, and makes Hooks tables visually consistent with Skills tables.