@detergent-software/conductor 1.0.1-dev.1 → 1.1.0-dev.1
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 +11 -0
- package/README.md +11 -0
- package/build/brief-cli.js.map +1 -1
- package/build/brief.js +24 -9
- package/build/brief.js.map +1 -1
- package/build/broker-cli.js +5 -1
- package/build/broker-cli.js.map +1 -1
- package/build/broker.js +19 -5
- package/build/broker.js.map +1 -1
- package/build/checkout-sync.js +265 -0
- package/build/checkout-sync.js.map +1 -0
- package/build/claude.js +3 -1
- package/build/claude.js.map +1 -1
- package/build/cli.js +114 -31
- package/build/cli.js.map +1 -1
- package/build/client.js +3 -1
- package/build/client.js.map +1 -1
- package/build/conditions.js +4 -2
- package/build/conditions.js.map +1 -1
- package/build/config.js +13 -1
- package/build/config.js.map +1 -1
- package/build/console.js +45 -8
- package/build/console.js.map +1 -1
- package/build/credential.js +4 -1
- package/build/credential.js.map +1 -1
- package/build/daemon.js +56 -7
- package/build/daemon.js.map +1 -1
- package/build/events.js +4 -1
- package/build/events.js.map +1 -1
- package/build/help.js +85 -42
- package/build/help.js.map +1 -1
- package/build/history.js +216 -52
- package/build/history.js.map +1 -1
- package/build/inbox.js +23 -10
- package/build/inbox.js.map +1 -1
- package/build/manager.js +84 -18
- package/build/manager.js.map +1 -1
- package/build/mod.js +3 -1
- package/build/mod.js.map +1 -1
- package/build/note.js +12 -5
- package/build/note.js.map +1 -1
- package/build/notes.js +8 -5
- package/build/notes.js.map +1 -1
- package/build/notify.js +111 -24
- package/build/notify.js.map +1 -1
- package/build/overrides.js +24 -6
- package/build/overrides.js.map +1 -1
- package/build/profile.js.map +1 -1
- package/build/pushover.js +4 -1
- package/build/pushover.js.map +1 -1
- package/build/queue.js +18 -3
- package/build/queue.js.map +1 -1
- package/build/registry.js.map +1 -1
- package/build/rule-actions.js +173 -36
- package/build/rule-actions.js.map +1 -1
- package/build/rule-input.js +3 -1
- package/build/rule-input.js.map +1 -1
- package/build/rules-service.js +41 -11
- package/build/rules-service.js.map +1 -1
- package/build/rules.js +86 -14
- package/build/rules.js.map +1 -1
- package/build/scheduler.js +48 -8
- package/build/scheduler.js.map +1 -1
- package/build/server.js +40 -10
- package/build/server.js.map +1 -1
- package/build/spend.js.map +1 -1
- package/build/store.js +71 -16
- package/build/store.js.map +1 -1
- package/build/token.js +3 -1
- package/build/token.js.map +1 -1
- package/build/trackers.js +3 -1
- package/build/trackers.js.map +1 -1
- package/build/upgrade.js.map +1 -1
- package/build/wait.js +3 -1
- package/build/wait.js.map +1 -1
- package/build/watchers/ado-api.js +5 -2
- package/build/watchers/ado-api.js.map +1 -1
- package/build/watchers/ado.js +18 -6
- package/build/watchers/ado.js.map +1 -1
- package/build/watchers/boards-brief.js +9 -3
- package/build/watchers/boards-brief.js.map +1 -1
- package/build/watchers/boards-queue.js +10 -5
- package/build/watchers/boards-queue.js.map +1 -1
- package/build/watchers/boards-writes.js +11 -3
- package/build/watchers/boards-writes.js.map +1 -1
- package/build/watchers/boards.js +12 -3
- package/build/watchers/boards.js.map +1 -1
- package/build/watchers/conflicts.js +11 -3
- package/build/watchers/conflicts.js.map +1 -1
- package/build/watchers/github.js +6 -2
- package/build/watchers/github.js.map +1 -1
- package/build/watchers/health.js.map +1 -1
- package/build/watchers/index.js +22 -4
- package/build/watchers/index.js.map +1 -1
- package/build/watchers/linear-api.js +1 -1
- package/build/watchers/linear-api.js.map +1 -1
- package/build/watchers/linear-labels.js +3 -1
- package/build/watchers/linear-labels.js.map +1 -1
- package/build/watchers/linear-queue.js +10 -4
- package/build/watchers/linear-queue.js.map +1 -1
- package/build/watchers/linear.js +13 -4
- package/build/watchers/linear.js.map +1 -1
- package/build/watchers/npm.js.map +1 -1
- package/build/watchers/source.js +1 -1
- package/build/watchers/source.js.map +1 -1
- package/build/why.js +7 -3
- package/build/why.js.map +1 -1
- package/build/worktrees.js +2 -1
- package/build/worktrees.js.map +1 -1
- package/mod/hooks/model.ts +90 -25
- package/mod/hooks/register.tsx +172 -62
- package/package.json +11 -12
package/mod/hooks/register.tsx
CHANGED
|
@@ -101,11 +101,20 @@ async function endpoint($: EngineInterface): Promise<Endpoint> {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// One call on the daemon's API (127.0.0.1, with its token). Throws when none runs.
|
|
104
|
-
async function callApi(
|
|
104
|
+
async function callApi(
|
|
105
|
+
$: EngineInterface,
|
|
106
|
+
method: 'GET' | 'POST',
|
|
107
|
+
path: string,
|
|
108
|
+
body?: unknown,
|
|
109
|
+
at?: Endpoint,
|
|
110
|
+
): Promise<ApiAnswer> {
|
|
105
111
|
const found = at ?? (await endpoint($));
|
|
106
112
|
const answer = await $.http.fetch(`http://127.0.0.1:${found.port}${path}`, {
|
|
107
113
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
108
|
-
headers: {
|
|
114
|
+
headers: {
|
|
115
|
+
authorization: `Bearer ${found.token}`,
|
|
116
|
+
...(body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
117
|
+
},
|
|
109
118
|
method,
|
|
110
119
|
});
|
|
111
120
|
let parsed: unknown = null;
|
|
@@ -130,7 +139,13 @@ async function refresh($: EngineInterface): Promise<ConductorSnapshot> {
|
|
|
130
139
|
]);
|
|
131
140
|
next = snapshotOf(fleet, inbox, readAt, notify);
|
|
132
141
|
} catch (error) {
|
|
133
|
-
next = {
|
|
142
|
+
next = {
|
|
143
|
+
error: error instanceof Error ? error.message : String(error),
|
|
144
|
+
inbox: [],
|
|
145
|
+
notify: null,
|
|
146
|
+
readAt,
|
|
147
|
+
workers: [],
|
|
148
|
+
};
|
|
134
149
|
}
|
|
135
150
|
await update($, snapshot, () => next);
|
|
136
151
|
return next;
|
|
@@ -177,7 +192,9 @@ async function onLine($: EngineInterface, text: string): Promise<void> {
|
|
|
177
192
|
return;
|
|
178
193
|
}
|
|
179
194
|
} catch (error) {
|
|
180
|
-
$.ui.log(`conductor: ${line.kind} failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
195
|
+
$.ui.log(`conductor: ${line.kind} failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
196
|
+
to: 'debug',
|
|
197
|
+
});
|
|
181
198
|
}
|
|
182
199
|
}
|
|
183
200
|
|
|
@@ -198,9 +215,15 @@ async function push($: EngineInterface, message: string): Promise<void> {
|
|
|
198
215
|
$.ui.toast(warning);
|
|
199
216
|
}
|
|
200
217
|
try {
|
|
201
|
-
await callApi($, 'POST', '/notify/report', {
|
|
218
|
+
await callApi($, 'POST', '/notify/report', {
|
|
219
|
+
channel: 'remote-control',
|
|
220
|
+
result: outcome.detail,
|
|
221
|
+
state: reportedState(outcome),
|
|
222
|
+
});
|
|
202
223
|
} catch (error) {
|
|
203
|
-
$.ui.log(`conductor: notify report failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
224
|
+
$.ui.log(`conductor: notify report failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
225
|
+
to: 'debug',
|
|
226
|
+
});
|
|
204
227
|
}
|
|
205
228
|
}
|
|
206
229
|
|
|
@@ -238,7 +261,9 @@ async function onIdle($: EngineInterface): Promise<void> {
|
|
|
238
261
|
await $.command.run({ command: 'clear' });
|
|
239
262
|
} catch (error) {
|
|
240
263
|
await update($, cleared, () => null);
|
|
241
|
-
$.ui.log(`conductor: the idle /clear failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
264
|
+
$.ui.log(`conductor: the idle /clear failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
265
|
+
to: 'debug',
|
|
266
|
+
});
|
|
242
267
|
return;
|
|
243
268
|
} finally {
|
|
244
269
|
isClearing = false;
|
|
@@ -250,7 +275,9 @@ async function onIdle($: EngineInterface): Promise<void> {
|
|
|
250
275
|
try {
|
|
251
276
|
outcome = pingOutcome(await $.model.fork({ prompt: WARM_PROMPT }), contextTokens);
|
|
252
277
|
} catch (error) {
|
|
253
|
-
$.ui.log(`conductor: the cache ping failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
278
|
+
$.ui.log(`conductor: the cache ping failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
279
|
+
to: 'debug',
|
|
280
|
+
});
|
|
254
281
|
outcome = { kind: 'failed', usage: null };
|
|
255
282
|
}
|
|
256
283
|
if (outcome.kind === 'nothing') return;
|
|
@@ -266,7 +293,9 @@ async function reportCache($: EngineInterface, body: Record<string, unknown>): P
|
|
|
266
293
|
try {
|
|
267
294
|
await callApi($, 'POST', '/console/cache', body);
|
|
268
295
|
} catch (error) {
|
|
269
|
-
$.ui.log(`conductor: console cache report failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
296
|
+
$.ui.log(`conductor: console cache report failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
297
|
+
to: 'debug',
|
|
298
|
+
});
|
|
270
299
|
}
|
|
271
300
|
}
|
|
272
301
|
|
|
@@ -291,12 +320,19 @@ async function follow($: EngineInterface): Promise<void> {
|
|
|
291
320
|
$.ui.log(`conductor: no ${cli}; the console polls instead of following events`, { to: 'debug' });
|
|
292
321
|
return;
|
|
293
322
|
}
|
|
294
|
-
for (let quickExits = 0; quickExits < 3;
|
|
323
|
+
for (let quickExits = 0; quickExits < 3;) {
|
|
295
324
|
const startedAt = await $.clock.now();
|
|
296
325
|
let buffer = '';
|
|
297
326
|
try {
|
|
298
327
|
// --console: while this stream is open, the daemon knows escalations have a console to wake.
|
|
299
|
-
const argv = [
|
|
328
|
+
const argv = [
|
|
329
|
+
'node',
|
|
330
|
+
cli,
|
|
331
|
+
'events',
|
|
332
|
+
'--follow',
|
|
333
|
+
'--console',
|
|
334
|
+
...(lastEventId === null ? [] : ['--since', String(lastEventId)]),
|
|
335
|
+
];
|
|
300
336
|
for await (const chunk of $.process.spawn({ argv })) {
|
|
301
337
|
if (chunk.stream !== 'stdout') continue;
|
|
302
338
|
buffer += chunk.text;
|
|
@@ -307,7 +343,9 @@ async function follow($: EngineInterface): Promise<void> {
|
|
|
307
343
|
}
|
|
308
344
|
}
|
|
309
345
|
} catch (error) {
|
|
310
|
-
$.ui.log(`conductor: conduct events failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
346
|
+
$.ui.log(`conductor: conduct events failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
347
|
+
to: 'debug',
|
|
348
|
+
});
|
|
311
349
|
}
|
|
312
350
|
quickExits = (await $.clock.now()) - startedAt < 10_000 ? quickExits + 1 : 0;
|
|
313
351
|
await new Promise<void>((done) => $.clock.after(RESTART_MS, done));
|
|
@@ -316,7 +354,11 @@ async function follow($: EngineInterface): Promise<void> {
|
|
|
316
354
|
}
|
|
317
355
|
|
|
318
356
|
// Jason's answer to an inbox item, as `conduct approve|revise|done` posts it; the line to show.
|
|
319
|
-
async function answer(
|
|
357
|
+
async function answer(
|
|
358
|
+
$: EngineInterface,
|
|
359
|
+
id: number,
|
|
360
|
+
body: { action: 'approve' | 'done' } | { action: 'revise'; text: string },
|
|
361
|
+
): Promise<string> {
|
|
320
362
|
try {
|
|
321
363
|
const result = await callApi($, 'POST', `/inbox/${id}/resolve`, body);
|
|
322
364
|
refreshSoon($);
|
|
@@ -359,7 +401,11 @@ async function attach($: EngineInterface, id: number, name: string): Promise<str
|
|
|
359
401
|
}
|
|
360
402
|
}
|
|
361
403
|
|
|
362
|
-
async function copy(
|
|
404
|
+
async function copy(
|
|
405
|
+
$: EngineInterface,
|
|
406
|
+
text: string,
|
|
407
|
+
surface: Parameters<EngineInterface['ui']['copy']>[0]['surface'],
|
|
408
|
+
): Promise<void> {
|
|
363
409
|
const copied = await $.ui.copy({ surface, text });
|
|
364
410
|
$.ui.toast(copied.isCopied ? `copied: ${text}` : `not copied: ${text}`);
|
|
365
411
|
}
|
|
@@ -375,15 +421,45 @@ async function openPane($: EngineInterface): Promise<void> {
|
|
|
375
421
|
}
|
|
376
422
|
|
|
377
423
|
async function registerCommands($: EngineInterface): Promise<void> {
|
|
378
|
-
await $.command.register({
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
await $.command.register({
|
|
383
|
-
|
|
384
|
-
|
|
424
|
+
await $.command.register({
|
|
425
|
+
description: 'Open the conductor pane: the inbox, the fleet and spend',
|
|
426
|
+
name: 'conductor',
|
|
427
|
+
});
|
|
428
|
+
await $.command.register({
|
|
429
|
+
description: 'What waits on you in the conductor, with commands and links',
|
|
430
|
+
name: 'inbox',
|
|
431
|
+
});
|
|
432
|
+
await $.command.register({
|
|
433
|
+
argumentHint: '<item | #id>',
|
|
434
|
+
description: "Approve an item's plan (posts approve as you)",
|
|
435
|
+
name: 'approve',
|
|
436
|
+
});
|
|
437
|
+
await $.command.register({
|
|
438
|
+
argumentHint: '<item | #id> <what to change>',
|
|
439
|
+
description: 'Ask for a revised plan (posts revise: … as you)',
|
|
440
|
+
name: 'revise',
|
|
441
|
+
});
|
|
442
|
+
await $.command.register({
|
|
443
|
+
argumentHint: '<item | worker>',
|
|
444
|
+
description: 'Wake a parked worker and open it in a tab',
|
|
445
|
+
name: 'attach',
|
|
446
|
+
});
|
|
447
|
+
await $.command.register({
|
|
448
|
+
argumentHint: '[<profile> [--until <when>]]',
|
|
449
|
+
description: 'Show or switch the notification profile (default, away, focus …)',
|
|
450
|
+
name: 'notify',
|
|
451
|
+
});
|
|
452
|
+
await $.command.register({
|
|
453
|
+
argumentHint: '[<project>] [--until <when>]',
|
|
454
|
+
description: 'No new launches in a project (every project without one)',
|
|
455
|
+
name: 'pause',
|
|
456
|
+
});
|
|
385
457
|
await $.command.register({ argumentHint: '[<project>]', description: 'Launch again after /pause', name: 'unpause' });
|
|
386
|
-
await $.command.register({
|
|
458
|
+
await $.command.register({
|
|
459
|
+
argumentHint: '<project> [--until <when>]',
|
|
460
|
+
description: "Put a project's items first (until midnight without --until)",
|
|
461
|
+
name: 'focus-project',
|
|
462
|
+
});
|
|
387
463
|
}
|
|
388
464
|
|
|
389
465
|
export const register: Register = (on) => {
|
|
@@ -519,15 +595,15 @@ export const register: Register = (on) => {
|
|
|
519
595
|
const clearedBy = await read($, cleared);
|
|
520
596
|
const withWarning = (row: RenderElement): RenderElement =>
|
|
521
597
|
isWarned ? (
|
|
522
|
-
<Box flexDirection=
|
|
598
|
+
<Box flexDirection='column'>
|
|
523
599
|
{clearedBy === null ? (
|
|
524
|
-
<Box key=
|
|
525
|
-
<Text bold color=
|
|
600
|
+
<Box key='no-skill'>
|
|
601
|
+
<Text bold color='warning'>
|
|
526
602
|
{NO_SKILL_WARNING}
|
|
527
603
|
</Text>
|
|
528
604
|
</Box>
|
|
529
605
|
) : (
|
|
530
|
-
<Box key=
|
|
606
|
+
<Box key='cleared'>
|
|
531
607
|
<Text dimColor>{clearedText(clearedBy)}</Text>
|
|
532
608
|
</Box>
|
|
533
609
|
)}
|
|
@@ -537,33 +613,34 @@ export const register: Register = (on) => {
|
|
|
537
613
|
row
|
|
538
614
|
);
|
|
539
615
|
const snap = await read($, snapshot);
|
|
540
|
-
if (snap === null || snap.error !== null || snap.inbox.length === 0)
|
|
616
|
+
if (snap === null || snap.error !== null || snap.inbox.length === 0)
|
|
617
|
+
return isWarned ? withWarning(await next(e)) : next(e);
|
|
541
618
|
const entries = bandEntries(snap.inbox);
|
|
542
619
|
const confirmId = await read($, confirming);
|
|
543
620
|
const pending = entries.find((x) => x.act === 'approve' && x.id === confirmId);
|
|
544
621
|
if (pending) {
|
|
545
622
|
return withWarning(
|
|
546
|
-
<Box columnGap={2} flexWrap=
|
|
623
|
+
<Box columnGap={2} flexWrap='wrap'>
|
|
547
624
|
<Text bold>conductor</Text>
|
|
548
625
|
<Text>
|
|
549
626
|
{pending.label}? {pending.text}
|
|
550
627
|
</Text>
|
|
551
628
|
<Button
|
|
552
|
-
hotkey=
|
|
553
|
-
key=
|
|
554
|
-
label=
|
|
629
|
+
hotkey='y'
|
|
630
|
+
key='confirm'
|
|
631
|
+
label='y approve'
|
|
555
632
|
onPress={async () => {
|
|
556
633
|
await update($, confirming, () => null);
|
|
557
634
|
$.ui.toast(await answer($, pending.id, { action: 'approve' }));
|
|
558
635
|
}}
|
|
559
|
-
variant=
|
|
636
|
+
variant='primary'
|
|
560
637
|
/>
|
|
561
|
-
<Button hotkey=
|
|
638
|
+
<Button hotkey='n' key='cancel' label='n cancel' onPress={() => update($, confirming, () => null)} />
|
|
562
639
|
</Box>,
|
|
563
640
|
);
|
|
564
641
|
}
|
|
565
642
|
return withWarning(
|
|
566
|
-
<Box columnGap={2} flexWrap=
|
|
643
|
+
<Box columnGap={2} flexWrap='wrap'>
|
|
567
644
|
<Text bold>conductor · inbox {snap.inbox.length}</Text>
|
|
568
645
|
{entries.map((x) => (
|
|
569
646
|
<Button
|
|
@@ -578,7 +655,7 @@ export const register: Register = (on) => {
|
|
|
578
655
|
/>
|
|
579
656
|
))}
|
|
580
657
|
{snap.inbox.length > entries.length && <Text dimColor>+{snap.inbox.length - entries.length} more</Text>}
|
|
581
|
-
<Button hotkey=
|
|
658
|
+
<Button hotkey='o' key='open-pane' label='o panel' onPress={() => openPane($)} />
|
|
582
659
|
</Box>,
|
|
583
660
|
);
|
|
584
661
|
});
|
|
@@ -590,27 +667,28 @@ export const register: Register = (on) => {
|
|
|
590
667
|
const snap = await read($, snapshot);
|
|
591
668
|
const open = await read($, expanded);
|
|
592
669
|
const now = await $.clock.now();
|
|
593
|
-
const rule = (title: string): string =>
|
|
670
|
+
const rule = (title: string): string =>
|
|
671
|
+
`─ ${title} ${'─'.repeat(Math.max(0, e.props.bodyColumns - title.length - 3))}`;
|
|
594
672
|
if (snap === null) return <Text dimColor>Reading the conductor…</Text>;
|
|
595
673
|
if (snap.error !== null) {
|
|
596
674
|
return (
|
|
597
|
-
<Box flexDirection=
|
|
675
|
+
<Box flexDirection='column'>
|
|
598
676
|
<Text bold>{TITLE}</Text>
|
|
599
677
|
<Text>{snap.error}</Text>
|
|
600
678
|
</Box>
|
|
601
679
|
);
|
|
602
680
|
}
|
|
603
681
|
return (
|
|
604
|
-
<Box flexDirection=
|
|
682
|
+
<Box flexDirection='column'>
|
|
605
683
|
<Text bold>
|
|
606
684
|
{TITLE} <Text dimColor>{statusText(snap)}</Text>
|
|
607
685
|
</Text>
|
|
608
686
|
{snap.notify !== null && (
|
|
609
|
-
<Box columnGap={2} flexWrap=
|
|
687
|
+
<Box columnGap={2} flexWrap='wrap'>
|
|
610
688
|
{e.surface !== 'mobile' && 'Select' in ui ? (
|
|
611
689
|
<ui.Select
|
|
612
|
-
key=
|
|
613
|
-
label=
|
|
690
|
+
key='notify-profile'
|
|
691
|
+
label='notify '
|
|
614
692
|
onSelect={async (name) => $.ui.toast(await switchProfile($, name))}
|
|
615
693
|
options={snap.notify.profiles.map((p) => ({ value: p }))}
|
|
616
694
|
value={snap.notify.profile}
|
|
@@ -631,61 +709,93 @@ export const register: Register = (on) => {
|
|
|
631
709
|
</Text>
|
|
632
710
|
</Box>
|
|
633
711
|
)}
|
|
634
|
-
<Text dimColor wrap=
|
|
712
|
+
<Text dimColor wrap='truncate'>
|
|
635
713
|
{rule(`Inbox (${snap.inbox.length})`)}
|
|
636
714
|
</Text>
|
|
637
715
|
{snap.inbox.length === 0 && <Text dimColor>Nothing waits on you.</Text>}
|
|
638
716
|
{snap.inbox.map((item, i) => (
|
|
639
|
-
<Box flexDirection=
|
|
717
|
+
<Box flexDirection='column' key={`inbox-${item.id}`} marginBottom={1}>
|
|
640
718
|
<Text>
|
|
641
|
-
[{i + 1}] <Text bold>{item.item ?? `#${item.id}`}</Text> {item.kind}
|
|
719
|
+
[{i + 1}] <Text bold>{item.item ?? `#${item.id}`}</Text> {item.kind}{' '}
|
|
720
|
+
<Text dimColor>{age(item.raisedAt, now)}</Text>
|
|
642
721
|
</Text>
|
|
643
722
|
<Text>{item.text}</Text>
|
|
644
|
-
<Box columnGap={2} flexWrap=
|
|
723
|
+
<Box columnGap={2} flexWrap='wrap'>
|
|
645
724
|
{item.kind === 'plan' && item.item !== null && (
|
|
646
725
|
<Button
|
|
647
726
|
key={`approve-${item.id}`}
|
|
648
|
-
label=
|
|
727
|
+
label='approve'
|
|
649
728
|
onPress={async () => $.ui.toast(await answer($, item.id, { action: 'approve' }))}
|
|
650
|
-
variant=
|
|
729
|
+
variant='primary'
|
|
651
730
|
/>
|
|
652
731
|
)}
|
|
653
732
|
{item.kind === 'plan' && item.item !== null && (
|
|
654
|
-
<Button
|
|
733
|
+
<Button
|
|
734
|
+
key={`revise-${item.id}`}
|
|
735
|
+
label='revise'
|
|
736
|
+
onPress={() => $.prompt.fill({ text: `/revise ${item.item ?? ''} ` })}
|
|
737
|
+
/>
|
|
655
738
|
)}
|
|
656
739
|
{item.commands.slice(0, 2).map((command, n) => (
|
|
657
|
-
<Button
|
|
740
|
+
<Button
|
|
741
|
+
key={`copy-${item.id}-${n}`}
|
|
742
|
+
label={`copy: ${command}`}
|
|
743
|
+
onPress={(press) => copy($, command, press.surface)}
|
|
744
|
+
/>
|
|
658
745
|
))}
|
|
659
746
|
{item.links.slice(0, 2).map((href, n) => (
|
|
660
|
-
<Link href={href} key={`link-${item.id}-${n}`} label=
|
|
747
|
+
<Link href={href} key={`link-${item.id}-${n}`} label='open' />
|
|
661
748
|
))}
|
|
662
749
|
{item.kind !== 'plan' && (
|
|
663
|
-
<Button
|
|
750
|
+
<Button
|
|
751
|
+
key={`done-${item.id}`}
|
|
752
|
+
label='done'
|
|
753
|
+
onPress={async () => $.ui.toast(await answer($, item.id, { action: 'done' }))}
|
|
754
|
+
/>
|
|
664
755
|
)}
|
|
665
756
|
</Box>
|
|
666
757
|
</Box>
|
|
667
758
|
))}
|
|
668
759
|
{projectGroups(snap.workers).map((group) => (
|
|
669
|
-
<Box flexDirection=
|
|
760
|
+
<Box flexDirection='column' key={`project-${group.project}`}>
|
|
670
761
|
<Box columnGap={1}>
|
|
671
|
-
<Text dimColor wrap=
|
|
672
|
-
{rule(`${group.project} · ${group.running}/${group.rows.length} running`).slice(
|
|
762
|
+
<Text dimColor wrap='truncate'>
|
|
763
|
+
{rule(`${group.project} · ${group.running}/${group.rows.length} running`).slice(
|
|
764
|
+
0,
|
|
765
|
+
Math.max(10, e.props.bodyColumns - 18),
|
|
766
|
+
)}
|
|
673
767
|
</Text>
|
|
674
|
-
<Button
|
|
675
|
-
|
|
768
|
+
<Button
|
|
769
|
+
key={`pause-${group.project}`}
|
|
770
|
+
label='pause'
|
|
771
|
+
onPress={async () => $.ui.toast(await override($, 'pause', group.project))}
|
|
772
|
+
/>
|
|
773
|
+
<Button
|
|
774
|
+
key={`focus-${group.project}`}
|
|
775
|
+
label='focus'
|
|
776
|
+
onPress={async () => $.ui.toast(await override($, 'focus', group.project))}
|
|
777
|
+
/>
|
|
676
778
|
</Box>
|
|
677
779
|
{group.rows.map((w) => (
|
|
678
|
-
<Box flexDirection=
|
|
679
|
-
<Button
|
|
780
|
+
<Box flexDirection='column' key={`worker-${w.id}`}>
|
|
781
|
+
<Button
|
|
782
|
+
key={`row-${w.id}`}
|
|
783
|
+
onPress={() => update($, expanded, (name) => (name === w.name ? null : w.name))}
|
|
784
|
+
plain
|
|
785
|
+
>
|
|
680
786
|
{glyph(w.status)} {w.name} {w.phase ?? ''}{' '}
|
|
681
787
|
<Text dimColor>
|
|
682
788
|
{workerDoing(w)} · {age(w.updatedAt, now)} · {tokens(w.spendToday)} tok
|
|
683
789
|
</Text>
|
|
684
790
|
</Button>
|
|
685
791
|
{open === w.name && (
|
|
686
|
-
<Box columnGap={2} flexWrap=
|
|
687
|
-
<Button
|
|
688
|
-
|
|
792
|
+
<Box columnGap={2} flexWrap='wrap' marginLeft={2}>
|
|
793
|
+
<Button
|
|
794
|
+
key={`attach-${w.id}`}
|
|
795
|
+
label='attach'
|
|
796
|
+
onPress={async () => $.ui.toast(await attach($, w.id, w.name))}
|
|
797
|
+
/>
|
|
798
|
+
<Button key={`why-${w.id}`} label='why' onPress={async () => $.ui.toast(await why($, w.item))} />
|
|
689
799
|
<Text dimColor>
|
|
690
800
|
{w.status}
|
|
691
801
|
{w.waits.length > 0 ? ` · waits on ${w.waits.join(', ')}` : ''}
|
|
@@ -696,7 +806,7 @@ export const register: Register = (on) => {
|
|
|
696
806
|
))}
|
|
697
807
|
</Box>
|
|
698
808
|
))}
|
|
699
|
-
<Text dimColor wrap=
|
|
809
|
+
<Text dimColor wrap='truncate'>
|
|
700
810
|
{rule(`Today: ${tokens(spendToday(snap.workers))} tokens · ${snap.workers.length} workers`)}
|
|
701
811
|
</Text>
|
|
702
812
|
</Box>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@detergent-software/conductor",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0-dev.1",
|
|
4
4
|
"description": "Conductor: the local daemon that runs conducted projects' work loops (conduct serve) and its conduct CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"dev": "tsc --watch",
|
|
20
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\" \"mod/**/*.{ts,tsx}\" \"scripts/**/*.mjs\"",
|
|
21
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx}\" \"mod/**/*.{ts,tsx}\" \"scripts/**/*.mjs\"",
|
|
20
22
|
"lint": "eslint src/",
|
|
21
23
|
"lint:fix": "eslint src/ --fix --cache",
|
|
22
24
|
"test": "vitest run",
|
|
@@ -41,22 +43,19 @@
|
|
|
41
43
|
"zod": "^4.0.0"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
|
-
"@eslint
|
|
46
|
+
"@atk/eslint-config": "workspace:*",
|
|
47
|
+
"@atk/tsconfig": "workspace:*",
|
|
45
48
|
"@semantic-release/changelog": "^6.0.3",
|
|
46
49
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
47
50
|
"@semantic-release/git": "^10.0.1",
|
|
48
51
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
49
|
-
"@types/node": "
|
|
50
|
-
"@types/semver": "
|
|
51
|
-
"@vitest/eslint-plugin": "^1.0.0",
|
|
52
|
+
"@types/node": "catalog:",
|
|
53
|
+
"@types/semver": "catalog:",
|
|
52
54
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
53
|
-
"eslint": "
|
|
54
|
-
"
|
|
55
|
-
"eslint-plugin-perfectionist": "^5.0.0",
|
|
56
|
-
"prettier": "^3.4.0",
|
|
55
|
+
"eslint": "catalog:",
|
|
56
|
+
"prettier": "catalog:",
|
|
57
57
|
"semantic-release": "^25.0.3",
|
|
58
|
-
"typescript": "
|
|
59
|
-
"
|
|
60
|
-
"vitest": "^4.0.0"
|
|
58
|
+
"typescript": "catalog:",
|
|
59
|
+
"vitest": "catalog:"
|
|
61
60
|
}
|
|
62
61
|
}
|