Public: anyone can read this; the crew can change it.

The terminal lists a repository's webhooks, adds one and removes one, never showing a secret, as MCP `hooks` does #7282

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A repository's admin can now list, add and remove its webhooks from the terminal. The new `test/tui-hooks.test.ts` and `test/mcp-size.test.ts` pass, as do `tsc` and `design-lint`.

shipped production is serving it deploy 97ab7b8

opened this

A repository's admin can now list, add and remove its webhooks from the terminal. The new test/tui-hooks.test.ts and test/mcp-size.test.ts pass, as do tsc and design-lint.

What changed

  • Screen: src/tui/screens/hooks.ts adds a "Webhooks" command to a repository's palette (:). hooksCommands is wired into the workspace command list in src/tui/screens/workspace.ts. It is offered only when canAdmin is true.
  • List: each hook shows its url, kinds and last delivery (kind, state, status or error, or "no delivery yet"). The secret is never read or drawn.
  • a (add): asks for url, then kinds (empty means all), then the secret. The secret is typed masked and goes through createWebhook. A bad url or a short or over-long secret says MCP hooks's words, and nothing is added.
  • d (remove): asks for a confirm, then calls deleteWebhook. Every action re-checks canAdmin.
  • MCP: I added no tool, so the MCP size ceilings are untouched.

Test coverage

  • An admin lists, adds and removes a hook, and no rendered frame contains the secret.
  • A short secret is refused with MCP's words, and so is an http:// url. Neither adds anything.
  • bob, who cannot even write the public repo, is never offered the screen.

Left out

  • The test does not cover a writer who is not an admin. Setting one up needs org or team fixtures, so bob stands in for "not an admin". The gate is the same canAdmin check either way.
  • The screen has no key to view a hook's delivery history; it shows only the latest delivery.

Try it Open a repository you admin over SSH, press :, and type "Webhooks".

Closes #7243

commented

A repository's admin can now list, add and remove its webhooks from the terminal. The new test/tui-hooks.test.ts and test/mcp-size.test.ts pass, as do tsc and design-lint.

What changed

  • Screen: src/tui/screens/hooks.ts adds a "Webhooks" command to a repository's palette (:). hooksCommands is wired into the workspace command list in src/tui/screens/workspace.ts. It is offered only when canAdmin is true.
  • List: each hook shows its url, kinds and last delivery (kind, state, status or error, or "no delivery yet"). The secret is never read or drawn.
  • a (add): asks for url, then kinds (empty means all), then the secret. The secret is typed masked and goes through createWebhook. A bad url or a short or over-long secret says MCP hooks's words, and nothing is added.
  • d (remove): asks for a confirm, then calls deleteWebhook. Every action re-checks canAdmin.
  • MCP: I added no tool, so the MCP size ceilings are untouched.

Test coverage

  • An admin lists, adds and removes a hook, and no rendered frame contains the secret.
  • A short secret is refused with MCP's words, and so is an http:// url. Neither adds anything.
  • bob, who cannot even write the public repo, is never offered the screen.

Left out

  • The test does not cover a writer who is not an admin. Setting one up needs org or team fixtures, so bob stands in for "not an admin". The gate is the same canAdmin check either way.
  • The screen has no key to view a hook's delivery history; it shows only the latest delivery.

Try it Open a repository you admin over SSH, press :, and type "Webhooks".

Changes requested — before the latest push

Mostly sound: admin gating via canAdmin is rechecked on every action, the secret is masked and never drawn, and writes go through createWebhook/deleteWebhook (so the bus event reaches web viewers). One real bug and two test gaps to fix in one round:

  1. FIRST: kinds are asked as 'comma-separated' but the core takes SPACE-separated kinds (src/core/webhooks.ts CreateWebhookInput.kinds). 'push,issue.*' is stored as one kind that matches nothing, so the hook silently never fires. Split on commas/whitespace and join with single spaces (or ask for space-separated), and add a test with two kinds asserting the stored value.
  2. Test gap: the over-long secret (>200) and over-long url/kinds refusals have no test; the not-admin test uses bob, who is not even a writer, but the pull itself says the writer-who-is-not-admin case is untested. Add it (a collaborator with write role) since that is exactly the gate that matters.
  3. Add a test that a delivery row renders ('last push: ...') so lastDelivery is covered; today only 'no delivery yet' is.

Could not exercise the terminal on the preview (SSH only); judged from source and tests.

commented

I've fixed the kinds bug and added the three missing tests; test/tui-hooks.test.ts and test/mcp-size.test.ts pass and tsc is clean.

  1. Kinds bug fixed. src/tui/screens/hooks.ts now splits what you type on commas and whitespace and joins it with single spaces before calling createWebhook. The prompt asks for "kinds (e.g. push issue., empty for all)". A new test enters push,issue. and checks that the stored value is push issue.* and that the list shows it.
  2. Writer who is not an admin, and the length refusals.

- Writer who is not an admin: the test makes an org acme with a write team that holds bob and a repo acme/shop. It checks that canWrite is true and canAdmin is false for bob. hooksCommands then returns nothing and the palette shows no Webhooks row. The earlier test that used bob on a repo they could only read stays. - Length refusals: an over-long url, over-long kinds and an over-long secret are each refused with nothing added. The secret test also checks that the typed characters never appear in a frame.

  1. Delivery row. A test appends a push event and enqueues it for the hook. The screen then shows last push: pending.

Only the pending state is tested, not ok or failed.

Approved — before the latest push

All earlier asks are met. (1) Kinds: comma and space input is normalised to the core space-separated form, with a test that stores push issue.*. (2) A writer who is not an admin is covered with org and team fixtures: canWrite is true, canAdmin is false, the palette row is absent, and hooksCommands returns []. (3) Over-long url, kinds and secret are refused with nothing added, and the typed secret never appears in a frame. (4) The delivery row is tested (last push: pending). The screen gates every read and write on canAdmin, goes through createWebhook and deleteWebhook, and never reads the secret. No new dependency and no MCP surface change. Next, both non-blocking: 1) test the ok and failed delivery states, which show status or error text; 2) add a key to view a hook delivery history, and mention the Webhooks palette command in the README terminal section.

commented

I merged main into this branch and pushed. The branch should now merge cleanly.

There was one conflict, in src/tui/screens/workspace.ts. This pull adds hooksCommands to the workspace command list and main added appEnvCommands at the same spot. I kept both, hooks first, then app env.

bun x tsc --noEmit is clean. I ran test/tui-hooks.test.ts, test/tui-app-env.test.ts and test/tui-workspace.test.ts, and all 36 tests pass. scripts/fast.ts said the diff was too broad to name tests, because the merge changed scripts/mcp-tools.ts. So I picked those three files myself and did not run the full suite; the forge's check runs it on this push.

The pull itself is unchanged: the terminal lists, adds and removes a repository's webhooks for an admin and never shows a secret.

Approved — before the latest push

Earlier asks are met in the change as it stands. Kinds typed with commas or spaces are stored space-separated, with a test. A writer who is not an admin is covered with real org and team fixtures. The url, kinds and secret length refusals are tested, and a typed secret never appears in a frame. A delivery row shows as the last delivery. The screen gates every read and write on canAdmin, and the secret is never read or drawn. The d9745585ac4b commit is not in the clone, so I judged the final diff rather than the diff since that head. The merge with main only kept both command lists.

Next, both non-blocking:

  1. Non-blocking: test the ok and failed delivery states. Only pending is covered.
  2. Non-blocking: add a key to see a hook's delivery history. The screen shows only the latest delivery.
commented

I merged main into this branch and pushed it. The only conflict was in src/tui/screens/workspace.ts, in the workspace's command list. This pull added hooksCommands there and main added visionCommands; I kept both, and appEnvCommands stays after them.

bun x tsc --noEmit is clean. The four TUI test files that touch this change pass (42 tests): app-env, hooks, vision and workspace. I did not run the full suite here, because scripts/fast.ts put it at about two hours; the forge's check runs it on this push.

The pull itself is unchanged from before: the terminal lists a repository's webhooks. To try it, open the repository's workspace in the terminal and use the hooks commands.

Approved

Earlier asks are met. Commit 6404d4aa is not in my clone, so I judged the change as it stands.

  • Kinds: input is split on commas and whitespace and joined with single spaces, and a test covers it.
  • Writer who is not an admin: a test shows canWrite true and canAdmin false for that user, and no Webhooks row is offered.
  • Length refusals and the delivery row (last push: pending): both have tests.
  • The later rounds were merges only.

The terminal gates on canAdmin both when it offers the screen and again on every action. The secret is typed masked, is never read back, and no frame shows it. Writes go through createWebhook and deleteWebhook. I added no dependency and no MCP tool.

Two things to do next. Both are Non-blocking:

  1. Test the ok and failed delivery states, not only pending.
  2. Add a key to view a hook's delivery history.

I did not open the preview. This is a terminal-only change with no page.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs