@monotykamary/pi-supervisor 0.5.9
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 +120 -0
- package/LICENSE +21 -0
- package/README.md +341 -0
- package/media/demo.mp4 +0 -0
- package/media/screenshot.png +0 -0
- package/package.json +87 -0
- package/src/compaction/brief.ts +841 -0
- package/src/compaction/build-sections.ts +340 -0
- package/src/compaction/causal-keys.ts +138 -0
- package/src/compaction/content.ts +68 -0
- package/src/compaction/extract/commits.ts +78 -0
- package/src/compaction/extract/goals.ts +79 -0
- package/src/compaction/extract/preferences.ts +52 -0
- package/src/compaction/extract/shared-symbols.ts +376 -0
- package/src/compaction/filter-noise.ts +47 -0
- package/src/compaction/format.ts +89 -0
- package/src/compaction/index.ts +38 -0
- package/src/compaction/normalize.ts +73 -0
- package/src/compaction/sanitize.ts +5 -0
- package/src/compaction/sections.ts +19 -0
- package/src/compaction/skill-collapse.ts +35 -0
- package/src/compaction/tool-args.ts +14 -0
- package/src/compaction/types.ts +26 -0
- package/src/core/analyzer.ts +58 -0
- package/src/core/index.ts +8 -0
- package/src/core/inference.ts +77 -0
- package/src/core/prompt-builder.ts +137 -0
- package/src/core/prompt-loader.ts +125 -0
- package/src/core/reframe.ts +27 -0
- package/src/fabric-provider.ts +115 -0
- package/src/global-config.ts +65 -0
- package/src/index.ts +514 -0
- package/src/session/client.ts +46 -0
- package/src/session/response-parser.ts +37 -0
- package/src/session/supervisor-session.ts +102 -0
- package/src/state/manager.ts +133 -0
- package/src/state/mid-run-signals.ts +103 -0
- package/src/state/patterns.ts +82 -0
- package/src/state/reframe.ts +27 -0
- package/src/subagent-detector.ts +94 -0
- package/src/types.ts +42 -0
- package/src/ui/animations.ts +95 -0
- package/src/ui/model-picker.ts +72 -0
- package/src/ui/model-settings-selector.ts +440 -0
- package/src/ui/model-sort.ts +101 -0
- package/src/ui/renderer.ts +314 -0
- package/src/ui/types.ts +48 -0
- package/tests/compaction.test.ts +507 -0
- package/tests/engine.test.ts +622 -0
- package/tests/ephemeral-supervision.test.ts +347 -0
- package/tests/fabric-provider.test.ts +55 -0
- package/tests/full-fidelity-snapshot.test.ts +250 -0
- package/tests/global-config.test.ts +74 -0
- package/tests/model-sort.test.ts +157 -0
- package/tests/parsing.test.ts +303 -0
- package/tests/state.test.ts +474 -0
- package/tests/status-widget.test.ts +539 -0
- package/tests/subagent-detector.test.ts +191 -0
- package/tests/supervise-command.test.ts +363 -0
- package/tests/supervise-model-command.test.ts +184 -0
- package/tsconfig.json +14 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock collaborators so importing the extension entry point is lightweight.
|
|
4
|
+
vi.mock('../src/core/analyzer.js', () => ({ analyze: vi.fn() }));
|
|
5
|
+
vi.mock('../src/core/inference.js', () => ({ inferOutcome: vi.fn() }));
|
|
6
|
+
vi.mock('../src/core/prompt-loader.js', () => ({
|
|
7
|
+
loadSystemPrompt: vi.fn().mockReturnValue({ prompt: 'p', source: 'built-in' }),
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('../src/ui/renderer.js', () => ({ updateUI: vi.fn(), toggleWidget: vi.fn() }));
|
|
10
|
+
vi.mock('../src/ui/model-picker.js', () => ({ pickModel: vi.fn() }));
|
|
11
|
+
vi.mock('../src/global-config.js', () => ({
|
|
12
|
+
loadGlobalModel: vi.fn().mockReturnValue(null),
|
|
13
|
+
saveGlobalModel: vi.fn(),
|
|
14
|
+
}));
|
|
15
|
+
vi.mock('../src/session/client.js', () => ({ disposeSession: vi.fn() }));
|
|
16
|
+
vi.mock('../src/subagent-detector.js', () => ({
|
|
17
|
+
checkChildPiProcesses: vi.fn().mockResolvedValue({ hasActiveSubagents: false, count: 0 }),
|
|
18
|
+
waitForSubagents: vi
|
|
19
|
+
.fn()
|
|
20
|
+
.mockResolvedValue({ completed: true, finalStatus: { hasActiveSubagents: false, count: 0 } }),
|
|
21
|
+
}));
|
|
22
|
+
vi.mock('../src/compaction/index.js', () => ({
|
|
23
|
+
extractMessages: vi.fn().mockReturnValue([]),
|
|
24
|
+
buildCompactionSummary: vi.fn(),
|
|
25
|
+
formatForSupervisor: vi.fn(),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import piSupervisor from '../src/index.js';
|
|
29
|
+
import { pickModel } from '../src/ui/model-picker.js';
|
|
30
|
+
import { loadGlobalModel, saveGlobalModel } from '../src/global-config.js';
|
|
31
|
+
import { updateUI } from '../src/ui/renderer.js';
|
|
32
|
+
|
|
33
|
+
function createMockApi() {
|
|
34
|
+
let superviseDef:
|
|
35
|
+
| {
|
|
36
|
+
handler: (args: string, ctx: any) => Promise<void>;
|
|
37
|
+
getArgumentCompletions?: (prefix: string) => any[] | null;
|
|
38
|
+
}
|
|
39
|
+
| undefined;
|
|
40
|
+
const api = {
|
|
41
|
+
appendEntry: vi.fn(),
|
|
42
|
+
on: vi.fn(),
|
|
43
|
+
registerCommand: vi.fn((name: string, def: any) => {
|
|
44
|
+
if (name === 'supervise') superviseDef = def;
|
|
45
|
+
}),
|
|
46
|
+
registerTool: vi.fn(),
|
|
47
|
+
sendUserMessage: vi.fn(),
|
|
48
|
+
sendMessage: vi.fn(),
|
|
49
|
+
setModel: vi.fn().mockResolvedValue(true),
|
|
50
|
+
events: { emit: vi.fn(), on: vi.fn() },
|
|
51
|
+
} as any;
|
|
52
|
+
return { api, getSupervise: () => superviseDef };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createMockCtx(overrides: { isIdle?: boolean; cwd?: string } = {}) {
|
|
56
|
+
return {
|
|
57
|
+
ui: {
|
|
58
|
+
notify: vi.fn(),
|
|
59
|
+
custom: vi.fn(),
|
|
60
|
+
setWidget: vi.fn(),
|
|
61
|
+
setFooter: vi.fn(),
|
|
62
|
+
setHeader: vi.fn(),
|
|
63
|
+
setStatus: vi.fn(),
|
|
64
|
+
setWorkingMessage: vi.fn(),
|
|
65
|
+
},
|
|
66
|
+
hasUI: true,
|
|
67
|
+
cwd: overrides.cwd ?? '/test/project',
|
|
68
|
+
sessionManager: { getBranch: vi.fn().mockReturnValue([]) },
|
|
69
|
+
modelRegistry: {
|
|
70
|
+
getApiKeyForProvider: vi.fn().mockResolvedValue('test-key'),
|
|
71
|
+
find: vi.fn(),
|
|
72
|
+
getAvailable: vi.fn().mockReturnValue([]),
|
|
73
|
+
},
|
|
74
|
+
model: { provider: 'anthropic', id: 'claude-haiku' },
|
|
75
|
+
isIdle: vi.fn().mockReturnValue(overrides.isIdle ?? true),
|
|
76
|
+
abort: vi.fn(),
|
|
77
|
+
hasPendingMessages: vi.fn().mockReturnValue(false),
|
|
78
|
+
shutdown: vi.fn(),
|
|
79
|
+
getContextUsage: vi.fn(),
|
|
80
|
+
compact: vi.fn(),
|
|
81
|
+
getSystemPrompt: vi.fn().mockReturnValue('test system prompt'),
|
|
82
|
+
} as any;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe('/supervise model command', () => {
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
vi.mocked(loadGlobalModel).mockReturnValue(null);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('exposes subcommand autocomplete including model', () => {
|
|
92
|
+
const { api, getSupervise } = createMockApi();
|
|
93
|
+
piSupervisor(api);
|
|
94
|
+
const getArgumentCompletions = getSupervise()!.getArgumentCompletions!;
|
|
95
|
+
|
|
96
|
+
expect(getArgumentCompletions('')).toEqual([
|
|
97
|
+
{ value: 'model', label: 'model', description: 'Pick the supervisor model' },
|
|
98
|
+
{ value: 'stop', label: 'stop', description: 'Stop active supervision' },
|
|
99
|
+
{ value: 'widget', label: 'widget', description: 'Toggle the status widget' },
|
|
100
|
+
]);
|
|
101
|
+
expect(getArgumentCompletions('m')).toEqual([
|
|
102
|
+
{ value: 'model', label: 'model', description: 'Pick the supervisor model' },
|
|
103
|
+
]);
|
|
104
|
+
expect(getArgumentCompletions('s')).toEqual([
|
|
105
|
+
{ value: 'stop', label: 'stop', description: 'Stop active supervision' },
|
|
106
|
+
]);
|
|
107
|
+
// Free-form goal text (no subcommand match) yields no suggestions
|
|
108
|
+
expect(getArgumentCompletions('refactor auth')).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('saves the picked model and notifies when supervision is inactive', async () => {
|
|
112
|
+
const { api, getSupervise } = createMockApi();
|
|
113
|
+
piSupervisor(api);
|
|
114
|
+
const handler = getSupervise()!.handler;
|
|
115
|
+
const ctx = createMockCtx();
|
|
116
|
+
|
|
117
|
+
vi.mocked(pickModel).mockResolvedValue({ provider: 'openai', id: 'gpt-4o' } as any);
|
|
118
|
+
|
|
119
|
+
await handler('model', ctx);
|
|
120
|
+
|
|
121
|
+
// Pre-highlights the chat model (no active state, no global config)
|
|
122
|
+
expect(pickModel).toHaveBeenCalledWith(ctx, 'anthropic', 'claude-haiku');
|
|
123
|
+
expect(saveGlobalModel).toHaveBeenCalledWith('/test/project', {
|
|
124
|
+
provider: 'openai',
|
|
125
|
+
modelId: 'gpt-4o',
|
|
126
|
+
});
|
|
127
|
+
// Not active → widget not refreshed
|
|
128
|
+
expect(updateUI).not.toHaveBeenCalled();
|
|
129
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(
|
|
130
|
+
expect.stringContaining('Supervisor model set to openai/gpt-4o'),
|
|
131
|
+
'info'
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('notifies cancelled and does not save when the picker is dismissed', async () => {
|
|
136
|
+
const { api, getSupervise } = createMockApi();
|
|
137
|
+
piSupervisor(api);
|
|
138
|
+
const handler = getSupervise()!.handler;
|
|
139
|
+
const ctx = createMockCtx();
|
|
140
|
+
|
|
141
|
+
vi.mocked(pickModel).mockResolvedValue(null);
|
|
142
|
+
|
|
143
|
+
await handler('model', ctx);
|
|
144
|
+
|
|
145
|
+
expect(saveGlobalModel).not.toHaveBeenCalled();
|
|
146
|
+
expect(updateUI).not.toHaveBeenCalled();
|
|
147
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith('Supervisor model selection cancelled.', 'info');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('updates the live session model and refreshes the widget when supervision is active', async () => {
|
|
151
|
+
const { api, getSupervise } = createMockApi();
|
|
152
|
+
piSupervisor(api);
|
|
153
|
+
const handler = getSupervise()!.handler;
|
|
154
|
+
const ctx = createMockCtx({ isIdle: true });
|
|
155
|
+
|
|
156
|
+
// Start active supervision with the explicit-goal path (API key present)
|
|
157
|
+
await handler('Refactor auth module', ctx);
|
|
158
|
+
expect(api.sendUserMessage).toHaveBeenCalled();
|
|
159
|
+
|
|
160
|
+
updateUI.mockClear();
|
|
161
|
+
api.appendEntry.mockClear();
|
|
162
|
+
|
|
163
|
+
vi.mocked(pickModel).mockResolvedValue({ provider: 'openai', id: 'gpt-4o' } as any);
|
|
164
|
+
|
|
165
|
+
await handler('model', ctx);
|
|
166
|
+
|
|
167
|
+
// Pre-highlights the active session model, not the chat model
|
|
168
|
+
expect(pickModel).toHaveBeenCalledWith(ctx, 'anthropic', 'claude-haiku');
|
|
169
|
+
expect(saveGlobalModel).toHaveBeenCalledWith('/test/project', {
|
|
170
|
+
provider: 'openai',
|
|
171
|
+
modelId: 'gpt-4o',
|
|
172
|
+
});
|
|
173
|
+
// Active → widget refreshed
|
|
174
|
+
expect(updateUI).toHaveBeenCalled();
|
|
175
|
+
// state.setModel() persisted the new model into the session
|
|
176
|
+
const lastEntry = api.appendEntry.mock.calls[api.appendEntry.mock.calls.length - 1];
|
|
177
|
+
expect(lastEntry[1].provider).toBe('openai');
|
|
178
|
+
expect(lastEntry[1].modelId).toBe('gpt-4o');
|
|
179
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(
|
|
180
|
+
expect.stringContaining('Supervisor model set to openai/gpt-4o'),
|
|
181
|
+
'info'
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*.ts"]
|
|
14
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
environment: 'node',
|
|
6
|
+
include: ['tests/**/*.test.ts'],
|
|
7
|
+
clearMocks: true,
|
|
8
|
+
restoreMocks: true,
|
|
9
|
+
coverage: {
|
|
10
|
+
provider: 'v8',
|
|
11
|
+
reporter: ['text', 'json', 'html'],
|
|
12
|
+
exclude: ['node_modules/', 'tests/', '**/*.d.ts', '**/*.config.ts'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|