@agentuity/coder 1.0.37

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 (92) hide show
  1. package/README.md +57 -0
  2. package/dist/chain-preview.d.ts +55 -0
  3. package/dist/chain-preview.d.ts.map +1 -0
  4. package/dist/chain-preview.js +472 -0
  5. package/dist/chain-preview.js.map +1 -0
  6. package/dist/client.d.ts +43 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +402 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/commands.d.ts +22 -0
  11. package/dist/commands.d.ts.map +1 -0
  12. package/dist/commands.js +99 -0
  13. package/dist/commands.js.map +1 -0
  14. package/dist/footer.d.ts +34 -0
  15. package/dist/footer.d.ts.map +1 -0
  16. package/dist/footer.js +249 -0
  17. package/dist/footer.js.map +1 -0
  18. package/dist/handlers.d.ts +24 -0
  19. package/dist/handlers.d.ts.map +1 -0
  20. package/dist/handlers.js +83 -0
  21. package/dist/handlers.js.map +1 -0
  22. package/dist/hub-overlay.d.ts +107 -0
  23. package/dist/hub-overlay.d.ts.map +1 -0
  24. package/dist/hub-overlay.js +1794 -0
  25. package/dist/hub-overlay.js.map +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +1585 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/output-viewer.d.ts +49 -0
  31. package/dist/output-viewer.d.ts.map +1 -0
  32. package/dist/output-viewer.js +389 -0
  33. package/dist/output-viewer.js.map +1 -0
  34. package/dist/overlay.d.ts +40 -0
  35. package/dist/overlay.d.ts.map +1 -0
  36. package/dist/overlay.js +225 -0
  37. package/dist/overlay.js.map +1 -0
  38. package/dist/protocol.d.ts +118 -0
  39. package/dist/protocol.d.ts.map +1 -0
  40. package/dist/protocol.js +3 -0
  41. package/dist/protocol.js.map +1 -0
  42. package/dist/remote-session.d.ts +113 -0
  43. package/dist/remote-session.d.ts.map +1 -0
  44. package/dist/remote-session.js +645 -0
  45. package/dist/remote-session.js.map +1 -0
  46. package/dist/remote-tui.d.ts +40 -0
  47. package/dist/remote-tui.d.ts.map +1 -0
  48. package/dist/remote-tui.js +606 -0
  49. package/dist/remote-tui.js.map +1 -0
  50. package/dist/renderers.d.ts +34 -0
  51. package/dist/renderers.d.ts.map +1 -0
  52. package/dist/renderers.js +669 -0
  53. package/dist/renderers.js.map +1 -0
  54. package/dist/review.d.ts +15 -0
  55. package/dist/review.d.ts.map +1 -0
  56. package/dist/review.js +154 -0
  57. package/dist/review.js.map +1 -0
  58. package/dist/titlebar.d.ts +3 -0
  59. package/dist/titlebar.d.ts.map +1 -0
  60. package/dist/titlebar.js +59 -0
  61. package/dist/titlebar.js.map +1 -0
  62. package/dist/todo/index.d.ts +3 -0
  63. package/dist/todo/index.d.ts.map +1 -0
  64. package/dist/todo/index.js +3 -0
  65. package/dist/todo/index.js.map +1 -0
  66. package/dist/todo/store.d.ts +6 -0
  67. package/dist/todo/store.d.ts.map +1 -0
  68. package/dist/todo/store.js +43 -0
  69. package/dist/todo/store.js.map +1 -0
  70. package/dist/todo/types.d.ts +13 -0
  71. package/dist/todo/types.d.ts.map +1 -0
  72. package/dist/todo/types.js +2 -0
  73. package/dist/todo/types.js.map +1 -0
  74. package/package.json +44 -0
  75. package/src/chain-preview.ts +621 -0
  76. package/src/client.ts +515 -0
  77. package/src/commands.ts +132 -0
  78. package/src/footer.ts +305 -0
  79. package/src/handlers.ts +113 -0
  80. package/src/hub-overlay.ts +2324 -0
  81. package/src/index.ts +1907 -0
  82. package/src/output-viewer.ts +480 -0
  83. package/src/overlay.ts +294 -0
  84. package/src/protocol.ts +157 -0
  85. package/src/remote-session.ts +800 -0
  86. package/src/remote-tui.ts +707 -0
  87. package/src/renderers.ts +740 -0
  88. package/src/review.ts +201 -0
  89. package/src/titlebar.ts +63 -0
  90. package/src/todo/index.ts +2 -0
  91. package/src/todo/store.ts +49 -0
  92. package/src/todo/types.ts +14 -0
package/src/overlay.ts ADDED
@@ -0,0 +1,294 @@
1
+ import type { Theme } from '@mariozechner/pi-coding-agent';
2
+ import { matchesKey } from '@mariozechner/pi-tui';
3
+ import type { AgentDefinition } from './protocol.ts';
4
+ import { truncateToWidth } from './renderers.ts';
5
+
6
+ export interface AgentManagerResult {
7
+ agent: string;
8
+ action: 'run';
9
+ }
10
+
11
+ interface Component {
12
+ render(width: number): string[];
13
+ handleInput?(data: string): void;
14
+ invalidate(): void;
15
+ }
16
+
17
+ interface Focusable {
18
+ focused: boolean;
19
+ }
20
+
21
+ type DoneFn = (result: AgentManagerResult | undefined) => void;
22
+ type Screen = 'list' | 'detail';
23
+
24
+ const ANSI_RE = /\x1b\[[0-9;]*m/g;
25
+
26
+ function visibleWidth(text: string): number {
27
+ return text.replace(ANSI_RE, '').length;
28
+ }
29
+
30
+ function padRight(text: string, width: number): string {
31
+ if (width <= 0) return '';
32
+ const truncated = truncateToWidth(text, width);
33
+ const remaining = width - visibleWidth(truncated);
34
+ return remaining > 0 ? truncated + ' '.repeat(remaining) : truncated;
35
+ }
36
+
37
+ function hLine(width: number): string {
38
+ return width > 0 ? '─'.repeat(width) : '';
39
+ }
40
+
41
+ function buildTopBorder(width: number, title: string): string {
42
+ if (width <= 0) return '';
43
+ if (width === 1) return '╭';
44
+ if (width === 2) return '╭╮';
45
+
46
+ const inner = width - 2;
47
+ const titleText = ` ${title} `;
48
+ if (titleText.length >= inner) {
49
+ return `╭${hLine(inner)}╮`;
50
+ }
51
+
52
+ const left = Math.floor((inner - titleText.length) / 2);
53
+ const right = inner - titleText.length - left;
54
+ return `╭${hLine(left)}${titleText}${hLine(right)}╮`;
55
+ }
56
+
57
+ function buildBottomBorder(width: number): string {
58
+ if (width <= 0) return '';
59
+ if (width === 1) return '╰';
60
+ if (width === 2) return '╰╯';
61
+ return `╰${hLine(width - 2)}╯`;
62
+ }
63
+
64
+ export class AgentManagerOverlay implements Component, Focusable {
65
+ public focused = true;
66
+
67
+ private readonly theme: Theme;
68
+ private readonly agents: AgentDefinition[];
69
+ private readonly done: DoneFn;
70
+
71
+ private screen: Screen = 'list';
72
+ private selectedIndex = 0;
73
+ private readonly listWindowSize = 6;
74
+ private disposed = false;
75
+
76
+ constructor(theme: Theme, agents: AgentDefinition[], done: DoneFn) {
77
+ this.theme = theme;
78
+ this.agents = agents;
79
+ this.done = done;
80
+ }
81
+
82
+ handleInput(data: string): void {
83
+ if (this.disposed) return;
84
+
85
+ if (matchesKey(data, 'escape')) {
86
+ if (this.screen === 'detail') {
87
+ this.screen = 'list';
88
+ this.invalidate();
89
+ return;
90
+ }
91
+ this.close(undefined);
92
+ return;
93
+ }
94
+
95
+ if (this.screen === 'list') {
96
+ this.handleListInput(data);
97
+ return;
98
+ }
99
+
100
+ this.handleDetailInput(data);
101
+ }
102
+
103
+ render(width: number): string[] {
104
+ const safeWidth = Math.max(4, width);
105
+ const termHeight = process.stdout.rows || 40;
106
+ // Match overlay maxHeight of 95%, leave margin for overlay chrome
107
+ const maxLines = Math.max(10, Math.floor(termHeight * 0.95) - 2);
108
+
109
+ const lines =
110
+ this.screen === 'detail'
111
+ ? this.renderDetailScreen(safeWidth)
112
+ : this.renderListScreen(safeWidth, maxLines);
113
+ return lines.map((line) => truncateToWidth(line, safeWidth));
114
+ }
115
+
116
+ invalidate(): void {
117
+ // Stateless rendering; no cache invalidation required.
118
+ }
119
+
120
+ dispose(): void {
121
+ this.disposed = true;
122
+ }
123
+
124
+ private handleListInput(data: string): void {
125
+ const count = this.agents.length;
126
+ if (count === 0) return;
127
+
128
+ if (matchesKey(data, 'up')) {
129
+ this.selectedIndex = (this.selectedIndex - 1 + count) % count;
130
+ this.invalidate();
131
+ return;
132
+ }
133
+
134
+ if (matchesKey(data, 'down')) {
135
+ this.selectedIndex = (this.selectedIndex + 1) % count;
136
+ this.invalidate();
137
+ return;
138
+ }
139
+
140
+ if (matchesKey(data, 'enter')) {
141
+ this.screen = 'detail';
142
+ this.invalidate();
143
+ }
144
+ }
145
+
146
+ private handleDetailInput(data: string): void {
147
+ if (matchesKey(data, 'r') || data.toLowerCase() === 'r') {
148
+ const selected = this.agents[this.selectedIndex];
149
+ if (!selected) return;
150
+ this.close({ action: 'run', agent: selected.name });
151
+ }
152
+ }
153
+
154
+ private close(result: AgentManagerResult | undefined): void {
155
+ if (this.disposed) return;
156
+ this.disposed = true;
157
+ this.done(result);
158
+ }
159
+
160
+ private renderListScreen(width: number, maxLines: number): string[] {
161
+ const inner = Math.max(0, width - 2);
162
+
163
+ // Fixed header (always rendered)
164
+ const header: string[] = [
165
+ buildTopBorder(width, 'Agent Manager'),
166
+ this.contentLine('', inner),
167
+ ];
168
+
169
+ // Fixed footer (always rendered)
170
+ const footer: string[] = [
171
+ this.contentLine(
172
+ this.theme.fg('dim', ' [↑↓] Navigate [Enter] Details [Esc] Close'),
173
+ inner
174
+ ),
175
+ buildBottomBorder(width),
176
+ ];
177
+
178
+ // Available lines for scrollable content area
179
+ const contentBudget = Math.max(4, maxLines - header.length - footer.length);
180
+
181
+ if (this.agents.length === 0) {
182
+ const content = [
183
+ this.contentLine(this.theme.fg('muted', ' No agents available'), inner),
184
+ this.contentLine('', inner),
185
+ ];
186
+ return [...header, ...content, ...footer];
187
+ }
188
+
189
+ // Each agent takes 4 lines: name, description, capabilities, empty line
190
+ const LINES_PER_AGENT = 4;
191
+ // Reserve 2 lines for possible scroll indicators
192
+ const scrollReserve = 2;
193
+ const maxAgents = Math.max(1, Math.floor((contentBudget - scrollReserve) / LINES_PER_AGENT));
194
+
195
+ // Dynamic window size based on available space
196
+ const windowSize = Math.min(maxAgents, this.agents.length);
197
+ const [start, end] = this.getVisibleRange(windowSize);
198
+
199
+ const content: string[] = [];
200
+
201
+ if (start > 0) {
202
+ content.push(this.contentLine(this.theme.fg('dim', ` ↑ ${start} more above`), inner));
203
+ }
204
+
205
+ for (let i = start; i < end; i++) {
206
+ const agent = this.agents[i]!;
207
+ const selected = i === this.selectedIndex;
208
+ const prefix = selected ? this.theme.fg('accent', '› ') : ' ';
209
+
210
+ const model = agent.model ? this.theme.fg('dim', ` [${agent.model}]`) : '';
211
+ const readOnly = agent.readOnly ? this.theme.fg('warning', ' read-only') : '';
212
+ const title = `${prefix}${this.theme.bold(agent.name)}${model}${readOnly}`;
213
+ content.push(this.contentLine(title, inner));
214
+
215
+ const description = this.theme.fg('text', ` ${agent.description || 'No description'}`);
216
+ content.push(this.contentLine(description, inner));
217
+
218
+ const capsText = agent.capabilities?.length ? agent.capabilities.join(', ') : 'none';
219
+ const caps = this.theme.fg('muted', ` capabilities: ${capsText}`);
220
+ content.push(this.contentLine(caps, inner));
221
+
222
+ content.push(this.contentLine('', inner));
223
+ }
224
+
225
+ if (end < this.agents.length) {
226
+ content.push(
227
+ this.contentLine(
228
+ this.theme.fg('dim', ` ↓ ${this.agents.length - end} more below`),
229
+ inner
230
+ )
231
+ );
232
+ }
233
+
234
+ return [...header, ...content, ...footer];
235
+ }
236
+
237
+ private renderDetailScreen(width: number): string[] {
238
+ const inner = Math.max(0, width - 2);
239
+ const lines: string[] = [];
240
+ const agent = this.agents[this.selectedIndex];
241
+
242
+ lines.push(buildTopBorder(width, agent?.name || 'Agent'));
243
+ lines.push(this.contentLine('', inner));
244
+
245
+ if (!agent) {
246
+ lines.push(this.contentLine(this.theme.fg('error', ' Agent not found'), inner));
247
+ lines.push(this.contentLine('', inner));
248
+ lines.push(this.contentLine(this.theme.fg('dim', ' [Esc] Back'), inner));
249
+ lines.push(buildBottomBorder(width));
250
+ return lines;
251
+ }
252
+
253
+ const readOnly = agent.readOnly ? 'yes' : 'no';
254
+ const capabilities = agent.capabilities?.length ? agent.capabilities.join(', ') : 'none';
255
+ const status = agent.status || 'idle';
256
+
257
+ lines.push(this.fieldLine('Model', agent.model || 'default', inner));
258
+ lines.push(this.fieldLine('Read-only', readOnly, inner));
259
+ lines.push(this.fieldLine('Description', agent.description || 'No description', inner));
260
+ lines.push(this.fieldLine('Capabilities', capabilities, inner));
261
+ lines.push(this.fieldLine('Status', status, inner));
262
+ lines.push(this.contentLine('', inner));
263
+ lines.push(this.contentLine(this.theme.fg('dim', ' [r] Run agent [Esc] Back'), inner));
264
+ lines.push(buildBottomBorder(width));
265
+ return lines;
266
+ }
267
+
268
+ private fieldLine(label: string, value: string, innerWidth: number): string {
269
+ const labelText = this.theme.fg('dim', ` ${label}:`);
270
+ const content = `${labelText} ${this.theme.fg('text', value)}`;
271
+ return this.contentLine(content, innerWidth);
272
+ }
273
+
274
+ private contentLine(content: string, innerWidth: number): string {
275
+ return `│${padRight(content, innerWidth)}│`;
276
+ }
277
+
278
+ private getVisibleRange(windowSize?: number): [number, number] {
279
+ const count = this.agents.length;
280
+ const ws = windowSize ?? this.listWindowSize;
281
+ if (count <= ws) return [0, count];
282
+
283
+ const half = Math.floor(ws / 2);
284
+ let start = Math.max(0, this.selectedIndex - half);
285
+ let end = start + ws;
286
+
287
+ if (end > count) {
288
+ end = count;
289
+ start = Math.max(0, end - ws);
290
+ }
291
+
292
+ return [start, end];
293
+ }
294
+ }
@@ -0,0 +1,157 @@
1
+ // ---- Init Message (Server → Client on connect) ----
2
+
3
+ export interface HubToolDefinition {
4
+ name: string;
5
+ label: string;
6
+ description: string;
7
+ parameters: Record<string, unknown>; // JSON Schema object
8
+ promptSnippet?: string;
9
+ promptGuidelines?: string;
10
+ }
11
+
12
+ /** Command definition sent by Hub for agent routing slash commands. */
13
+ export interface HubCommandDefinition {
14
+ name: string;
15
+ description: string;
16
+ }
17
+
18
+ export interface AgentDefinition {
19
+ name: string;
20
+ description: string;
21
+ systemPrompt: string;
22
+ model?: string;
23
+ tools?: string[];
24
+ temperature?: number;
25
+ thinkingLevel?: string;
26
+ readOnly?: boolean;
27
+ hubTools?: HubToolDefinition[];
28
+ capabilities?: string[];
29
+ status?: 'available' | 'busy' | 'offline';
30
+ }
31
+
32
+ export interface HubConfig {
33
+ systemPromptPrefix?: string;
34
+ systemPromptSuffix?: string;
35
+ }
36
+
37
+ export interface InitMessage {
38
+ type: 'init';
39
+ sessionId?: string;
40
+ tools?: HubToolDefinition[];
41
+ commands?: HubCommandDefinition[];
42
+ agents?: AgentDefinition[];
43
+ config?: HubConfig;
44
+ }
45
+
46
+ // ---- Request Messages (Client → Server) ----
47
+
48
+ export interface EventRequest {
49
+ id: string;
50
+ type: 'event';
51
+ event: string;
52
+ data: Record<string, unknown>;
53
+ }
54
+
55
+ export interface ToolRequest {
56
+ id: string;
57
+ type: 'tool';
58
+ name: string;
59
+ toolCallId: string;
60
+ params: Record<string, unknown>;
61
+ }
62
+
63
+ /** Command request (Client -> Server) for slash command execution. */
64
+ export interface CommandRequest {
65
+ id: string;
66
+ type: 'command';
67
+ name: string;
68
+ args: string;
69
+ }
70
+
71
+ export type HubRequest = EventRequest | ToolRequest | CommandRequest;
72
+
73
+ // ---- Actions ----
74
+
75
+ export interface AckAction {
76
+ action: 'ACK';
77
+ }
78
+
79
+ export interface BlockAction {
80
+ action: 'BLOCK';
81
+ reason: string;
82
+ }
83
+
84
+ export interface ConfirmAction {
85
+ action: 'CONFIRM';
86
+ title: string;
87
+ message: string;
88
+ deny_reason?: string;
89
+ }
90
+
91
+ export interface NotifyAction {
92
+ action: 'NOTIFY';
93
+ message: string;
94
+ level?: 'info' | 'warning' | 'error';
95
+ }
96
+
97
+ export interface ReturnAction {
98
+ action: 'RETURN';
99
+ result: unknown;
100
+ }
101
+
102
+ export interface StatusAction {
103
+ action: 'STATUS';
104
+ key: string;
105
+ text?: string; // undefined = clear status
106
+ }
107
+
108
+ export interface SystemPromptAction {
109
+ action: 'SYSTEM_PROMPT';
110
+ systemPrompt: string;
111
+ mode?: 'replace' | 'prefix' | 'suffix';
112
+ }
113
+
114
+ export interface InjectMessageAction {
115
+ action: 'INJECT_MESSAGE';
116
+ message: {
117
+ role: 'user' | 'assistant';
118
+ content: string;
119
+ };
120
+ }
121
+
122
+ export type HubAction =
123
+ | AckAction
124
+ | BlockAction
125
+ | ConfirmAction
126
+ | NotifyAction
127
+ | ReturnAction
128
+ | StatusAction
129
+ | SystemPromptAction
130
+ | InjectMessageAction;
131
+
132
+ // ---- Progress Tracking (Sub-Agent → Parent) ----
133
+
134
+ /** Progress update from a running sub-agent */
135
+ export interface AgentProgressUpdate {
136
+ agentName: string;
137
+ status:
138
+ | 'running'
139
+ | 'tool_start'
140
+ | 'tool_end'
141
+ | 'completed'
142
+ | 'failed'
143
+ | 'thinking_delta'
144
+ | 'text_delta';
145
+ currentTool?: string;
146
+ currentToolArgs?: string;
147
+ elapsed: number;
148
+ tokens?: { input: number; output: number; cost: number };
149
+ delta?: string; // Streaming token delta for thinking_delta / text_delta
150
+ }
151
+
152
+ // ---- Response Message (Server → Client) ----
153
+
154
+ export interface HubResponse {
155
+ id: string;
156
+ actions: HubAction[];
157
+ }