feat(prime-agent): filterable project picker + a visible active-project indicator
Two gaps the pi switcher had already closed. **The indicator was invisible.** `ctx.ui.setStatus` was the wrong channel: prime-agent stores extension statuses (`FooterDataProvider.setExtensionStatus`) but nothing ever reads them back — there is no `getExtensionStatuses()` call site anywhere in the app, so the value was recorded and never rendered. pi's `footer.js` does render them, which is why the same code showed a status line there and nothing here. The active project is now published as a *widget* (`extensionWidgetsBelow` → `renderWidgets()`), which prime-agent does render. `setStatus` is still called so pi keeps its footer entry. **No search.** With 356 projects the picker was a wall of `smoke-mcptoken-*`. Above 20 projects it now asks for a filter first; space-separated terms must all match, case-insensitively, against name or description, so `home auto` finds `homeautomation`. Active project sorts first, then alphabetical. Deliberately no client-side truncation here, unlike the pi picker: pi's `ExtensionSelectorComponent.updateList()` renders every option, so a long list fills the screen and has to be capped. prime-agent's selector windows the list itself and shows a true `(20/356)` counter — capping would replace an accurate total with a misleading one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017BMXdb2qZbPSh8Q7XpTyjB
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -528,6 +528,38 @@ describe('config prime-agent', () => {
|
|||||||
expect(ext).not.toContain("'success'");
|
expect(ext).not.toContain("'success'");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('the installed switcher shows the active project via a widget, not just setStatus', async () => {
|
||||||
|
const settingsPath = join(tmpDir, 'settings.json');
|
||||||
|
const cmd = createConfigCommand(
|
||||||
|
{ configDeps: { configDir: tmpDir }, log },
|
||||||
|
{ client, credentialsDeps: { configDir: tmpDir }, log },
|
||||||
|
);
|
||||||
|
await cmd.parseAsync(['prime-agent', '--project', 'ha', '-o', settingsPath, '--skip-skills', '--token', 'mcpctl_pat_x'], { from: 'user' });
|
||||||
|
|
||||||
|
const ext = readFileSync(join(tmpDir, 'extensions', 'mcpctl-switch.ts'), 'utf-8');
|
||||||
|
// prime-agent stores extension statuses but never renders them, so a
|
||||||
|
// setStatus-only indicator is invisible there. Widgets are rendered.
|
||||||
|
expect(ext).toContain('ctx.ui.setWidget(STATUS_KEY');
|
||||||
|
expect(ext).toContain("placement: 'belowEditor'");
|
||||||
|
// Still set the status: pi's footer does render it.
|
||||||
|
expect(ext).toContain('ctx.ui.setStatus(STATUS_KEY');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the installed switcher filters the project list before showing it', async () => {
|
||||||
|
const settingsPath = join(tmpDir, 'settings.json');
|
||||||
|
const cmd = createConfigCommand(
|
||||||
|
{ configDeps: { configDir: tmpDir }, log },
|
||||||
|
{ client, credentialsDeps: { configDir: tmpDir }, log },
|
||||||
|
);
|
||||||
|
await cmd.parseAsync(['prime-agent', '--project', 'ha', '-o', settingsPath, '--skip-skills', '--token', 'mcpctl_pat_x'], { from: 'user' });
|
||||||
|
|
||||||
|
const ext = readFileSync(join(tmpDir, 'extensions', 'mcpctl-switch.ts'), 'utf-8');
|
||||||
|
// Hundreds of projects in a plain arrow-key list is unusable.
|
||||||
|
expect(ext).toContain('export function filterProjects');
|
||||||
|
expect(ext).toContain('ctx.ui.input(');
|
||||||
|
expect(ext).toContain('FILTER_THRESHOLD');
|
||||||
|
});
|
||||||
|
|
||||||
it('the installed switcher extension passes --skip-marker', async () => {
|
it('the installed switcher extension passes --skip-marker', async () => {
|
||||||
const settingsPath = join(tmpDir, 'settings.json');
|
const settingsPath = join(tmpDir, 'settings.json');
|
||||||
const cmd = createConfigCommand(
|
const cmd = createConfigCommand(
|
||||||
|
|||||||
Reference in New Issue
Block a user