@detergent-software/conductor 1.0.0-dev.31 → 1.0.0-dev.33

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.
@@ -1,5 +1,5 @@
1
1
  import { atom, read, update } from 'claude-code';
2
- import type { EngineInterface, Register } from 'claude-code';
2
+ import type { EngineInterface, Register, RenderElement } from 'claude-code';
3
3
 
4
4
  import type { ConductorSnapshot } from '../types';
5
5
 
@@ -8,6 +8,7 @@ import {
8
8
  type ApiAnswer,
9
9
  answerText,
10
10
  bandEntries,
11
+ CONSOLE_SKILL,
11
12
  endpointOf,
12
13
  errorOf,
13
14
  findPlan,
@@ -15,6 +16,8 @@ import {
15
16
  glyph,
16
17
  homeOf,
17
18
  inboxText,
19
+ NO_SKILL_CONTEXT,
20
+ NO_SKILL_WARNING,
18
21
  notifyArgs,
19
22
  notifyText,
20
23
  overrideArgs,
@@ -49,6 +52,7 @@ const NOT_RUNNING = 'the conductor is not running (conduct serve)';
49
52
  const snapshot = atom({ plugin: 'conductor', key: 'snapshot' } as const, null);
50
53
  const confirming = atom({ plugin: 'conductor', key: 'confirming' } as const, null);
51
54
  const expanded = atom({ plugin: 'conductor', key: 'expanded' } as const, null);
55
+ const hasSkill = atom({ plugin: 'conductor', key: 'hasSkill' } as const, false);
52
56
 
53
57
  // The console is the session started with CONDUCTOR_CONSOLE=1 (`conduct console` sets it): only it
54
58
  // draws the band and the pane, answers the commands, follows the stream and acts on events. Both
@@ -315,6 +319,27 @@ export const register: Register = (on) => {
315
319
  return started;
316
320
  });
317
321
 
322
+ // A console the skill never ran in (one started by hand as `claude -n conductor`, ATK-385) is
323
+ // told to load it before it answers, each prompt until it has. `/conductor-console` or the Skill
324
+ // tool expands it before its prompt is submitted; a /clear or a resume starts a conversation
325
+ // without it.
326
+ on('skill.prompt', { skill: CONSOLE_SKILL }, async ($, e, next) => {
327
+ const prompt = await next(e);
328
+ if (isConsole) await update($, hasSkill, () => true);
329
+ return prompt;
330
+ });
331
+
332
+ on('session.end', async ($, e, next) => {
333
+ if (isConsole) await update($, hasSkill, () => false);
334
+ return next(e);
335
+ });
336
+
337
+ on('prompt.submit', async ($, e, next) => {
338
+ if (!isConsole || (await read($, hasSkill))) return next(e);
339
+ $.ui.log(`conductor: /${CONSOLE_SKILL} hasn't run; asking the model to load it`, { to: 'debug' });
340
+ return next({ ...e, context: [...(e.context ?? []), NO_SKILL_CONTEXT] });
341
+ });
342
+
318
343
  on('command.run', { command: 'conductor' }, async ($) => {
319
344
  await openPane($);
320
345
  return { text: 'Conductor pane opened.' };
@@ -369,17 +394,32 @@ export const register: Register = (on) => {
369
394
  on('command.run', { command: 'focus-project' }, async ($, e) => ({ text: await override($, 'focus', e.args) }));
370
395
 
371
396
  // The band above the prompt, in the console, while the inbox has items. A bare digit typed into
372
- // an empty prompt presses a band Button, so a plan's approve takes a second press (`y`).
397
+ // an empty prompt presses a band Button, so a plan's approve takes a second press (`y`). Until
398
+ // /conductor-console runs, a warning sits above it, or above the engine's own (ATK-385).
373
399
  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
374
400
  if (!isConsole || e.props.hasSurvey) return next(e);
375
- const snap = await read($, snapshot);
376
- if (snap === null || snap.error !== null || snap.inbox.length === 0) return next(e);
377
401
  const { Box, Button, Text } = $.ui.resolve(e);
402
+ const isWarned = !(await read($, hasSkill));
403
+ const withWarning = (row: RenderElement): RenderElement =>
404
+ isWarned ? (
405
+ <Box flexDirection="column">
406
+ <Box key="no-skill">
407
+ <Text bold color="warning">
408
+ {NO_SKILL_WARNING}
409
+ </Text>
410
+ </Box>
411
+ {row}
412
+ </Box>
413
+ ) : (
414
+ row
415
+ );
416
+ const snap = await read($, snapshot);
417
+ if (snap === null || snap.error !== null || snap.inbox.length === 0) return isWarned ? withWarning(await next(e)) : next(e);
378
418
  const entries = bandEntries(snap.inbox);
379
419
  const confirmId = await read($, confirming);
380
420
  const pending = entries.find((x) => x.act === 'approve' && x.id === confirmId);
381
421
  if (pending) {
382
- return (
422
+ return withWarning(
383
423
  <Box columnGap={2} flexWrap="wrap">
384
424
  <Text bold>conductor</Text>
385
425
  <Text>
@@ -396,10 +436,10 @@ export const register: Register = (on) => {
396
436
  variant="primary"
397
437
  />
398
438
  <Button hotkey="n" key="cancel" label="n cancel" onPress={() => update($, confirming, () => null)} />
399
- </Box>
439
+ </Box>,
400
440
  );
401
441
  }
402
- return (
442
+ return withWarning(
403
443
  <Box columnGap={2} flexWrap="wrap">
404
444
  <Text bold>conductor · inbox {snap.inbox.length}</Text>
405
445
  {entries.map((x) => (
@@ -416,7 +456,7 @@ export const register: Register = (on) => {
416
456
  ))}
417
457
  {snap.inbox.length > entries.length && <Text dimColor>+{snap.inbox.length - entries.length} more</Text>}
418
458
  <Button hotkey="o" key="open-pane" label="o panel" onPress={() => openPane($)} />
419
- </Box>
459
+ </Box>,
420
460
  );
421
461
  });
422
462
 
@@ -51,6 +51,9 @@ declare module 'claude-code' {
51
51
  confirming: number | null;
52
52
  // The worker whose row the pane shows expanded, by name.
53
53
  expanded: string | null;
54
+ // Whether /conductor-console ran in this conversation (ATK-385). Kept in $.state, so a reload
55
+ // of the mod doesn't forget it; a /clear or a resume (session.end) sets it back to false.
56
+ hasSkill: boolean;
54
57
  snapshot: ConductorSnapshot | null;
55
58
  };
56
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@detergent-software/conductor",
3
- "version": "1.0.0-dev.31",
3
+ "version": "1.0.0-dev.33",
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": {