@a5c-ai/babysitter-pi 0.1.0
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/README.md +79 -0
- package/bin/cli.cjs +78 -0
- package/bin/install.cjs +144 -0
- package/bin/uninstall.cjs +40 -0
- package/commands/babysitter-call.md +12 -0
- package/commands/babysitter-doctor.md +10 -0
- package/commands/babysitter-resume.md +16 -0
- package/commands/babysitter-status.md +15 -0
- package/extensions/babysitter/cli-wrapper.ts +95 -0
- package/extensions/babysitter/constants.ts +77 -0
- package/extensions/babysitter/custom-tools.ts +208 -0
- package/extensions/babysitter/effect-executor.ts +362 -0
- package/extensions/babysitter/guards.ts +257 -0
- package/extensions/babysitter/index.ts +554 -0
- package/extensions/babysitter/loop-driver.ts +256 -0
- package/extensions/babysitter/result-poster.ts +115 -0
- package/extensions/babysitter/sdk-bridge.ts +243 -0
- package/extensions/babysitter/session-binder.ts +284 -0
- package/extensions/babysitter/status-line.ts +54 -0
- package/extensions/babysitter/task-interceptor.ts +82 -0
- package/extensions/babysitter/todo-replacement.ts +125 -0
- package/extensions/babysitter/tool-renderer.ts +263 -0
- package/extensions/babysitter/tui-widgets.ts +164 -0
- package/extensions/babysitter/types.ts +222 -0
- package/package.json +56 -0
- package/scripts/setup.sh +74 -0
- package/scripts/sync-command-docs.cjs +115 -0
- package/skills/babysitter/SKILL.md +45 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom message renderer for babysitter tool calls.
|
|
3
|
+
*
|
|
4
|
+
* Registered with oh-my-pi via `pi.registerMessageRenderer` so that
|
|
5
|
+
* babysitter effect execution results, run status updates, and iteration
|
|
6
|
+
* progress are displayed in a structured, human-readable format rather
|
|
7
|
+
* than raw JSON.
|
|
8
|
+
*
|
|
9
|
+
* @module tool-renderer
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { MessageRenderer } from './types';
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Payload interfaces
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
/** The payload shape expected by the babysitter tool renderer. */
|
|
19
|
+
export interface BabysitterToolPayload {
|
|
20
|
+
effectId: string;
|
|
21
|
+
kind: string;
|
|
22
|
+
title: string;
|
|
23
|
+
success: boolean;
|
|
24
|
+
value?: unknown;
|
|
25
|
+
error?: string;
|
|
26
|
+
durationMs?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Payload for the `babysitter:status` message type. */
|
|
30
|
+
export interface RunStatusPayload {
|
|
31
|
+
runId: string;
|
|
32
|
+
processId?: string;
|
|
33
|
+
iteration?: number;
|
|
34
|
+
status: string;
|
|
35
|
+
pendingEffectsCount?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Payload for the `babysitter:effect-result` message type. */
|
|
39
|
+
export interface EffectResultPayload {
|
|
40
|
+
effectId: string;
|
|
41
|
+
kind: string;
|
|
42
|
+
status: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Payload for the `babysitter:iteration` message type. */
|
|
46
|
+
export interface IterationPayload {
|
|
47
|
+
iteration: number;
|
|
48
|
+
status: string;
|
|
49
|
+
pendingCount?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Box-drawing helpers
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
const BOX_HORIZONTAL = '\u2500';
|
|
57
|
+
const BOX_VERTICAL = '\u2502';
|
|
58
|
+
const BOX_TOP_LEFT = '\u250C';
|
|
59
|
+
const BOX_TOP_RIGHT = '\u2510';
|
|
60
|
+
const BOX_BOTTOM_LEFT = '\u2514';
|
|
61
|
+
const BOX_BOTTOM_RIGHT = '\u2518';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Wrap a set of lines inside a simple Unicode box.
|
|
65
|
+
*
|
|
66
|
+
* @param lines - Content lines (no newlines within each entry).
|
|
67
|
+
* @param minWidth - Minimum inner width of the box.
|
|
68
|
+
* @returns The formatted box as a single string.
|
|
69
|
+
*/
|
|
70
|
+
function drawBox(lines: string[], minWidth = 40): string {
|
|
71
|
+
const innerWidth = Math.max(minWidth, ...lines.map((l) => l.length));
|
|
72
|
+
const top = `${BOX_TOP_LEFT}${BOX_HORIZONTAL.repeat(innerWidth + 2)}${BOX_TOP_RIGHT}`;
|
|
73
|
+
const bottom = `${BOX_BOTTOM_LEFT}${BOX_HORIZONTAL.repeat(innerWidth + 2)}${BOX_BOTTOM_RIGHT}`;
|
|
74
|
+
const body = lines.map((l) => `${BOX_VERTICAL} ${l.padEnd(innerWidth)} ${BOX_VERTICAL}`);
|
|
75
|
+
return [top, ...body, bottom].join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Public format helpers
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Format run status as a clean multi-line display inside a box.
|
|
84
|
+
*
|
|
85
|
+
* Shows: run ID, process, iteration, status, and pending effects count.
|
|
86
|
+
*
|
|
87
|
+
* @param data - The run status payload (loosely typed for renderer compatibility).
|
|
88
|
+
* @returns A formatted multi-line string.
|
|
89
|
+
*/
|
|
90
|
+
export function formatRunStatus(data: unknown): string {
|
|
91
|
+
const d = (data ?? {}) as Record<string, unknown>;
|
|
92
|
+
const runId = typeof d['runId'] === 'string' ? d['runId'] : 'unknown';
|
|
93
|
+
const processId = typeof d['processId'] === 'string' ? d['processId'] : 'n/a';
|
|
94
|
+
const iteration =
|
|
95
|
+
typeof d['iteration'] === 'number' ? String(d['iteration']) : 'n/a';
|
|
96
|
+
const status = typeof d['status'] === 'string' ? d['status'] : 'unknown';
|
|
97
|
+
const pending =
|
|
98
|
+
typeof d['pendingEffectsCount'] === 'number'
|
|
99
|
+
? String(d['pendingEffectsCount'])
|
|
100
|
+
: '0';
|
|
101
|
+
|
|
102
|
+
const lines = [
|
|
103
|
+
`Run Status`,
|
|
104
|
+
`${'─'.repeat(38)}`,
|
|
105
|
+
` Run ID : ${runId}`,
|
|
106
|
+
` Process : ${processId}`,
|
|
107
|
+
` Iter : ${iteration}`,
|
|
108
|
+
` Status : ${status}`,
|
|
109
|
+
` Pending : ${pending} effect(s)`,
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
return drawBox(lines);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Compact one-line format for an effect completion summary.
|
|
117
|
+
*
|
|
118
|
+
* Format: `Effect <effectId> (<kind>): <status>`
|
|
119
|
+
*
|
|
120
|
+
* @param data - The effect result payload (loosely typed for renderer compatibility).
|
|
121
|
+
* @returns A single-line formatted string.
|
|
122
|
+
*/
|
|
123
|
+
export function formatEffectResult(data: unknown): string {
|
|
124
|
+
const d = (data ?? {}) as Record<string, unknown>;
|
|
125
|
+
const effectId = typeof d['effectId'] === 'string' ? d['effectId'] : 'unknown';
|
|
126
|
+
const kind = typeof d['kind'] === 'string' ? d['kind'] : 'unknown';
|
|
127
|
+
const status = typeof d['status'] === 'string' ? d['status'] : 'unknown';
|
|
128
|
+
|
|
129
|
+
return `Effect ${effectId} (${kind}): ${status}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Format an iteration progress summary.
|
|
134
|
+
*
|
|
135
|
+
* Format: `Iteration N: <status> | <pending count> pending effects`
|
|
136
|
+
*
|
|
137
|
+
* @param data - The iteration payload (loosely typed for renderer compatibility).
|
|
138
|
+
* @returns A single-line formatted string.
|
|
139
|
+
*/
|
|
140
|
+
export function formatIterationSummary(data: unknown): string {
|
|
141
|
+
const d = (data ?? {}) as Record<string, unknown>;
|
|
142
|
+
const iteration =
|
|
143
|
+
typeof d['iteration'] === 'number' ? d['iteration'] : 0;
|
|
144
|
+
const status = typeof d['status'] === 'string' ? d['status'] : 'unknown';
|
|
145
|
+
const pendingCount =
|
|
146
|
+
typeof d['pendingCount'] === 'number' ? d['pendingCount'] : 0;
|
|
147
|
+
|
|
148
|
+
return `Iteration ${iteration}: ${status} | ${pendingCount} pending effects`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Tool-result renderer (original functionality, preserved)
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create the babysitter tool-result message renderer.
|
|
157
|
+
*
|
|
158
|
+
* Returns a {@link MessageRenderer} that formats babysitter effect results
|
|
159
|
+
* for the TUI.
|
|
160
|
+
*
|
|
161
|
+
* @returns A renderer function that converts payloads to display strings.
|
|
162
|
+
*/
|
|
163
|
+
export function createToolRenderer(): MessageRenderer {
|
|
164
|
+
return (payload: unknown): string => {
|
|
165
|
+
if (!isBabysitterPayload(payload)) {
|
|
166
|
+
return `[babysitter] Unrecognised payload: ${JSON.stringify(payload)}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return formatToolResult(payload);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Registration entry point
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Register all babysitter-related message renderers with oh-my-pi.
|
|
179
|
+
*
|
|
180
|
+
* Registers renderers for the following message types:
|
|
181
|
+
* - `babysitter:tool-result` -- per-effect execution result
|
|
182
|
+
* - `babysitter:status` -- run status overview (formatted box)
|
|
183
|
+
* - `babysitter:effect-result` -- compact effect completion summary
|
|
184
|
+
* - `babysitter:iteration` -- iteration progress line
|
|
185
|
+
*
|
|
186
|
+
* @param pi - The oh-my-pi ExtensionAPI (or compatible object exposing
|
|
187
|
+
* `registerMessageRenderer`).
|
|
188
|
+
*/
|
|
189
|
+
export function registerBabysitterRenderers(pi: unknown): void {
|
|
190
|
+
const api = pi as { registerMessageRenderer(type: string, renderer: MessageRenderer): void };
|
|
191
|
+
|
|
192
|
+
// Per-effect tool execution result (original renderer)
|
|
193
|
+
api.registerMessageRenderer('babysitter:tool-result', createToolRenderer());
|
|
194
|
+
|
|
195
|
+
// Run status box
|
|
196
|
+
api.registerMessageRenderer('babysitter:status', (payload: unknown): string => {
|
|
197
|
+
return formatRunStatus(payload);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Effect completion one-liner
|
|
201
|
+
api.registerMessageRenderer('babysitter:effect-result', (payload: unknown): string => {
|
|
202
|
+
return formatEffectResult(payload);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Iteration progress
|
|
206
|
+
api.registerMessageRenderer('babysitter:iteration', (payload: unknown): string => {
|
|
207
|
+
return formatIterationSummary(payload);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Internal helpers
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Format a single effect result into a readable string.
|
|
217
|
+
*
|
|
218
|
+
* @param p - The structured payload.
|
|
219
|
+
* @returns A formatted string for display.
|
|
220
|
+
*/
|
|
221
|
+
function formatToolResult(p: BabysitterToolPayload): string {
|
|
222
|
+
const statusIcon = p.success ? '[OK]' : '[FAIL]';
|
|
223
|
+
const duration = p.durationMs !== undefined ? ` (${p.durationMs}ms)` : '';
|
|
224
|
+
|
|
225
|
+
const lines: string[] = [
|
|
226
|
+
`${statusIcon} ${p.kind}: ${p.title}${duration}`,
|
|
227
|
+
` effect: ${p.effectId}`,
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
if (p.success && p.value !== undefined) {
|
|
231
|
+
const valueStr =
|
|
232
|
+
typeof p.value === 'string' ? p.value : JSON.stringify(p.value, null, 2);
|
|
233
|
+
// Truncate long values
|
|
234
|
+
const truncated =
|
|
235
|
+
valueStr.length > 500 ? valueStr.slice(0, 497) + '...' : valueStr;
|
|
236
|
+
lines.push(` result: ${truncated}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!p.success && p.error) {
|
|
240
|
+
lines.push(` error: ${p.error}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return lines.join('\n');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Type guard for {@link BabysitterToolPayload}.
|
|
248
|
+
*/
|
|
249
|
+
function isBabysitterPayload(
|
|
250
|
+
value: unknown,
|
|
251
|
+
): value is BabysitterToolPayload {
|
|
252
|
+
if (typeof value !== 'object' || value === null) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const obj = value as Record<string, unknown>;
|
|
257
|
+
return (
|
|
258
|
+
typeof obj['effectId'] === 'string' &&
|
|
259
|
+
typeof obj['kind'] === 'string' &&
|
|
260
|
+
typeof obj['title'] === 'string' &&
|
|
261
|
+
typeof obj['success'] === 'boolean'
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI widget rendering for babysitter orchestration state.
|
|
3
|
+
*
|
|
4
|
+
* Renders run progress, pending effects, and quality scores into
|
|
5
|
+
* oh-my-pi's widget panels via `pi.setWidget(key, lines[])` so the
|
|
6
|
+
* user can monitor babysitter activity without leaving the terminal.
|
|
7
|
+
*
|
|
8
|
+
* @module tui-widgets
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { RunState } from './session-binder.js';
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Widget keys
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
const WIDGET_KEY_RUN = 'babysitter:run';
|
|
18
|
+
const WIDGET_KEY_EFFECTS = 'babysitter:effects';
|
|
19
|
+
const WIDGET_KEY_QUALITY = 'babysitter:quality';
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// formatElapsed
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Format elapsed time from an ISO-8601 timestamp to a human-readable string.
|
|
27
|
+
*
|
|
28
|
+
* Returns the most compact representation that still conveys the duration:
|
|
29
|
+
* - Under a minute: `"42s"`
|
|
30
|
+
* - Under an hour: `"5m 32s"`
|
|
31
|
+
* - An hour or more: `"1h 12m 5s"`
|
|
32
|
+
*
|
|
33
|
+
* @param startedAt - ISO-8601 timestamp string.
|
|
34
|
+
* @returns A formatted duration string.
|
|
35
|
+
*/
|
|
36
|
+
export function formatElapsed(startedAt: string): string {
|
|
37
|
+
const elapsedMs = Date.now() - new Date(startedAt).getTime();
|
|
38
|
+
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1000));
|
|
39
|
+
|
|
40
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
41
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
42
|
+
const seconds = totalSeconds % 60;
|
|
43
|
+
|
|
44
|
+
if (hours > 0) {
|
|
45
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
|
46
|
+
}
|
|
47
|
+
if (minutes > 0) {
|
|
48
|
+
return `${minutes}m ${seconds}s`;
|
|
49
|
+
}
|
|
50
|
+
return `${seconds}s`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// renderRunWidget
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Render the babysitter run progress widget.
|
|
59
|
+
*
|
|
60
|
+
* Displays the run ID, process ID, current iteration/status, and elapsed
|
|
61
|
+
* time as a set of compact lines in the `"babysitter:run"` widget panel.
|
|
62
|
+
*
|
|
63
|
+
* @param runState - The current {@link RunState} snapshot.
|
|
64
|
+
* @param pi - The oh-my-pi extension API handle (must expose `setWidget`).
|
|
65
|
+
*/
|
|
66
|
+
export function renderRunWidget(runState: RunState, pi: any): void {
|
|
67
|
+
const elapsed = formatElapsed(runState.startedAt);
|
|
68
|
+
const statusLabel = runState.status ?? 'unknown';
|
|
69
|
+
|
|
70
|
+
const lines: string[] = [
|
|
71
|
+
`Babysitter Run: ${runState.runId}`,
|
|
72
|
+
`Process: ${runState.processId}`,
|
|
73
|
+
`Iteration: ${runState.iteration}/${runState.maxIterations} | Status: ${statusLabel}`,
|
|
74
|
+
`Elapsed: ${elapsed}`,
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
pi.setWidget(WIDGET_KEY_RUN, lines);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// renderEffectsWidget
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Render the pending effects widget.
|
|
86
|
+
*
|
|
87
|
+
* Shows a compact list of effects currently awaiting resolution. Each
|
|
88
|
+
* effect is expected to have at least a `kind` property; a `title` or
|
|
89
|
+
* `label` property is used for the human-readable description when
|
|
90
|
+
* available.
|
|
91
|
+
*
|
|
92
|
+
* @param effects - Array of effect descriptors (objects with `kind` and
|
|
93
|
+
* optionally `title` / `label` / `taskId` fields).
|
|
94
|
+
* @param pi - The oh-my-pi extension API handle.
|
|
95
|
+
*/
|
|
96
|
+
export function renderEffectsWidget(effects: any[], pi: any): void {
|
|
97
|
+
if (!effects || effects.length === 0) {
|
|
98
|
+
pi.setWidget(WIDGET_KEY_EFFECTS, ['Pending Effects (0)']);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const header = `Pending Effects (${effects.length}):`;
|
|
103
|
+
const itemLines = effects.map((effect: any) => {
|
|
104
|
+
const kind: string = effect.kind ?? 'unknown';
|
|
105
|
+
const title: string = effect.title ?? effect.label ?? effect.taskId ?? kind;
|
|
106
|
+
return ` [${kind}] ${title}`;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
pi.setWidget(WIDGET_KEY_EFFECTS, [header, ...itemLines]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// renderQualityWidget
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Render the quality score widget with a visual progress bar.
|
|
118
|
+
*
|
|
119
|
+
* Displays the current score against the target threshold using an
|
|
120
|
+
* ASCII progress bar. The bar is 16 characters wide; an arrow (`>`)
|
|
121
|
+
* marks the current position.
|
|
122
|
+
*
|
|
123
|
+
* @param score - The current quality score (0-100).
|
|
124
|
+
* @param target - The target quality threshold (0-100).
|
|
125
|
+
* @param pi - The oh-my-pi extension API handle.
|
|
126
|
+
*/
|
|
127
|
+
export function renderQualityWidget(score: number, target: number, pi: any): void {
|
|
128
|
+
const barWidth = 16;
|
|
129
|
+
const maxVal = Math.max(target, 100);
|
|
130
|
+
const filled = Math.round((score / maxVal) * barWidth);
|
|
131
|
+
const clamped = Math.min(filled, barWidth);
|
|
132
|
+
|
|
133
|
+
let bar = '';
|
|
134
|
+
for (let i = 0; i < barWidth; i++) {
|
|
135
|
+
if (i < clamped && i !== clamped - 1) {
|
|
136
|
+
bar += '=';
|
|
137
|
+
} else if (i === clamped - 1 && clamped > 0) {
|
|
138
|
+
bar += '>';
|
|
139
|
+
} else {
|
|
140
|
+
bar += ' ';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const line = `Quality: ${score}/${target} [${bar}]`;
|
|
145
|
+
pi.setWidget(WIDGET_KEY_QUALITY, [line]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// clearWidgets
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Clear all babysitter TUI widgets.
|
|
154
|
+
*
|
|
155
|
+
* Sets each widget key to an empty array so oh-my-pi removes the panels
|
|
156
|
+
* from the display.
|
|
157
|
+
*
|
|
158
|
+
* @param pi - The oh-my-pi extension API handle.
|
|
159
|
+
*/
|
|
160
|
+
export function clearWidgets(pi: any): void {
|
|
161
|
+
pi.setWidget(WIDGET_KEY_RUN, []);
|
|
162
|
+
pi.setWidget(WIDGET_KEY_EFFECTS, []);
|
|
163
|
+
pi.setWidget(WIDGET_KEY_QUALITY, []);
|
|
164
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript type definitions for the babysitter oh-my-pi extension.
|
|
3
|
+
*
|
|
4
|
+
* Defines the contracts between extension modules,
|
|
5
|
+
* the oh-my-pi ExtensionAPI surface, and babysitter run state.
|
|
6
|
+
*
|
|
7
|
+
* @module types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// oh-my-pi ExtensionAPI surface (subset relevant to this extension)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/** Handler that can optionally block a tool call. */
|
|
15
|
+
export interface ToolCallInterceptResult {
|
|
16
|
+
block: boolean;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A tool definition registered with oh-my-pi. */
|
|
21
|
+
export interface ToolDefinition {
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
parameters: Record<string, unknown>;
|
|
25
|
+
handler: (params: Record<string, unknown>) => Promise<unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** An entry appended to the oh-my-pi session log. */
|
|
29
|
+
export interface SessionEntry {
|
|
30
|
+
type: string;
|
|
31
|
+
content: unknown;
|
|
32
|
+
timestamp?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Message payload for pi.sendMessage / pi.sendUserMessage. */
|
|
36
|
+
export interface PiMessage {
|
|
37
|
+
role: 'user' | 'assistant' | 'system';
|
|
38
|
+
content: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Custom renderer for a specific message type. */
|
|
42
|
+
export type MessageRenderer = (payload: unknown) => string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The oh-my-pi Extension API surface.
|
|
46
|
+
*
|
|
47
|
+
* This is the primary interface handed to `activate()` by the host.
|
|
48
|
+
*/
|
|
49
|
+
export interface ExtensionAPI {
|
|
50
|
+
/** Subscribe to a lifecycle or data event. */
|
|
51
|
+
on(
|
|
52
|
+
event:
|
|
53
|
+
| 'session_start'
|
|
54
|
+
| 'agent_end'
|
|
55
|
+
| 'session_shutdown'
|
|
56
|
+
| 'before_agent_start'
|
|
57
|
+
| 'tool_call'
|
|
58
|
+
| 'tool_result'
|
|
59
|
+
| 'context'
|
|
60
|
+
| 'input'
|
|
61
|
+
| 'turn_start'
|
|
62
|
+
| 'turn_end',
|
|
63
|
+
handler: (...args: unknown[]) => unknown,
|
|
64
|
+
): void;
|
|
65
|
+
|
|
66
|
+
/** Register a custom tool with the host. */
|
|
67
|
+
registerTool(toolDef: ToolDefinition): void;
|
|
68
|
+
|
|
69
|
+
/** Register a slash command. */
|
|
70
|
+
registerCommand(name: string, options: { description?: string; handler: (...args: unknown[]) => unknown }): void;
|
|
71
|
+
|
|
72
|
+
/** Register a custom message renderer for a given type. */
|
|
73
|
+
registerMessageRenderer(type: string, renderer: MessageRenderer): void;
|
|
74
|
+
|
|
75
|
+
/** Append an entry to the session log. */
|
|
76
|
+
appendEntry(entry: SessionEntry): void;
|
|
77
|
+
|
|
78
|
+
/** Inject a message into the conversation. */
|
|
79
|
+
sendMessage(msg: PiMessage): void;
|
|
80
|
+
|
|
81
|
+
/** Inject a user-role message into the conversation. */
|
|
82
|
+
sendUserMessage(msg: PiMessage): void;
|
|
83
|
+
|
|
84
|
+
/** Retrieve the currently active tool set. */
|
|
85
|
+
getActiveTools(): ToolDefinition[];
|
|
86
|
+
|
|
87
|
+
/** Replace the active tool set. */
|
|
88
|
+
setActiveTools(tools: ToolDefinition[]): void;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Babysitter run / effect state
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/** Possible states of a babysitter run. */
|
|
96
|
+
export type RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'waiting';
|
|
97
|
+
|
|
98
|
+
/** Kinds of effects the babysitter runtime can request. */
|
|
99
|
+
export type EffectKind =
|
|
100
|
+
| 'agent'
|
|
101
|
+
| 'node'
|
|
102
|
+
| 'shell'
|
|
103
|
+
| 'breakpoint'
|
|
104
|
+
| 'sleep'
|
|
105
|
+
| 'skill'
|
|
106
|
+
| 'orchestrator_task';
|
|
107
|
+
|
|
108
|
+
/** A single babysitter effect descriptor. */
|
|
109
|
+
export interface EffectDef {
|
|
110
|
+
/** Unique effect identifier (ULID). */
|
|
111
|
+
effectId: string;
|
|
112
|
+
/** The kind of work to perform. */
|
|
113
|
+
kind: EffectKind;
|
|
114
|
+
/** Human-readable title. */
|
|
115
|
+
title: string;
|
|
116
|
+
/** Task identifier. */
|
|
117
|
+
taskId: string;
|
|
118
|
+
/** Arbitrary arguments for the task implementation. */
|
|
119
|
+
args: Record<string, unknown>;
|
|
120
|
+
/** Labels attached to the task definition. */
|
|
121
|
+
labels?: string[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Snapshot of a babysitter run as tracked by this extension. */
|
|
125
|
+
export interface RunState {
|
|
126
|
+
/** The active run identifier. */
|
|
127
|
+
runId: string;
|
|
128
|
+
/** The oh-my-pi session identifier bound to this run. */
|
|
129
|
+
sessionId: string;
|
|
130
|
+
/** Current run status. */
|
|
131
|
+
status: RunStatus;
|
|
132
|
+
/** Number of orchestration iterations completed so far. */
|
|
133
|
+
iterationCount: number;
|
|
134
|
+
/** ISO-8601 timestamp when the run started. */
|
|
135
|
+
startedAt: string;
|
|
136
|
+
/** Effects currently pending execution. */
|
|
137
|
+
pendingEffects: EffectDef[];
|
|
138
|
+
/** Effects that have been resolved. */
|
|
139
|
+
resolvedEffects: EffectDef[];
|
|
140
|
+
/** Consecutive error counter (for guard thresholds). */
|
|
141
|
+
consecutiveErrors: number;
|
|
142
|
+
/** Most recent quality / score value, if available. */
|
|
143
|
+
lastScore?: number;
|
|
144
|
+
/** Current phase label (e.g. "plan", "execute", "verify"). */
|
|
145
|
+
currentPhase?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Guard configuration
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/** Configurable limits checked by the guard module. */
|
|
153
|
+
export interface GuardConfig {
|
|
154
|
+
/** Maximum number of orchestration iterations before aborting. */
|
|
155
|
+
maxIterations: number;
|
|
156
|
+
/** Maximum wall-clock time (ms) before aborting. */
|
|
157
|
+
maxDurationMs: number;
|
|
158
|
+
/** Consecutive error count that triggers an abort. */
|
|
159
|
+
errorThreshold: number;
|
|
160
|
+
/** Number of identical iteration outputs that indicate a doom loop. */
|
|
161
|
+
doomLoopWindow: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Result returned by the guard check. */
|
|
165
|
+
export interface GuardResult {
|
|
166
|
+
/** Whether the run should continue. */
|
|
167
|
+
allowed: boolean;
|
|
168
|
+
/** Human-readable reason when `allowed` is false. */
|
|
169
|
+
reason?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// TUI / widget state
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
/** State bag consumed by TUI widget renderers. */
|
|
177
|
+
export interface WidgetState {
|
|
178
|
+
/** Run identifier. */
|
|
179
|
+
runId: string;
|
|
180
|
+
/** Current phase label. */
|
|
181
|
+
phase: string;
|
|
182
|
+
/** Number of pending tasks. */
|
|
183
|
+
pendingCount: number;
|
|
184
|
+
/** Number of resolved tasks. */
|
|
185
|
+
resolvedCount: number;
|
|
186
|
+
/** Total tasks seen so far. */
|
|
187
|
+
totalCount: number;
|
|
188
|
+
/** Latest quality score. */
|
|
189
|
+
score?: number;
|
|
190
|
+
/** Elapsed wall-clock time in ms. */
|
|
191
|
+
elapsedMs: number;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// CLI output shapes
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
/** JSON output from `babysitter run:iterate --json`. */
|
|
199
|
+
export interface IterateOutput {
|
|
200
|
+
status: RunStatus;
|
|
201
|
+
pendingActions?: EffectDef[];
|
|
202
|
+
output?: unknown;
|
|
203
|
+
error?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** JSON output from `babysitter session:init --json`. */
|
|
207
|
+
export interface SessionInitOutput {
|
|
208
|
+
sessionId: string;
|
|
209
|
+
runsDir: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** JSON output from `babysitter session:associate --json`. */
|
|
213
|
+
export interface SessionAssociateOutput {
|
|
214
|
+
runId: string;
|
|
215
|
+
sessionId: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** JSON output from `babysitter task:post --json`. */
|
|
219
|
+
export interface TaskPostOutput {
|
|
220
|
+
effectId: string;
|
|
221
|
+
recorded: boolean;
|
|
222
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a5c-ai/babysitter-pi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Babysitter plugin package for the upstream pi coding agent",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi",
|
|
8
|
+
"babysitter",
|
|
9
|
+
"orchestration",
|
|
10
|
+
"ai-agent"
|
|
11
|
+
],
|
|
12
|
+
"omp": {
|
|
13
|
+
"extensions": [
|
|
14
|
+
"./extensions"
|
|
15
|
+
],
|
|
16
|
+
"skills": [
|
|
17
|
+
"./skills"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@a5c-ai/babysitter-sdk": "0.0.184-staging.26c556be"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@mariozechner/pi-coding-agent": "*"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "node --test test/integration.test.js && npx tsx --test test/harness.test.js && npx tsx --test test/tui.test.js",
|
|
28
|
+
"sync:commands": "node scripts/sync-command-docs.cjs",
|
|
29
|
+
"test:integration": "node --test test/integration.test.js",
|
|
30
|
+
"test:harness": "npx tsx --test test/harness.test.js",
|
|
31
|
+
"test:tui": "npx tsx --test test/tui.test.js",
|
|
32
|
+
"deploy": "npm publish --access public",
|
|
33
|
+
"deploy:staging": "npm publish --access public --tag staging"
|
|
34
|
+
},
|
|
35
|
+
"bin": {
|
|
36
|
+
"babysitter-pi": "bin/cli.cjs"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"bin/",
|
|
40
|
+
"package.json",
|
|
41
|
+
"extensions/",
|
|
42
|
+
"skills/",
|
|
43
|
+
"commands/",
|
|
44
|
+
"scripts/"
|
|
45
|
+
],
|
|
46
|
+
"author": "a5c.ai",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/a5c-ai/babysitter"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/a5c-ai/babysitter/tree/main/plugins/babysitter-pi#readme"
|
|
56
|
+
}
|