@hyperdrive.bot/paseo-cli 0.3.45 → 0.3.47
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.
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `paseo agent ls` filtering, over the SAME predicate the app and the MCP tool
|
|
3
|
+
* use (`@hyperdrive.bot/paseo-protocol/agent-filter`).
|
|
4
|
+
*
|
|
5
|
+
* Two things this file exists to avoid. First, a third hand-rolled copy of the
|
|
6
|
+
* semantics: the app has drifted from itself three times this way, and a CLI
|
|
7
|
+
* that disagreed with the UI about what "running" means would be worse, because
|
|
8
|
+
* nobody is looking at a list while it lies. Second, the `--status`/`--cwd`
|
|
9
|
+
* situation this replaces -- both were declared on the options interface and
|
|
10
|
+
* both were applied by `runLsCommand`, but neither was ever registered as a
|
|
11
|
+
* Commander option, so the filtering code was live and completely unreachable.
|
|
12
|
+
*/
|
|
13
|
+
import { type AgentArchivedMode, type AgentFilterCriteria, type AgentTimeField, type AgentTimeSelection } from "@hyperdrive.bot/paseo-protocol/agent-filter";
|
|
14
|
+
import { type WorkspaceStateBucket } from "@hyperdrive.bot/paseo-protocol/agent-state-bucket";
|
|
15
|
+
import type { AgentSnapshotPayload } from "@hyperdrive.bot/paseo-protocol/messages";
|
|
16
|
+
/** The filter flags `agent ls` accepts, mirroring the UI's dimensions. */
|
|
17
|
+
export interface AgentLsFilterOptions {
|
|
18
|
+
archived?: string;
|
|
19
|
+
all?: boolean;
|
|
20
|
+
state?: string[];
|
|
21
|
+
origin?: string[];
|
|
22
|
+
tag?: string[];
|
|
23
|
+
notTag?: string[];
|
|
24
|
+
kanban?: string[];
|
|
25
|
+
notKanban?: string[];
|
|
26
|
+
provider?: string[];
|
|
27
|
+
notProvider?: string[];
|
|
28
|
+
timeField?: string;
|
|
29
|
+
window?: string;
|
|
30
|
+
from?: string;
|
|
31
|
+
to?: string;
|
|
32
|
+
text?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `--archived` wins when given; otherwise `-a` means "all" and the default is
|
|
36
|
+
* `hide`, which is the behaviour `agent ls` has always had.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveLsArchivedMode(options: AgentLsFilterOptions): AgentArchivedMode;
|
|
39
|
+
export declare function resolveLsTimeField(options: AgentLsFilterOptions): AgentTimeField;
|
|
40
|
+
/**
|
|
41
|
+
* A window and an exact range are the same dimension, so the more specific
|
|
42
|
+
* request (the range) wins rather than intersecting into an unexplainable
|
|
43
|
+
* empty list.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveLsTimeSelection(options: AgentLsFilterOptions): AgentTimeSelection | null;
|
|
46
|
+
/**
|
|
47
|
+
* Builds the criteria.
|
|
48
|
+
*
|
|
49
|
+
* `state` and `origin` are closed sets, so they carry no exclude half: "except
|
|
50
|
+
* running" is the other five buckets, and listing them is not the burden that
|
|
51
|
+
* enumerating an open set of tags would be.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildAgentLsCriteria(options: AgentLsFilterOptions): AgentFilterCriteria;
|
|
54
|
+
type FilterableAgent = Pick<AgentSnapshotPayload, "id" | "status" | "cwd" | "createdAt" | "updatedAt" | "archivedAt" | "labels" | "provider"> & Partial<Pick<AgentSnapshotPayload, "title" | "lastUserMessageAt" | "requiresAttention" | "attentionReason" | "pendingPermissions" | "activeBackgroundTaskCount">>;
|
|
55
|
+
/**
|
|
56
|
+
* `parentAgentId -> live child count`, computed over the corpus BEFORE any
|
|
57
|
+
* narrowing so a parent does not lose its `pending` bucket because its only
|
|
58
|
+
* live child was filtered out.
|
|
59
|
+
*/
|
|
60
|
+
export declare function countActiveChildrenByParent(agents: readonly FilterableAgent[]): Map<string, number>;
|
|
61
|
+
/** The derived bucket, matching what the app shows for the same agent. */
|
|
62
|
+
export declare function deriveLsStateBucket(agent: FilterableAgent, activeChildCount: number): WorkspaceStateBucket;
|
|
63
|
+
/** Narrows `agents` with the shared predicate. `now` is injected so tests are deterministic. */
|
|
64
|
+
export declare function applyAgentLsFilter<T extends FilterableAgent>(agents: readonly T[], criteria: AgentFilterCriteria, timeField: AgentTimeField, now: Date): T[];
|
|
65
|
+
/** `agentId -> derived bucket`, so the table can SHOW what `--state` filtered on. */
|
|
66
|
+
export declare function stateBucketsById(agents: readonly FilterableAgent[]): Map<string, WorkspaceStateBucket>;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=ls-filter.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `paseo agent ls` filtering, over the SAME predicate the app and the MCP tool
|
|
3
|
+
* use (`@hyperdrive.bot/paseo-protocol/agent-filter`).
|
|
4
|
+
*
|
|
5
|
+
* Two things this file exists to avoid. First, a third hand-rolled copy of the
|
|
6
|
+
* semantics: the app has drifted from itself three times this way, and a CLI
|
|
7
|
+
* that disagreed with the UI about what "running" means would be worse, because
|
|
8
|
+
* nobody is looking at a list while it lies. Second, the `--status`/`--cwd`
|
|
9
|
+
* situation this replaces -- both were declared on the options interface and
|
|
10
|
+
* both were applied by `runLsCommand`, but neither was ever registered as a
|
|
11
|
+
* Commander option, so the filtering code was live and completely unreachable.
|
|
12
|
+
*/
|
|
13
|
+
import { agentPassesFilter, deriveAgentOrigin, emptyAgentFilterCriteria, filterSelectionFrom, isAgentDay, } from "@hyperdrive.bot/paseo-protocol/agent-filter";
|
|
14
|
+
import { PARENT_AGENT_ID_LABEL } from "@hyperdrive.bot/paseo-protocol/agent-labels";
|
|
15
|
+
import { deriveAgentStateBucket, } from "@hyperdrive.bot/paseo-protocol/agent-state-bucket";
|
|
16
|
+
const TAG_LABEL_PREFIX = "tag:";
|
|
17
|
+
const KANBAN_LABEL_KEY = "kanban";
|
|
18
|
+
function isTimeWindow(value) {
|
|
19
|
+
return (value === "today" || value === "3d" || value === "7d" || value === "30d" || value === "stale");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `--archived` wins when given; otherwise `-a` means "all" and the default is
|
|
23
|
+
* `hide`, which is the behaviour `agent ls` has always had.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveLsArchivedMode(options) {
|
|
26
|
+
const explicit = options.archived?.trim();
|
|
27
|
+
if (explicit === "all" || explicit === "only" || explicit === "hide")
|
|
28
|
+
return explicit;
|
|
29
|
+
return options.all ? "all" : "hide";
|
|
30
|
+
}
|
|
31
|
+
export function resolveLsTimeField(options) {
|
|
32
|
+
return options.timeField === "created" ? "created" : "active";
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A window and an exact range are the same dimension, so the more specific
|
|
36
|
+
* request (the range) wins rather than intersecting into an unexplainable
|
|
37
|
+
* empty list.
|
|
38
|
+
*/
|
|
39
|
+
export function resolveLsTimeSelection(options) {
|
|
40
|
+
const { from, to, window } = options;
|
|
41
|
+
if (isAgentDay(from) && isAgentDay(to))
|
|
42
|
+
return { kind: "range", range: { from, to } };
|
|
43
|
+
if (isTimeWindow(window))
|
|
44
|
+
return { kind: "window", window };
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds the criteria.
|
|
49
|
+
*
|
|
50
|
+
* `state` and `origin` are closed sets, so they carry no exclude half: "except
|
|
51
|
+
* running" is the other five buckets, and listing them is not the burden that
|
|
52
|
+
* enumerating an open set of tags would be.
|
|
53
|
+
*/
|
|
54
|
+
export function buildAgentLsCriteria(options) {
|
|
55
|
+
return {
|
|
56
|
+
...emptyAgentFilterCriteria(),
|
|
57
|
+
stateSelection: filterSelectionFrom(options.state, undefined),
|
|
58
|
+
originSelection: filterSelectionFrom(options.origin, undefined),
|
|
59
|
+
tagSelection: filterSelectionFrom(options.tag, options.notTag),
|
|
60
|
+
kanbanSelection: filterSelectionFrom(options.kanban, options.notKanban),
|
|
61
|
+
providerSelection: filterSelectionFrom(options.provider, options.notProvider),
|
|
62
|
+
archivedMode: resolveLsArchivedMode(options),
|
|
63
|
+
timeSelection: resolveLsTimeSelection(options),
|
|
64
|
+
textQuery: (options.text ?? "").trim().toLowerCase(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function isAliveChild(agent) {
|
|
68
|
+
if (agent.archivedAt)
|
|
69
|
+
return false;
|
|
70
|
+
return agent.status === "running" || agent.status === "initializing";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `parentAgentId -> live child count`, computed over the corpus BEFORE any
|
|
74
|
+
* narrowing so a parent does not lose its `pending` bucket because its only
|
|
75
|
+
* live child was filtered out.
|
|
76
|
+
*/
|
|
77
|
+
export function countActiveChildrenByParent(agents) {
|
|
78
|
+
const counts = new Map();
|
|
79
|
+
for (const agent of agents) {
|
|
80
|
+
const parentId = agent.labels?.[PARENT_AGENT_ID_LABEL]?.trim();
|
|
81
|
+
if (!parentId || !isAliveChild(agent))
|
|
82
|
+
continue;
|
|
83
|
+
counts.set(parentId, (counts.get(parentId) ?? 0) + 1);
|
|
84
|
+
}
|
|
85
|
+
return counts;
|
|
86
|
+
}
|
|
87
|
+
/** The derived bucket, matching what the app shows for the same agent. */
|
|
88
|
+
export function deriveLsStateBucket(agent, activeChildCount) {
|
|
89
|
+
return deriveAgentStateBucket({
|
|
90
|
+
status: agent.status,
|
|
91
|
+
pendingPermissionCount: agent.pendingPermissions?.length ?? 0,
|
|
92
|
+
requiresAttention: agent.requiresAttention ?? false,
|
|
93
|
+
attentionReason: agent.attentionReason ?? null,
|
|
94
|
+
activeChildCount,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function timeValueFor(agent, field) {
|
|
98
|
+
const raw = field === "created" ? agent.createdAt : (agent.lastUserMessageAt ?? agent.updatedAt);
|
|
99
|
+
if (!raw)
|
|
100
|
+
return null;
|
|
101
|
+
const date = new Date(raw);
|
|
102
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
103
|
+
}
|
|
104
|
+
function tagsFromLabels(labels) {
|
|
105
|
+
if (!labels)
|
|
106
|
+
return [];
|
|
107
|
+
return Object.keys(labels)
|
|
108
|
+
.filter((key) => key.startsWith(TAG_LABEL_PREFIX) && key.length > TAG_LABEL_PREFIX.length)
|
|
109
|
+
.map((key) => key.slice(TAG_LABEL_PREFIX.length))
|
|
110
|
+
.sort();
|
|
111
|
+
}
|
|
112
|
+
/** Narrows `agents` with the shared predicate. `now` is injected so tests are deterministic. */
|
|
113
|
+
export function applyAgentLsFilter(agents, criteria, timeField, now) {
|
|
114
|
+
const childrenByParent = countActiveChildrenByParent(agents);
|
|
115
|
+
return agents.filter((agent) => {
|
|
116
|
+
const activeChildCount = (childrenByParent.get(agent.id) ?? 0) + (agent.activeBackgroundTaskCount ?? 0);
|
|
117
|
+
return agentPassesFilter({
|
|
118
|
+
stateBucket: deriveLsStateBucket(agent, activeChildCount),
|
|
119
|
+
tags: tagsFromLabels(agent.labels),
|
|
120
|
+
kanbanBucket: agent.labels?.[KANBAN_LABEL_KEY]?.trim() || null,
|
|
121
|
+
isArchived: Boolean(agent.archivedAt),
|
|
122
|
+
timeValue: timeValueFor(agent, timeField),
|
|
123
|
+
provider: agent.provider,
|
|
124
|
+
origin: deriveAgentOrigin(agent.labels),
|
|
125
|
+
searchableText: `${agent.title ?? ""} ${agent.cwd}`.toLowerCase(),
|
|
126
|
+
}, criteria, now);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/** `agentId -> derived bucket`, so the table can SHOW what `--state` filtered on. */
|
|
130
|
+
export function stateBucketsById(agents) {
|
|
131
|
+
const childrenByParent = countActiveChildrenByParent(agents);
|
|
132
|
+
const buckets = new Map();
|
|
133
|
+
for (const agent of agents) {
|
|
134
|
+
const activeChildCount = (childrenByParent.get(agent.id) ?? 0) + (agent.activeBackgroundTaskCount ?? 0);
|
|
135
|
+
buckets.set(agent.id, deriveLsStateBucket(agent, activeChildCount));
|
|
136
|
+
}
|
|
137
|
+
return buckets;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=ls-filter.js.map
|
|
@@ -11,6 +11,12 @@ export interface AgentListItem {
|
|
|
11
11
|
provider: string;
|
|
12
12
|
thinking: string;
|
|
13
13
|
status: string;
|
|
14
|
+
/**
|
|
15
|
+
* The DERIVED bucket the app groups by. Shown next to the raw lifecycle
|
|
16
|
+
* because `--state pending` on a row whose STATUS reads `idle` is otherwise
|
|
17
|
+
* unreadable: they are different questions and both answers matter.
|
|
18
|
+
*/
|
|
19
|
+
state: string;
|
|
14
20
|
cwd: string;
|
|
15
21
|
created: string;
|
|
16
22
|
}
|
|
@@ -30,8 +36,23 @@ export interface AgentLsOptions extends CommandOptions {
|
|
|
30
36
|
label?: string[];
|
|
31
37
|
/** Filter by thinking option ID */
|
|
32
38
|
thinking?: string;
|
|
39
|
+
/** Derived-state bucket(s), repeatable. */
|
|
40
|
+
state?: string[];
|
|
41
|
+
origin?: string[];
|
|
42
|
+
tag?: string[];
|
|
43
|
+
notTag?: string[];
|
|
44
|
+
kanban?: string[];
|
|
45
|
+
notKanban?: string[];
|
|
46
|
+
provider?: string[];
|
|
47
|
+
notProvider?: string[];
|
|
48
|
+
archived?: string;
|
|
49
|
+
timeField?: string;
|
|
50
|
+
window?: string;
|
|
51
|
+
from?: string;
|
|
52
|
+
to?: string;
|
|
53
|
+
text?: string;
|
|
33
54
|
}
|
|
34
|
-
export declare function buildAgentLsFetchOptions(options: Pick<AgentLsOptions, "all" | "global" | "label" | "thinking">): FetchAgentsOptions;
|
|
55
|
+
export declare function buildAgentLsFetchOptions(options: Pick<AgentLsOptions, "all" | "global" | "label" | "thinking" | "archived">): FetchAgentsOptions;
|
|
35
56
|
/**
|
|
36
57
|
* Agent ls command semantics:
|
|
37
58
|
* - `paseo agent ls` → active non-archived agents
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
|
|
2
2
|
import { collectMultiple } from "../../utils/command-options.js";
|
|
3
3
|
import { isSameOrDescendantPath } from "../../utils/paths.js";
|
|
4
|
+
import { applyAgentLsFilter, buildAgentLsCriteria, resolveLsTimeField, stateBucketsById, } from "./ls-filter.js";
|
|
4
5
|
export function addLsOptions(cmd) {
|
|
5
|
-
return cmd
|
|
6
|
+
return (cmd
|
|
6
7
|
.description("List agents. By default excludes archived agents.")
|
|
7
8
|
.option("-a, --all", "Include archived agents")
|
|
8
9
|
.option("-g, --global", "List agents across all directories")
|
|
9
10
|
.option("--label <key=value>", "Filter by label (can be used multiple times)", collectMultiple, [])
|
|
10
|
-
.option("--thinking <id>", "Filter by thinking option ID")
|
|
11
|
+
.option("--thinking <id>", "Filter by thinking option ID")
|
|
12
|
+
// The dimensions the paseo UI filters by. `--status` and `--cwd` are not new
|
|
13
|
+
// behaviour: `runLsCommand` has always applied them, but neither was ever
|
|
14
|
+
// registered here, so the code was unreachable from the command line.
|
|
15
|
+
.option("--status <status>", "Filter by raw lifecycle status (initializing|idle|running|error|closed)")
|
|
16
|
+
.option("--cwd <path>", "Filter to this directory and its descendants")
|
|
17
|
+
.option("--state <bucket>", "Filter by derived state bucket (needs_input|failed|running|pending|attention|done). Repeatable.", collectMultiple, [])
|
|
18
|
+
.option("--origin <origin>", "Filter by origin (root|delegated). Repeatable.", collectMultiple, [])
|
|
19
|
+
.option("--tag <tag>", "Keep agents carrying this tag. Repeatable.", collectMultiple, [])
|
|
20
|
+
.option("--not-tag <tag>", "Drop agents carrying this tag. Repeatable.", collectMultiple, [])
|
|
21
|
+
.option("--kanban <bucket>", "Keep agents in this kanban bucket. Repeatable.", collectMultiple, [])
|
|
22
|
+
.option("--not-kanban <bucket>", "Drop agents in this kanban bucket. Repeatable.", collectMultiple, [])
|
|
23
|
+
.option("--provider <id>", "Keep agents on this provider. Repeatable.", collectMultiple, [])
|
|
24
|
+
.option("--not-provider <id>", "Drop agents on this provider. Repeatable.", collectMultiple, [])
|
|
25
|
+
.option("--archived <mode>", "Archived handling: all|only|hide (default hide; -a means all)")
|
|
26
|
+
.option("--time-field <field>", "Which timestamp dates measure: created|active (default active)")
|
|
27
|
+
.option("--window <window>", "Rolling window: today|3d|7d|30d|stale")
|
|
28
|
+
.option("--from <YYYY-MM-DD>", "Exact range start (requires --to)")
|
|
29
|
+
.option("--to <YYYY-MM-DD>", "Exact range end, inclusive (requires --from)")
|
|
30
|
+
.option("--text <query>", "Case-insensitive substring over title and cwd"));
|
|
11
31
|
}
|
|
12
32
|
/** Helper to get relative time string */
|
|
13
33
|
function relativeTime(date) {
|
|
@@ -60,12 +80,26 @@ export const agentLsSchema = {
|
|
|
60
80
|
return undefined;
|
|
61
81
|
},
|
|
62
82
|
},
|
|
83
|
+
{
|
|
84
|
+
header: "STATE",
|
|
85
|
+
field: "state",
|
|
86
|
+
width: 12,
|
|
87
|
+
color: (value) => {
|
|
88
|
+
if (value === "running")
|
|
89
|
+
return "green";
|
|
90
|
+
if (value === "needs_input" || value === "failed")
|
|
91
|
+
return "red";
|
|
92
|
+
if (value === "pending" || value === "attention")
|
|
93
|
+
return "yellow";
|
|
94
|
+
return undefined;
|
|
95
|
+
},
|
|
96
|
+
},
|
|
63
97
|
{ header: "CWD", field: "cwd", width: 30 },
|
|
64
98
|
{ header: "CREATED", field: "created", width: 15 },
|
|
65
99
|
],
|
|
66
100
|
};
|
|
67
101
|
/** Transform agent snapshot to AgentListItem */
|
|
68
|
-
function toListItem(agent) {
|
|
102
|
+
function toListItem(agent, state) {
|
|
69
103
|
const model = normalizeModelId(agent.runtimeInfo?.model) ?? normalizeModelId(agent.model);
|
|
70
104
|
return {
|
|
71
105
|
id: agent.id,
|
|
@@ -74,6 +108,7 @@ function toListItem(agent) {
|
|
|
74
108
|
provider: model ? `${agent.provider}/${model}` : agent.provider,
|
|
75
109
|
thinking: agent.effectiveThinkingOptionId ?? "auto",
|
|
76
110
|
status: agent.status,
|
|
111
|
+
state,
|
|
77
112
|
cwd: shortenPath(agent.cwd),
|
|
78
113
|
created: relativeTime(agent.createdAt),
|
|
79
114
|
};
|
|
@@ -94,7 +129,10 @@ export function buildAgentLsFetchOptions(options) {
|
|
|
94
129
|
const labelFilters = parseLabelFilters(options.label);
|
|
95
130
|
const normalizedThinkingOptionId = options.thinking?.trim();
|
|
96
131
|
const daemonFilter = {};
|
|
97
|
-
|
|
132
|
+
// `only` needs archived records on the wire just as much as `all` does; asking
|
|
133
|
+
// the daemon to hide them and then filtering FOR them client-side returns an
|
|
134
|
+
// always-empty list, which reads as "nothing is archived".
|
|
135
|
+
if (options.all || options.archived === "all" || options.archived === "only") {
|
|
98
136
|
daemonFilter.includeArchived = true;
|
|
99
137
|
}
|
|
100
138
|
if (Object.keys(labelFilters).length > 0) {
|
|
@@ -146,15 +184,11 @@ export async function runLsCommand(options, _command) {
|
|
|
146
184
|
const labelFilters = parseLabelFilters(options.label);
|
|
147
185
|
const fetchPayload = await client.fetchAgents(buildAgentLsFetchOptions(options));
|
|
148
186
|
let agents = fetchPayload.entries.map((entry) => entry.agent);
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
agents = agents.filter((a) => !a.archivedAt);
|
|
152
|
-
}
|
|
153
|
-
// If explicit status filter is provided, apply it.
|
|
187
|
+
// Raw lifecycle status and cwd stay separate from the shared predicate: they
|
|
188
|
+
// are properties of the record rather than filter dimensions the UI models.
|
|
154
189
|
if (options.status) {
|
|
155
190
|
agents = agents.filter((a) => a.status === options.status);
|
|
156
191
|
}
|
|
157
|
-
// Optional cwd filter.
|
|
158
192
|
if (options.cwd) {
|
|
159
193
|
agents = agents.filter((a) => isSameOrDescendantPath(options.cwd, a.cwd));
|
|
160
194
|
}
|
|
@@ -171,6 +205,11 @@ export async function runLsCommand(options, _command) {
|
|
|
171
205
|
});
|
|
172
206
|
}
|
|
173
207
|
await client.close();
|
|
208
|
+
// Every UI dimension, through the one shared predicate. The state buckets are
|
|
209
|
+
// computed BEFORE narrowing so a parent keeps its `pending` bucket even when
|
|
210
|
+
// its only live child is filtered out of the result.
|
|
211
|
+
const buckets = stateBucketsById(agents);
|
|
212
|
+
agents = applyAgentLsFilter(agents, buildAgentLsCriteria(options), resolveLsTimeField(options), new Date());
|
|
174
213
|
// Sort agents: running first, then idle, then others; within each group, most recent first
|
|
175
214
|
const statusOrder = { running: 0, idle: 1 };
|
|
176
215
|
agents.sort((a, b) => {
|
|
@@ -184,7 +223,7 @@ export async function runLsCommand(options, _command) {
|
|
|
184
223
|
const bTime = new Date(b.createdAt).getTime();
|
|
185
224
|
return bTime - aTime;
|
|
186
225
|
});
|
|
187
|
-
const items = agents.map(toListItem);
|
|
226
|
+
const items = agents.map((agent) => toListItem(agent, buckets.get(agent.id) ?? "done"));
|
|
188
227
|
return {
|
|
189
228
|
type: "list",
|
|
190
229
|
data: items,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperdrive.bot/paseo-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.47",
|
|
4
4
|
"description": "Paseo CLI - control your AI coding agents from the command line",
|
|
5
5
|
"bin": {
|
|
6
6
|
"paseo": "bin/paseo"
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^1.0.0",
|
|
30
|
-
"@hyperdrive.bot/paseo-client": "0.3.
|
|
31
|
-
"@hyperdrive.bot/paseo-protocol": "0.3.
|
|
32
|
-
"@hyperdrive.bot/paseo-server": "0.3.
|
|
30
|
+
"@hyperdrive.bot/paseo-client": "0.3.47",
|
|
31
|
+
"@hyperdrive.bot/paseo-protocol": "0.3.47",
|
|
32
|
+
"@hyperdrive.bot/paseo-server": "0.3.47",
|
|
33
33
|
"chalk": "^5.3.0",
|
|
34
34
|
"commander": "^12.0.0",
|
|
35
35
|
"mime-types": "^2.1.35",
|