@detergent-software/conductor 1.0.0-dev.1 → 1.0.0-dev.10

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 (68) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +175 -9
  3. package/build/cli.js +368 -21
  4. package/build/cli.js.map +1 -1
  5. package/build/clock.js +17 -0
  6. package/build/clock.js.map +1 -0
  7. package/build/conditions.js +5 -6
  8. package/build/conditions.js.map +1 -1
  9. package/build/config.js +92 -1
  10. package/build/config.js.map +1 -1
  11. package/build/credential.js +52 -0
  12. package/build/credential.js.map +1 -0
  13. package/build/daemon.js +56 -1
  14. package/build/daemon.js.map +1 -1
  15. package/build/events.js +106 -0
  16. package/build/events.js.map +1 -0
  17. package/build/inbox.js +385 -0
  18. package/build/inbox.js.map +1 -0
  19. package/build/manager.js +26 -3
  20. package/build/manager.js.map +1 -1
  21. package/build/mod.js +51 -0
  22. package/build/mod.js.map +1 -0
  23. package/build/note.js +71 -0
  24. package/build/note.js.map +1 -0
  25. package/build/notes.js +188 -0
  26. package/build/notes.js.map +1 -0
  27. package/build/notify.js +393 -0
  28. package/build/notify.js.map +1 -0
  29. package/build/paths.js +1 -0
  30. package/build/paths.js.map +1 -1
  31. package/build/profile.js +7 -0
  32. package/build/profile.js.map +1 -1
  33. package/build/pushover.js +67 -0
  34. package/build/pushover.js.map +1 -0
  35. package/build/registry.js +3 -1
  36. package/build/registry.js.map +1 -1
  37. package/build/rule-input.js +17 -0
  38. package/build/rule-input.js.map +1 -0
  39. package/build/rules-service.js +391 -0
  40. package/build/rules-service.js.map +1 -0
  41. package/build/rules.js +842 -0
  42. package/build/rules.js.map +1 -0
  43. package/build/server.js +140 -0
  44. package/build/server.js.map +1 -1
  45. package/build/spend.js +8 -0
  46. package/build/spend.js.map +1 -1
  47. package/build/startup.js +30 -0
  48. package/build/startup.js.map +1 -1
  49. package/build/store.js +196 -0
  50. package/build/store.js.map +1 -1
  51. package/build/token.js +3 -3
  52. package/build/token.js.map +1 -1
  53. package/build/upgrade.js +105 -0
  54. package/build/upgrade.js.map +1 -0
  55. package/build/wait.js +32 -32
  56. package/build/wait.js.map +1 -1
  57. package/build/watchers/linear-key.js +6 -45
  58. package/build/watchers/linear-key.js.map +1 -1
  59. package/build/watchers/linear.js +16 -7
  60. package/build/watchers/linear.js.map +1 -1
  61. package/mod/.claude-plugin/marketplace.json +6 -0
  62. package/mod/.claude-plugin/plugin.json +7 -0
  63. package/mod/README.md +17 -0
  64. package/mod/hooks/hooks.json +1 -0
  65. package/mod/hooks/model.ts +395 -0
  66. package/mod/hooks/register.tsx +505 -0
  67. package/mod/types/index.d.ts +57 -0
  68. package/package.json +6 -1
@@ -1,56 +1,17 @@
1
- import { run } from "../run.js";
1
+ import { readCredential } from "../credential.js";
2
2
  // The Linear API key the conductor reads with (ATK-272, ported from conduct-atk's linear-key.mjs):
3
3
  // LINEAR_API_KEY, else the Windows generic credential `atk-conductor-linear` (the password is the
4
- // key). Node can't read Credential Manager and `cmdkey` can't print a secret, so PowerShell calls
5
- // advapi32's CredRead. The key is only returned: never logged, stored or put on a command line.
4
+ // key). The key is only returned: never logged, stored or put on a command line.
6
5
  export const LINEAR_CREDENTIAL = 'atk-conductor-linear';
7
- const NOT_FOUND = 3;
8
- // Win32 ERROR_NOT_FOUND: CredRead's "no such credential". Any other failure is a broken read.
9
- const ERROR_NOT_FOUND = 1168;
10
- const CRED_READ = `
11
- $ErrorActionPreference = 'Stop'
12
- try {
13
- Add-Type -Namespace AtkConduct -Name Cred -MemberDefinition @'
14
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
15
- public static extern bool CredRead(string target, int type, int flags, out System.IntPtr cred);
16
- [DllImport("advapi32.dll")]
17
- public static extern void CredFree(System.IntPtr cred);
18
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
19
- public struct CREDENTIAL { public int Flags; public int Type; public string TargetName; public string Comment;
20
- public long LastWritten; public int CredentialBlobSize; public System.IntPtr CredentialBlob; public int Persist;
21
- public int AttributeCount; public System.IntPtr Attributes; public string TargetAlias; public string UserName; }
22
- '@
23
- $p = [IntPtr]::Zero
24
- if (-not [AtkConduct.Cred]::CredRead($env:ATK_CONDUCT_CRED_TARGET, 1, 0, [ref]$p)) {
25
- $code = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
26
- if ($code -eq ${ERROR_NOT_FOUND}) { exit ${NOT_FOUND} }
27
- [Console]::Error.WriteLine("CredRead failed with Win32 error $code"); exit 4
28
- }
29
- try {
30
- $c = [Runtime.InteropServices.Marshal]::PtrToStructure($p, [type][AtkConduct.Cred+CREDENTIAL])
31
- [Console]::Out.Write([Runtime.InteropServices.Marshal]::PtrToStringUni($c.CredentialBlob, $c.CredentialBlobSize / 2))
32
- } finally { [AtkConduct.Cred]::CredFree($p) }
33
- } catch { [Console]::Error.WriteLine($_.Exception.Message); exit 4 }
34
- `;
35
6
  // ATK_CONDUCT_LINEAR_CREDENTIAL names another credential target, as in conduct-atk.
36
7
  export async function readLinearKey(env = process.env) {
37
8
  const fromEnv = env.LINEAR_API_KEY?.trim();
38
9
  if (fromEnv)
39
10
  return { key: fromEnv, source: 'LINEAR_API_KEY' };
40
11
  const target = env.ATK_CONDUCT_LINEAR_CREDENTIAL?.trim() || LINEAR_CREDENTIAL;
41
- if (process.platform !== 'win32')
42
- return { key: null, reason: `no LINEAR_API_KEY (the ${target} credential is Windows-only)` };
43
- const result = await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(CRED_READ, 'utf16le').toString('base64')], { env: { ...env, ATK_CONDUCT_CRED_TARGET: target }, timeoutMs: 30_000 });
44
- const key = result.code === 0 ? result.stdout.trim() : '';
45
- if (key)
46
- return { key, source: `credential ${target}` };
47
- if (result.code === NOT_FOUND)
48
- return { key: null, reason: `no LINEAR_API_KEY and no credential ${target}` };
49
- // Never echo stdout here: on a partial failure it could hold the key.
50
- const line = result.stderr
51
- .split(/\r?\n/)
52
- .map((l) => l.trim())
53
- .find((l) => l && !l.startsWith('#< CLIXML') && !l.startsWith('<Objs'));
54
- return { key: null, reason: `no LINEAR_API_KEY, and the credential ${target} couldn't be read (${result.error ?? line ?? `exit ${result.code}`})` };
12
+ const read = await readCredential(target, env);
13
+ if (read.found)
14
+ return { key: read.credential.secret, source: `credential ${target}` };
15
+ return { key: null, reason: `no LINEAR_API_KEY, and ${read.reason}` };
55
16
  }
56
17
  //# sourceMappingURL=linear-key.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"linear-key.js","sourceRoot":"","sources":["../../src/watchers/linear-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,mGAAmG;AACnG,kGAAkG;AAClG,kGAAkG;AAClG,gGAAgG;AAChG,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AAIxD,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,8FAA8F;AAC9F,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;kBAgBA,eAAe,YAAY,SAAS;;;;;;;;CAQrD,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,MAAM,OAAO,GAAG,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,IAAI,iBAAiB,CAAC;IAC9E,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,0BAA0B,MAAM,8BAA8B,EAAE,CAAC;IAC/H,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,gBAAgB,EAChB,CAAC,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAC1G,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CACxE,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,IAAI,GAAG;QAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,MAAM,EAAE,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,uCAAuC,MAAM,EAAE,EAAE,CAAC;IAC7G,sEAAsE;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM;SACvB,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1E,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,yCAAyC,MAAM,sBAAsB,MAAM,CAAC,KAAK,IAAI,IAAI,IAAI,QAAQ,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;AACtJ,CAAC"}
1
+ {"version":3,"file":"linear-key.js","sourceRoot":"","sources":["../../src/watchers/linear-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,mGAAmG;AACnG,kGAAkG;AAClG,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AAIxD,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,MAAM,OAAO,GAAG,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,IAAI,iBAAiB,CAAC;IAC9E,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,MAAM,EAAE,EAAE,CAAC;IACvF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,0BAA0B,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACxE,CAAC"}
@@ -198,21 +198,30 @@ export function linearSource({ fetch: fetcher = globalThis.fetch, key = readLine
198
198
  name: 'linear',
199
199
  };
200
200
  }
201
- // The phase of a `## <skill> · <Phase>[ · <kind>: …]` note's heading line, or null. "Plan approved
202
- // (via …)" reads as "Plan approved".
203
- export function phaseOf(body, notePrefix) {
201
+ // A `## <skill> · <Phase>[ · <kind>: <one line>]` note's heading, or null when the body doesn't
202
+ // start with one. "Plan approved (via …)" reads as "Plan approved".
203
+ export function noteHeading(body, notePrefix) {
204
204
  const first = body.split(/\r?\n/).find((line) => line.trim() !== '') ?? '';
205
205
  const heading = first.replace(/^[#*\s]+/, '').replace(/[*\s]+$/, '');
206
206
  if (!heading.startsWith(notePrefix))
207
207
  return null;
208
208
  const rest = heading.slice(notePrefix.length);
209
209
  const split = /^([^(·]+?) · (.+)$/.exec(rest);
210
- return (split?.[1] ?? rest.replace(/\s*\(.*\)$/, '')).trim() || null;
210
+ const phase = (split?.[1] ?? rest.replace(/\s*\(.*\)$/, '')).trim();
211
+ if (!phase)
212
+ return null;
213
+ const kind = split?.[2] === undefined ? null : /^([a-z][a-z-]*):\s*(.*)$/.exec(split[2].trim());
214
+ return { kind: kind?.[1] ?? null, line: kind ? kind[2]?.trim() || null : null, phase };
211
215
  }
212
- function itemNumber(item) {
213
- return Number(item.split('-')[1]);
216
+ // The phase of a note's heading, or null.
217
+ export function phaseOf(body, notePrefix) {
218
+ return noteHeading(body, notePrefix)?.phase ?? null;
214
219
  }
215
- function redact(text, secret) {
220
+ // The text with every copy of the key replaced, for errors that quote what Linear or fetch said.
221
+ export function redact(text, secret) {
216
222
  return text.split(secret).join('lin_api_…');
217
223
  }
224
+ function itemNumber(item) {
225
+ return Number(item.split('-')[1]);
226
+ }
218
227
  //# sourceMappingURL=linear.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"linear.js","sourceRoot":"","sources":["../../src/watchers/linear.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"linear.js","sourceRoot":"","sources":["../../src/watchers/linear.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "conductor",
3
+ "description": "The conductor mod, shipped inside @detergent-software/conductor and installed by `conduct install-mod`.",
4
+ "owner": { "name": "Emergent Software" },
5
+ "plugins": [{ "name": "conductor", "source": "./" }]
6
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "conductor",
3
+ "version": "1.0.0",
4
+ "description": "The conductor console's plumbing: a status line in every session, and in the console the inbox band, the fleet pane, zero-token commands, Remote Control pushes and escalation wake-ups.",
5
+ "author": { "name": "Emergent Software" },
6
+ "types": "./types/index.d.ts"
7
+ }
package/mod/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Conductor mod
2
+
3
+ A Claude Code plugin (a hooks module) that does the conductor console's plumbing in code, so it costs no tokens: a status line in every session and, in the console (`CONDUCTOR_CONSOLE=1`), the inbox band, the fleet pane, `/conductor` `/inbox` `/approve` `/revise` `/attach` `/notify`, toasts, Remote Control pushes (and the one warning when Remote Control is off), and the one model wake-up for an escalation. What it shows and does is in the conductor README's *Mod* section (`apps/conductor/README.md`).
4
+
5
+ Install it with the daemon's CLI, which ships this folder:
6
+
7
+ ```text
8
+ conduct install-mod
9
+ ```
10
+
11
+ | File | Holds |
12
+ |---|---|
13
+ | `hooks/register.tsx` | the hooks: session start, commands, the band, the pane, the event stream; only its top-level functions take `$` |
14
+ | `hooks/model.ts` | the logic without the engine: parsing the daemon's answers, what the status line, band and pane show, what each event asks for |
15
+ | `types/index.d.ts` | the `$.state` contract (`conductor.snapshot`, `confirming`, `expanded`) |
16
+ | `tests/conductor.test.tsx` | `claude plugin test`: the hooks against the engine, the daemon faked beneath them |
17
+ | `tests/model.spec.ts` | vitest (`pnpm -C apps/conductor test`, in CI): `model.ts` |
@@ -0,0 +1 @@
1
+ { "modules": ["./register.tsx"] }
@@ -0,0 +1,395 @@
1
+ // The conductor mod's logic, with nothing of the engine in it: what the daemon's answers mean, what
2
+ // the status line, band and pane show, and what each event asks the mod to do. register.tsx does
3
+ // the engine's part (only its top-level functions may take `$`); vitest tests this file in CI
4
+ // (apps/conductor/mod/tests/model.spec.ts).
5
+ import type { ConductorInboxItem, ConductorNotify, ConductorSnapshot, ConductorWorker } from '../types';
6
+
7
+ // A daemon API answer: its status and parsed JSON body.
8
+ export type ApiAnswer = { body: unknown; status: number };
9
+
10
+ // One line of `conduct events --follow`: an event of the log (with its id), or the stream's state.
11
+ export type StreamLine = { data: Record<string, unknown>; id: number | null; kind: string; subject: string | null };
12
+
13
+ // What an event asks of the mod. Only `escalate` wakes the console's model.
14
+ export type Reaction =
15
+ | { kind: 'disconnected' }
16
+ | { kind: 'escalate'; text: string }
17
+ | { kind: 'ignore' }
18
+ | { kind: 'push'; message: string }
19
+ | { kind: 'refresh'; toast: string | null }
20
+ | { kind: 'send'; text: string; to: string };
21
+
22
+ // What a Remote Control push came to: reached the phone (ok); not, as Remote Control is off
23
+ // (inactive) or pushes are off in Claude Code's config (disabled); not sent to the phone on
24
+ // purpose (skipped: Jason is at the terminal, or the answer says nothing about the phone); or the
25
+ // call failed (failed: it threw, was denied, or answered an error).
26
+ export type PushOutcome = { detail: string; state: 'disabled' | 'failed' | 'inactive' | 'ok' | 'skipped' };
27
+
28
+ // Said once in the console when pushes stop reaching the phone (ATK-255), by why.
29
+ export function pushWarning(outcome: PushOutcome): null | string {
30
+ switch (outcome.state) {
31
+ case 'disabled':
32
+ return "conductor: mobile pushes are off in Claude Code's config, so pushes reach this terminal only. Turn them on, or /notify away for Pushover.";
33
+ case 'failed':
34
+ return `conductor: a Remote Control push failed (${outcome.detail}). /notify away sends by Pushover instead.`;
35
+ case 'inactive':
36
+ return 'conductor: Remote Control is off, so pushes reach this terminal only. Connect Remote Control, or /notify away for Pushover.';
37
+ case 'ok':
38
+ case 'skipped':
39
+ return null;
40
+ }
41
+ }
42
+
43
+ // The state the daemon records for a push's outcome (POST /notify/report).
44
+ export function reportedState(outcome: PushOutcome): 'failing' | 'inactive' | 'ok' | 'skipped' {
45
+ if (outcome.state === 'disabled') return 'inactive';
46
+ return outcome.state === 'failed' ? 'failing' : outcome.state;
47
+ }
48
+
49
+ // A band entry and the act its press does.
50
+ export type BandEntry = { act: 'approve' | 'copy' | 'pane'; hotkey: string; id: number; label: string; text: string };
51
+
52
+ export type ProjectGroup = { project: string; rows: ConductorWorker[]; running: number };
53
+
54
+ // A lookup by the item key, or an inbox id written #12; a message when it doesn't resolve to one.
55
+ export type Found<T> = { error: string; found?: undefined } | { error?: undefined; found: T };
56
+
57
+ const BAND_LIMIT = 5;
58
+
59
+ // `conductor 3▶ 2⏸ ✉2`, or undefined (the line cleared) when no daemon answers.
60
+ export function statusText(snapshot: ConductorSnapshot | null): string | undefined {
61
+ if (snapshot === null || snapshot.error !== null) return undefined;
62
+ const running = snapshot.workers.filter((w) => w.status === 'running').length;
63
+ const parked = snapshot.workers.filter((w) => w.status === 'parked').length;
64
+ return `conductor ${running}▶ ${parked}⏸ ✉${snapshot.inbox.length}`;
65
+ }
66
+
67
+ // The band's buttons: a plan approves (behind a confirm), an npm OTP copies its command, the rest
68
+ // open the pane, where each item's commands and links are.
69
+ export function bandEntries(inbox: readonly ConductorInboxItem[]): BandEntry[] {
70
+ return inbox.slice(0, BAND_LIMIT).map((item, i) => {
71
+ const key = item.item ?? `#${item.id}`;
72
+ const hotkey = String(i + 1);
73
+ if (item.kind === 'plan' && item.item !== null) return { act: 'approve', hotkey, id: item.id, label: `approve ${key}`, text: item.text };
74
+ const command = item.commands[0];
75
+ if (item.kind === 'npm-otp' && command !== undefined) return { act: 'copy', hotkey, id: item.id, label: `copy OTP line ${key}`, text: command };
76
+ return { act: 'pane', hotkey, id: item.id, label: `${item.kind} ${key}`, text: item.text };
77
+ });
78
+ }
79
+
80
+ // The fleet by project, running workers first within each, projects by name.
81
+ export function projectGroups(workers: readonly ConductorWorker[]): ProjectGroup[] {
82
+ const byProject = new Map<string, ConductorWorker[]>();
83
+ for (const w of workers) byProject.set(w.project, [...(byProject.get(w.project) ?? []), w]);
84
+ return [...byProject.entries()]
85
+ .sort(([a], [b]) => a.localeCompare(b))
86
+ .map(([project, rows]) => ({
87
+ project,
88
+ rows: [...rows].sort((a, b) => Number(b.status === 'running') - Number(a.status === 'running') || a.name.localeCompare(b.name)),
89
+ running: rows.filter((w) => w.status === 'running').length,
90
+ }));
91
+ }
92
+
93
+ // A worker row's glyph: ▶ running, ⏸ parked, ✖ failed or stopped, · otherwise (launching, exited).
94
+ export function glyph(status: string): string {
95
+ if (status === 'running') return '▶';
96
+ if (status === 'parked') return '⏸';
97
+ if (status === 'failed' || status === 'stopped-unexpectedly') return '✖';
98
+ return '·';
99
+ }
100
+
101
+ // What a worker waits on or does: its open wait, else the session's activity.
102
+ export function workerDoing(w: ConductorWorker): string {
103
+ return w.waits[0] ?? w.activity;
104
+ }
105
+
106
+ // The open plan of `ref` (as `conduct approve` finds it): an item key, or an inbox id as #12.
107
+ export function findPlan(inbox: readonly ConductorInboxItem[], ref: string): Found<ConductorInboxItem> {
108
+ const byId = /^#(\d+)$/.exec(ref.trim());
109
+ if (byId) {
110
+ const item = inbox.find((i) => i.id === Number(byId[1]));
111
+ return item ? { found: item } : { error: `no open inbox item ${ref}` };
112
+ }
113
+ if (/^\d+$/.test(ref.trim())) return { error: `"${ref}" is ambiguous: name the item, such as ATK-${ref.trim()}, or the inbox id as #${ref.trim()}` };
114
+ const plans = inbox.filter((i) => i.kind === 'plan' && i.item?.toUpperCase() === ref.trim().toUpperCase());
115
+ if (plans.length === 1 && plans[0]) return { found: plans[0] };
116
+ if (plans.length === 0) return { error: `no plan of ${ref} waits in the inbox` };
117
+ return { error: `several plans of ${ref} wait: name one as ${plans.map((p) => `#${p.id}`).join(' or ')}` };
118
+ }
119
+
120
+ // A worker in flight by name or item; a name match wins, as `conduct attach` finds it.
121
+ export function findWorker(workers: readonly ConductorWorker[], ref: string): Found<ConductorWorker> {
122
+ const byName = workers.filter((w) => w.name === ref);
123
+ const matches = byName.length > 0 ? byName : workers.filter((w) => w.item.toUpperCase() === ref.toUpperCase());
124
+ if (matches.length === 1 && matches[0]) return { found: matches[0] };
125
+ if (matches.length === 0) return { error: `no worker "${ref}" in flight` };
126
+ return { error: `"${ref}" names several workers: ${matches.map((w) => `${w.name} in ${w.project}`).join(', ')}` };
127
+ }
128
+
129
+ // `/inbox`'s answer: one line per item, then its commands and links.
130
+ export function inboxText(inbox: readonly ConductorInboxItem[], now: number): string {
131
+ if (inbox.length === 0) return 'The inbox is empty.';
132
+ return inbox
133
+ .flatMap((i) => [
134
+ `#${i.id} ${i.kind} ${i.item ?? ''}${i.project ? ` (${i.project})` : ''} ${age(i.raisedAt, now)}`,
135
+ ` ${i.text}`,
136
+ ...i.commands.map((c) => ` $ ${c}`),
137
+ ...i.links.map((l) => ` ${l}`),
138
+ ])
139
+ .join('\n');
140
+ }
141
+
142
+ // "now", "12m", "3h", "2d".
143
+ export function age(iso: string, now: number): string {
144
+ const minutes = Math.floor((now - Date.parse(iso)) / 60_000);
145
+ if (!(minutes >= 1)) return 'now';
146
+ if (minutes < 60) return `${minutes}m`;
147
+ if (minutes < 24 * 60) return `${Math.floor(minutes / 60)}h`;
148
+ return `${Math.floor(minutes / (24 * 60))}d`;
149
+ }
150
+
151
+ // The time left until `iso`: "12m", "3h", "2d"; "<1m" when it is (almost) up.
152
+ export function remaining(iso: string, now: number): string {
153
+ const minutes = Math.floor((Date.parse(iso) - now) / 60_000);
154
+ if (!(minutes >= 1)) return '<1m';
155
+ if (minutes < 60) return `${minutes}m`;
156
+ if (minutes < 24 * 60) return `${Math.floor(minutes / 60)}h`;
157
+ return `${Math.floor(minutes / (24 * 60))}d`;
158
+ }
159
+
160
+ // 950, 42k, 6.1M.
161
+ export function tokens(n: number): string {
162
+ if (n < 1000) return String(n);
163
+ if (n < 1_000_000) return `${Math.round(n / 1000)}k`;
164
+ return `${(n / 1_000_000).toFixed(1)}M`;
165
+ }
166
+
167
+ export function spendToday(workers: readonly ConductorWorker[]): number {
168
+ return workers.reduce((sum, w) => sum + w.spendToday, 0);
169
+ }
170
+
171
+ // One line of the follow stream, or null for anything that isn't an event object.
172
+ export function parseLine(line: string): null | StreamLine {
173
+ let value: unknown;
174
+ try {
175
+ value = JSON.parse(line);
176
+ } catch {
177
+ return null;
178
+ }
179
+ if (!isRecord(value) || typeof value.kind !== 'string') return null;
180
+ return {
181
+ data: isRecord(value.data) ? value.data : {},
182
+ id: typeof value.id === 'number' ? value.id : null,
183
+ kind: value.kind,
184
+ subject: typeof value.subject === 'string' ? value.subject : null,
185
+ };
186
+ }
187
+
188
+ // What an event asks of the mod. Remote Control pushes, escalations and worker messages are
189
+ // contracts the daemon's notifier (ATK-313) and rules (ATK-315) emit; the mod only carries them out.
190
+ export function reaction(line: StreamLine): Reaction {
191
+ const text = (key: string): null | string => {
192
+ const value = line.data[key];
193
+ return typeof value === 'string' && value.trim() !== '' ? value : null;
194
+ };
195
+ switch (line.kind) {
196
+ case 'conduct.connected':
197
+ return { kind: 'refresh', toast: null };
198
+ case 'conduct.disconnected':
199
+ return { kind: 'disconnected' };
200
+ case 'console.escalated': {
201
+ const ask = text('text');
202
+ return ask === null ? { kind: 'ignore' } : { kind: 'escalate', text: `[conductor] escalation${line.subject ? ` on ${line.subject}` : ''}: ${ask}` };
203
+ }
204
+ case 'inbox.raised': {
205
+ const ask = text('text');
206
+ return { kind: 'refresh', toast: ask === null ? null : `conductor: ${line.subject ? `${line.subject} · ` : ''}${ask}` };
207
+ }
208
+ case 'notify.failed':
209
+ case 'notify.routed':
210
+ case 'notify.sent':
211
+ // Nothing the mod draws: only notify.profile and notify.channel change the pane.
212
+ return { kind: 'ignore' };
213
+ case 'notify.requested': {
214
+ const message = text('message');
215
+ return line.data.channel === 'remote-control' && message !== null ? { kind: 'push', message } : { kind: 'ignore' };
216
+ }
217
+ case 'watcher.stats':
218
+ return { kind: 'ignore' };
219
+ case 'worker.message-requested': {
220
+ const to = text('to');
221
+ const message = text('message');
222
+ return to === null || message === null ? { kind: 'ignore' } : { kind: 'send', text: message, to };
223
+ }
224
+ default:
225
+ return { kind: 'refresh', toast: null };
226
+ }
227
+ }
228
+
229
+ // GET /fleet's body, kept to the fields the mod draws; null when it isn't one.
230
+ export function parseFleet(body: unknown): ConductorWorker[] | null {
231
+ if (!isRecord(body) || !Array.isArray(body.workers)) return null;
232
+ return body.workers.flatMap((w): ConductorWorker[] => {
233
+ if (!isRecord(w) || typeof w.id !== 'number' || typeof w.name !== 'string' || typeof w.status !== 'string') return [];
234
+ const spend = isRecord(w.spend) && typeof w.spend.today === 'number' ? w.spend.today : 0;
235
+ const waits = Array.isArray(w.waits)
236
+ ? w.waits.flatMap((x) => (isRecord(x) && typeof x.condition === 'string' && x.resolvedAt === null ? [x.condition] : []))
237
+ : [];
238
+ return [
239
+ {
240
+ activity: typeof w.activity === 'string' ? w.activity : w.status,
241
+ id: w.id,
242
+ item: typeof w.item === 'string' ? w.item : w.name,
243
+ name: w.name,
244
+ phase: typeof w.phase === 'string' ? w.phase : null,
245
+ project: typeof w.project === 'string' ? w.project : '?',
246
+ spendToday: spend,
247
+ status: w.status,
248
+ updatedAt: typeof w.updatedAt === 'string' ? w.updatedAt : '',
249
+ waits,
250
+ },
251
+ ];
252
+ });
253
+ }
254
+
255
+ // GET /inbox's body; null when it isn't one.
256
+ export function parseInbox(body: unknown): ConductorInboxItem[] | null {
257
+ if (!isRecord(body) || !Array.isArray(body.items)) return null;
258
+ return body.items.flatMap((i): ConductorInboxItem[] => {
259
+ if (!isRecord(i) || typeof i.id !== 'number' || typeof i.kind !== 'string' || typeof i.text !== 'string') return [];
260
+ return [
261
+ {
262
+ commands: strings(i.commands),
263
+ id: i.id,
264
+ item: typeof i.item === 'string' ? i.item : null,
265
+ kind: i.kind,
266
+ links: strings(i.links),
267
+ project: typeof i.project === 'string' ? i.project : null,
268
+ raisedAt: typeof i.raisedAt === 'string' ? i.raisedAt : '',
269
+ text: i.text,
270
+ },
271
+ ];
272
+ });
273
+ }
274
+
275
+ // The error an API answer carries (`{ error, details }`), as one line.
276
+ export function errorOf(answer: ApiAnswer): string {
277
+ const { body } = answer;
278
+ if (isRecord(body) && typeof body.error === 'string') return [body.error, ...strings(body.details)].join('; ');
279
+ return `the conductor answered ${answer.status}`;
280
+ }
281
+
282
+ // POST /inbox/:id/resolve's answer as the line to show, as `conduct approve|revise|done` says it.
283
+ export function answerText(action: 'approve' | 'done' | 'revise', id: number, answer: ApiAnswer): string {
284
+ if (answer.status !== 200) return `not ${action === 'done' ? 'done' : `${action}d`}: ${errorOf(answer)}`;
285
+ const body = isRecord(answer.body) ? answer.body : {};
286
+ const key = isRecord(body.item) && typeof body.item.item === 'string' ? body.item.item : `#${id}`;
287
+ if (action === 'done') return `done: ${key}`;
288
+ const comment = isRecord(body.comment) ? body.comment : {};
289
+ const where = typeof comment.url === 'string' ? comment.url : typeof comment.id === 'string' ? comment.id : 'comment posted';
290
+ return `${action === 'approve' ? 'approved' : 'asked for a revision of'} ${key}: ${where}`;
291
+ }
292
+
293
+ // The daemon's home as the CLI finds it (apps/conductor/src/paths.ts): CONDUCTOR_HOME, else
294
+ // .conductor under USERPROFILE or HOME.
295
+ export function homeOf(env: { conductorHome?: string; home?: string; userProfile?: string }): null | string {
296
+ if (env.conductorHome) return env.conductorHome;
297
+ const user = env.userProfile || env.home;
298
+ return user ? `${user.replace(/[\\/]+$/, '')}/.conductor` : null;
299
+ }
300
+
301
+ // The port and token files' text as an endpoint; null unless both are whole.
302
+ export function endpointOf(portText: string, tokenText: string): { port: number; token: string } | null {
303
+ const port = Number(portText.trim());
304
+ const token = tokenText.trim();
305
+ return Number.isInteger(port) && port > 0 && /^[0-9a-f]{64}$/.test(token) ? { port, token } : null;
306
+ }
307
+
308
+ // GET /fleet, GET /inbox and GET /notify as one snapshot; `error` set when the daemon didn't answer
309
+ // the first two. A daemon older than the notifier answers /notify 404, which leaves `notify` null.
310
+ export function snapshotOf(fleet: ApiAnswer, inbox: ApiAnswer, readAt: number, notify?: ApiAnswer): ConductorSnapshot {
311
+ const workers = fleet.status === 200 ? parseFleet(fleet.body) : null;
312
+ if (workers === null) return { error: errorOf(fleet), inbox: [], notify: null, readAt, workers: [] };
313
+ const items = inbox.status === 200 ? parseInbox(inbox.body) : null;
314
+ if (items === null) return { error: errorOf(inbox), inbox: [], notify: null, readAt, workers: [] };
315
+ return { error: null, inbox: items, notify: notify?.status === 200 ? parseNotify(notify.body) : null, readAt, workers };
316
+ }
317
+
318
+ // GET /notify's body, kept to what the mod draws; null when it isn't one.
319
+ export function parseNotify(body: unknown): ConductorNotify | null {
320
+ if (!isRecord(body) || typeof body.profile !== 'string') return null;
321
+ const channels = isRecord(body.channels) ? body.channels : {};
322
+ const remote = isRecord(channels['remote-control']) ? channels['remote-control'] : {};
323
+ return {
324
+ held: typeof body.held === 'number' ? body.held : 0,
325
+ profile: body.profile,
326
+ profiles: strings(body.profiles),
327
+ profileUntil: typeof body.profileUntil === 'string' ? body.profileUntil : null,
328
+ quietNow: body.quietNow === true,
329
+ remoteControl: typeof remote.state === 'string' ? remote.state : 'unknown',
330
+ };
331
+ }
332
+
333
+ // What `$.tool.call` of PushNotification answered: `{ result: { pushSent, disabledReason }, text }`.
334
+ // Only a push that reached the phone is ok; Remote Control off (no_transport, or the text saying
335
+ // so) is inactive; pushes turned off in the config are disabled; the rest say nothing of the phone.
336
+ export function pushOutcome(answer: unknown): PushOutcome {
337
+ const call = isRecord(answer) ? answer : {};
338
+ if (typeof call.deny === 'string') return { detail: `refused: ${call.deny}`, state: 'failed' };
339
+ const result = isRecord(call.result) ? call.result : {};
340
+ const detail = typeof call.text === 'string' && call.text.trim() !== '' ? call.text.trim() : typeof result.message === 'string' ? result.message : '';
341
+ if (call.isError === true) return { detail: detail || 'PushNotification answered an error', state: 'failed' };
342
+ if (result.pushSent === true) return { detail, state: 'ok' };
343
+ if (result.disabledReason === 'config_off') return { detail: "Mobile push not sent (off in Claude Code's config)", state: 'disabled' };
344
+ if (result.disabledReason === 'no_transport' || /remote control inactive/i.test(detail)) {
345
+ return { detail: detail || 'Mobile push not sent (Remote Control inactive)', state: 'inactive' };
346
+ }
347
+ if (result.disabledReason === 'user_present') return { detail: 'Mobile push not sent: you were at the terminal (user_present)', state: 'skipped' };
348
+ return { detail: detail || 'PushNotification said nothing about the phone', state: 'skipped' };
349
+ }
350
+
351
+ // `/notify <profile> [--until <when>]`: --until is a local time (the next one), a date (local
352
+ // midnight) or a date and time, as `conduct notify profile` reads it.
353
+ export function notifyArgs(args: string, now: number): { error: string } | { name: string; until: null | string } {
354
+ const typed = /^(\S+)(?:\s+--until\s+(\S+))?$/.exec(args.trim());
355
+ if (!typed?.[1]) return { error: 'usage: /notify [<profile> [--until <when>]]' };
356
+ const when = typed[2];
357
+ if (when === undefined) return { name: typed[1], until: null };
358
+ const clock = /^([01]?\d|2[0-3]):([0-5]\d)$/.exec(when);
359
+ let at: Date;
360
+ if (clock) {
361
+ at = new Date(now);
362
+ at.setHours(Number(clock[1]), Number(clock[2]), 0, 0);
363
+ if (at.getTime() <= now) at.setDate(at.getDate() + 1);
364
+ } else {
365
+ at = new Date(/^\d{4}-\d{2}-\d{2}$/.test(when) ? `${when}T00:00` : when);
366
+ }
367
+ if (Number.isNaN(at.getTime())) return { error: `--until "${when}" is not a time: a local time such as 17:00, or a date and time such as 2026-10-12T09:00` };
368
+ return { name: typed[1], until: at.toISOString() };
369
+ }
370
+
371
+ // `/notify` with no profile: the profile, quiet hours, Remote Control.
372
+ export function notifyText(notify: ConductorNotify | null, now: number): string {
373
+ if (notify === null) return 'the running conductor has no notifier: restart it on this build (conduct upgrade)';
374
+ const until = notify.profileUntil === null ? '' : ` for ${remaining(notify.profileUntil, now)} more`;
375
+ return [
376
+ `notify profile: ${notify.profile}${until} (profiles: ${notify.profiles.join(', ')}; /notify <profile> switches)`,
377
+ `Remote Control: ${notify.remoteControl}${notify.quietNow ? ` · quiet hours now, ${notify.held} held for the summary` : ''}`,
378
+ ].join('\n');
379
+ }
380
+
381
+ // POST /notify/profile's answer as the line to show.
382
+ export function profileText(name: string, answer: ApiAnswer, now: number): string {
383
+ if (answer.status !== 200) return `notify profile not switched: ${errorOf(answer)}`;
384
+ const notify = parseNotify(answer.body);
385
+ const until = notify?.profileUntil ? ` for ${remaining(notify.profileUntil, now)}` : '';
386
+ return `notify profile: ${notify?.profile ?? name}${until}`;
387
+ }
388
+
389
+ function isRecord(value: unknown): value is Record<string, unknown> {
390
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
391
+ }
392
+
393
+ function strings(value: unknown): string[] {
394
+ return Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string') : [];
395
+ }