@bragi-gmbh/codebus 1.2.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.
Files changed (133) hide show
  1. package/bin/codebus.js +2 -0
  2. package/dist/binding-guard.js +97 -0
  3. package/dist/binding-guard.js.map +1 -0
  4. package/dist/client.js +94 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/commands/ack.js +47 -0
  7. package/dist/commands/ack.js.map +1 -0
  8. package/dist/commands/archive.js +21 -0
  9. package/dist/commands/archive.js.map +1 -0
  10. package/dist/commands/as.js +299 -0
  11. package/dist/commands/as.js.map +1 -0
  12. package/dist/commands/ask.js +189 -0
  13. package/dist/commands/ask.js.map +1 -0
  14. package/dist/commands/auth.js +33 -0
  15. package/dist/commands/auth.js.map +1 -0
  16. package/dist/commands/checkin.js +235 -0
  17. package/dist/commands/checkin.js.map +1 -0
  18. package/dist/commands/decision.js +52 -0
  19. package/dist/commands/decision.js.map +1 -0
  20. package/dist/commands/digest.js +117 -0
  21. package/dist/commands/digest.js.map +1 -0
  22. package/dist/commands/doctor.js +106 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/factory.js +191 -0
  25. package/dist/commands/factory.js.map +1 -0
  26. package/dist/commands/github-login.js +205 -0
  27. package/dist/commands/github-login.js.map +1 -0
  28. package/dist/commands/graph.js +88 -0
  29. package/dist/commands/graph.js.map +1 -0
  30. package/dist/commands/health.js +34 -0
  31. package/dist/commands/health.js.map +1 -0
  32. package/dist/commands/inbox.js +83 -0
  33. package/dist/commands/inbox.js.map +1 -0
  34. package/dist/commands/ingest.js +553 -0
  35. package/dist/commands/ingest.js.map +1 -0
  36. package/dist/commands/init.js +97 -0
  37. package/dist/commands/init.js.map +1 -0
  38. package/dist/commands/install.js +520 -0
  39. package/dist/commands/install.js.map +1 -0
  40. package/dist/commands/invite.js +152 -0
  41. package/dist/commands/invite.js.map +1 -0
  42. package/dist/commands/jira.js +237 -0
  43. package/dist/commands/jira.js.map +1 -0
  44. package/dist/commands/join.js +96 -0
  45. package/dist/commands/join.js.map +1 -0
  46. package/dist/commands/links.js +66 -0
  47. package/dist/commands/links.js.map +1 -0
  48. package/dist/commands/login.js +84 -0
  49. package/dist/commands/login.js.map +1 -0
  50. package/dist/commands/morning.js +121 -0
  51. package/dist/commands/morning.js.map +1 -0
  52. package/dist/commands/next.js +66 -0
  53. package/dist/commands/next.js.map +1 -0
  54. package/dist/commands/onboarding.js +109 -0
  55. package/dist/commands/onboarding.js.map +1 -0
  56. package/dist/commands/pack.js +276 -0
  57. package/dist/commands/pack.js.map +1 -0
  58. package/dist/commands/plan-gh.js +425 -0
  59. package/dist/commands/plan-gh.js.map +1 -0
  60. package/dist/commands/plan.js +465 -0
  61. package/dist/commands/plan.js.map +1 -0
  62. package/dist/commands/pr.js +88 -0
  63. package/dist/commands/pr.js.map +1 -0
  64. package/dist/commands/question.js +142 -0
  65. package/dist/commands/question.js.map +1 -0
  66. package/dist/commands/read.js +84 -0
  67. package/dist/commands/read.js.map +1 -0
  68. package/dist/commands/refs.js +357 -0
  69. package/dist/commands/refs.js.map +1 -0
  70. package/dist/commands/reply.js +38 -0
  71. package/dist/commands/reply.js.map +1 -0
  72. package/dist/commands/repo.js +1198 -0
  73. package/dist/commands/repo.js.map +1 -0
  74. package/dist/commands/report.js +41 -0
  75. package/dist/commands/report.js.map +1 -0
  76. package/dist/commands/resolve-refs.js +21 -0
  77. package/dist/commands/resolve-refs.js.map +1 -0
  78. package/dist/commands/resolve.js +93 -0
  79. package/dist/commands/resolve.js.map +1 -0
  80. package/dist/commands/send.js +168 -0
  81. package/dist/commands/send.js.map +1 -0
  82. package/dist/commands/sent.js +22 -0
  83. package/dist/commands/sent.js.map +1 -0
  84. package/dist/commands/session.js +426 -0
  85. package/dist/commands/session.js.map +1 -0
  86. package/dist/commands/statusline.js +43 -0
  87. package/dist/commands/statusline.js.map +1 -0
  88. package/dist/commands/task.js +140 -0
  89. package/dist/commands/task.js.map +1 -0
  90. package/dist/commands/tick.js +233 -0
  91. package/dist/commands/tick.js.map +1 -0
  92. package/dist/commands/upgrade.js +76 -0
  93. package/dist/commands/upgrade.js.map +1 -0
  94. package/dist/commands/watch.js +169 -0
  95. package/dist/commands/watch.js.map +1 -0
  96. package/dist/commands/whoami.js +27 -0
  97. package/dist/commands/whoami.js.map +1 -0
  98. package/dist/config.js +103 -0
  99. package/dist/config.js.map +1 -0
  100. package/dist/directives.js +102 -0
  101. package/dist/directives.js.map +1 -0
  102. package/dist/errors.js +110 -0
  103. package/dist/errors.js.map +1 -0
  104. package/dist/git-hooks.js +116 -0
  105. package/dist/git-hooks.js.map +1 -0
  106. package/dist/graph-bootstrap.js +95 -0
  107. package/dist/graph-bootstrap.js.map +1 -0
  108. package/dist/graph-remote.js +889 -0
  109. package/dist/graph-remote.js.map +1 -0
  110. package/dist/graph.js +335 -0
  111. package/dist/graph.js.map +1 -0
  112. package/dist/index.js +154 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/kit/graph-publish.mjs +400 -0
  115. package/dist/mail-latency.js +120 -0
  116. package/dist/mail-latency.js.map +1 -0
  117. package/dist/mcp-registry.js +859 -0
  118. package/dist/mcp-registry.js.map +1 -0
  119. package/dist/mcp.js +193 -0
  120. package/dist/mcp.js.map +1 -0
  121. package/dist/output.js +12 -0
  122. package/dist/output.js.map +1 -0
  123. package/dist/positioning.js +21 -0
  124. package/dist/positioning.js.map +1 -0
  125. package/dist/rolestore.js +89 -0
  126. package/dist/rolestore.js.map +1 -0
  127. package/dist/session.js +49 -0
  128. package/dist/session.js.map +1 -0
  129. package/dist/sessionstore.js +736 -0
  130. package/dist/sessionstore.js.map +1 -0
  131. package/dist/version-check.js +97 -0
  132. package/dist/version-check.js.map +1 -0
  133. package/package.json +47 -0
@@ -0,0 +1,426 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { basename } from "node:path";
4
+ import { resolveConfig } from "../config.js";
5
+ import { forgetSession } from "../sessionstore.js";
6
+ import { makeCtx, workspaceId } from "../client.js";
7
+ import { fetchAndCacheDigest, nextActionableItem } from "./digest.js";
8
+ import { updateStatus } from "../version-check.js";
9
+ import { render } from "../output.js";
10
+ import { CodebusError, DeadSessionError, SessionStoreError } from "../errors.js";
11
+ const exec = promisify(execFile);
12
+ // Git context is best-effort: session rituals must work outside a repo, and
13
+ // each probe degrades independently (e.g. a commitless fresh repo has no
14
+ // resolvable HEAD, but symbolic-ref still names the branch).
15
+ async function gitContext() {
16
+ let repo = null;
17
+ let branch = null;
18
+ let changed = [];
19
+ try {
20
+ const { stdout } = await exec("git", ["rev-parse", "--show-toplevel"]);
21
+ repo = basename(stdout.trim());
22
+ }
23
+ catch {
24
+ return { repo, branch, changed };
25
+ }
26
+ try {
27
+ const { stdout } = await exec("git", ["symbolic-ref", "--short", "HEAD"]);
28
+ branch = stdout.trim();
29
+ }
30
+ catch {
31
+ try { // detached HEAD: fall back to the short sha
32
+ const { stdout } = await exec("git", ["rev-parse", "--short", "HEAD"]);
33
+ branch = stdout.trim();
34
+ }
35
+ catch { /* no commits and no symbolic ref — leave null */ }
36
+ }
37
+ try {
38
+ const { stdout } = await exec("git", ["status", "--porcelain"]);
39
+ changed = stdout.split("\n").filter(Boolean).slice(0, 20);
40
+ }
41
+ catch { /* keep repo/branch */ }
42
+ return { repo, branch, changed };
43
+ }
44
+ async function personOf(ctx) {
45
+ const { data } = await ctx.supabase
46
+ .from("agents").select("people!person_id(slug)").eq("slug", ctx.agentSlug).single();
47
+ return data?.people?.slug ?? null;
48
+ }
49
+ function statuslineOf(snap) {
50
+ const parts = Object.entries(snap.counts).map(([k, v]) => `${v} ${k}`);
51
+ const speakNow = snap.speak_now_count ?? 0;
52
+ const urgentPrefix = speakNow > 0 ? `!! URGENT ${speakNow} | ` : "";
53
+ return `CodeBus: ${urgentPrefix}${parts.length ? parts.join(" | ") : "clear"}`;
54
+ }
55
+ // ── session start ─────────────────────────────────────────────────────────────
56
+ // Pull-at-session-start is the delivery contract (spec §7). Read-only by
57
+ // design: it never marks anything seen or acted unless --mark-seen is passed.
58
+ /**
59
+ * Resolve THIS command's identity via the SAME shared path as every other
60
+ * command (resolveConfig → sessionstore/rolestore) — the FM-4 consolidation
61
+ * tick.ts already got (docs/plans/codebus-v2-reliability.md §2.3, tick.ts's
62
+ * tickIdentity). This function REPLACES a hand-rolled, DIVERGENT identity
63
+ * resolver (formerly `hookToken` here): raw CODEBUS_TOKEN, then
64
+ * sessionIdentity() (which — this was the bug an ai-review gate caught —
65
+ * collapses resolveSession()'s "dead" and "none" into the SAME null), then
66
+ * an exact-basename dir binding, raw-token only. Collapsing "dead" into null
67
+ * meant a session that WAS checked in but whose role's credential later died
68
+ * (forgotten/rotated via `as --forget`) or aged out (the 30-day env-key
69
+ * policy) would silently run `session start --hook` as whatever the CWD
70
+ * happens to be bound to instead — the exact dead-tier violation
71
+ * resolveConfig's DeadSessionError exists to eliminate (§2.4 precedent 3),
72
+ * reintroduced here by a second, divergent resolver that never learned that
73
+ * rule.
74
+ *
75
+ * The hook's own gate on top of resolveConfig's shared precedence is
76
+ * UNCHANGED in intent from the old hookToken: identitySource "file" (the
77
+ * bare author fallback — config.json used only because nothing more
78
+ * specific matched) is excluded, same as "none" (truly nothing configured)
79
+ * — this runs machine-wide on every Claude Code session start, and the
80
+ * author's personal identity must never leak into an unrelated terminal.
81
+ * identitySource "cwd-binding" is DELIBERATELY still let through for a
82
+ * session with no checkin of its own (resolveSession() status "none"): that
83
+ * IS the old hookToken's exact-basename fallback for a session that was
84
+ * never itself checked in but sits in a directory someone else deliberately
85
+ * bound (`codebus as --bind`) — kept, just now driven by the same
86
+ * glob/full-path/{assume}-aware precedence every other command uses instead
87
+ * of a second, narrower hand-rolled reimplementation of it.
88
+ *
89
+ * DeadSessionError and SessionStoreError are the two exceptions that must
90
+ * NOT collapse to null: the session tier EXISTED for this session and
91
+ * either died or couldn't be trusted (a corrupt/unreadable
92
+ * session-roles.json — sessionstore fails closed rather than guess). Both
93
+ * propagate so startCmd's hook wrapper can swallow them to SILENCE — never
94
+ * to an inferred fallback identity, since resolveConfig itself already
95
+ * stopped before ever reaching the cwd-binding tier for either (dead-tier
96
+ * rule, same as resolveConfig's other callers).
97
+ *
98
+ * Exported for the regression suite.
99
+ */
100
+ export function sessionStartIdentity(o, env = process.env) {
101
+ let cfg;
102
+ try {
103
+ cfg = resolveConfig(o, env);
104
+ }
105
+ catch (e) {
106
+ if (e instanceof DeadSessionError || e instanceof SessionStoreError)
107
+ throw e;
108
+ return null;
109
+ }
110
+ return cfg.identitySource === "file" || cfg.identitySource === "none" ? null : cfg;
111
+ }
112
+ export async function startCmd(o) {
113
+ if (o.hook) {
114
+ // SessionStart-hook contract (tick.ts philosophy): identity-explicit (or
115
+ // this session's own checkin, or a deliberate cwd binding — see
116
+ // sessionStartIdentity's doc comment) or SILENT, and a bus outage must
117
+ // never break session start — every error swallowed to a silent exit 0.
118
+ // Stdout from a SessionStart hook lands in the model's context, so when
119
+ // identity resolves we run the full orientation AS that identity; when
120
+ // it doesn't, print NOTHING (a hint in an unbound directory would nag
121
+ // every session on the machine). A DeadSessionError/SessionStoreError
122
+ // thrown by sessionStartIdentity is caught by this SAME try/catch and
123
+ // swallowed to silence too — loud in plain mode below (resolveConfig
124
+ // throws there with no wrapper), but hook mode's bus-outage-must-never-
125
+ // block-coding policy applies uniformly to every failure mode, the
126
+ // dead/untrusted-store tiers included.
127
+ try {
128
+ const cfg = sessionStartIdentity(o);
129
+ if (!cfg)
130
+ return;
131
+ const { hook: _hook, ...rest } = o;
132
+ await runStart(rest, cfg);
133
+ }
134
+ catch {
135
+ /* bus outage must never break session start */
136
+ }
137
+ return;
138
+ }
139
+ await runStart(o);
140
+ }
141
+ /** `cfg`, when given, is a Config already resolved by the hook path above —
142
+ * used as-is (never re-resolved) so hook mode never calls resolveConfig
143
+ * twice for one session start (which would double the "acting as ..."
144
+ * stderr announcement resolveConfig makes for session/cwd-binding tiers). */
145
+ async function runStart(o, cfg) {
146
+ const ctx = await makeCtx(cfg ?? resolveConfig(o));
147
+ const ws = await workspaceId(ctx);
148
+ const [person, git] = await Promise.all([personOf(ctx), gitContext()]);
149
+ const snapshot = await fetchAndCacheDigest(ctx);
150
+ // NEXT is the top ACTIONABLE item — the same actionable-only selection
151
+ // `codebus next` and MCP `codebus_state.next` use (nextActionableItem skips
152
+ // class-8 FYI, 0061). A class-8 FYI is never presented as the next work item
153
+ // here either, so session-start/onboarding agrees with those surfaces; FYI
154
+ // mail stays visible in the statusline counts and via `codebus inbox --unread`.
155
+ const top = nextActionableItem(snapshot.items);
156
+ // FYI-only inbox: nothing actionable, but mail arrived — say so honestly
157
+ // rather than either a false "bus is clear" or a false work-order NEXT.
158
+ const fyiWaiting = !top && snapshot.items.length > 0 ? snapshot.items.length : 0;
159
+ // Onboarding reliability (field incident: a fresh checkin into a large inbox
160
+ // read only the NEXT teaser — a display_id and title, never learning WHO sent
161
+ // the top item or HOW to answer). When NEXT is a message, one cheap best-effort
162
+ // lookup resolves the sender + type; both the JSON and human surfaces carry it,
163
+ // so no delivery surface is left with a bare teaser. `top` is actionable by
164
+ // construction, so this is always a work order. Silent fallback on any failure.
165
+ const topIsMessage = typeof top?.display_id === "string" && top.display_id.startsWith("CB-MSG");
166
+ let nextFrom = null;
167
+ let nextType = null;
168
+ if (topIsMessage) {
169
+ try {
170
+ const { data: m } = await ctx.supabase
171
+ .from("messages")
172
+ .select("type, from_agent_id, agents:from_agent_id(slug)")
173
+ // display_id (CB-MSG-*) is unique per WORKSPACE, not globally — scope to
174
+ // the active workspace or a multi-workspace agent's lookup can match
175
+ // several rows (maybeSingle throws → enrichment lost) or surface the
176
+ // wrong workspace's message.
177
+ .eq("workspace_id", ws)
178
+ .eq("display_id", top.display_id)
179
+ .maybeSingle();
180
+ nextFrom = m?.agents?.slug ?? null;
181
+ nextType = m?.type ?? null;
182
+ }
183
+ catch {
184
+ /* best-effort; the NEXT/Reason lines still orient */
185
+ }
186
+ }
187
+ // adoption metric (best-effort: a logging failure never breaks the ritual)
188
+ await ctx.supabase.from("session_log")
189
+ .insert({ workspace_id: ws, kind: "start", repo: git.repo, branch: git.branch })
190
+ .then(() => undefined, () => undefined);
191
+ if (o.markSeen && top) {
192
+ await ctx.supabase.rpc("mark_seen", { p_kind: top.kind, p_subject: top.id, p_workspace: ws });
193
+ }
194
+ // Out-of-date / linked-build check against the bus-advertised release
195
+ // (best-effort; silent on any failure). Surfaced on both the human and JSON
196
+ // session-start surfaces so a stale or linked CLI is caught at every session.
197
+ const cli = await updateStatus(ctx);
198
+ if (o.json) {
199
+ // Same work-order contract as the human surface, in structured form: when
200
+ // NEXT is a message, a JSON consumer gets the sender, its type, and the
201
+ // directive — not just the teaser fields on `next`.
202
+ const nextMessage = top && topIsMessage
203
+ ? {
204
+ from: nextFrom, type: nextType,
205
+ directive: "work order — read (codebus read <id> --thread), do what it asks, then reply with the outcome (not a bare acknowledgement)",
206
+ reply_cmd: `codebus reply ${top.display_id} --body "<what you did / your answer>"`,
207
+ }
208
+ : null;
209
+ console.log(JSON.stringify({
210
+ agent: ctx.agentSlug, person, workspace: ctx.workspaceSlug,
211
+ repo: git.repo, branch: git.branch, counts: snapshot.counts,
212
+ speak_now_count: snapshot.speak_now_count ?? 0, next: top,
213
+ next_message: nextMessage,
214
+ cli_update: cli.message
215
+ ? { message: cli.message, local: cli.local, current: cli.current, dev_linked: cli.devLinked, behind: cli.behind }
216
+ : null,
217
+ }, null, 2));
218
+ return;
219
+ }
220
+ // first session ever for this agent? (no prior session_log starts) → welcome
221
+ const { count: priorStarts } = await ctx.supabase.from("session_log")
222
+ .select("*", { count: "exact", head: true })
223
+ .eq("workspace_id", ws).eq("kind", "start")
224
+ .eq("agent_id", (await ctx.supabase.from("agents").select("id").eq("slug", ctx.agentSlug).single()).data?.id ?? "");
225
+ if ((priorStarts ?? 0) <= 1) { // <=1: the row this very session just wrote
226
+ const { data: meRow } = await ctx.supabase.from("agents")
227
+ .select("people!person_id(id)").eq("slug", ctx.agentSlug).single();
228
+ const myPersonId = meRow?.people?.id;
229
+ const { data: conn } = myPersonId ? await ctx.supabase.from("connections")
230
+ .select("kind, expires_at, inviter:other_person_id(slug)")
231
+ .eq("workspace_id", ws).eq("person_id", myPersonId) : { data: null };
232
+ const invitedBy = conn?.find((c) => c.kind === "invited_by")?.inviter?.slug;
233
+ const mentor = conn?.find((c) => c.kind === "mentor" && new Date(c.expires_at) > new Date())?.inviter?.slug;
234
+ const { data: repoRows } = await ctx.supabase.from("repos").select("slug").eq("workspace_id", ws).order("slug");
235
+ console.log(`Welcome to CodeBus.`);
236
+ if (invitedBy)
237
+ console.log(`You joined via: ${invitedBy}${mentor ? ` (your mentor for the first week — ask them anything: codebus question ask --to-mentor …)` : ""}`);
238
+ console.log(`Workspace: ${ctx.workspaceSlug}`);
239
+ if (repoRows?.length)
240
+ console.log(`Repositories: ${repoRows.map((r) => r.slug).join(", ")}`);
241
+ // Don't promise an onboarding question that may not exist: the welcome
242
+ // question is only minted on the invited-human path (0024), never for
243
+ // checkin-born agents — a newborn told to "answer your onboarding
244
+ // question" goes looking for something that isn't there.
245
+ console.log(`Next: review the current work below, then check your inbox.`);
246
+ // show what kind of work flows through CodeBus — not necessarily theirs to act on
247
+ const [{ data: oq }, { data: rt }, { data: dd }] = await Promise.all([
248
+ ctx.supabase.from("questions").select("display_id,title").eq("workspace_id", ws)
249
+ .eq("status", "open").not("title", "like", "Welcome %").limit(1),
250
+ ctx.supabase.from("tasks").select("display_id,title").eq("workspace_id", ws)
251
+ .eq("status", "review").limit(1),
252
+ ctx.supabase.from("decisions").select("display_id,title").eq("workspace_id", ws)
253
+ .eq("status", "accepted").order("created_at", { ascending: false }).limit(1),
254
+ ]);
255
+ const suggestions = [
256
+ oq?.[0] && `Open question: ${oq[0].display_id} — ${oq[0].title}`,
257
+ rt?.[0] && `Review request: ${rt[0].display_id} — ${rt[0].title}`,
258
+ dd?.[0] && `Recent decision: ${dd[0].display_id} — ${dd[0].title}`,
259
+ ].filter(Boolean);
260
+ if (suggestions.length) {
261
+ console.log(`Suggested work (to see what flows through here):`);
262
+ suggestions.forEach((x) => console.log(` ${x}`));
263
+ }
264
+ console.log(``);
265
+ }
266
+ console.log(`session: agent=${ctx.agentSlug} person=${person ?? "—"} repo=${git.repo ?? "—"} branch=${git.branch ?? "—"}`);
267
+ console.log(statuslineOf(snapshot));
268
+ if (top) {
269
+ console.log(`NEXT: ${top.display_id} — ${top.title}`);
270
+ console.log(`Reason: ${top.reason} · score ${top.score}`);
271
+ // Onboarding reliability (field incident: a fresh checkin into a large inbox
272
+ // read only this teaser — it never learned WHO to answer or HOW, so it
273
+ // neither acted nor replied). `top` is the top ACTIONABLE item, so when it's
274
+ // a message this is always a work order: one cheap best-effort lookup names
275
+ // the sender and gives the exact reply command. A lookup miss only drops the
276
+ // sender line, never the directive.
277
+ if (topIsMessage) {
278
+ if (nextFrom)
279
+ console.log(`From: ${nextFrom}${nextType ? ` · ${nextType}` : ""}`);
280
+ console.log(`→ Your move: this is a work order, not a notification. Read it ` +
281
+ `(codebus read ${top.display_id} --thread), do what it asks in THIS session, ` +
282
+ `then reply${nextFrom ? ` to ${nextFrom}` : ""} with the outcome — not a bare acknowledgement:`);
283
+ console.log(` codebus reply ${top.display_id} --body "<what you did / your answer>"`);
284
+ }
285
+ if (o.markSeen)
286
+ console.log("(marked seen)");
287
+ }
288
+ else if (fyiWaiting > 0) {
289
+ // Mail arrived but none of it is actionable — the same verdict `codebus next`
290
+ // gives, said honestly so FYI mail is neither hidden nor framed as owed work.
291
+ console.log(`NEXT: no actionable work — ${fyiWaiting} FYI item${fyiWaiting === 1 ? "" : "s"} waiting (plain note/plan). Skim: codebus inbox --unread · clear: codebus ack <id>`);
292
+ }
293
+ else {
294
+ console.log("NEXT: nothing pending — bus is clear.");
295
+ }
296
+ // Update nudge last, so a stale/linked CLI is the final thing the reader sees.
297
+ if (cli.message)
298
+ console.log(cli.message);
299
+ }
300
+ const LINK_TABLE = {
301
+ task: { table: "tasks", kind: "task", re: /^CB-TASK-/i },
302
+ pr: { table: "pull_requests", kind: "pr", re: /^CB-PR-/i },
303
+ question: { table: "questions", kind: "question", re: /^CB-Q-/i },
304
+ };
305
+ /**
306
+ * Shared core: end a session with optional handoff/note.
307
+ * Exported for reuse in MCP tool.
308
+ */
309
+ export async function endSessionCore(ctx, opts) {
310
+ const ws = await workspaceId(ctx);
311
+ const git = await gitContext();
312
+ const type = opts.type ?? "handoff";
313
+ if (!["handoff", "note"].includes(type))
314
+ throw new CodebusError(`--type must be handoff|note, got: ${type}`);
315
+ // Resolve linked entities up front so a typo fails before anything is posted.
316
+ const linked = [];
317
+ for (const [opt, cfg] of Object.entries(LINK_TABLE)) {
318
+ const raw = opts[opt];
319
+ if (!raw)
320
+ continue;
321
+ for (const display of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
322
+ if (!cfg.re.test(display))
323
+ throw new CodebusError(`--${opt} expects ${cfg.re.source.replace(/[\^\\]/g, "")}… ids, got: ${display}`);
324
+ // display ids are only unique per workspace — always scope the lookup
325
+ const { data } = await ctx.supabase.from(cfg.table).select("id")
326
+ .eq("workspace_id", ws).eq("display_id", display.toUpperCase()).single();
327
+ if (!data)
328
+ throw new CodebusError(`${opt} not found: ${display}`);
329
+ linked.push({ kind: cfg.kind, id: data.id, display: display.toUpperCase() });
330
+ }
331
+ }
332
+ const body = [
333
+ "## Session handoff",
334
+ `Summary: ${opts.summary ?? "—"}`,
335
+ `Repo/branch: ${git.repo ?? "—"}${git.branch ? `@${git.branch}` : ""}`,
336
+ `Changed files:${git.changed.length ? "\n" + git.changed.map((f) => ` ${f}`).join("\n") : " —"}`,
337
+ `Links: ${linked.length ? linked.map((l) => l.display).join(" ") : "—"}`,
338
+ `Open questions: ${opts.openQuestion ?? "—"}`,
339
+ `Caveats: ${opts.caveat ?? "—"}`,
340
+ `Next: ${opts.next ?? "—"}`,
341
+ ].join("\n");
342
+ const { data: msg, error } = await ctx.supabase.from("messages")
343
+ .insert({ workspace_id: ws, type, subject: `session end: ${ctx.agentSlug}`, body })
344
+ .select().single();
345
+ if (error)
346
+ throw new CodebusError(error.message);
347
+ if (opts.to) {
348
+ const slugs = opts.to.split(",").map((s) => s.trim()).filter(Boolean);
349
+ const { data: recips } = await ctx.supabase.from("agents").select("id,slug").in("slug", slugs);
350
+ const missing = slugs.filter((s) => !recips.some((r) => r.slug === s));
351
+ if (missing.length)
352
+ throw new CodebusError(`unknown recipient(s): ${missing.join(", ")}`);
353
+ await ctx.supabase.from("message_recipients")
354
+ .insert(recips.map((r) => ({ message_id: msg.id, agent_id: r.id })));
355
+ }
356
+ if (linked.length) {
357
+ const { error: lerr } = await ctx.supabase.from("links").insert(linked.map((l) => ({
358
+ workspace_id: ws, src_kind: "message", src_id: msg.id, rel: "relates_to",
359
+ dst_kind: l.kind, dst_id: l.id,
360
+ })));
361
+ if (lerr)
362
+ throw new CodebusError(lerr.message);
363
+ }
364
+ await ctx.supabase.from("session_log")
365
+ .insert({ workspace_id: ws, kind: "end", repo: git.repo, branch: git.branch })
366
+ .then(() => undefined, () => undefined);
367
+ await fetchAndCacheDigest(ctx); // statusline reflects post-handoff state
368
+ return {
369
+ display_id: msg.display_id,
370
+ type,
371
+ linked: linked.length ? linked.map((l) => l.display).join(",") : null,
372
+ id: msg.id,
373
+ };
374
+ }
375
+ async function endCmd(o) {
376
+ const ctx = await makeCtx(resolveConfig(o));
377
+ const result = await endSessionCore(ctx, {
378
+ summary: o.summary,
379
+ to: o.to,
380
+ type: o.type,
381
+ task: o.task,
382
+ pr: o.pr,
383
+ question: o.question,
384
+ openQuestion: o.openQuestion,
385
+ caveat: o.caveat,
386
+ next: o.next,
387
+ });
388
+ console.log(render([{
389
+ display_id: result.display_id, type: result.type, linked: result.linked,
390
+ }], { json: !!o.json }));
391
+ }
392
+ /**
393
+ * `codebus session forget` — the explicit escape hatch for durable env-keyed
394
+ * session entries (sessionstore.ts). Those entries outlive their process BY
395
+ * DESIGN (that's the whole fix for FM-1/root-cause §2.1 — mortal keys used to
396
+ * silently fall through to the directory tier on every restart) and are only
397
+ * ever pruned by age otherwise; this is how a human deliberately un-checks-in
398
+ * a session before that age-out.
399
+ */
400
+ function forgetCmd() {
401
+ const role = forgetSession();
402
+ if (!role) {
403
+ console.log("no session identity here to forget (this session was never checked in, or its entry already aged out).");
404
+ return;
405
+ }
406
+ console.log(`forgot this session's identity (was: ${role}) — it will resolve to a directory binding (if any) or need a fresh checkin.`);
407
+ }
408
+ export function registerSession(program, g) {
409
+ const s = program.command("session").description("session rituals (start/end)");
410
+ g(s.command("start"))
411
+ .option("--mark-seen", "mark the surfaced NEXT item as seen (explicit opt-in)")
412
+ .option("--hook", "SessionStart-hook mode: orient only when identity is explicit (CODEBUS_TOKEN or exact cwd binding), silent otherwise, never fail")
413
+ .action(startCmd);
414
+ g(s.command("end"))
415
+ .option("--summary <text>").option("--to <slugs>", "recipients (comma-separated)")
416
+ .option("--type <t>", "handoff|note", "handoff")
417
+ .option("--task <ids>", "CB-TASK-… (comma-separated)")
418
+ .option("--pr <ids>", "CB-PR-… (comma-separated)")
419
+ .option("--question <ids>", "CB-Q-… (comma-separated)")
420
+ .option("--open-question <text>").option("--caveat <text>").option("--next <text>")
421
+ .action(endCmd);
422
+ s.command("forget")
423
+ .description("remove THIS session's durable identity binding (env-keyed entries otherwise only age out — see sessionstore.ts)")
424
+ .action(forgetCmd);
425
+ }
426
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,aAAa,EAAqB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAO,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAkB,MAAM,aAAa,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjF,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEjC,4EAA4E;AAC5E,yEAAyE;AACzE,6DAA6D;AAC7D,KAAK,UAAU,UAAU;IACvB,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC;QACvE,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACnC,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QAC1E,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC,CAAC,4CAA4C;YAChD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;YACvE,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC,CAAC,iDAAiD,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;QAChE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC;IAClC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,GAAQ;IAC9B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ;SAChC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IACtF,OAAQ,IAAmD,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC;AACpF,CAAC;AAED,SAAS,YAAY,CAAC,IAAoB;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,QAAQ,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,OAAO,YAAY,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;AACjF,CAAC;AAED,iFAAiF;AACjF,yEAAyE;AACzE,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,UAAU,oBAAoB,CAAC,CAAY,EAAE,MAAyB,OAAO,CAAC,GAAG;IACrF,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,gBAAgB,IAAI,CAAC,YAAY,iBAAiB;YAAE,MAAM,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC,cAAc,KAAK,MAAM,IAAI,GAAG,CAAC,cAAc,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AACrF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,CAAqE;IAClG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,yEAAyE;QACzE,gEAAgE;QAChE,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,uEAAuE;QACvE,sEAAsE;QACtE,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,mEAAmE;QACnE,uCAAuC;QACvC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;YACpC,IAAI,CAAC,GAAG;gBAAE,OAAO;YACjB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;YACnC,MAAM,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;6EAG6E;AAC7E,KAAK,UAAU,QAAQ,CAAC,CAAqD,EAAE,GAAY;IACzF,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAChD,uEAAuE;IACvE,4EAA4E;IAC5E,6EAA6E;IAC7E,2EAA2E;IAC3E,gFAAgF;IAChF,MAAM,GAAG,GAAG,kBAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjF,6EAA6E;IAC7E,8EAA8E;IAC9E,gFAAgF;IAChF,gFAAgF;IAChF,4EAA4E;IAC5E,gFAAgF;IAChF,MAAM,YAAY,GAAG,OAAO,GAAG,EAAE,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAChG,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ;iBACnC,IAAI,CAAC,UAAU,CAAC;iBAChB,MAAM,CAAC,iDAAiD,CAAC;gBAC1D,yEAAyE;gBACzE,qEAAqE;gBACrE,qEAAqE;gBACrE,6BAA6B;iBAC5B,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;iBACtB,EAAE,CAAC,YAAY,EAAE,GAAG,CAAC,UAAU,CAAC;iBAChC,WAAW,EAAE,CAAC;YACjB,QAAQ,GAAI,CAAkD,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC;YACrF,QAAQ,GAAI,CAA8B,EAAE,IAAI,IAAI,IAAI,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;SACnC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;SAC/E,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAE1C,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,sEAAsE;IACtE,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,0EAA0E;QAC1E,wEAAwE;QACxE,oDAAoD;QACpD,MAAM,WAAW,GAAG,GAAG,IAAI,YAAY;YACrC,CAAC,CAAC;gBACE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ;gBAC9B,SAAS,EAAE,2HAA2H;gBACtI,SAAS,EAAE,iBAAiB,GAAG,CAAC,UAAU,wCAAwC;aACnF;YACH,CAAC,CAAC,IAAI,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YACzB,KAAK,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,aAAa;YAC1D,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM;YAC3D,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG;YACzD,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,GAAG,CAAC,OAAO;gBACrB,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;gBACjH,CAAC,CAAC,IAAI;SACT,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACb,OAAO;IACT,CAAC;IACD,6EAA6E;IAC7E,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;SAClE,MAAM,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAC3C,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;SAC1C,EAAE,CAAC,UAAU,EAAE,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACtH,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAG,4CAA4C;QAC3E,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;aACtD,MAAM,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;QACrE,MAAM,UAAU,GAAI,KAAa,EAAE,MAAM,EAAE,EAAE,CAAC;QAC9C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;aACvE,MAAM,CAAC,iDAAiD,CAAC;aACzD,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACvE,MAAM,SAAS,GAAI,IAAqB,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC;QAC9F,MAAM,MAAM,GAAI,IAAqB,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC;QAC9H,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChH,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,IAAI,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,2FAA2F,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvK,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;QAC/C,IAAI,QAAQ,EAAE,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7F,uEAAuE;QACvE,sEAAsE;QACtE,kEAAkE;QAClE,yDAAyD;QACzD,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,kFAAkF;QAClF,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;iBAC7E,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YAClE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;iBACzE,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YAClC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;iBAC7E,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;SAC/E,CAAC,CAAC;QACH,MAAM,WAAW,GAAG;YAClB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;YAClE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;YAClE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;SACnE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClB,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;YAChE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,CAAC,SAAS,WAAW,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC,IAAI,IAAI,GAAG,WAAW,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;IAC3H,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpC,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,UAAU,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1D,6EAA6E;QAC7E,uEAAuE;QACvE,6EAA6E;QAC7E,4EAA4E;QAC5E,6EAA6E;QAC7E,oCAAoC;QACpC,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,SAAS,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClF,OAAO,CAAC,GAAG,CACT,iEAAiE;gBACjE,iBAAiB,GAAG,CAAC,UAAU,+CAA+C;gBAC9E,aAAa,QAAQ,CAAC,CAAC,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,iDAAiD,CAChG,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,CAAC,UAAU,wCAAwC,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QAC1B,8EAA8E;QAC9E,8EAA8E;QAC9E,OAAO,CAAC,GAAG,CAAC,8BAA8B,UAAU,YAAY,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,sFAAsF,CAAC,CAAC;IACrL,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;IACvD,CAAC;IACD,+EAA+E;IAC/E,IAAI,GAAG,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC5C,CAAC;AAWD,MAAM,UAAU,GAAgE;IAC9E,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE;IACxD,EAAE,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE;IAC1D,QAAQ,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE;CAClE,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAQ,EACR,IAIC;IAED,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;IACpC,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;IAE7G,8EAA8E;IAC9E,MAAM,MAAM,GAAoD,EAAE,CAAC;IACnE,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACpD,MAAM,GAAG,GAAI,IAA2C,CAAC,GAAG,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1E,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,MAAM,IAAI,YAAY,CAAC,KAAK,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;YACpI,sEAAsE;YACtE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;iBAC7D,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;YAC3E,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,YAAY,CAAC,GAAG,GAAG,eAAe,OAAO,EAAE,CAAC,CAAC;YAClE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG;QACX,oBAAoB;QACpB,YAAY,IAAI,CAAC,OAAO,IAAI,GAAG,EAAE;QACjC,gBAAgB,GAAG,CAAC,IAAI,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;QACtE,iBAAiB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QACjG,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;QACxE,mBAAmB,IAAI,CAAC,YAAY,IAAI,GAAG,EAAE;QAC7C,YAAY,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE;QAChC,SAAS,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;SAC7D,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC;SAClF,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC;IACrB,IAAI,KAAK;QAAE,MAAM,IAAI,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEjD,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC/F,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACxE,IAAI,OAAO,CAAC,MAAM;YAAE,MAAM,IAAI,YAAY,CAAC,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1F,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,oBAAoB,CAAC;aAC1C,MAAM,CAAC,MAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,GAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjF,YAAY,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,GAAI,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY;YACzE,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE;SAC/B,CAAC,CAAC,CAAC,CAAC;QACL,IAAI,IAAI;YAAE,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;SACnC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;SAC7E,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAE1C,MAAM,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,yCAAyC;IAEzE,OAAO;QACL,UAAU,EAAE,GAAI,CAAC,UAAU;QAC3B,IAAI;QACJ,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;QACrE,EAAE,EAAE,GAAI,CAAC,EAAE;KACZ,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,CAAU;IAC9B,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE;QACvC,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;KACb,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAClB,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM;SACxE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,wGAAwG,CAAC,CAAC;QACtH,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,wCAAwC,IAAI,8EAA8E,CAAC,CAAC;AAC1I,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAgB,EAAE,CAA0B;IAC1E,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,6BAA6B,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAClB,MAAM,CAAC,aAAa,EAAE,uDAAuD,CAAC;SAC9E,MAAM,CAAC,QAAQ,EAAE,kIAAkI,CAAC;SACpJ,MAAM,CAAC,QAAQ,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAChB,MAAM,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,8BAA8B,CAAC;SACjF,MAAM,CAAC,YAAY,EAAE,cAAc,EAAE,SAAS,CAAC;SAC/C,MAAM,CAAC,cAAc,EAAE,6BAA6B,CAAC;SACrD,MAAM,CAAC,YAAY,EAAE,2BAA2B,CAAC;SACjD,MAAM,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;SACtD,MAAM,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC;SAClF,MAAM,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;SAChB,WAAW,CAAC,iHAAiH,CAAC;SAC9H,MAAM,CAAC,SAAS,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { readCachedDigest } from "./digest.js";
2
+ // Statusline renders frequently (Claude Code re-runs it constantly), so it
3
+ // NEVER touches the network: no makeCtx, no JWT exchange, no Supabase query.
4
+ // It reads ~/.codebus/digest.json and degrades gracefully when absent/stale.
5
+ // MESSAGE (0048, delivery gap #1; narrowed to actionable-only by 0061):
6
+ // unhandled ask/handoff mail (or any type with required_action set),
7
+ // class_rank 7 — the tail of the actual-work ladder, after UNCLAIMED, since
8
+ // ordinary mail is lower pressure than open work; a speak_now message still
9
+ // gets its own urgent surface via the "!! URGENT N" prefix below
10
+ // (speak_now_count), independent of this order.
11
+ // FYI (0061 — RC2): unhandled note/plan mail with no required_action, class_
12
+ // rank 8. Ranked last of all: nothing here is owed, it's purely informational
13
+ // — kept visible (so it isn't silently dropped) but never mixed into the
14
+ // same count as genuine work orders above it.
15
+ const ORDER = ["ASK", "REVIEW", "BLOCKER", "STALE", "DECISION", "UNCLAIMED", "MESSAGE", "FYI"];
16
+ /**
17
+ * Count urgent (speak_now) messages from the cached digest snapshot.
18
+ * Zero network — derived entirely from the in-memory snapshot.
19
+ */
20
+ export function countSpeakNow(snap) {
21
+ return snap.speak_now_count ?? 0;
22
+ }
23
+ export async function statuslineCmd(o) {
24
+ const snap = await readCachedDigest();
25
+ if (!snap) {
26
+ console.log("CodeBus: — (run: codebus digest refresh)");
27
+ return;
28
+ }
29
+ const ttlSec = parseInt(process.env.CODEBUS_STATUSLINE_TTL ?? "120", 10);
30
+ const ageSec = (Date.now() - Date.parse(snap.generated_at)) / 1000;
31
+ const stale = Number.isFinite(ageSec) ? ageSec > ttlSec : true;
32
+ const speakNowCount = countSpeakNow(snap);
33
+ if (o.json) {
34
+ console.log(JSON.stringify({ ...snap, stale, speak_now_count: speakNowCount }, null, 2));
35
+ return;
36
+ }
37
+ const parts = ORDER.filter((k) => snap.counts[k]).map((k) => `${snap.counts[k]} ${k}`);
38
+ const body = parts.length ? parts.join(" | ") : "clear";
39
+ // speak_now highlight: prefix with !! URGENT N to make it visually prominent.
40
+ const urgentPrefix = speakNowCount > 0 ? `!! URGENT ${speakNowCount} | ` : "";
41
+ console.log(`CodeBus: ${urgentPrefix}${body}${stale ? " (stale)" : ""}`);
42
+ }
43
+ //# sourceMappingURL=statusline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statusline.js","sourceRoot":"","sources":["../../src/commands/statusline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,aAAa,CAAC;AAE/D,2EAA2E;AAC3E,6EAA6E;AAC7E,6EAA6E;AAC7E,wEAAwE;AACxE,qEAAqE;AACrE,4EAA4E;AAC5E,4EAA4E;AAC5E,iEAAiE;AACjE,gDAAgD;AAChD,6EAA6E;AAC7E,8EAA8E;AAC9E,yEAAyE;AACzE,8CAA8C;AAC9C,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAE/F;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAoB;IAChD,OAAO,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,CAAqB;IACvD,MAAM,IAAI,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,KAAK,EAAE,EAAE,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,IAAI,CAAC;IACnE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/D,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAE1C,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACzF,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvF,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,8EAA8E;IAC9E,MAAM,YAAY,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,aAAa,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,OAAO,CAAC,GAAG,CAAC,YAAY,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { resolveConfig } from "../config.js";
2
+ import { makeCtx, workspaceId, projectId } from "../client.js";
3
+ import { render } from "../output.js";
4
+ import { CodebusError } from "../errors.js";
5
+ import { parseRef, writeRefs } from "./refs.js";
6
+ /**
7
+ * Resolve a task display ID (CB-TASK-…) or UUID to UUID.
8
+ * display ids are only unique per workspace (bus_counters, 0004) — always
9
+ * scope the lookup, or a multi-workspace agent can resolve the WRONG
10
+ * workspace's task when two workspaces happen to share a display_id.
11
+ */
12
+ async function resolveTaskId(ctx, idOrDisplay) {
13
+ if (/^CB-TASK-/i.test(idOrDisplay)) {
14
+ const ws = await workspaceId(ctx);
15
+ const { data, error } = await ctx.supabase.from("tasks").select("id")
16
+ .eq("workspace_id", ws).eq("display_id", idOrDisplay.toUpperCase()).single();
17
+ if (error || !data)
18
+ throw new CodebusError(`task not found: ${idOrDisplay}`);
19
+ return data.id;
20
+ }
21
+ return idOrDisplay;
22
+ }
23
+ /**
24
+ * Shared core: create a task.
25
+ * Exported for reuse in MCP tool.
26
+ */
27
+ export async function createTaskCore(ctx, opts) {
28
+ const ws = await workspaceId(ctx);
29
+ let ownerId = null;
30
+ if (opts.owner) {
31
+ const { data } = await ctx.supabase.from("agents").select("id").eq("slug", opts.owner).single();
32
+ if (!data)
33
+ throw new CodebusError(`unknown owner: ${opts.owner}`);
34
+ ownerId = data.id;
35
+ }
36
+ const projId = await projectId(ctx, ws, opts.project);
37
+ const { data: task, error } = await ctx.supabase.from("tasks")
38
+ .insert({ workspace_id: ws, project_id: projId, title: opts.title, body: opts.body ?? null, owner_agent_id: ownerId })
39
+ .select().single();
40
+ if (error)
41
+ throw new CodebusError(error.message);
42
+ // Write knowledge refs if provided
43
+ if (opts.refs && opts.refs.length > 0) {
44
+ const parsed = opts.refs.map((r) => parseRef(r));
45
+ await writeRefs(ctx, ws, "task", task.id, parsed);
46
+ }
47
+ return { display_id: task.display_id, title: task.title, status: task.status, id: task.id };
48
+ }
49
+ /**
50
+ * Shared core: claim a task.
51
+ * Exported for reuse in MCP tool.
52
+ */
53
+ export async function claimTaskCore(ctx, idOrDisplay) {
54
+ const id = await resolveTaskId(ctx, idOrDisplay);
55
+ const { data, error } = await ctx.supabase.rpc("claim_task", { p_task: id });
56
+ if (error)
57
+ throw new CodebusError(error.message);
58
+ const row = Array.isArray(data) ? data[0] : data;
59
+ return { display_id: row.display_id, status: row.status, done: row.done };
60
+ }
61
+ /**
62
+ * Shared core: mark a task done.
63
+ * Exported for reuse in MCP tool.
64
+ */
65
+ export async function completeTaskCore(ctx, idOrDisplay, opts = {}) {
66
+ const id = await resolveTaskId(ctx, idOrDisplay);
67
+ // Evidence-required done (plan §4 item 2): the RPC itself refuses bare
68
+ // completion (neither p_evidence nor p_no_evidence_reason), so the error
69
+ // surfaces from the server verbatim — no client-side pre-check duplicating
70
+ // that rule here.
71
+ const { data, error } = await ctx.supabase.rpc("complete_task", {
72
+ p_task: id,
73
+ p_evidence: opts.evidence ?? null,
74
+ p_note: opts.note ?? null,
75
+ p_no_evidence_reason: opts.noEvidenceReason ?? null,
76
+ });
77
+ if (error)
78
+ throw new CodebusError(error.message);
79
+ const row = Array.isArray(data) ? data[0] : data;
80
+ return { display_id: row.display_id, status: row.status, done: row.done };
81
+ }
82
+ async function createTask(o) {
83
+ const ctx = await makeCtx(resolveConfig(o));
84
+ const rawRefs = o.ref ? o.ref.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
85
+ const task = await createTaskCore(ctx, { title: o.title, body: o.body, owner: o.owner, project: o.project, refs: rawRefs });
86
+ console.log(render([{ display_id: task.display_id, title: task.title, status: task.status }], { json: !!o.json }));
87
+ }
88
+ async function listTasks(o) {
89
+ const ctx = await makeCtx(resolveConfig(o));
90
+ const ws = await workspaceId(ctx);
91
+ const projId = await projectId(ctx, ws, o.project);
92
+ let q = ctx.supabase.from("tasks").select("display_id, title, status, done, owner_agent_id");
93
+ if (o.status)
94
+ q = q.eq("status", o.status);
95
+ if (projId)
96
+ q = q.eq("project_id", projId);
97
+ const { data, error } = await q.order("created_at", { ascending: false });
98
+ if (error)
99
+ throw new CodebusError(error.message);
100
+ console.log(render(data ?? [], { json: !!o.json }));
101
+ }
102
+ async function runRpc(name, args, o, idOrDisplay) {
103
+ const ctx = await makeCtx(resolveConfig(o));
104
+ const id = await resolveTaskId(ctx, idOrDisplay);
105
+ const { data, error } = await ctx.supabase.rpc(name, { p_task: id, ...args });
106
+ if (error)
107
+ throw new CodebusError(error.message);
108
+ const row = Array.isArray(data) ? data[0] : data;
109
+ console.log(render([{ display_id: row.display_id, status: row.status, done: row.done }], { json: !!o.json }));
110
+ }
111
+ export function registerTask(program, g) {
112
+ const task = program.command("task").description("task operations");
113
+ g(task.command("create")).requiredOption("--title <t>").option("--body <b>").option("--owner <slug>").option("--project <slug>").option("--ref <refs>", "comma-separated knowledge refs (e.g. CB-DEC-000001,commit:<sha>)").action(createTask);
114
+ g(task.command("list")).option("--owner <slug>").option("--status <s>").option("--project <slug>").action(listTasks);
115
+ g(task.command("claim").argument("<id>")).action(async (id, o) => {
116
+ const ctx = await makeCtx(resolveConfig(o));
117
+ const result = await claimTaskCore(ctx, id);
118
+ console.log(render([result], { json: !!o.json }));
119
+ });
120
+ g(task.command("done").argument("<id>"))
121
+ .option("--evidence <ref>")
122
+ .option("--note <n>")
123
+ // NOT named --no-evidence-reason: commander treats any --no-* flag as a
124
+ // boolean negation of a same-named positive option and stores its value
125
+ // under the prefix-stripped property (`evidenceReason`, not
126
+ // `noEvidenceReason`) regardless of whether it takes a value — a subtle
127
+ // footgun that silently dropped this flag's value in an earlier draft.
128
+ // --escape-reason sidesteps that parser special-case entirely.
129
+ .option("--escape-reason <reason>", "explicit escape hatch when there is truly no evidence to attach (recorded, not silent)")
130
+ .action(async (id, o) => {
131
+ const ctx = await makeCtx(resolveConfig(o));
132
+ const result = await completeTaskCore(ctx, id, { evidence: o.evidence, note: o.note, noEvidenceReason: o.escapeReason });
133
+ console.log(render([result], { json: !!o.json }));
134
+ });
135
+ g(task.command("block").argument("<id>")).requiredOption("--note <n>").action((id, o) => runRpc("block_task", { p_note: o.note }, o, id));
136
+ g(task.command("review").argument("<id>")).option("--note <n>").action((id, o) => runRpc("review_task", { p_note: o.note ?? null }, o, id));
137
+ g(task.command("reopen").argument("<id>")).option("--note <n>").action((id, o) => runRpc("reopen_task", { p_note: o.note ?? null }, o, id));
138
+ g(task.command("cancel").argument("<id>")).option("--note <n>").action((id, o) => runRpc("cancel_task", { p_note: o.note ?? null }, o, id));
139
+ }
140
+ //# sourceMappingURL=task.js.map