mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-20 01:11:18 +00:00
fix: spawn list <cloud> now correctly filters by cloud instead of failing (#563)
Previously, `spawn list hetzner` always treated the bare positional argument as an agent filter, returning 0 results since "hetzner" is a cloud, not an agent. Now resolveListFilters auto-detects: when the filter doesn't resolve as an agent but does resolve as a cloud, it reclassifies to a cloud filter. This matches the help text which promises "Filter history by agent or cloud name". Agent: ux-engineer Co-authored-by: A <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
746adb4f41
commit
477ce58367
3 changed files with 73 additions and 3 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.2.58",
|
||||
"version": "0.2.59",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -308,6 +308,66 @@ describe("cmdList filter resolution via display names", () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ── Bare positional arg: auto-detect cloud vs agent ───────────────────────
|
||||
|
||||
describe("bare positional arg reclassified as cloud filter when appropriate", () => {
|
||||
it("should reclassify 'hetzner' from agentFilter to cloudFilter", async () => {
|
||||
await setManifest(mockManifest);
|
||||
writeHistory(sampleRecords);
|
||||
|
||||
// "hetzner" passed as agentFilter (bare positional), should be reclassified
|
||||
await cmdList("hetzner");
|
||||
|
||||
const output = consoleOutput();
|
||||
// Should find 2 records on hetzner (aider + claude), not 0
|
||||
expect(output).toContain("2 of 4");
|
||||
});
|
||||
|
||||
it("should reclassify 'sprite' from agentFilter to cloudFilter", async () => {
|
||||
await setManifest(mockManifest);
|
||||
writeHistory(sampleRecords);
|
||||
|
||||
await cmdList("sprite");
|
||||
|
||||
const output = consoleOutput();
|
||||
// Should find 2 records on sprite
|
||||
expect(output).toContain("2 of 4");
|
||||
});
|
||||
|
||||
it("should reclassify cloud display name 'Hetzner Cloud' to cloudFilter", async () => {
|
||||
await setManifest(mockManifest);
|
||||
writeHistory(sampleRecords);
|
||||
|
||||
await cmdList("Hetzner Cloud");
|
||||
|
||||
const output = consoleOutput();
|
||||
expect(output).toContain("2 of 4");
|
||||
});
|
||||
|
||||
it("should NOT reclassify when agentFilter resolves to an agent", async () => {
|
||||
await setManifest(mockManifest);
|
||||
writeHistory(sampleRecords);
|
||||
|
||||
await cmdList("claude");
|
||||
|
||||
const output = consoleOutput();
|
||||
// "claude" is a valid agent, should filter by agent
|
||||
expect(output).toContain("2 of 4");
|
||||
});
|
||||
|
||||
it("should NOT reclassify when explicit cloudFilter is already set", async () => {
|
||||
await setManifest(mockManifest);
|
||||
writeHistory(sampleRecords);
|
||||
|
||||
// When both are set, don't reclassify
|
||||
await cmdList("unknown-thing", "hetzner");
|
||||
|
||||
const info = logInfoOutput();
|
||||
// Should show "no spawns" since agent=unknown-thing finds nothing
|
||||
expect(info).toContain("No spawns found matching");
|
||||
});
|
||||
});
|
||||
|
||||
// ── Key that matches directly vs display name ──────────────────────────────
|
||||
|
||||
describe("direct key match takes precedence over display name", () => {
|
||||
|
|
|
|||
|
|
@ -933,7 +933,8 @@ function buildRecordHint(r: SpawnRecord): string {
|
|||
return when;
|
||||
}
|
||||
|
||||
/** Try to load manifest and resolve filter display names to keys */
|
||||
/** Try to load manifest and resolve filter display names to keys.
|
||||
* When a bare positional filter doesn't match an agent, try it as a cloud. */
|
||||
async function resolveListFilters(
|
||||
agentFilter?: string,
|
||||
cloudFilter?: string
|
||||
|
|
@ -947,7 +948,16 @@ async function resolveListFilters(
|
|||
|
||||
if (manifest && agentFilter) {
|
||||
const resolved = resolveAgentKey(manifest, agentFilter);
|
||||
if (resolved) agentFilter = resolved;
|
||||
if (resolved) {
|
||||
agentFilter = resolved;
|
||||
} else if (!cloudFilter) {
|
||||
// Bare positional arg didn't match an agent -- try as a cloud filter
|
||||
const resolvedCloud = resolveCloudKey(manifest, agentFilter);
|
||||
if (resolvedCloud) {
|
||||
cloudFilter = resolvedCloud;
|
||||
agentFilter = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (manifest && cloudFilter) {
|
||||
const resolved = resolveCloudKey(manifest, cloudFilter);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue