feat: one-click GitHub, Jira and Linear credentials pane

Merged
HK
2d ago
+1550-4

Summary

Adds one-click GitHub, Jira and Linear integrations to the plugin as a hooks module (a Claude Code mod). It collects the same credentials the Altimate workspace web form asks for; there is no OAuth app. Stacked on #11; needs altimate integrations from altimate-code.

  • Band above the prompt: lists credentials found on the machine with Connect and Keep local. It says when a token stopped working, with Update.
  • Side pane (/integrations): one row per provider, with its state, account and whether it is local or synced.
    • Add credentials / Update expand the row into the workspace's form fields, pre-filled from the machine.
    • The secret line shows only ••••a1b2, with Create token ↗ and Paste from clipboard.
    • The footer has Connect & sync, Keep local and Cancel. The provider's error shows inline on rejection.
    • Rows also offer Sync, Allow sync and Disconnect.
  • Signed out: the band and pane offer Sign in to sync, and sync controls stay hidden until you sign in. Everything else works locally.
  • Keep local moves a workspace copy back to this machine and keeps it local until Allow sync. Update and Disconnect act on the connection this tool made, while the row shows the most recently validated one.
  • Status line: connected, synced and "needs attention" counts.
  • Secrets never pass through the mod: not its input, its state, toasts or logs. The CLI reads them.
  • The browser never opens without a click, and long work runs outside the button's time budget.

Test plan

  • claude plugin validate plugins/altimate-code passes, and tsc is clean.
  • claude plugin test plugins/altimate-code: 19 pass, 0 fail. Every test case is listed below.
  • Existing plugin suites pass: tests/*.sh (data-mode, refresh-skills, resolve-cli, session-start, warehouse-guard, workspace-tools), and pytest with 65 passed.
  • The CLI side (AltimateAI/altimate-code#1622) passes its 153 integration tests.
  • Ran in Claude Code (claude --plugin-dir) against a local Altimate backend with real GitHub and Jira credentials:
    • keep local
    • connect & sync
    • a rejected key
    • "Token rejected · Update", then the token updated on the same connection
    • Link workspace listing every workspace, one per line
    • a connection deleted in the web app, offered again because gh and jira-cli still hold the credential
All 19 test cases in hooks/integrations.test.tsx
  • rows offer Add credentials or Update, never a browser sign-in
  • the form shows a secret as a masked tail and keeps the form open on a refusal
  • a synced row cannot be blocked, and a hand-made one is only replaced by adding credentials
  • a row showing a newer hand-made connection still offers Update and Disconnect for the one this tool made
  • Keep local shows the notes the CLI returned
  • a failed action shows the reason the CLI printed on stdout, and a disconnect says what it removed
  • a sync with a failed provider shows its reason, and a timeout is not reported as a missing CLI
  • Sync all makes one call per provider with the per-provider timeout, and a failing provider does not stop the rest
  • the band connects what was found, after dropping what an earlier form left behind
  • the band does not connect when dropping earlier input fails
  • a credential found here is offered next to a hand-made connection
  • a session without a person at the prompt does not scan the machine
  • a status refresh that times out does not tell the user to install the CLI
  • connecting a found credential kept local shows the notes the CLI returned
  • a connect that is not linked to a workspace says so
  • a sync the CLI skipped shows its reason, not synced
  • an unlinked folder offers Link workspace; choosing one links, then syncs
  • a timed-out command is not rerun against the second binary
  • signed out: the band and pane offer Sign in to sync and no sync control; the click runs the browser sign-in

Known limitations and follow-ups

  • Linux and Windows: a kept-local secret goes to a 0600 file, not an OS keychain. Spec AC5 asks for the keychain only; this needs libsecret and DPAPI support.
  • Connection identity: a managed connection is matched by its name, Provider (from <hostname>), not a source.machine field. Renaming the host orphans it, and fixing that needs a backend field. Keychain keys are not yet namespaced per Altimate tenant.
  • Org policy "local only": not built. The backend has no policy source yet.
  • Workspace integrations list: the backend only replaces the whole list (PATCH /datamates/{id}). The CLI re-reads it just before writing, but two concurrent writers can still race until there is an incremental or conditional update.
  • Values starting with -: integrations set can't take one, because the argument parser reads it as a flag.
  • Token expiry: shown for GitHub only. Jira and Linear don't report it.
  • After a failed workspace removal: when Keep local can't remove the workspace copy, the credential is kept here and a warning says so, but status doesn't yet show both copies coherently.
  • Rollback: restoring the previous secrets after a failed keychain write is best effort. A failed restore is not reported.
  • Secret-name fallback: treating catalog fields with known secret names as secret applies only to freshly fetched catalogs, not to a cached one.

🤖 Generated with Claude Code