agent-tempo 1.7.0 → 2.0.0-beta.2

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 (171) hide show
  1. package/CLAUDE.md +10 -13
  2. package/README.md +21 -15
  3. package/dashboard/dist/assets/index-BbR7zIdK.js.map +1 -1
  4. package/dashboard/package.json +1 -1
  5. package/dist/activities/hard-terminate.d.ts +2 -2
  6. package/dist/activities/hard-terminate.js +3 -9
  7. package/dist/activities/maestro.d.ts +8 -13
  8. package/dist/activities/maestro.js +3 -10
  9. package/dist/activities/outbox.js +16 -2
  10. package/dist/activities/resolve.d.ts +18 -7
  11. package/dist/activities/resolve.js +58 -46
  12. package/dist/adapters/base.js +3 -0
  13. package/dist/adapters/claude-code/adapter.js +7 -0
  14. package/dist/cli/command-center-command.js +15 -1
  15. package/dist/cli/commands.d.ts +8 -0
  16. package/dist/cli/commands.js +79 -11
  17. package/dist/cli/daemon-command.js +19 -33
  18. package/dist/cli/help-text.js +9 -7
  19. package/dist/cli/home-command.d.ts +26 -0
  20. package/dist/cli/home-command.js +156 -0
  21. package/dist/cli/removed-verbs.d.ts +0 -4
  22. package/dist/cli/removed-verbs.js +23 -12
  23. package/dist/cli/sa-preflight.d.ts +0 -14
  24. package/dist/cli/sa-preflight.js +1 -17
  25. package/dist/cli/startup.d.ts +1 -10
  26. package/dist/cli/startup.js +0 -40
  27. package/dist/cli.js +34 -80
  28. package/dist/client/core.js +13 -15
  29. package/dist/client/index.d.ts +2 -1
  30. package/dist/client/interface.d.ts +5 -14
  31. package/dist/client/with-spawn.d.ts +9 -4
  32. package/dist/client/with-spawn.js +7 -10
  33. package/dist/config.d.ts +14 -13
  34. package/dist/config.js +14 -0
  35. package/dist/constants.d.ts +50 -0
  36. package/dist/constants.js +60 -1
  37. package/dist/daemon.js +81 -0
  38. package/dist/http/auth.d.ts +3 -21
  39. package/dist/http/auth.js +10 -31
  40. package/dist/http/event-types.d.ts +38 -4
  41. package/dist/http/orphans.d.ts +3 -3
  42. package/dist/http/orphans.js +3 -3
  43. package/dist/http/server.d.ts +0 -5
  44. package/dist/http/server.js +79 -29
  45. package/dist/http/snapshot.js +3 -0
  46. package/dist/http/writes.d.ts +1 -1
  47. package/dist/http/writes.js +25 -2
  48. package/dist/observability/nondeterminism-alarm.d.ts +113 -0
  49. package/dist/observability/nondeterminism-alarm.js +162 -0
  50. package/dist/palette/index.d.ts +6 -7
  51. package/dist/palette/index.js +6 -7
  52. package/dist/pi/mission-control/actions.d.ts +67 -1
  53. package/dist/pi/mission-control/actions.js +61 -0
  54. package/dist/pi/mission-control/extension.d.ts +77 -0
  55. package/dist/pi/mission-control/extension.js +171 -0
  56. package/dist/pi/workflow-client.d.ts +14 -16
  57. package/dist/pi/workflow-client.js +18 -44
  58. package/dist/server-tools.js +30 -29
  59. package/dist/server.js +42 -0
  60. package/dist/tools/action-guard.d.ts +23 -0
  61. package/dist/tools/action-guard.js +33 -0
  62. package/dist/tools/coat-check.d.ts +20 -0
  63. package/dist/tools/coat-check.js +129 -0
  64. package/dist/tools/gate.d.ts +13 -0
  65. package/dist/tools/gate.js +88 -0
  66. package/dist/tools/schedule.d.ts +18 -0
  67. package/dist/tools/schedule.js +93 -1
  68. package/dist/tools/stage.d.ts +17 -0
  69. package/dist/tools/stage.js +85 -1
  70. package/dist/tools/state.d.ts +14 -0
  71. package/dist/tools/state.js +95 -0
  72. package/dist/types.d.ts +54 -7
  73. package/dist/upgrade/boot-guard.d.ts +95 -0
  74. package/dist/upgrade/boot-guard.js +88 -0
  75. package/dist/upgrade/from-upgrade.d.ts +95 -0
  76. package/dist/upgrade/from-upgrade.js +330 -0
  77. package/dist/upgrade/phase-engine.js +4 -1
  78. package/dist/utils/duration.d.ts +3 -6
  79. package/dist/utils/duration.js +3 -6
  80. package/dist/utils/format-hosts.d.ts +1 -1
  81. package/dist/utils/format-hosts.js +0 -2
  82. package/dist/utils/grpc-shutdown-guard.js +0 -4
  83. package/dist/utils/hosts.d.ts +4 -10
  84. package/dist/utils/hosts.js +12 -28
  85. package/dist/utils/orphan-guard.d.ts +37 -0
  86. package/dist/utils/orphan-guard.js +26 -0
  87. package/dist/utils/search-attributes.d.ts +28 -18
  88. package/dist/utils/search-attributes.js +48 -18
  89. package/dist/utils/visibility-deadline.d.ts +16 -0
  90. package/dist/utils/visibility-deadline.js +16 -0
  91. package/dist/workflows/maestro-signals.d.ts +0 -13
  92. package/dist/workflows/maestro-signals.js +6 -14
  93. package/dist/workflows/maestro.js +27 -32
  94. package/dist/workflows/scheduler.js +7 -1
  95. package/dist/workflows/session.js +237 -153
  96. package/dist/workflows/signals.d.ts +9 -1
  97. package/dist/workflows/signals.js +8 -6
  98. package/package.json +1 -6
  99. package/workflow-bundle.js +478 -225
  100. package/dist/cli/legacy-migration.d.ts +0 -35
  101. package/dist/cli/legacy-migration.js +0 -341
  102. package/dist/client/ensure-conductor-spawned.d.ts +0 -35
  103. package/dist/client/ensure-conductor-spawned.js +0 -48
  104. package/dist/tui/App.d.ts +0 -85
  105. package/dist/tui/App.js +0 -1791
  106. package/dist/tui/bootstrap-types.d.ts +0 -46
  107. package/dist/tui/bootstrap-types.js +0 -7
  108. package/dist/tui/commands.d.ts +0 -71
  109. package/dist/tui/commands.js +0 -1375
  110. package/dist/tui/components/ChatView.d.ts +0 -35
  111. package/dist/tui/components/ChatView.js +0 -54
  112. package/dist/tui/components/CommandPalette.d.ts +0 -21
  113. package/dist/tui/components/CommandPalette.js +0 -67
  114. package/dist/tui/components/ConversationStream.d.ts +0 -114
  115. package/dist/tui/components/ConversationStream.js +0 -307
  116. package/dist/tui/components/CreateEnsembleWizard.d.ts +0 -19
  117. package/dist/tui/components/CreateEnsembleWizard.js +0 -223
  118. package/dist/tui/components/DestroyConfirmModal.d.ts +0 -17
  119. package/dist/tui/components/DestroyConfirmModal.js +0 -62
  120. package/dist/tui/components/ErrorView.d.ts +0 -31
  121. package/dist/tui/components/ErrorView.js +0 -129
  122. package/dist/tui/components/HomeView.d.ts +0 -54
  123. package/dist/tui/components/HomeView.js +0 -306
  124. package/dist/tui/components/LoadLineupModal.d.ts +0 -18
  125. package/dist/tui/components/LoadLineupModal.js +0 -79
  126. package/dist/tui/components/MainView.d.ts +0 -21
  127. package/dist/tui/components/MainView.js +0 -107
  128. package/dist/tui/components/NewEnsembleModal.d.ts +0 -9
  129. package/dist/tui/components/NewEnsembleModal.js +0 -73
  130. package/dist/tui/components/Picker.d.ts +0 -23
  131. package/dist/tui/components/Picker.js +0 -70
  132. package/dist/tui/components/PlayerDetailView.d.ts +0 -26
  133. package/dist/tui/components/PlayerDetailView.js +0 -118
  134. package/dist/tui/components/PromptArea.d.ts +0 -50
  135. package/dist/tui/components/PromptArea.js +0 -303
  136. package/dist/tui/components/RecruitWizard.d.ts +0 -17
  137. package/dist/tui/components/RecruitWizard.js +0 -221
  138. package/dist/tui/components/RestoreConfirmModal.d.ts +0 -18
  139. package/dist/tui/components/RestoreConfirmModal.js +0 -71
  140. package/dist/tui/components/ScheduleWizard.d.ts +0 -19
  141. package/dist/tui/components/ScheduleWizard.js +0 -259
  142. package/dist/tui/components/Splash.d.ts +0 -23
  143. package/dist/tui/components/Splash.js +0 -221
  144. package/dist/tui/components/StatusBar.d.ts +0 -48
  145. package/dist/tui/components/StatusBar.js +0 -128
  146. package/dist/tui/components/StatusOverlay.d.ts +0 -15
  147. package/dist/tui/components/StatusOverlay.js +0 -76
  148. package/dist/tui/components/TitleBar.d.ts +0 -10
  149. package/dist/tui/components/TitleBar.js +0 -21
  150. package/dist/tui/index.d.ts +0 -15
  151. package/dist/tui/index.js +0 -162
  152. package/dist/tui/ink-context.d.ts +0 -18
  153. package/dist/tui/ink-context.js +0 -59
  154. package/dist/tui/ink-loader.d.ts +0 -26
  155. package/dist/tui/ink-loader.js +0 -42
  156. package/dist/tui/removed-commands.d.ts +0 -9
  157. package/dist/tui/removed-commands.js +0 -22
  158. package/dist/tui/sse-handler.d.ts +0 -52
  159. package/dist/tui/sse-handler.js +0 -157
  160. package/dist/tui/store.d.ts +0 -598
  161. package/dist/tui/store.js +0 -753
  162. package/dist/tui/utils/format.d.ts +0 -56
  163. package/dist/tui/utils/format.js +0 -155
  164. package/dist/tui/utils/fullscreen.d.ts +0 -23
  165. package/dist/tui/utils/fullscreen.js +0 -71
  166. package/dist/tui/utils/history.d.ts +0 -10
  167. package/dist/tui/utils/history.js +0 -85
  168. package/dist/tui/utils/platform.d.ts +0 -45
  169. package/dist/tui/utils/platform.js +0 -258
  170. package/dist/tui/utils/theme.d.ts +0 -21
  171. package/dist/tui/utils/theme.js +0 -24
@@ -1,70 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Picker = Picker;
7
- /**
8
- * Picker — generic interactive list overlay.
9
- * Renders as a SINGLE Text element (1 Yoga node) with newlines for rows.
10
- */
11
- const react_1 = __importDefault(require("react"));
12
- const ink_context_1 = require("../ink-context");
13
- const theme_1 = require("../utils/theme");
14
- const MAX_VISIBLE = 10;
15
- function Picker({ title, items, selectedIndex, hint }) {
16
- const { Text } = (0, ink_context_1.useInk)();
17
- if (items.length === 0) {
18
- return react_1.default.createElement(Text, null, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.accent }, ` ${title}`), '\n', react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' (none)'));
19
- }
20
- // Window around selected index
21
- let startIdx = 0;
22
- if (items.length > MAX_VISIBLE) {
23
- startIdx = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE / 2), items.length - MAX_VISIBLE));
24
- }
25
- const visible = items.slice(startIdx, startIdx + MAX_VISIBLE);
26
- const hasAbove = startIdx > 0;
27
- const hasBelow = startIdx + MAX_VISIBLE < items.length;
28
- const children = [];
29
- // Title
30
- children.push(react_1.default.createElement(Text, { key: 'title', bold: true, color: theme_1.THEME.accent }, ` ${title}`));
31
- children.push('\n');
32
- // Scroll-up
33
- if (hasAbove) {
34
- children.push(react_1.default.createElement(Text, { key: 'above', color: theme_1.THEME.dim }, ` \u2191 ${startIdx} more above`));
35
- children.push('\n');
36
- }
37
- // Items (with optional group headers)
38
- let lastGroup;
39
- for (let i = 0; i < visible.length; i++) {
40
- const item = visible[i];
41
- const actualIdx = startIdx + i;
42
- const isSelected = actualIdx === selectedIndex;
43
- const indicator = isSelected ? '\u25B8 ' : ' ';
44
- const currentMark = item.current ? ' \u25C0' : '';
45
- const labelColor = isSelected ? theme_1.THEME.accent : (item.color || theme_1.THEME.text);
46
- const icon = item.icon ? `${item.icon} ` : '';
47
- // Group header
48
- if (item.group && item.group !== lastGroup) {
49
- if (i > 0 || lastGroup !== undefined)
50
- children.push('\n');
51
- children.push('\n');
52
- children.push(react_1.default.createElement(Text, { key: `grp-${item.group}`, color: theme_1.THEME.dim }, ` \u2500\u2500 ${item.group} \u2500\u2500`));
53
- lastGroup = item.group;
54
- }
55
- if (i > 0 || lastGroup !== undefined)
56
- children.push('\n');
57
- children.push(react_1.default.createElement(react_1.default.Fragment, { key: item.id }, react_1.default.createElement(Text, { color: isSelected ? theme_1.THEME.accent : theme_1.THEME.dim }, indicator), react_1.default.createElement(Text, { color: labelColor, bold: isSelected }, `${icon}${item.label}`), react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ` ${item.detail}`), item.meta ? react_1.default.createElement(Text, { color: theme_1.THEME.muted }, ` ${item.meta}`) : null, currentMark ? react_1.default.createElement(Text, { color: theme_1.THEME.warning }, currentMark) : null));
58
- }
59
- // Scroll-down
60
- if (hasBelow) {
61
- children.push('\n');
62
- children.push(react_1.default.createElement(Text, { key: 'below', color: theme_1.THEME.dim }, ` \u2193 ${items.length - startIdx - MAX_VISIBLE} more below`));
63
- }
64
- // Hint
65
- if (hint) {
66
- children.push('\n');
67
- children.push(react_1.default.createElement(Text, { key: 'hint', color: theme_1.THEME.dim }, ` ${hint}`));
68
- }
69
- return react_1.default.createElement(Text, null, ...children);
70
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * PlayerDetailView — shows player metadata + scrollable message history.
3
- * Single <Text> pattern, zero Yoga nodes — all content pre-formatted as strings.
4
- *
5
- * PR-F cross-host indicator (design §16, brief §8 answer 3 — Option 3):
6
- * - Local session (`player.hostname === localHost`) → omit "Host:" field
7
- * from the metadata line (reduces noise; local is the common case).
8
- * - Remote session → show `Host: {hostname}` in `THEME.accent` (amber).
9
- * Zero new Yoga nodes: the host segment goes into the same `<Text>` children
10
- * tree as an additional nested `<Text>` (virtual text node, not a Box).
11
- */
12
- import React from 'react';
13
- import type { MaestroPlayerInfo, SessionMetadata, Message, SentMessage } from '../../types';
14
- export interface PlayerDetailViewProps {
15
- playerId: string;
16
- ensemble: string;
17
- player: MaestroPlayerInfo | null;
18
- metadata: SessionMetadata | null;
19
- messages: Array<Message | (SentMessage & {
20
- direction: 'sent';
21
- })>;
22
- scrollOffset: number;
23
- /** Local TUI process hostname. When equal to the player's hostname, the Host field is omitted. */
24
- localHost?: string;
25
- }
26
- export declare function PlayerDetailView({ playerId, player, metadata, messages, scrollOffset, localHost, }: PlayerDetailViewProps): React.CElement<{}, React.Component<{}, any, any>>;
@@ -1,118 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PlayerDetailView = PlayerDetailView;
7
- /**
8
- * PlayerDetailView — shows player metadata + scrollable message history.
9
- * Single <Text> pattern, zero Yoga nodes — all content pre-formatted as strings.
10
- *
11
- * PR-F cross-host indicator (design §16, brief §8 answer 3 — Option 3):
12
- * - Local session (`player.hostname === localHost`) → omit "Host:" field
13
- * from the metadata line (reduces noise; local is the common case).
14
- * - Remote session → show `Host: {hostname}` in `THEME.accent` (amber).
15
- * Zero new Yoga nodes: the host segment goes into the same `<Text>` children
16
- * tree as an additional nested `<Text>` (virtual text node, not a Box).
17
- */
18
- const react_1 = __importDefault(require("react"));
19
- const ink_context_1 = require("../ink-context");
20
- const theme_1 = require("../utils/theme");
21
- const format_1 = require("../utils/format");
22
- const platform_1 = require("../utils/platform");
23
- const MAX_VISIBLE = 20;
24
- function isSent(m) {
25
- return 'direction' in m && m.direction === 'sent';
26
- }
27
- function formatTime(ts) {
28
- try {
29
- const d = new Date(ts);
30
- return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
31
- }
32
- catch {
33
- return '??:??';
34
- }
35
- }
36
- function PlayerDetailView({ playerId, player, metadata, messages, scrollOffset, localHost, }) {
37
- const { Text } = (0, ink_context_1.useInk)();
38
- const children = [];
39
- // Header — player identity
40
- const icon = player?.isConductor ? '\u2605 ' : ' ';
41
- children.push(react_1.default.createElement(Text, { key: 'name', bold: true, color: theme_1.THEME.accent }, `${icon}${playerId}`));
42
- // Metadata line 1: Type · Status [· Host when remote]
43
- // Collapse the attachment phase via the Option-B helpers in utils/format.
44
- const type = player?.playerType || player?.agentType || '(default)';
45
- const status = (0, format_1.phaseToLabel)(player?.phase);
46
- const phaseIcons = (0, platform_1.statusIcons)((0, platform_1.supportsUnicode)());
47
- const statusDot = phaseIcons[(0, format_1.phaseToIconName)(player?.phase)];
48
- const host = player?.hostname || metadata?.hostname || '';
49
- const showRemoteHost = Boolean(host && localHost && host !== localHost);
50
- children.push('\n');
51
- if (showRemoteHost) {
52
- // Remote — Host segment rendered in amber, same Text tree (zero Yoga nodes).
53
- children.push(react_1.default.createElement(Text, { key: 'meta1', color: theme_1.THEME.dim }, ` Type: ${type} \u00B7 Status: ${statusDot} ${status} \u00B7 Host: `, react_1.default.createElement(Text, { color: theme_1.THEME.accent }, host)));
54
- }
55
- else {
56
- // Local (or unknown) — omit Host entirely.
57
- children.push(react_1.default.createElement(Text, { key: 'meta1', color: theme_1.THEME.dim }, ` Type: ${type} \u00B7 Status: ${statusDot} ${status}`));
58
- }
59
- // Metadata line 2: Branch · Dir
60
- const branch = player?.gitBranch || metadata?.gitBranch || '';
61
- const dir = metadata?.workDir || player?.workDir || '';
62
- if (branch || dir) {
63
- children.push('\n');
64
- const parts = [];
65
- if (branch)
66
- parts.push(`Branch: ${branch}`);
67
- if (dir)
68
- parts.push(`Dir: ${dir}`);
69
- children.push(react_1.default.createElement(Text, { key: 'meta2', color: theme_1.THEME.dim }, ` ${parts.join(' \u00B7 ')}`));
70
- }
71
- // Part
72
- if (player?.part) {
73
- children.push('\n');
74
- children.push(react_1.default.createElement(Text, { key: 'part', color: theme_1.THEME.textMuted }, ` Part: ${player.part}`));
75
- }
76
- // Divider
77
- children.push('\n\n');
78
- children.push(react_1.default.createElement(Text, { key: 'div', color: theme_1.THEME.border }, ' ' + '\u2500'.repeat(55)));
79
- // Messages header
80
- const total = messages.length;
81
- const windowStart = Math.min(scrollOffset, Math.max(0, total - MAX_VISIBLE));
82
- const visible = messages.slice(windowStart, windowStart + MAX_VISIBLE);
83
- const earlierCount = windowStart;
84
- children.push('\n');
85
- if (total === 0) {
86
- children.push(react_1.default.createElement(Text, { key: 'nomsg', color: theme_1.THEME.dim }, ' No messages yet.'));
87
- }
88
- else {
89
- children.push(react_1.default.createElement(Text, { key: 'mhdr', color: theme_1.THEME.dim }, ` Messages (${total} total \u00B7 showing ${visible.length})`));
90
- // #110: use the plural-aware helper instead of hardcoded "earlier messages".
91
- // The helper returns null for count ≤ 0 / non-finite, subsuming the prior
92
- // `if (earlierCount > 0)` guard. Singular/plural split is covered by the
93
- // helper's unit tests in `tests/tui/chat-cap.test.ts`.
94
- const earlierIndicator = (0, format_1.formatEarlierIndicator)(earlierCount);
95
- if (earlierIndicator) {
96
- children.push('\n');
97
- children.push(react_1.default.createElement(Text, { key: 'earlier', color: theme_1.THEME.dim }, ` ${earlierIndicator}`));
98
- }
99
- for (let i = 0; i < visible.length; i++) {
100
- const m = visible[i];
101
- const time = formatTime(m.timestamp);
102
- children.push('\n');
103
- if (isSent(m)) {
104
- const preview = m.text.length > 80 ? m.text.slice(0, 77).replace(/\n/g, ' ') + '...' : m.text.replace(/\n/g, ' ');
105
- children.push(react_1.default.createElement(react_1.default.Fragment, { key: `m-${i}` }, react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ` ${time} `), react_1.default.createElement(Text, { color: theme_1.THEME.accent, bold: true }, '\u276F '), react_1.default.createElement(Text, { color: theme_1.THEME.text }, preview)));
106
- }
107
- else {
108
- const msg = m;
109
- const preview = msg.text.length > 80 ? msg.text.slice(0, 77).replace(/\n/g, ' ') + '...' : msg.text.replace(/\n/g, ' ');
110
- children.push(react_1.default.createElement(react_1.default.Fragment, { key: `m-${i}` }, react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ` ${time} \u2190 `), react_1.default.createElement(Text, { color: theme_1.THEME.accent }, `${msg.from}: `), react_1.default.createElement(Text, { color: theme_1.THEME.text }, preview)));
111
- }
112
- }
113
- }
114
- // Footer hint
115
- children.push('\n\n');
116
- children.push(react_1.default.createElement(Text, { key: 'hint', color: theme_1.THEME.dim }, ' \u2191\u2193 scroll \u00B7 Esc back \u00B7 /cue to chat'));
117
- return react_1.default.createElement(Text, null, ...children);
118
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * PromptArea — pinned bottom input area with tab completion and command history.
3
- *
4
- * UNCONTROLLED: Input value lives in local useState, NOT in parent state.
5
- * This prevents parent re-renders on every keystroke, keeping Yoga node
6
- * recalculation to this subtree only (~4 nodes).
7
- *
8
- * Parent communicates via:
9
- * - inputRef.current.setValue(v) — to set input programmatically (palette select)
10
- * - inputRef.current.getValue() — to read current value
11
- * - onInputChange(v) — called on every keystroke (for palette filtering via ref, no dispatch)
12
- * - onSubmit(v) — called on Enter
13
- */
14
- import React from 'react';
15
- /** Commands that take a player name as their first argument. */
16
- export interface PromptAreaHandle {
17
- setValue: (v: string) => void;
18
- getValue: () => string;
19
- }
20
- export interface PromptAreaProps {
21
- /** Hint text displayed above the input. */
22
- hints: string;
23
- /** Called when user presses Enter. */
24
- onSubmit: (value: string) => void;
25
- /** Disable input (e.g., during splash or recruit wizard). */
26
- disabled?: boolean;
27
- /** Available command names (without '/') for tab completion. */
28
- commandNames?: string[];
29
- /** Available player names for argument completion. */
30
- playerNames?: string[];
31
- /** Initial command history (loaded from disk). */
32
- initialHistory?: string[];
33
- /** Called when history is updated (for persistence). */
34
- onHistoryUpdate?: (entries: string[]) => void;
35
- /** Called on every input change (lightweight — caller should use ref, not dispatch). */
36
- onInputChange?: (value: string) => void;
37
- /** Whether the command palette is visible. */
38
- paletteVisible?: boolean;
39
- /** Called when palette should show/hide. */
40
- onPaletteToggle?: (visible: boolean) => void;
41
- /** Called to navigate palette up. */
42
- onPaletteUp?: () => void;
43
- /** Called to navigate palette down. */
44
- onPaletteDown?: () => void;
45
- /** Called when palette item is selected. */
46
- onPaletteSelect?: () => void;
47
- /** Ref for parent to read/set input value. */
48
- inputRef?: React.Ref<PromptAreaHandle>;
49
- }
50
- export declare const PromptArea: React.NamedExoticComponent<PromptAreaProps>;
@@ -1,303 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.PromptArea = void 0;
37
- /**
38
- * PromptArea — pinned bottom input area with tab completion and command history.
39
- *
40
- * UNCONTROLLED: Input value lives in local useState, NOT in parent state.
41
- * This prevents parent re-renders on every keystroke, keeping Yoga node
42
- * recalculation to this subtree only (~4 nodes).
43
- *
44
- * Parent communicates via:
45
- * - inputRef.current.setValue(v) — to set input programmatically (palette select)
46
- * - inputRef.current.getValue() — to read current value
47
- * - onInputChange(v) — called on every keystroke (for palette filtering via ref, no dispatch)
48
- * - onSubmit(v) — called on Enter
49
- */
50
- const react_1 = __importStar(require("react"));
51
- const ink_context_1 = require("../ink-context");
52
- const theme_1 = require("../utils/theme");
53
- const commands_1 = require("../commands");
54
- const MAX_HISTORY = 50;
55
- exports.PromptArea = react_1.default.memo(function PromptArea({ hints, onSubmit, disabled, commandNames = [], playerNames = [], initialHistory = [], onHistoryUpdate, onInputChange, paletteVisible, onPaletteToggle, onPaletteUp, onPaletteDown, onPaletteSelect, inputRef, }) {
56
- const { Box, Text, useInput } = (0, ink_context_1.useInk)();
57
- // ── LOCAL input state (uncontrolled — no parent dispatch per keystroke) ──
58
- const [value, setValueState] = (0, react_1.useState)('');
59
- const valueRef = (0, react_1.useRef)('');
60
- const setValue = (0, react_1.useCallback)((v) => {
61
- valueRef.current = v;
62
- ref.current.value = v; // Keep ref in sync between renders (prevents stale reads on rapid input)
63
- setValueState(v);
64
- }, []);
65
- // ── Expose handle for parent ──
66
- (0, react_1.useImperativeHandle)(inputRef, () => ({
67
- setValue: (v) => setValue(v),
68
- getValue: () => valueRef.current,
69
- }), [setValue]);
70
- // ── Internal state ──
71
- const [history, setHistory] = (0, react_1.useState)(() => [...initialHistory]);
72
- const [historyIndex, setHistoryIndex] = (0, react_1.useState)(-1);
73
- const savedInput = (0, react_1.useRef)('');
74
- const [completionHint, setCompletionHint] = (0, react_1.useState)('');
75
- const [tabMatches, setTabMatches] = (0, react_1.useState)([]);
76
- const [tabCycleIndex, setTabCycleIndex] = (0, react_1.useState)(0);
77
- // ── Ref for all values the useInput callback reads (stable callback pattern) ──
78
- const ref = (0, react_1.useRef)({
79
- value: '', onSubmit, disabled, commandNames, playerNames,
80
- history, historyIndex, tabMatches, tabCycleIndex,
81
- paletteVisible, onPaletteToggle, onPaletteUp, onPaletteDown, onPaletteSelect,
82
- onHistoryUpdate, onInputChange,
83
- });
84
- ref.current = {
85
- value: valueRef.current, onSubmit, disabled, commandNames, playerNames,
86
- history, historyIndex, tabMatches, tabCycleIndex,
87
- paletteVisible, onPaletteToggle, onPaletteUp, onPaletteDown, onPaletteSelect,
88
- onHistoryUpdate, onInputChange,
89
- };
90
- // ── Helpers (read from ref) ──
91
- const doChange = (0, react_1.useCallback)((newValue) => {
92
- const r = ref.current;
93
- setValue(newValue);
94
- r.onInputChange?.(newValue);
95
- // Avoid new refs when already in default state — prevents unnecessary re-renders
96
- setCompletionHint(prev => prev === '' ? prev : '');
97
- setTabMatches(prev => prev.length === 0 ? prev : []);
98
- setTabCycleIndex(prev => prev === 0 ? prev : 0);
99
- // Only toggle palette when visibility actually changes — avoids parent dispatch on every keystroke
100
- if (r.onPaletteToggle) {
101
- // The palette shows for: "/cmd", "@name", and "/PLAYER_PARAM_CMD <partial>".
102
- // classifyPaletteInput centralizes the rule so PromptArea + App stay in sync.
103
- const shouldShow = (0, commands_1.classifyPaletteInput)(newValue) !== null;
104
- if (shouldShow !== !!r.paletteVisible) {
105
- r.onPaletteToggle(shouldShow);
106
- }
107
- }
108
- }, [setValue]);
109
- // PLAYER_PARAM_COMMANDS and SUBCOMMAND_MAP imported from commands.ts
110
- const getCompletions = (0, react_1.useCallback)((input) => {
111
- const r = ref.current;
112
- const trimmed = input.trimStart();
113
- // Command name completion: /par → /players
114
- if (trimmed.startsWith('/') && !trimmed.includes(' ')) {
115
- const partial = trimmed.slice(1).toLowerCase();
116
- if (!partial)
117
- return r.commandNames.map(c => `/${c} `);
118
- return r.commandNames
119
- .filter(c => c.startsWith(partial) && c !== partial)
120
- .map(c => `/${c} `);
121
- }
122
- // Parameter completion: /stop te → /stop tempo-eng
123
- if (trimmed.startsWith('/') && trimmed.includes(' ')) {
124
- const spaceIdx = trimmed.indexOf(' ');
125
- const cmd = trimmed.slice(1, spaceIdx).toLowerCase();
126
- const afterCmd = trimmed.slice(spaceIdx + 1);
127
- const parts = afterCmd.split(' ');
128
- const partial = parts[parts.length - 1].toLowerCase();
129
- const prefix = trimmed.slice(0, trimmed.length - parts[parts.length - 1].length);
130
- // First param: player name for player commands (prefix + segment match)
131
- if (parts.length === 1 && commands_1.PLAYER_PARAM_COMMANDS.has(cmd)) {
132
- const names = r.playerNames || [];
133
- const matches = names.filter(n => {
134
- const lower = n.toLowerCase();
135
- if (lower === partial)
136
- return false;
137
- return lower.startsWith(partial) || lower.split('-').some(seg => seg.startsWith(partial));
138
- });
139
- if (matches.length > 0)
140
- return matches.map(n => `${prefix}${n} `);
141
- }
142
- // First param: subcommand for structured commands
143
- if (parts.length === 1 && commands_1.SUBCOMMAND_MAP[cmd]) {
144
- const subs = commands_1.SUBCOMMAND_MAP[cmd];
145
- const matches = subs.filter(s => s.startsWith(partial) && s !== partial);
146
- if (matches.length > 0)
147
- return matches.map(s => `${prefix}${s} `);
148
- }
149
- // Second param after subcommand: player name for worktree create/remove
150
- if (parts.length === 2 && cmd === 'worktree' && (parts[0] === 'create' || parts[0] === 'remove')) {
151
- const names = r.playerNames || [];
152
- const matches = names.filter(n => n.toLowerCase().startsWith(partial) && n.toLowerCase() !== partial);
153
- if (matches.length > 0)
154
- return matches.map(n => `${prefix}${n} `);
155
- }
156
- return [];
157
- }
158
- // @player completion: @tem → @tempo-eng
159
- if (trimmed.startsWith('@') && !trimmed.includes(' ')) {
160
- const partial = trimmed.slice(1).toLowerCase();
161
- if (!partial)
162
- return r.playerNames.map(n => `@${n} `);
163
- return r.playerNames
164
- .filter(n => {
165
- const lower = n.toLowerCase();
166
- if (lower === partial)
167
- return false;
168
- return lower.startsWith(partial) || lower.split('-').some(seg => seg.startsWith(partial));
169
- })
170
- .map(n => `@${n} `);
171
- }
172
- return [];
173
- }, []);
174
- // ── Stable useInput callback (never recreated) ──
175
- useInput((0, react_1.useCallback)((input, key) => {
176
- const r = ref.current;
177
- if (r.disabled)
178
- return;
179
- // ── Command palette mode ──
180
- if (r.paletteVisible) {
181
- if (key.upArrow) {
182
- r.onPaletteUp?.();
183
- return;
184
- }
185
- if (key.downArrow) {
186
- r.onPaletteDown?.();
187
- return;
188
- }
189
- if (key.tab) {
190
- r.onPaletteSelect?.();
191
- return;
192
- }
193
- if (key.return) {
194
- // Enter always submits. Use Tab to accept a palette suggestion. This
195
- // avoids accept-vs-submit ambiguity when the typed command already
196
- // matches exactly but the palette has no remaining suggestions
197
- // (e.g. "/destroy conductor"). Close the palette and fall through to
198
- // the regular Enter/submit block below.
199
- r.onPaletteToggle?.(false);
200
- }
201
- if (key.escape) {
202
- r.onPaletteToggle?.(false);
203
- return;
204
- }
205
- }
206
- // Tab: complete
207
- if (key.tab) {
208
- const matches = r.tabMatches.length > 0 ? r.tabMatches : getCompletions(r.value);
209
- if (matches.length === 0)
210
- return;
211
- if (matches.length === 1) {
212
- setValue(matches[0]);
213
- r.onInputChange?.(matches[0]);
214
- setCompletionHint('');
215
- setTabMatches([]);
216
- setTabCycleIndex(0);
217
- }
218
- else {
219
- const newMatches = r.tabMatches.length > 0 ? r.tabMatches : matches;
220
- const idx = r.tabMatches.length > 0 ? (r.tabCycleIndex + 1) % newMatches.length : 0;
221
- setValue(newMatches[idx]);
222
- r.onInputChange?.(newMatches[idx]);
223
- setTabMatches(newMatches);
224
- setTabCycleIndex(idx);
225
- const options = newMatches.map(m => m.trim().split(/\s+/).pop() || '');
226
- setCompletionHint(options.join(' '));
227
- }
228
- return;
229
- }
230
- // Up arrow: previous history
231
- if (key.upArrow && !r.paletteVisible) {
232
- if (r.history.length === 0)
233
- return;
234
- if (r.historyIndex === -1)
235
- savedInput.current = r.value;
236
- const newIdx = Math.min(r.historyIndex + 1, r.history.length - 1);
237
- setHistoryIndex(newIdx);
238
- const histValue = r.history[r.history.length - 1 - newIdx];
239
- setValue(histValue);
240
- r.onInputChange?.(histValue);
241
- setCompletionHint('');
242
- setTabMatches([]);
243
- return;
244
- }
245
- // Down arrow: next history
246
- if (key.downArrow && !r.paletteVisible) {
247
- if (r.historyIndex <= 0) {
248
- setHistoryIndex(-1);
249
- setValue(savedInput.current);
250
- r.onInputChange?.(savedInput.current);
251
- return;
252
- }
253
- const newIdx = r.historyIndex - 1;
254
- setHistoryIndex(newIdx);
255
- const histValue = r.history[r.history.length - 1 - newIdx];
256
- setValue(histValue);
257
- r.onInputChange?.(histValue);
258
- setCompletionHint('');
259
- setTabMatches([]);
260
- return;
261
- }
262
- // Enter: submit
263
- if (key.return) {
264
- const trimmed = r.value.trim();
265
- if (trimmed) {
266
- if (r.history.length === 0 || r.history[r.history.length - 1] !== trimmed) {
267
- const updated = [...r.history, trimmed].slice(-MAX_HISTORY);
268
- setHistory(updated);
269
- if (r.onHistoryUpdate)
270
- r.onHistoryUpdate(updated);
271
- }
272
- setHistoryIndex(-1);
273
- savedInput.current = '';
274
- r.onSubmit(trimmed);
275
- }
276
- setValue('');
277
- r.onInputChange?.('');
278
- setCompletionHint('');
279
- setTabMatches([]);
280
- return;
281
- }
282
- // Backspace
283
- if (key.backspace || key.delete) {
284
- if (r.value.length > 0)
285
- doChange(r.value.slice(0, -1));
286
- return;
287
- }
288
- // Regular character input
289
- if (input && !key.ctrl && !key.meta) {
290
- doChange(r.value + input);
291
- }
292
- }, [doChange, getCompletions, setValue])); // Stable — reads ref.current
293
- // ── Render (minimal nodes: 1 Box + 2-3 Text, nested Text = 0 Yoga nodes) ──
294
- return react_1.default.createElement(Box, { flexDirection: 'column', paddingX: 1 }, completionHint
295
- ? react_1.default.createElement(Text, { color: theme_1.THEME.muted }, ` ${completionHint}`)
296
- : null,
297
- // Prompt line with inline placeholder hint (like Claude Code)
298
- react_1.default.createElement(Text, null, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.accent }, '\u2669 '), disabled
299
- ? react_1.default.createElement(Text, { color: theme_1.THEME.muted }, '...')
300
- : value
301
- ? react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(Text, { color: theme_1.THEME.text }, value), react_1.default.createElement(Text, { color: theme_1.THEME.accent }, '\u2588'))
302
- : react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement(Text, { color: theme_1.THEME.accent }, '\u2588'), react_1.default.createElement(Text, { color: theme_1.THEME.dim }, hints))));
303
- });
@@ -1,17 +0,0 @@
1
- /**
2
- * RecruitWizard — step-by-step wizard for spawning a new player session.
3
- *
4
- * Minimal-Box pattern: single <Text> root for all static content (0 Yoga nodes),
5
- * with ONE <Box> wrapper for Ink's TextInput when a text-input step is active.
6
- */
7
- import React from 'react';
8
- import type { RecruitState, RecruitAnswers } from '../store';
9
- export interface RecruitWizardProps {
10
- state: RecruitState;
11
- onAnswer: (answer: Partial<RecruitAnswers>) => void;
12
- onBack: () => void;
13
- onConfirm: () => void;
14
- onCancel: () => void;
15
- onDone: () => void;
16
- }
17
- export declare function RecruitWizard({ state, onAnswer, onBack, onConfirm, onCancel, onDone }: RecruitWizardProps): React.FunctionComponentElement<React.FragmentProps> | React.CElement<{}, React.Component<{}, any, any>>;