@detergent-software/conductor 1.0.0-dev.11 → 1.0.0-dev.12
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.
- package/CHANGELOG.md +6 -0
- package/README.md +26 -7
- package/build/cli.js +2 -1
- package/build/cli.js.map +1 -1
- package/build/daemon.js +22 -3
- package/build/daemon.js.map +1 -1
- package/build/inbox.js +1 -1
- package/build/inbox.js.map +1 -1
- package/build/manager.js +28 -16
- package/build/manager.js.map +1 -1
- package/build/notes.js +17 -34
- package/build/notes.js.map +1 -1
- package/build/notify.js +32 -3
- package/build/notify.js.map +1 -1
- package/build/overrides.js +3 -2
- package/build/overrides.js.map +1 -1
- package/build/rule-actions.js +444 -0
- package/build/rule-actions.js.map +1 -0
- package/build/rules.js +5 -3
- package/build/rules.js.map +1 -1
- package/build/store.js +53 -6
- package/build/store.js.map +1 -1
- package/build/watchers/linear-api.js +55 -0
- package/build/watchers/linear-api.js.map +1 -0
- package/build/watchers/linear-labels.js +43 -0
- package/build/watchers/linear-labels.js.map +1 -0
- package/build/watchers/linear-queue.js +4 -35
- package/build/watchers/linear-queue.js.map +1 -1
- package/mod/README.md +1 -1
- package/mod/hooks/model.ts +64 -5
- package/mod/hooks/register.tsx +41 -3
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear-queue.js","sourceRoot":"","sources":["../../src/watchers/linear-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"linear-queue.js","sourceRoot":"","sources":["../../src/watchers/linear-queue.ts"],"names":[],"mappings":"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"}
|
package/mod/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Conductor mod
|
|
2
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`).
|
|
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` `/pause` `/unpause` `/focus-project`, 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
4
|
|
|
5
5
|
Install it with the daemon's CLI, which ships this folder:
|
|
6
6
|
|
package/mod/hooks/model.ts
CHANGED
|
@@ -355,17 +355,76 @@ export function notifyArgs(args: string, now: number): { error: string } | { nam
|
|
|
355
355
|
if (!typed?.[1]) return { error: 'usage: /notify [<profile> [--until <when>]]' };
|
|
356
356
|
const when = typed[2];
|
|
357
357
|
if (when === undefined) return { name: typed[1], until: null };
|
|
358
|
+
const until = untilOf(when, now);
|
|
359
|
+
return typeof until === 'string' ? { name: typed[1], until } : until;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const DAYS = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
|
363
|
+
|
|
364
|
+
// `--until <when>` as an ISO time: 17:00 (the next one), tomorrow or a day (local midnight), a date
|
|
365
|
+
// (local midnight), or a date and time, as `conduct` reads them.
|
|
366
|
+
export function untilOf(when: string, now: number): string | { error: string } {
|
|
358
367
|
const clock = /^([01]?\d|2[0-3]):([0-5]\d)$/.exec(when);
|
|
359
|
-
|
|
368
|
+
const lower = when.toLowerCase();
|
|
369
|
+
const day = DAYS.findIndex((d) => d === lower || d.slice(0, 3) === lower);
|
|
370
|
+
const at = new Date(now);
|
|
360
371
|
if (clock) {
|
|
361
|
-
at = new Date(now);
|
|
362
372
|
at.setHours(Number(clock[1]), Number(clock[2]), 0, 0);
|
|
363
373
|
if (at.getTime() <= now) at.setDate(at.getDate() + 1);
|
|
374
|
+
} else if (lower === 'tomorrow' || day !== -1) {
|
|
375
|
+
at.setHours(0, 0, 0, 0);
|
|
376
|
+
at.setDate(at.getDate() + (lower === 'tomorrow' ? 1 : (day - at.getDay() + 7) % 7 || 7));
|
|
364
377
|
} else {
|
|
365
|
-
at
|
|
378
|
+
at.setTime(new Date(/^\d{4}-\d{2}-\d{2}$/.test(when) ? `${when}T00:00` : when).getTime());
|
|
366
379
|
}
|
|
367
|
-
if (Number.isNaN(at.getTime()))
|
|
368
|
-
|
|
380
|
+
if (Number.isNaN(at.getTime())) {
|
|
381
|
+
return { error: `--until "${when}" is not a time: 17:00, tomorrow, a day such as monday, or a date and time such as 2026-10-12T09:00` };
|
|
382
|
+
}
|
|
383
|
+
return at.toISOString();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// `/pause [<project>] [--until <when>]`, `/unpause [<project>]` and `/focus-project <project> [--until
|
|
387
|
+
// <when>]` as POST /overrides' body. A focus with no end lasts until midnight, and puts the
|
|
388
|
+
// project's items first (weight 100).
|
|
389
|
+
export function overrideArgs(command: 'focus' | 'pause' | 'unpause', args: string, now: number): { body: Record<string, unknown> } | { error: string } {
|
|
390
|
+
const typed = /^(?:([a-z][a-z0-9-]*))?(?:\s*--until\s+(\S+))?$/.exec(args.trim());
|
|
391
|
+
const usage = { error: command === 'focus' ? 'usage: /focus-project <project> [--until <when>]' : `usage: /${command} [<project>]${command === 'pause' ? ' [--until <when>]' : ''}` };
|
|
392
|
+
if (!typed) return usage;
|
|
393
|
+
const [, project, when] = typed;
|
|
394
|
+
if (command === 'unpause') return when === undefined ? { body: { action: 'unpause', by: 'conductor mod /unpause', ...(project ? { project } : {}) } } : usage;
|
|
395
|
+
const until = when === undefined ? (command === 'focus' ? untilOf('tomorrow', now) : null) : untilOf(when, now);
|
|
396
|
+
if (until !== null && typeof until !== 'string') return until;
|
|
397
|
+
if (command === 'focus') {
|
|
398
|
+
if (!project) return usage;
|
|
399
|
+
return { body: { action: 'focus', by: 'conductor mod /focus-project', project, until, weight: 100 } };
|
|
400
|
+
}
|
|
401
|
+
return { body: { action: 'pause', by: 'conductor mod /pause', ...(project ? { project } : {}), ...(until === null ? {} : { until }) } };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// POST /overrides' answer as the line to show.
|
|
405
|
+
export function overrideText(command: 'focus' | 'pause' | 'unpause', project: null | string, answer: ApiAnswer, now: number): string {
|
|
406
|
+
if (answer.status === 404) return 'the running conductor has no overrides: restart it on this build (conduct upgrade)';
|
|
407
|
+
if (answer.status !== 201) return `not ${command === 'focus' ? 'focused' : `${command}d`}: ${errorOf(answer)}`;
|
|
408
|
+
const set = isRecord(answer.body) && Array.isArray(answer.body.set) ? answer.body.set.filter(isRecord) : [];
|
|
409
|
+
const until = typeof set[0]?.until === 'string' ? ` for ${remaining(set[0].until, now)}` : '';
|
|
410
|
+
const who = project ?? 'every project';
|
|
411
|
+
if (command === 'pause') return `paused launches in ${who}${until || ' until /unpause'}`;
|
|
412
|
+
if (command === 'unpause') return `launches in ${who} are no longer paused`;
|
|
413
|
+
return `${who} goes first${until}`;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// GET /why?item=…'s answer: the rule behind the item's newest action, in one line.
|
|
417
|
+
export function whyText(item: string, answer: ApiAnswer): string {
|
|
418
|
+
if (answer.status === 404) return 'the running conductor has no conduct why: restart it on this build (conduct upgrade)';
|
|
419
|
+
if (answer.status !== 200) return `why ${item}: ${errorOf(answer)}`;
|
|
420
|
+
const first = isRecord(answer.body) && Array.isArray(answer.body.items) ? answer.body.items.find(isRecord) : undefined;
|
|
421
|
+
if (!first) return `no action on ${item} was caused by a rule`;
|
|
422
|
+
const rule = isRecord(first.rule) ? first.rule : null;
|
|
423
|
+
const latest = Array.isArray(first.chain) && isRecord(first.chain[0]) ? first.chain[0] : null;
|
|
424
|
+
const what = latest && typeof latest.kind === 'string' ? `${latest.kind}: ` : '';
|
|
425
|
+
if (!rule || typeof rule.ref !== 'string') return `${item}: ${what}no rule`;
|
|
426
|
+
const text = typeof rule.when === 'string' && Array.isArray(rule.do) ? ` (when ${rule.when} → ${strings(rule.do).join('; ')})` : '';
|
|
427
|
+
return `${item}: ${what}${rule.ref}${text}`;
|
|
369
428
|
}
|
|
370
429
|
|
|
371
430
|
// `/notify` with no profile: the profile, quiet hours, Remote Control.
|
package/mod/hooks/register.tsx
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
inboxText,
|
|
18
18
|
notifyArgs,
|
|
19
19
|
notifyText,
|
|
20
|
+
overrideArgs,
|
|
21
|
+
overrideText,
|
|
20
22
|
parseLine,
|
|
21
23
|
profileText,
|
|
22
24
|
projectGroups,
|
|
@@ -29,6 +31,7 @@ import {
|
|
|
29
31
|
spendToday,
|
|
30
32
|
statusText,
|
|
31
33
|
tokens,
|
|
34
|
+
whyText,
|
|
32
35
|
workerDoing,
|
|
33
36
|
} from './model.ts';
|
|
34
37
|
|
|
@@ -238,6 +241,29 @@ async function answer($: EngineInterface, id: number, body: { action: 'approve'
|
|
|
238
241
|
}
|
|
239
242
|
}
|
|
240
243
|
|
|
244
|
+
// `/pause`, `/unpause`, `/focus-project` and the pane's buttons: an override that ends on its own, as
|
|
245
|
+
// `conduct pause|unpause|focus` sets it; the line to show.
|
|
246
|
+
async function override($: EngineInterface, command: 'focus' | 'pause' | 'unpause', args: string): Promise<string> {
|
|
247
|
+
const now = await $.clock.now();
|
|
248
|
+
const typed = overrideArgs(command, args, now);
|
|
249
|
+
if ('error' in typed) return typed.error;
|
|
250
|
+
try {
|
|
251
|
+
const project = typeof typed.body.project === 'string' ? typed.body.project : null;
|
|
252
|
+
return overrideText(command, project, await callApi($, 'POST', '/overrides', typed.body), now);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return error instanceof Error ? error.message : String(error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// The rule behind an item's newest action (`conduct why <item>`), in one line.
|
|
259
|
+
async function why($: EngineInterface, item: string): Promise<string> {
|
|
260
|
+
try {
|
|
261
|
+
return whyText(item, await callApi($, 'GET', `/why?item=${encodeURIComponent(item)}`));
|
|
262
|
+
} catch (error) {
|
|
263
|
+
return error instanceof Error ? error.message : String(error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
241
267
|
async function attach($: EngineInterface, id: number, name: string): Promise<string> {
|
|
242
268
|
try {
|
|
243
269
|
const result = await callApi($, 'POST', `/workers/${id}/attach`);
|
|
@@ -270,6 +296,9 @@ async function registerCommands($: EngineInterface): Promise<void> {
|
|
|
270
296
|
await $.command.register({ argumentHint: '<item | #id> <what to change>', description: 'Ask for a revised plan (posts revise: … as you)', name: 'revise' });
|
|
271
297
|
await $.command.register({ argumentHint: '<item | worker>', description: 'Wake a parked worker and open it in a tab', name: 'attach' });
|
|
272
298
|
await $.command.register({ argumentHint: '[<profile> [--until <when>]]', description: 'Show or switch the notification profile (default, away, focus …)', name: 'notify' });
|
|
299
|
+
await $.command.register({ argumentHint: '[<project>] [--until <when>]', description: 'No new launches in a project (every project without one)', name: 'pause' });
|
|
300
|
+
await $.command.register({ argumentHint: '[<project>]', description: 'Launch again after /pause', name: 'unpause' });
|
|
301
|
+
await $.command.register({ argumentHint: '<project> [--until <when>]', description: "Put a project's items first (until midnight without --until)", name: 'focus-project' });
|
|
273
302
|
}
|
|
274
303
|
|
|
275
304
|
export const register: Register = (on) => {
|
|
@@ -336,6 +365,10 @@ export const register: Register = (on) => {
|
|
|
336
365
|
return { text: snap.error ?? notifyText(snap.notify, await $.clock.now()) };
|
|
337
366
|
});
|
|
338
367
|
|
|
368
|
+
on('command.run', { command: 'pause' }, async ($, e) => ({ text: await override($, 'pause', e.args) }));
|
|
369
|
+
on('command.run', { command: 'unpause' }, async ($, e) => ({ text: await override($, 'unpause', e.args) }));
|
|
370
|
+
on('command.run', { command: 'focus-project' }, async ($, e) => ({ text: await override($, 'focus', e.args) }));
|
|
371
|
+
|
|
339
372
|
// The band above the prompt, in the console, while the inbox has items. A bare digit typed into
|
|
340
373
|
// an empty prompt presses a band Button, so a plan's approve takes a second press (`y`).
|
|
341
374
|
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
|
|
@@ -472,9 +505,13 @@ export const register: Register = (on) => {
|
|
|
472
505
|
))}
|
|
473
506
|
{projectGroups(snap.workers).map((group) => (
|
|
474
507
|
<Box flexDirection="column" key={`project-${group.project}`}>
|
|
475
|
-
<
|
|
476
|
-
|
|
477
|
-
|
|
508
|
+
<Box columnGap={1}>
|
|
509
|
+
<Text dimColor wrap="truncate">
|
|
510
|
+
{rule(`${group.project} · ${group.running}/${group.rows.length} running`).slice(0, Math.max(10, e.props.bodyColumns - 18))}
|
|
511
|
+
</Text>
|
|
512
|
+
<Button key={`pause-${group.project}`} label="pause" onPress={async () => $.ui.toast(await override($, 'pause', group.project))} />
|
|
513
|
+
<Button key={`focus-${group.project}`} label="focus" onPress={async () => $.ui.toast(await override($, 'focus', group.project))} />
|
|
514
|
+
</Box>
|
|
478
515
|
{group.rows.map((w) => (
|
|
479
516
|
<Box flexDirection="column" key={`worker-${w.id}`}>
|
|
480
517
|
<Button key={`row-${w.id}`} onPress={() => update($, expanded, (name) => (name === w.name ? null : w.name))} plain>
|
|
@@ -486,6 +523,7 @@ export const register: Register = (on) => {
|
|
|
486
523
|
{open === w.name && (
|
|
487
524
|
<Box columnGap={2} flexWrap="wrap" marginLeft={2}>
|
|
488
525
|
<Button key={`attach-${w.id}`} label="attach" onPress={async () => $.ui.toast(await attach($, w.id, w.name))} />
|
|
526
|
+
<Button key={`why-${w.id}`} label="why" onPress={async () => $.ui.toast(await why($, w.item))} />
|
|
489
527
|
<Text dimColor>
|
|
490
528
|
{w.status}
|
|
491
529
|
{w.waits.length > 0 ? ` · waits on ${w.waits.join(', ')}` : ''}
|
package/package.json
CHANGED