@korso/shepherd-ui 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,6 +25,45 @@ const ENV_FLAG = {
25
25
  claude: "-e",
26
26
  codex: "--env",
27
27
  };
28
+ // The shepherd-inbox-hook bin: delivers teammate announcements out-of-band AND
29
+ // nudges the agent to run `link` before its first write in an unlinked repo.
30
+ // Neither `claude mcp add` nor `codex mcp add` can install hooks, so the
31
+ // snippet is a paste-into-config block surfaced next to the install command.
32
+ // Cursor has no hook mechanism; Pi uses the bundled extension (see the
33
+ // @korso/shepherd README) — neither gets a snippet here.
34
+ const HOOK_COMMAND = "npx -y --package=@korso/shepherd shepherd-inbox-hook";
35
+ function hookSnippet(tool) {
36
+ if (tool === "claude") {
37
+ // SessionStart front-loads the link ask; PreToolUse delivers announcements
38
+ // and re-nudges right before a write in a still-unlinked repo.
39
+ return {
40
+ target: "~/.claude/settings.json",
41
+ snippet: JSON.stringify({
42
+ hooks: {
43
+ SessionStart: [{ hooks: [{ type: "command", command: HOOK_COMMAND }] }],
44
+ PreToolUse: [
45
+ { matcher: "*", hooks: [{ type: "command", command: HOOK_COMMAND }] },
46
+ ],
47
+ },
48
+ }, null, 2),
49
+ };
50
+ }
51
+ if (tool === "codex") {
52
+ // Codex's PreToolUse only fires for Bash, so UserPromptSubmit is the
53
+ // frequent event there; hooks must be feature-flagged on.
54
+ return {
55
+ target: "~/.codex/config.toml",
56
+ snippet: [
57
+ "[features]",
58
+ "hooks = true",
59
+ "",
60
+ "[[hooks.UserPromptSubmit]]",
61
+ 'command = ["npx", "-y", "--package=@korso/shepherd", "shepherd-inbox-hook"]',
62
+ ].join("\n"),
63
+ };
64
+ }
65
+ return null;
66
+ }
28
67
  // "created 3d ago · never used" / "created 3d ago · last used 2h ago" — helps
29
68
  // an operator tell which tokens are still active before revoking one.
30
69
  function tokenMeta(token, nowMs) {
@@ -105,6 +144,7 @@ export function ConnectAgent({ hubUrl }) {
105
144
  async function generate() {
106
145
  setBusy(true);
107
146
  setError(null);
147
+ setStatus(null);
108
148
  try {
109
149
  const res = await client.mintAccountToken(name.trim() ? { name: name.trim() } : {});
110
150
  setRawToken(res.token);
@@ -137,12 +177,18 @@ export function ConnectAgent({ hubUrl }) {
137
177
  }
138
178
  }
139
179
  const command = installCommand(tool, directHubUrl, rawToken ?? TOKEN_PLACEHOLDER);
180
+ const hook = hookSnippet(tool);
140
181
  async function copyCommand() {
141
182
  await navigator.clipboard.writeText(command);
142
183
  setCopied(true);
143
184
  setTimeout(() => setCopied(false), 2000);
144
185
  }
145
- return (_jsxs("section", { className: "shepherd-connect-agent", "aria-labelledby": headingId, children: [_jsxs("div", { className: "card-head", children: [_jsx("h3", { id: headingId, children: "Connect your agent" }), _jsx("p", { className: "card-sub", children: "Generate a token \u2014 it works across all your workspaces \u2014 then paste the command into your coding tool." }), _jsx("p", { className: "card-sub", children: "The first time the agent opens a repo, it'll ask which workspace to coordinate that repo with." })] }), _jsxs("div", { className: "card-body", children: [_jsxs("div", { className: "field", children: [_jsx("label", { htmlFor: "connect-tool", children: "Tool" }), _jsx("select", { id: "connect-tool", value: tool, onChange: (e) => setTool(e.target.value), children: TOOLS.map((t) => (_jsx("option", { value: t.id, children: t.label }, t.id))) })] }), _jsxs("div", { className: "field generate", children: [_jsx("label", { htmlFor: "token-name", children: "Token name (optional)" }), _jsxs("div", { className: "field__row", children: [_jsx("input", { id: "token-name", type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "e.g. laptop" }), _jsx("button", { type: "button", onClick: () => void generate(), disabled: busy, children: "Generate token" })] })] }), error && _jsx("p", { role: "alert", children: error }), status && _jsx("p", { role: "status", children: status }), rawToken && (_jsx("p", { className: "token-once", role: "status", children: "Copy this token now \u2014 it won't be shown again." })), _jsxs("div", { className: "install-command", children: [_jsx("pre", { "data-testid": "install-command", children: command }), _jsx("button", { type: "button", className: "install-command__copy", "aria-label": copied ? "Copied" : "Copy command", title: copied ? "Copied" : "Copy", onClick: () => void copyCommand(), children: copied ? (_jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })) : (_jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] })) })] }), _jsx("h4", { children: "Existing tokens" }), loading ? (_jsx("p", { role: "status", children: "Loading\u2026" })) : tokens.length === 0 ? (_jsx("p", { children: "No tokens yet." })) : (_jsx("ul", { children: tokens.map((t) => {
186
+ return (_jsxs("section", { className: "shepherd-connect-agent", "aria-labelledby": headingId, children: [_jsxs("div", { className: "card-head", children: [_jsx("h3", { id: headingId, children: "Connect your agent" }), _jsx("p", { className: "card-sub", children: "Generate a token \u2014 it works across all your workspaces \u2014 then paste the command into your coding tool." }), _jsx("p", { className: "card-sub", children: "The first time the agent opens a repo, it'll ask which workspace to coordinate that repo with." })] }), _jsxs("div", { className: "card-body", children: [_jsxs("div", { className: "field", children: [_jsx("label", { htmlFor: "connect-tool", children: "Tool" }), _jsx("select", { id: "connect-tool", value: tool, onChange: (e) => setTool(e.target.value), children: TOOLS.map((t) => (_jsx("option", { value: t.id, children: t.label }, t.id))) })] }), _jsxs("div", { className: "field generate", children: [_jsx("label", { htmlFor: "token-name", children: "Token name (optional)" }), _jsxs("div", { className: "field__row", children: [_jsx("input", { id: "token-name", type: "text", value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => {
187
+ if (e.key === "Enter" && !busy) {
188
+ e.preventDefault();
189
+ void generate();
190
+ }
191
+ }, placeholder: "e.g. laptop" }), _jsx("button", { type: "button", onClick: () => void generate(), disabled: busy, children: "Generate token" })] })] }), error && _jsx("p", { role: "alert", children: error }), status && _jsx("p", { role: "status", children: status }), rawToken && (_jsx("p", { className: "token-once", role: "status", children: "Copy this token now \u2014 it won't be shown again." })), _jsxs("div", { className: "install-command", children: [_jsx("pre", { "data-testid": "install-command", children: command }), _jsx("button", { type: "button", className: "install-command__copy", "aria-label": copied ? "Copied" : "Copy command", title: copied ? "Copied" : "Copy", onClick: () => void copyCommand(), children: copied ? (_jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })) : (_jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] })) })] }), hook && (_jsxs("div", { className: "hook-setup", children: [_jsxs("p", { className: "card-sub", children: ["Recommended: merge this into ", _jsx("code", { children: hook.target }), " \u2014 it delivers teammate announcements to the agent and reminds it to link each repo before its first write."] }), _jsx("div", { className: "install-command", children: _jsx("pre", { "data-testid": "hook-snippet", children: hook.snippet }) })] })), _jsx("h4", { children: "Existing tokens" }), loading ? (_jsx("p", { role: "status", children: "Loading\u2026" })) : tokens.length === 0 ? (_jsx("p", { children: "No tokens yet." })) : (_jsx("ul", { children: tokens.map((t) => {
146
192
  const name = t.name ?? t.id;
147
193
  return (_jsxs("li", { children: [_jsx("span", { children: name }), _jsx("span", { className: "token-meta", children: tokenMeta(t, Date.now()) }), t.revokedAt ? (_jsx("span", { className: "revoked", children: "revoked" })) : (_jsx("button", { type: "button", "aria-label": `Revoke token ${name}`, onClick: () => void revoke(t.id), disabled: revokingId === t.id, children: "Revoke" }))] }, t.id));
148
194
  }) }))] })] }));
package/dist/lib/index.js CHANGED
@@ -1,56 +1,56 @@
1
- import { u as B, d as R, S as H, f as O, D as K } from "./Dashboard-D7tbhMwK.js";
2
- import { E as he, a as me, c as pe } from "./Dashboard-D7tbhMwK.js";
3
- import { jsxs as n, jsx as e, Fragment as P } from "react/jsx-runtime";
4
- import { useId as $, useRef as U, useState as i, useEffect as A, useCallback as Y } from "react";
5
- function F(t) {
1
+ import { u as j, d as S, S as Y, f as P, D as J } from "./Dashboard-D7tbhMwK.js";
2
+ import { E as ve, a as fe, c as ye } from "./Dashboard-D7tbhMwK.js";
3
+ import { jsxs as n, jsx as e, Fragment as G } from "react/jsx-runtime";
4
+ import { useId as $, useRef as W, useState as i, useEffect as A, useCallback as M } from "react";
5
+ function U(t) {
6
6
  return (t.trim()[0] ?? "?").toUpperCase();
7
7
  }
8
- function J({
8
+ function V({
9
9
  workspaces: t,
10
10
  selected: a,
11
11
  onSelect: o,
12
- onChanged: u,
13
- onMembersChanged: l
12
+ onChanged: h,
13
+ onMembersChanged: r
14
14
  }) {
15
- const m = B(), c = $(), b = U(null), [r, y] = i(!1), [p, v] = i("menu"), [g, k] = i(""), [h, _] = i(""), [E, s] = i(null), [N, I] = i(!1);
16
- function C() {
17
- y(!1), v("menu"), k(""), _(""), s(null);
15
+ const p = j(), c = $(), b = W(null), [s, y] = i(!1), [v, f] = i("menu"), [N, k] = i(""), [m, C] = i(""), [E, l] = i(null), [g, x] = i(!1);
16
+ function _() {
17
+ y(!1), f("menu"), k(""), C(""), l(null);
18
18
  }
19
19
  A(() => {
20
- if (!r) return;
21
- function d(j) {
22
- b.current && !b.current.contains(j.target) && C();
20
+ if (!s) return;
21
+ function d(D) {
22
+ b.current && !b.current.contains(D.target) && _();
23
23
  }
24
- function x(j) {
25
- j.key === "Escape" && C();
24
+ function R(D) {
25
+ D.key === "Escape" && _();
26
26
  }
27
- return document.addEventListener("mousedown", d), document.addEventListener("keydown", x), () => {
28
- document.removeEventListener("mousedown", d), document.removeEventListener("keydown", x);
27
+ return document.addEventListener("mousedown", d), document.addEventListener("keydown", R), () => {
28
+ document.removeEventListener("mousedown", d), document.removeEventListener("keydown", R);
29
29
  };
30
- }, [r]);
31
- async function S() {
32
- if (!(!g.trim() || N)) {
33
- I(!0), s(null);
30
+ }, [s]);
31
+ async function I() {
32
+ if (!(!N.trim() || g)) {
33
+ x(!0), l(null);
34
34
  try {
35
- const d = await m.createWorkspace({ name: g.trim() });
36
- o(d.id), u(), C();
35
+ const d = await p.createWorkspace({ name: N.trim() });
36
+ o(d.id), h(), _();
37
37
  } catch (d) {
38
- s(R(d));
38
+ l(S(d));
39
39
  } finally {
40
- I(!1);
40
+ x(!1);
41
41
  }
42
42
  }
43
43
  }
44
44
  async function T() {
45
- if (!(!h.trim() || N)) {
46
- I(!0), s(null);
45
+ if (!(!m.trim() || g)) {
46
+ x(!0), l(null);
47
47
  try {
48
- const d = await m.redeemInvite(h.trim());
49
- o(d.workspace.id), u(), l == null || l(), C();
48
+ const d = await p.redeemInvite(m.trim());
49
+ o(d.workspace.id), h(), r == null || r(), _();
50
50
  } catch (d) {
51
- s(R(d));
51
+ l(S(d));
52
52
  } finally {
53
- I(!1);
53
+ x(!1);
54
54
  }
55
55
  }
56
56
  }
@@ -62,35 +62,35 @@ function J({
62
62
  type: "button",
63
63
  className: "ws-trig" + (w ? "" : " ws-trig--empty"),
64
64
  "aria-haspopup": "menu",
65
- "aria-expanded": r,
66
- "aria-controls": r ? c : void 0,
67
- onClick: () => r ? C() : y(!0),
65
+ "aria-expanded": s,
66
+ "aria-controls": s ? c : void 0,
67
+ onClick: () => s ? _() : y(!0),
68
68
  children: [
69
- w ? /* @__PURE__ */ e("span", { className: "ws-avatar", "aria-hidden": "true", children: F(L) }) : /* @__PURE__ */ e("span", { className: "ws-plus", "aria-hidden": "true", children: "+" }),
69
+ w ? /* @__PURE__ */ e("span", { className: "ws-avatar", "aria-hidden": "true", children: U(L) }) : /* @__PURE__ */ e("span", { className: "ws-plus", "aria-hidden": "true", children: "+" }),
70
70
  /* @__PURE__ */ e("span", { className: "ws-name", children: L }),
71
71
  /* @__PURE__ */ e("span", { className: "ws-caret", "aria-hidden": "true", children: "▾" })
72
72
  ]
73
73
  }
74
74
  ),
75
- r && /* @__PURE__ */ n("div", { className: "ws-menu", id: c, role: "menu", children: [
76
- p === "menu" && /* @__PURE__ */ n(P, { children: [
75
+ s && /* @__PURE__ */ n("div", { className: "ws-menu", id: c, role: "menu", children: [
76
+ v === "menu" && /* @__PURE__ */ n(G, { children: [
77
77
  /* @__PURE__ */ e("p", { className: "ws-menu__head", children: w ? "Switch workspace" : "Your workspaces" }),
78
78
  t.length === 0 ? /* @__PURE__ */ e("p", { className: "ws-menu__none", children: "None yet" }) : /* @__PURE__ */ e("ul", { className: "ws-menu__list", children: t.map((d) => {
79
- const x = d.id === (a == null ? void 0 : a.id);
79
+ const R = d.id === (a == null ? void 0 : a.id);
80
80
  return /* @__PURE__ */ e("li", { children: /* @__PURE__ */ n(
81
81
  "button",
82
82
  {
83
83
  type: "button",
84
84
  role: "menuitemradio",
85
- "aria-checked": x,
86
- className: "ws-mi" + (x ? " ws-mi--on" : ""),
85
+ "aria-checked": R,
86
+ className: "ws-mi" + (R ? " ws-mi--on" : ""),
87
87
  onClick: () => {
88
- o(d.id), C();
88
+ o(d.id), _();
89
89
  },
90
90
  children: [
91
- /* @__PURE__ */ e("span", { className: "ws-avatar ws-avatar--sm", "aria-hidden": "true", children: F(d.name) }),
91
+ /* @__PURE__ */ e("span", { className: "ws-avatar ws-avatar--sm", "aria-hidden": "true", children: U(d.name) }),
92
92
  /* @__PURE__ */ e("span", { className: "ws-mi__name", children: d.name }),
93
- x && /* @__PURE__ */ e("span", { className: "ws-mi__check", "aria-hidden": "true", children: "✓" })
93
+ R && /* @__PURE__ */ e("span", { className: "ws-mi__check", "aria-hidden": "true", children: "✓" })
94
94
  ]
95
95
  }
96
96
  ) }, d.id);
@@ -103,7 +103,7 @@ function J({
103
103
  role: "menuitem",
104
104
  className: "ws-action",
105
105
  onClick: () => {
106
- s(null), v("create");
106
+ l(null), f("create");
107
107
  },
108
108
  children: "+ Create workspace"
109
109
  }
@@ -115,19 +115,19 @@ function J({
115
115
  role: "menuitem",
116
116
  className: "ws-action",
117
117
  onClick: () => {
118
- s(null), v("join");
118
+ l(null), f("join");
119
119
  },
120
120
  children: "⎘ Join with a code"
121
121
  }
122
122
  )
123
123
  ] })
124
124
  ] }),
125
- p === "create" && /* @__PURE__ */ n(
125
+ v === "create" && /* @__PURE__ */ n(
126
126
  "form",
127
127
  {
128
128
  className: "ws-form",
129
129
  onSubmit: (d) => {
130
- d.preventDefault(), S();
130
+ d.preventDefault(), I();
131
131
  },
132
132
  children: [
133
133
  /* @__PURE__ */ e("label", { htmlFor: `${c}-name`, children: "New workspace name" }),
@@ -137,19 +137,19 @@ function J({
137
137
  id: `${c}-name`,
138
138
  type: "text",
139
139
  placeholder: "e.g. Acme Engineering",
140
- value: g,
140
+ value: N,
141
141
  autoFocus: !0,
142
142
  onChange: (d) => k(d.target.value)
143
143
  }
144
144
  ),
145
145
  /* @__PURE__ */ n("div", { className: "ws-form__row", children: [
146
- /* @__PURE__ */ e("button", { type: "button", className: "ws-form__back", onClick: () => v("menu"), children: "Back" }),
147
- /* @__PURE__ */ e("button", { type: "submit", disabled: N || !g.trim(), children: "Create" })
146
+ /* @__PURE__ */ e("button", { type: "button", className: "ws-form__back", onClick: () => f("menu"), children: "Back" }),
147
+ /* @__PURE__ */ e("button", { type: "submit", disabled: g || !N.trim(), children: "Create" })
148
148
  ] })
149
149
  ]
150
150
  }
151
151
  ),
152
- p === "join" && /* @__PURE__ */ n(
152
+ v === "join" && /* @__PURE__ */ n(
153
153
  "form",
154
154
  {
155
155
  className: "ws-form",
@@ -164,14 +164,14 @@ function J({
164
164
  id: `${c}-code`,
165
165
  type: "text",
166
166
  placeholder: "Paste an invite code",
167
- value: h,
167
+ value: m,
168
168
  autoFocus: !0,
169
- onChange: (d) => _(d.target.value)
169
+ onChange: (d) => C(d.target.value)
170
170
  }
171
171
  ),
172
172
  /* @__PURE__ */ n("div", { className: "ws-form__row", children: [
173
- /* @__PURE__ */ e("button", { type: "button", className: "ws-form__back", onClick: () => v("menu"), children: "Back" }),
174
- /* @__PURE__ */ e("button", { type: "submit", disabled: N || !h.trim(), children: "Join" })
173
+ /* @__PURE__ */ e("button", { type: "button", className: "ws-form__back", onClick: () => f("menu"), children: "Back" }),
174
+ /* @__PURE__ */ e("button", { type: "submit", disabled: g || !m.trim(), children: "Join" })
175
175
  ] })
176
176
  ]
177
177
  }
@@ -180,30 +180,30 @@ function J({
180
180
  ] })
181
181
  ] });
182
182
  }
183
- function V({
183
+ function X({
184
184
  workspaceName: t,
185
185
  busy: a,
186
186
  error: o,
187
- onConfirm: u,
188
- onCancel: l
187
+ onConfirm: h,
188
+ onCancel: r
189
189
  }) {
190
- const m = $(), c = $(), [b, r] = i(""), y = U(null);
190
+ const p = $(), c = $(), [b, s] = i(""), y = W(null);
191
191
  A(() => {
192
- var v;
193
- (v = y.current) == null || v.focus();
192
+ var f;
193
+ (f = y.current) == null || f.focus();
194
194
  }, []), A(() => {
195
- function v(g) {
196
- g.key === "Escape" && !a && l();
195
+ function f(N) {
196
+ N.key === "Escape" && !a && r();
197
197
  }
198
- return document.addEventListener("keydown", v), () => document.removeEventListener("keydown", v);
199
- }, [a, l]);
200
- const p = b === t;
198
+ return document.addEventListener("keydown", f), () => document.removeEventListener("keydown", f);
199
+ }, [a, r]);
200
+ const v = b === t;
201
201
  return /* @__PURE__ */ e(
202
202
  "div",
203
203
  {
204
204
  className: "shepherd-modal__backdrop",
205
205
  onClick: () => {
206
- a || l();
206
+ a || r();
207
207
  },
208
208
  children: /* @__PURE__ */ n(
209
209
  "div",
@@ -211,17 +211,17 @@ function V({
211
211
  className: "shepherd-modal",
212
212
  role: "dialog",
213
213
  "aria-modal": "true",
214
- "aria-labelledby": m,
214
+ "aria-labelledby": p,
215
215
  "aria-describedby": c,
216
- onClick: (v) => v.stopPropagation(),
216
+ onClick: (f) => f.stopPropagation(),
217
217
  children: [
218
- /* @__PURE__ */ e("h3", { id: m, children: "Delete workspace" }),
218
+ /* @__PURE__ */ e("h3", { id: p, children: "Delete workspace" }),
219
219
  /* @__PURE__ */ n("p", { id: c, className: "shepherd-modal__body", children: [
220
220
  "This permanently deletes ",
221
221
  /* @__PURE__ */ e("b", { children: t }),
222
222
  " and all of its data — agents, sessions, tasks, announcements, change history, tokens, invites, and members. This cannot be undone."
223
223
  ] }),
224
- /* @__PURE__ */ n("label", { htmlFor: `${m}-input`, className: "shepherd-modal__label", children: [
224
+ /* @__PURE__ */ n("label", { htmlFor: `${p}-input`, className: "shepherd-modal__label", children: [
225
225
  "Type ",
226
226
  /* @__PURE__ */ e("b", { children: t }),
227
227
  " to confirm"
@@ -229,25 +229,25 @@ function V({
229
229
  /* @__PURE__ */ e(
230
230
  "input",
231
231
  {
232
- id: `${m}-input`,
232
+ id: `${p}-input`,
233
233
  ref: y,
234
234
  type: "text",
235
235
  value: b,
236
236
  autoComplete: "off",
237
237
  disabled: a,
238
- onChange: (v) => r(v.target.value)
238
+ onChange: (f) => s(f.target.value)
239
239
  }
240
240
  ),
241
241
  o && /* @__PURE__ */ e("p", { role: "alert", children: o }),
242
242
  /* @__PURE__ */ n("div", { className: "shepherd-modal__actions", children: [
243
- /* @__PURE__ */ e("button", { type: "button", onClick: l, disabled: a, children: "Cancel" }),
243
+ /* @__PURE__ */ e("button", { type: "button", onClick: r, disabled: a, children: "Cancel" }),
244
244
  /* @__PURE__ */ e(
245
245
  "button",
246
246
  {
247
247
  type: "button",
248
248
  className: "danger",
249
- onClick: u,
250
- disabled: !p || a,
249
+ onClick: h,
250
+ disabled: !v || a,
251
251
  children: a ? "Deleting…" : "Delete workspace"
252
252
  }
253
253
  )
@@ -258,40 +258,40 @@ function V({
258
258
  }
259
259
  );
260
260
  }
261
- function M({ workspace: t, onLeft: a, onDeleted: o }) {
262
- const u = B(), l = $(), [m, c] = i(null), [b, r] = i(!1), [y, p] = i(!1), [v, g] = i(!1), [k, h] = i(!1), [_, E] = i(null), s = t.role === "admin";
263
- async function N() {
261
+ function q({ workspace: t, onLeft: a, onDeleted: o }) {
262
+ const h = j(), r = $(), [p, c] = i(null), [b, s] = i(!1), [y, v] = i(!1), [f, N] = i(!1), [k, m] = i(!1), [C, E] = i(null), l = t.role === "admin";
263
+ async function g() {
264
264
  if (!b) {
265
- r(!0), c(null), p(!1);
265
+ s(!0), c(null), v(!1);
266
266
  try {
267
- await u.leave(t.id), a == null || a();
268
- } catch (C) {
269
- c(R(C)), C instanceof H && C.status === 409 && p(!0);
267
+ await h.leave(t.id), a == null || a();
268
+ } catch (_) {
269
+ c(S(_)), _ instanceof Y && _.status === 409 && v(!0);
270
270
  } finally {
271
- r(!1);
271
+ s(!1);
272
272
  }
273
273
  }
274
274
  }
275
- async function I() {
275
+ async function x() {
276
276
  if (!k) {
277
- h(!0), E(null);
277
+ m(!0), E(null);
278
278
  try {
279
- await u.deleteWorkspace(t.id), g(!1), o == null || o();
280
- } catch (C) {
281
- E(R(C));
279
+ await h.deleteWorkspace(t.id), N(!1), o == null || o();
280
+ } catch (_) {
281
+ E(S(_));
282
282
  } finally {
283
- h(!1);
283
+ m(!1);
284
284
  }
285
285
  }
286
286
  }
287
- return /* @__PURE__ */ n("section", { className: "shepherd-general", "aria-labelledby": l, children: [
287
+ return /* @__PURE__ */ n("section", { className: "shepherd-general", "aria-labelledby": r, children: [
288
288
  /* @__PURE__ */ n("div", { className: "card-head", children: [
289
- /* @__PURE__ */ e("h3", { id: l, children: "General" }),
289
+ /* @__PURE__ */ e("h3", { id: r, children: "General" }),
290
290
  /* @__PURE__ */ e("p", { className: "card-sub", children: "Your active workspace and its settings." })
291
291
  ] }),
292
292
  /* @__PURE__ */ n("div", { className: "card-body", children: [
293
- m && /* @__PURE__ */ e("p", { role: "alert", children: m }),
294
- y && s && /* @__PURE__ */ e("p", { className: "helper", children: "You're the last admin, so you can't leave. To remove this workspace entirely, delete it below." }),
293
+ p && /* @__PURE__ */ e("p", { role: "alert", children: p }),
294
+ y && l && /* @__PURE__ */ e("p", { className: "helper", children: "You're the last admin, so you can't leave. To remove this workspace entirely, delete it below." }),
295
295
  /* @__PURE__ */ n("div", { className: "field", children: [
296
296
  /* @__PURE__ */ e("label", { children: "Workspace name" }),
297
297
  /* @__PURE__ */ e("p", { className: "readonly-value", children: t.name })
@@ -303,9 +303,9 @@ function M({ workspace: t, onLeft: a, onDeleted: o }) {
303
303
  /* @__PURE__ */ n("div", { className: "field leave", children: [
304
304
  /* @__PURE__ */ e("label", { children: "Leave workspace" }),
305
305
  /* @__PURE__ */ e("p", { className: "helper", children: "Remove yourself from this workspace. You'll need a new invite to rejoin." }),
306
- /* @__PURE__ */ e("button", { type: "button", className: "danger", onClick: () => void N(), disabled: b, children: "Leave workspace" })
306
+ /* @__PURE__ */ e("button", { type: "button", className: "danger", onClick: () => void g(), disabled: b, children: "Leave workspace" })
307
307
  ] }),
308
- s && /* @__PURE__ */ n("div", { className: "field delete", children: [
308
+ l && /* @__PURE__ */ n("div", { className: "field delete", children: [
309
309
  /* @__PURE__ */ e("label", { children: "Delete workspace" }),
310
310
  /* @__PURE__ */ e("p", { className: "helper", children: "Permanently delete this workspace and all of its data. This cannot be undone." }),
311
311
  /* @__PURE__ */ e(
@@ -314,7 +314,7 @@ function M({ workspace: t, onLeft: a, onDeleted: o }) {
314
314
  type: "button",
315
315
  className: "danger",
316
316
  onClick: () => {
317
- E(null), g(!0);
317
+ E(null), N(!0);
318
318
  },
319
319
  disabled: k,
320
320
  children: "Delete workspace"
@@ -322,123 +322,123 @@ function M({ workspace: t, onLeft: a, onDeleted: o }) {
322
322
  )
323
323
  ] })
324
324
  ] }),
325
- v && /* @__PURE__ */ e(
326
- V,
325
+ f && /* @__PURE__ */ e(
326
+ X,
327
327
  {
328
328
  workspaceName: t.name,
329
329
  busy: k,
330
- error: _,
331
- onConfirm: () => void I(),
330
+ error: C,
331
+ onConfirm: () => void x(),
332
332
  onCancel: () => {
333
- k || g(!1);
333
+ k || N(!1);
334
334
  }
335
335
  }
336
336
  )
337
337
  ] });
338
338
  }
339
- function X({ workspaceId: t, refreshKey: a = 0, canRemove: o = !1 }) {
340
- const u = B(), l = $(), [m, c] = i([]), [b, r] = i(null), [y, p] = i(null), [v, g] = i(!0), [k, h] = i(null);
341
- async function _({ keepError: s = !1 } = {}) {
342
- s || r(null);
339
+ function z({ workspaceId: t, refreshKey: a = 0, canRemove: o = !1 }) {
340
+ const h = j(), r = $(), [p, c] = i([]), [b, s] = i(null), [y, v] = i(null), [f, N] = i(!0), [k, m] = i(null);
341
+ async function C({ keepError: l = !1 } = {}) {
342
+ l || s(null);
343
343
  try {
344
- const N = await u.listMembers(t);
345
- c(N.members);
346
- } catch (N) {
347
- r(R(N));
344
+ const g = await h.listMembers(t);
345
+ c(g.members);
346
+ } catch (g) {
347
+ s(S(g));
348
348
  } finally {
349
- g(!1);
349
+ N(!1);
350
350
  }
351
351
  }
352
352
  A(() => {
353
- g(!0), _();
354
- }, [u, t, a]);
355
- async function E(s, N) {
353
+ N(!0), C();
354
+ }, [h, t, a]);
355
+ async function E(l, g) {
356
356
  if (!k) {
357
- h(s), r(null), p(null), c((I) => I.filter((C) => C.accountId !== s));
357
+ m(l), s(null), v(null), c((x) => x.filter((_) => _.accountId !== l));
358
358
  try {
359
- await u.removeMember(t, s), p(`Removed ${N}`);
360
- } catch (I) {
361
- r(R(I)), _({ keepError: !0 });
359
+ await h.removeMember(t, l), v(`Removed ${g}`);
360
+ } catch (x) {
361
+ s(S(x)), C({ keepError: !0 });
362
362
  } finally {
363
- h(null);
363
+ m(null);
364
364
  }
365
365
  }
366
366
  }
367
- return /* @__PURE__ */ n("section", { className: "shepherd-members", "aria-labelledby": l, children: [
368
- /* @__PURE__ */ e("div", { className: "card-head", children: /* @__PURE__ */ e("h3", { id: l, children: "Members" }) }),
367
+ return /* @__PURE__ */ n("section", { className: "shepherd-members", "aria-labelledby": r, children: [
368
+ /* @__PURE__ */ e("div", { className: "card-head", children: /* @__PURE__ */ e("h3", { id: r, children: "Members" }) }),
369
369
  /* @__PURE__ */ n("div", { className: "card-body", children: [
370
370
  b && /* @__PURE__ */ e("p", { role: "alert", children: b }),
371
371
  y && /* @__PURE__ */ e("p", { role: "status", children: y }),
372
- v ? /* @__PURE__ */ e("p", { role: "status", children: "Loading…" }) : m.length === 0 ? /* @__PURE__ */ e("p", { children: "No members." }) : /* @__PURE__ */ e("ul", { children: m.map((s) => {
373
- const N = s.displayName ?? s.githubLogin ?? s.email ?? s.accountId;
372
+ f ? /* @__PURE__ */ e("p", { role: "status", children: "Loading…" }) : p.length === 0 ? /* @__PURE__ */ e("p", { children: "No members." }) : /* @__PURE__ */ e("ul", { children: p.map((l) => {
373
+ const g = l.displayName ?? l.githubLogin ?? l.email ?? l.accountId;
374
374
  return /* @__PURE__ */ n("li", { children: [
375
- /* @__PURE__ */ e("span", { children: N }),
376
- /* @__PURE__ */ e("span", { className: "role", children: s.role }),
375
+ /* @__PURE__ */ e("span", { children: g }),
376
+ /* @__PURE__ */ e("span", { className: "role", children: l.role }),
377
377
  o && /* @__PURE__ */ e(
378
378
  "button",
379
379
  {
380
380
  type: "button",
381
- "aria-label": `Remove ${N}`,
382
- onClick: () => void E(s.accountId, N),
383
- disabled: k === s.accountId,
381
+ "aria-label": `Remove ${g}`,
382
+ onClick: () => void E(l.accountId, g),
383
+ disabled: k === l.accountId,
384
384
  children: "Remove"
385
385
  }
386
386
  )
387
- ] }, s.accountId);
387
+ ] }, l.accountId);
388
388
  }) })
389
389
  ] })
390
390
  ] });
391
391
  }
392
- function q({ workspaceId: t, onMembersChanged: a }) {
393
- const o = B(), u = $(), [l, m] = i(null), [c, b] = i(null), [r, y] = i(!1), [p, v] = i(""), [g, k] = i(null), [h, _] = i(!1), [E, s] = i(null);
392
+ function Q({ workspaceId: t, onMembersChanged: a }) {
393
+ const o = j(), h = $(), [r, p] = i(null), [c, b] = i(null), [s, y] = i(!1), [v, f] = i(""), [N, k] = i(null), [m, C] = i(!1), [E, l] = i(null);
394
394
  A(() => {
395
- m(null), k(null);
395
+ p(null), k(null);
396
396
  }, [t]);
397
- async function N() {
398
- if (!r) {
397
+ async function g() {
398
+ if (!s) {
399
399
  y(!0), b(null);
400
400
  try {
401
401
  const w = await o.createInvite(t, {});
402
- m(w), a == null || a();
402
+ p(w), a == null || a();
403
403
  } catch (w) {
404
- b(R(w));
404
+ b(S(w));
405
405
  } finally {
406
406
  y(!1);
407
407
  }
408
408
  }
409
409
  }
410
- async function I() {
411
- if (!(!l || r)) {
410
+ async function x() {
411
+ if (!(!r || s)) {
412
412
  y(!0), b(null);
413
413
  try {
414
- await o.revokeInvite(t, l.code), m(null);
414
+ await o.revokeInvite(t, r.code), p(null);
415
415
  } catch (w) {
416
- b(R(w));
416
+ b(S(w));
417
417
  } finally {
418
418
  y(!1);
419
419
  }
420
420
  }
421
421
  }
422
- async function C() {
423
- if (!(!p.trim() || h)) {
424
- _(!0), k(null);
422
+ async function _() {
423
+ if (!(!v.trim() || m)) {
424
+ C(!0), k(null);
425
425
  try {
426
- const w = await o.inviteByEmail(t, p.trim());
427
- k(`Invite sent to ${w.email}.`), v(""), a == null || a();
426
+ const w = await o.inviteByEmail(t, v.trim());
427
+ k(`Invite sent to ${w.email}.`), f(""), a == null || a();
428
428
  } catch (w) {
429
- k(R(w));
429
+ k(S(w));
430
430
  } finally {
431
- _(!1);
431
+ C(!1);
432
432
  }
433
433
  }
434
434
  }
435
- const S = l ? `${window.location.origin}/shepherd/join/${encodeURIComponent(l.code)}` : null;
435
+ const I = r ? `${window.location.origin}/shepherd/join/${encodeURIComponent(r.code)}` : null;
436
436
  async function T(w, L) {
437
- await navigator.clipboard.writeText(L), s(w), setTimeout(() => s((d) => d === w ? null : d), 2e3);
437
+ await navigator.clipboard.writeText(L), l(w), setTimeout(() => l((d) => d === w ? null : d), 2e3);
438
438
  }
439
- return /* @__PURE__ */ n("section", { className: "shepherd-invites", "aria-labelledby": u, children: [
439
+ return /* @__PURE__ */ n("section", { className: "shepherd-invites", "aria-labelledby": h, children: [
440
440
  /* @__PURE__ */ n("div", { className: "card-head", children: [
441
- /* @__PURE__ */ e("h3", { id: u, children: "Invite people" }),
441
+ /* @__PURE__ */ e("h3", { id: h, children: "Invite people" }),
442
442
  /* @__PURE__ */ e("p", { className: "card-sub", children: "Share a code, or send a one-time join link by email." })
443
443
  ] }),
444
444
  /* @__PURE__ */ n("div", { className: "card-body", children: [
@@ -446,48 +446,48 @@ function q({ workspaceId: t, onMembersChanged: a }) {
446
446
  /* @__PURE__ */ n("div", { className: "field invites", children: [
447
447
  /* @__PURE__ */ e("label", { children: "Invite by code" }),
448
448
  /* @__PURE__ */ e("p", { className: "helper", children: "Anyone with the code can join this workspace." }),
449
- l && /* @__PURE__ */ n("div", { className: "invite-result", children: [
449
+ r && /* @__PURE__ */ n("div", { className: "invite-result", children: [
450
450
  /* @__PURE__ */ n("div", { className: "invite-result__head", children: [
451
451
  /* @__PURE__ */ e("span", { className: "invite-result__eyebrow", children: "Invite code" }),
452
- /* @__PURE__ */ e("span", { className: "invite-uses", children: l.maxUses === null ? `${l.useCount} uses` : `${l.useCount} / ${l.maxUses} uses` }),
452
+ /* @__PURE__ */ e("span", { className: "invite-uses", children: r.maxUses === null ? `${r.useCount} uses` : `${r.useCount} / ${r.maxUses} uses` }),
453
453
  /* @__PURE__ */ e(
454
454
  "button",
455
455
  {
456
456
  type: "button",
457
457
  className: "link-btn",
458
458
  "aria-label": "Revoke invite",
459
- onClick: () => void I(),
460
- disabled: r,
459
+ onClick: () => void x(),
460
+ disabled: s,
461
461
  children: "Revoke"
462
462
  }
463
463
  )
464
464
  ] }),
465
465
  /* @__PURE__ */ n("div", { className: "invite-result__copyrow", children: [
466
- /* @__PURE__ */ e("code", { children: l.code }),
466
+ /* @__PURE__ */ e("code", { children: r.code }),
467
467
  /* @__PURE__ */ e(
468
468
  "button",
469
469
  {
470
470
  type: "button",
471
471
  className: "copy-btn",
472
- onClick: () => void T("code", l.code),
472
+ onClick: () => void T("code", r.code),
473
473
  children: E === "code" ? "Copied" : "Copy"
474
474
  }
475
475
  )
476
476
  ] }),
477
- S && /* @__PURE__ */ n("div", { className: "invite-result__copyrow invite-result__copyrow--link", children: [
478
- /* @__PURE__ */ e("a", { href: S, children: S }),
477
+ I && /* @__PURE__ */ n("div", { className: "invite-result__copyrow invite-result__copyrow--link", children: [
478
+ /* @__PURE__ */ e("a", { href: I, children: I }),
479
479
  /* @__PURE__ */ e(
480
480
  "button",
481
481
  {
482
482
  type: "button",
483
483
  className: "copy-btn",
484
- onClick: () => void T("link", S),
484
+ onClick: () => void T("link", I),
485
485
  children: E === "link" ? "Copied" : "Copy link"
486
486
  }
487
487
  )
488
488
  ] })
489
489
  ] }),
490
- /* @__PURE__ */ e("button", { type: "button", onClick: () => void N(), disabled: r, children: "Create invite" })
490
+ /* @__PURE__ */ e("button", { type: "button", onClick: () => void g(), disabled: s, children: "Create invite" })
491
491
  ] }),
492
492
  /* @__PURE__ */ n("div", { className: "field email-invite", children: [
493
493
  /* @__PURE__ */ e("label", { htmlFor: "invite-email", children: "Invite by email" }),
@@ -499,44 +499,71 @@ function q({ workspaceId: t, onMembersChanged: a }) {
499
499
  id: "invite-email",
500
500
  type: "email",
501
501
  placeholder: "name@example.com",
502
- value: p,
503
- onChange: (w) => v(w.target.value)
502
+ value: v,
503
+ onChange: (w) => f(w.target.value)
504
504
  }
505
505
  ),
506
- /* @__PURE__ */ e("button", { type: "button", onClick: () => void C(), disabled: h, children: "Send invite" })
506
+ /* @__PURE__ */ e("button", { type: "button", onClick: () => void _(), disabled: m, children: "Send invite" })
507
507
  ] }),
508
- g && /* @__PURE__ */ e("p", { className: "email-invite__status", children: g })
508
+ N && /* @__PURE__ */ e("p", { className: "email-invite__status", children: N })
509
509
  ] })
510
510
  ] })
511
511
  ] });
512
512
  }
513
- const z = [
513
+ const Z = [
514
514
  { id: "claude", label: "Claude Code" },
515
515
  { id: "codex", label: "Codex" },
516
516
  { id: "pi", label: "Pi" },
517
517
  { id: "cursor", label: "Cursor" },
518
518
  { id: "generic", label: "Generic (JSON)" }
519
- ], Q = "shp_<paste-after-generating>", Z = {
519
+ ], ee = "shp_<paste-after-generating>", ne = {
520
520
  claude: "claude mcp add shepherd -s user",
521
521
  codex: "codex mcp add shepherd"
522
- }, ee = {
522
+ }, te = {
523
523
  claude: "-e",
524
524
  codex: "--env"
525
- };
526
- function ne(t, a) {
527
- const o = `created ${O(t.createdAt, a)}`, u = t.lastUsedAt ? `last used ${O(t.lastUsedAt, a)}` : "never used";
528
- return `${o} · ${u}`;
525
+ }, F = "npx -y --package=@korso/shepherd shepherd-inbox-hook";
526
+ function ae(t) {
527
+ return t === "claude" ? {
528
+ target: "~/.claude/settings.json",
529
+ snippet: JSON.stringify(
530
+ {
531
+ hooks: {
532
+ SessionStart: [{ hooks: [{ type: "command", command: F }] }],
533
+ PreToolUse: [
534
+ { matcher: "*", hooks: [{ type: "command", command: F }] }
535
+ ]
536
+ }
537
+ },
538
+ null,
539
+ 2
540
+ )
541
+ } : t === "codex" ? {
542
+ target: "~/.codex/config.toml",
543
+ snippet: [
544
+ "[features]",
545
+ "hooks = true",
546
+ "",
547
+ "[[hooks.UserPromptSubmit]]",
548
+ 'command = ["npx", "-y", "--package=@korso/shepherd", "shepherd-inbox-hook"]'
549
+ ].join(`
550
+ `)
551
+ } : null;
529
552
  }
530
- function te(t, a, o) {
553
+ function ie(t, a) {
554
+ const o = `created ${P(t.createdAt, a)}`, h = t.lastUsedAt ? `last used ${P(t.lastUsedAt, a)}` : "never used";
555
+ return `${o} · ${h}`;
556
+ }
557
+ function re(t, a, o) {
531
558
  if (t === "generic" || t === "pi" || t === "cursor") {
532
- const m = { HUB_URL: a, SHEPHERD_TOKEN: o };
533
- return t !== "generic" && (m.PROGRAM = t), JSON.stringify(
559
+ const p = { HUB_URL: a, SHEPHERD_TOKEN: o };
560
+ return t !== "generic" && (p.PROGRAM = t), JSON.stringify(
534
561
  {
535
562
  mcpServers: {
536
563
  shepherd: {
537
564
  command: "npx",
538
565
  args: ["-y", "--package=@korso/shepherd", "shepherd-mcp"],
539
- env: m
566
+ env: p
540
567
  }
541
568
  }
542
569
  },
@@ -544,60 +571,60 @@ function te(t, a, o) {
544
571
  2
545
572
  );
546
573
  }
547
- const u = ee[t], l = [
548
- Z[t],
549
- `${u} HUB_URL=${a}`,
550
- `${u} SHEPHERD_TOKEN=${o}`
574
+ const h = te[t], r = [
575
+ ne[t],
576
+ `${h} HUB_URL=${a}`,
577
+ `${h} SHEPHERD_TOKEN=${o}`
551
578
  ];
552
- return t === "codex" && l.push(`${u} PROGRAM=codex`), l.push("-- npx -y --package=@korso/shepherd shepherd-mcp"), l.join(" ");
579
+ return t === "codex" && r.push(`${h} PROGRAM=codex`), r.push("-- npx -y --package=@korso/shepherd shepherd-mcp"), r.join(" ");
553
580
  }
554
- function ae({ hubUrl: t }) {
555
- const a = B(), o = t ?? a.baseUrl, u = $(), [l, m] = i("claude"), [c, b] = i(""), [r, y] = i(null), [p, v] = i([]), [g, k] = i(null), [h, _] = i(null), [E, s] = i(!1), [N, I] = i(!1), [C, S] = i(!0), [T, w] = i(null);
581
+ function se({ hubUrl: t }) {
582
+ const a = j(), o = t ?? a.baseUrl, h = $(), [r, p] = i("claude"), [c, b] = i(""), [s, y] = i(null), [v, f] = i([]), [N, k] = i(null), [m, C] = i(null), [E, l] = i(!1), [g, x] = i(!1), [_, I] = i(!0), [T, w] = i(null);
556
583
  async function L() {
557
584
  try {
558
- const f = await a.listAccountTokens();
559
- v(f.tokens);
560
- } catch (f) {
561
- k(R(f));
585
+ const u = await a.listAccountTokens();
586
+ f(u.tokens);
587
+ } catch (u) {
588
+ k(S(u));
562
589
  } finally {
563
- S(!1);
590
+ I(!1);
564
591
  }
565
592
  }
566
593
  A(() => {
567
- S(!0), L();
594
+ I(!0), L();
568
595
  }, [a]);
569
596
  async function d() {
570
- s(!0), k(null);
597
+ l(!0), k(null), C(null);
571
598
  try {
572
- const f = await a.mintAccountToken(
599
+ const u = await a.mintAccountToken(
573
600
  c.trim() ? { name: c.trim() } : {}
574
601
  );
575
- y(f.token), b(""), await L();
576
- } catch (f) {
577
- k(R(f));
602
+ y(u.token), b(""), await L();
603
+ } catch (u) {
604
+ k(S(u));
578
605
  } finally {
579
- s(!1);
606
+ l(!1);
580
607
  }
581
608
  }
582
- async function x(f) {
609
+ async function R(u) {
583
610
  if (!T) {
584
- w(f), k(null), _(null);
611
+ w(u), k(null), C(null);
585
612
  try {
586
- await a.revokeAccountToken(f), v((D) => D.filter((W) => W.id !== f)), _("Token revoked");
587
- } catch (D) {
588
- k(R(D));
613
+ await a.revokeAccountToken(u), f((O) => O.filter((K) => K.id !== u)), C("Token revoked");
614
+ } catch (O) {
615
+ k(S(O));
589
616
  } finally {
590
617
  w(null);
591
618
  }
592
619
  }
593
620
  }
594
- const j = te(l, o, r ?? Q);
595
- async function G() {
596
- await navigator.clipboard.writeText(j), I(!0), setTimeout(() => I(!1), 2e3);
621
+ const D = re(r, o, s ?? ee), B = ae(r);
622
+ async function H() {
623
+ await navigator.clipboard.writeText(D), x(!0), setTimeout(() => x(!1), 2e3);
597
624
  }
598
- return /* @__PURE__ */ n("section", { className: "shepherd-connect-agent", "aria-labelledby": u, children: [
625
+ return /* @__PURE__ */ n("section", { className: "shepherd-connect-agent", "aria-labelledby": h, children: [
599
626
  /* @__PURE__ */ n("div", { className: "card-head", children: [
600
- /* @__PURE__ */ e("h3", { id: u, children: "Connect your agent" }),
627
+ /* @__PURE__ */ e("h3", { id: h, children: "Connect your agent" }),
601
628
  /* @__PURE__ */ e("p", { className: "card-sub", children: "Generate a token — it works across all your workspaces — then paste the command into your coding tool." }),
602
629
  /* @__PURE__ */ e("p", { className: "card-sub", children: "The first time the agent opens a repo, it'll ask which workspace to coordinate that repo with." })
603
630
  ] }),
@@ -608,9 +635,9 @@ function ae({ hubUrl: t }) {
608
635
  "select",
609
636
  {
610
637
  id: "connect-tool",
611
- value: l,
612
- onChange: (f) => m(f.target.value),
613
- children: z.map((f) => /* @__PURE__ */ e("option", { value: f.id, children: f.label }, f.id))
638
+ value: r,
639
+ onChange: (u) => p(u.target.value),
640
+ children: Z.map((u) => /* @__PURE__ */ e("option", { value: u.id, children: u.label }, u.id))
614
641
  }
615
642
  )
616
643
  ] }),
@@ -623,27 +650,30 @@ function ae({ hubUrl: t }) {
623
650
  id: "token-name",
624
651
  type: "text",
625
652
  value: c,
626
- onChange: (f) => b(f.target.value),
653
+ onChange: (u) => b(u.target.value),
654
+ onKeyDown: (u) => {
655
+ u.key === "Enter" && !E && (u.preventDefault(), d());
656
+ },
627
657
  placeholder: "e.g. laptop"
628
658
  }
629
659
  ),
630
660
  /* @__PURE__ */ e("button", { type: "button", onClick: () => void d(), disabled: E, children: "Generate token" })
631
661
  ] })
632
662
  ] }),
633
- g && /* @__PURE__ */ e("p", { role: "alert", children: g }),
634
- h && /* @__PURE__ */ e("p", { role: "status", children: h }),
635
- r && /* @__PURE__ */ e("p", { className: "token-once", role: "status", children: "Copy this token now — it won't be shown again." }),
663
+ N && /* @__PURE__ */ e("p", { role: "alert", children: N }),
664
+ m && /* @__PURE__ */ e("p", { role: "status", children: m }),
665
+ s && /* @__PURE__ */ e("p", { className: "token-once", role: "status", children: "Copy this token now — it won't be shown again." }),
636
666
  /* @__PURE__ */ n("div", { className: "install-command", children: [
637
- /* @__PURE__ */ e("pre", { "data-testid": "install-command", children: j }),
667
+ /* @__PURE__ */ e("pre", { "data-testid": "install-command", children: D }),
638
668
  /* @__PURE__ */ e(
639
669
  "button",
640
670
  {
641
671
  type: "button",
642
672
  className: "install-command__copy",
643
- "aria-label": N ? "Copied" : "Copy command",
644
- title: N ? "Copied" : "Copy",
645
- onClick: () => void G(),
646
- children: N ? /* @__PURE__ */ e(
673
+ "aria-label": g ? "Copied" : "Copy command",
674
+ title: g ? "Copied" : "Copy",
675
+ onClick: () => void H(),
676
+ children: g ? /* @__PURE__ */ e(
647
677
  "svg",
648
678
  {
649
679
  width: "15",
@@ -678,114 +708,122 @@ function ae({ hubUrl: t }) {
678
708
  }
679
709
  )
680
710
  ] }),
711
+ B && /* @__PURE__ */ n("div", { className: "hook-setup", children: [
712
+ /* @__PURE__ */ n("p", { className: "card-sub", children: [
713
+ "Recommended: merge this into ",
714
+ /* @__PURE__ */ e("code", { children: B.target }),
715
+ " — it delivers teammate announcements to the agent and reminds it to link each repo before its first write."
716
+ ] }),
717
+ /* @__PURE__ */ e("div", { className: "install-command", children: /* @__PURE__ */ e("pre", { "data-testid": "hook-snippet", children: B.snippet }) })
718
+ ] }),
681
719
  /* @__PURE__ */ e("h4", { children: "Existing tokens" }),
682
- C ? /* @__PURE__ */ e("p", { role: "status", children: "Loading…" }) : p.length === 0 ? /* @__PURE__ */ e("p", { children: "No tokens yet." }) : /* @__PURE__ */ e("ul", { children: p.map((f) => {
683
- const D = f.name ?? f.id;
720
+ _ ? /* @__PURE__ */ e("p", { role: "status", children: "Loading…" }) : v.length === 0 ? /* @__PURE__ */ e("p", { children: "No tokens yet." }) : /* @__PURE__ */ e("ul", { children: v.map((u) => {
721
+ const O = u.name ?? u.id;
684
722
  return /* @__PURE__ */ n("li", { children: [
685
- /* @__PURE__ */ e("span", { children: D }),
686
- /* @__PURE__ */ e("span", { className: "token-meta", children: ne(f, Date.now()) }),
687
- f.revokedAt ? /* @__PURE__ */ e("span", { className: "revoked", children: "revoked" }) : /* @__PURE__ */ e(
723
+ /* @__PURE__ */ e("span", { children: O }),
724
+ /* @__PURE__ */ e("span", { className: "token-meta", children: ie(u, Date.now()) }),
725
+ u.revokedAt ? /* @__PURE__ */ e("span", { className: "revoked", children: "revoked" }) : /* @__PURE__ */ e(
688
726
  "button",
689
727
  {
690
728
  type: "button",
691
- "aria-label": `Revoke token ${D}`,
692
- onClick: () => void x(f.id),
693
- disabled: T === f.id,
729
+ "aria-label": `Revoke token ${O}`,
730
+ onClick: () => void R(u.id),
731
+ disabled: T === u.id,
694
732
  children: "Revoke"
695
733
  }
696
734
  )
697
- ] }, f.id);
735
+ ] }, u.id);
698
736
  }) })
699
737
  ] })
700
738
  ] });
701
739
  }
702
- const ie = [
740
+ const le = [
703
741
  { id: "general", label: "General" },
704
742
  { id: "members", label: "Members" },
705
743
  { id: "agent", label: "Agent" }
706
744
  ];
707
- function le({
745
+ function oe({
708
746
  workspace: t,
709
747
  hubUrl: a,
710
748
  membersRefreshKey: o,
711
- onMembersChanged: u,
712
- onLeft: l,
713
- onDeleted: m
749
+ onMembersChanged: h,
750
+ onLeft: r,
751
+ onDeleted: p
714
752
  }) {
715
- const [c, b] = i("general"), r = t.role === "admin";
753
+ const [c, b] = i("general"), s = t.role === "admin";
716
754
  return /* @__PURE__ */ n("div", { className: "config-layout", children: [
717
- /* @__PURE__ */ e("nav", { className: "config-nav", "aria-label": "Configuration sections", children: ie.map(({ id: y, label: p }) => /* @__PURE__ */ e(
755
+ /* @__PURE__ */ e("nav", { className: "config-nav", "aria-label": "Configuration sections", children: le.map(({ id: y, label: v }) => /* @__PURE__ */ e(
718
756
  "button",
719
757
  {
720
758
  type: "button",
721
759
  className: "config-nav__item" + (c === y ? " config-nav__item--on" : ""),
722
760
  "aria-current": c === y ? "page" : void 0,
723
761
  onClick: () => b(y),
724
- children: p
762
+ children: v
725
763
  },
726
764
  y
727
765
  )) }),
728
766
  /* @__PURE__ */ n("div", { className: "config-panel", children: [
729
- c === "general" && /* @__PURE__ */ e(M, { workspace: t, onLeft: l, onDeleted: m }),
730
- c === "members" && /* @__PURE__ */ n(P, { children: [
767
+ c === "general" && /* @__PURE__ */ e(q, { workspace: t, onLeft: r, onDeleted: p }),
768
+ c === "members" && /* @__PURE__ */ n(G, { children: [
731
769
  /* @__PURE__ */ e(
732
- X,
770
+ z,
733
771
  {
734
772
  workspaceId: t.id,
735
773
  refreshKey: o,
736
- canRemove: r
774
+ canRemove: s
737
775
  }
738
776
  ),
739
- r && /* @__PURE__ */ e(q, { workspaceId: t.id, onMembersChanged: u })
777
+ s && /* @__PURE__ */ e(Q, { workspaceId: t.id, onMembersChanged: h })
740
778
  ] }),
741
- c === "agent" && /* @__PURE__ */ e(ae, { hubUrl: a })
779
+ c === "agent" && /* @__PURE__ */ e(se, { hubUrl: a })
742
780
  ] })
743
781
  ] });
744
782
  }
745
- function ce({ hubUrl: t }) {
746
- const a = B(), [o, u] = i({ status: "loading" }), [l, m] = i(null), [c, b] = i(0), r = Y(async () => {
783
+ function he({ hubUrl: t }) {
784
+ const a = j(), [o, h] = i({ status: "loading" }), [r, p] = i(null), [c, b] = i(0), s = M(async () => {
747
785
  try {
748
- const h = await a.listWorkspaces();
749
- u({ status: "ready", workspaces: h.workspaces }), m(
750
- (_) => {
786
+ const m = await a.listWorkspaces();
787
+ h({ status: "ready", workspaces: m.workspaces }), p(
788
+ (C) => {
751
789
  var E;
752
- return _ && h.workspaces.some((s) => s.id === _) ? _ : ((E = h.workspaces[0]) == null ? void 0 : E.id) ?? null;
790
+ return C && m.workspaces.some((l) => l.id === C) ? C : ((E = m.workspaces[0]) == null ? void 0 : E.id) ?? null;
753
791
  }
754
792
  );
755
- } catch (h) {
756
- u({ status: "error", message: R(h) });
793
+ } catch (m) {
794
+ h({ status: "error", message: S(m) });
757
795
  }
758
796
  }, [a]);
759
797
  A(() => {
760
- r();
761
- }, [r]);
762
- const y = o.status === "ready" ? o.workspaces : [], p = y.find((h) => h.id === l) ?? null;
798
+ s();
799
+ }, [s]);
800
+ const y = o.status === "ready" ? o.workspaces : [], v = y.find((m) => m.id === r) ?? null;
763
801
  if (o.status === "loading")
764
802
  return /* @__PURE__ */ e("div", { className: "shepherd-root", children: /* @__PURE__ */ e("p", { role: "status", children: "Loading…" }) });
765
803
  if (o.status === "error")
766
804
  return /* @__PURE__ */ e("div", { className: "shepherd-root", children: /* @__PURE__ */ e("p", { role: "alert", children: o.message }) });
767
- const v = y.length > 0, g = /* @__PURE__ */ e(
768
- J,
805
+ const f = y.length > 0, N = /* @__PURE__ */ e(
806
+ V,
769
807
  {
770
808
  workspaces: y,
771
- selected: p,
772
- onSelect: m,
809
+ selected: v,
810
+ onSelect: p,
773
811
  onChanged: () => {
774
- r();
812
+ s();
775
813
  },
776
- onMembersChanged: () => b((h) => h + 1)
814
+ onMembersChanged: () => b((m) => m + 1)
777
815
  }
778
816
  ), k = /* @__PURE__ */ n("section", { className: "shepherd-config", "aria-labelledby": "config-heading", children: [
779
817
  /* @__PURE__ */ e("h2", { id: "config-heading", children: "Configuration" }),
780
- p ? /* @__PURE__ */ e(
781
- le,
818
+ v ? /* @__PURE__ */ e(
819
+ oe,
782
820
  {
783
- workspace: p,
821
+ workspace: v,
784
822
  hubUrl: t,
785
823
  membersRefreshKey: c,
786
- onMembersChanged: () => b((h) => h + 1),
787
- onLeft: () => void r(),
788
- onDeleted: () => void r()
824
+ onMembersChanged: () => b((m) => m + 1),
825
+ onLeft: () => void s(),
826
+ onDeleted: () => void s()
789
827
  }
790
828
  ) : /* @__PURE__ */ n("p", { className: "config-none", children: [
791
829
  "You're not in a workspace yet. Use the ",
@@ -794,28 +832,28 @@ function ce({ hubUrl: t }) {
794
832
  ] })
795
833
  ] });
796
834
  return /* @__PURE__ */ e("div", { className: "shepherd-root", children: /* @__PURE__ */ e(
797
- K,
835
+ J,
798
836
  {
799
- workspaceId: p == null ? void 0 : p.id,
837
+ workspaceId: v == null ? void 0 : v.id,
800
838
  config: k,
801
- switcher: g,
802
- hasWorkspace: v
839
+ switcher: N,
840
+ hasWorkspace: f
803
841
  }
804
842
  ) });
805
843
  }
806
844
  export {
807
- le as ConfigPanel,
808
- ae as ConnectAgent,
809
- K as Dashboard,
810
- he as EmptyState,
811
- M as GeneralSettings,
812
- q as Invites,
813
- X as Members,
814
- H as ShepherdClientError,
815
- me as ShepherdClientProvider,
816
- ce as ShepherdRoot,
817
- J as WorkspaceSwitcher,
818
- pe as createShepherdClient,
819
- R as describeError,
820
- B as useShepherdClient
845
+ oe as ConfigPanel,
846
+ se as ConnectAgent,
847
+ J as Dashboard,
848
+ ve as EmptyState,
849
+ q as GeneralSettings,
850
+ Q as Invites,
851
+ z as Members,
852
+ Y as ShepherdClientError,
853
+ fe as ShepherdClientProvider,
854
+ he as ShepherdRoot,
855
+ V as WorkspaceSwitcher,
856
+ ye as createShepherdClient,
857
+ S as describeError,
858
+ j as useShepherdClient
821
859
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korso/shepherd-ui",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Auth-agnostic React UI library for the Shepherd wallboard (Dashboard, client, provider). Host injects auth; the library never reads tokens.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",