avtc-pi-featyard 1.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/LICENSE +21 -0
- package/README.md +194 -0
- package/agents/fy-design-reviewer.md +77 -0
- package/agents/fy-feature-verifier.md +24 -0
- package/agents/fy-general-reviewer.md +81 -0
- package/agents/fy-guidelines-reviewer.md +46 -0
- package/agents/fy-implementer.md +20 -0
- package/agents/fy-performance-reviewer.md +46 -0
- package/agents/fy-plan-reviewer.md +74 -0
- package/agents/fy-plan-verifier.md +44 -0
- package/agents/fy-quality-reviewer.md +50 -0
- package/agents/fy-requirements-reviewer.md +46 -0
- package/agents/fy-researcher.md +6 -0
- package/agents/fy-security-reviewer.md +46 -0
- package/agents/fy-task-verifier.md +42 -0
- package/agents/fy-testing-reviewer.md +60 -0
- package/assets/images/kanban-board.png +0 -0
- package/assets/images/settings-dropdown.png +0 -0
- package/assets/images/settings.png +0 -0
- package/assets/images/widget.png +0 -0
- package/docs/CONFIGURATION.md +266 -0
- package/index.ts +8 -0
- package/package.json +85 -0
- package/skills/fy-design/SKILL.md +108 -0
- package/skills/fy-design-review/SKILL.md +45 -0
- package/skills/fy-finish/SKILL.md +28 -0
- package/skills/fy-implement/SKILL.md +36 -0
- package/skills/fy-plan/SKILL.md +77 -0
- package/skills/fy-plan-review/SKILL.md +45 -0
- package/skills/fy-research/SKILL.md +38 -0
- package/skills/fy-review/SKILL.md +88 -0
- package/skills/fy-verify/SKILL.md +23 -0
- package/src/commands/auto-agent-commands.ts +748 -0
- package/src/commands/kanban-commands.ts +278 -0
- package/src/commands/workflow-commands.ts +531 -0
- package/src/compaction/compact-handler.ts +371 -0
- package/src/compaction/compact-message.ts +52 -0
- package/src/compaction/compact-trigger.ts +121 -0
- package/src/compaction/safe-editor-write.ts +18 -0
- package/src/events/agent/agent-end.ts +19 -0
- package/src/events/agent/agent-settled.ts +20 -0
- package/src/events/agent/agent-start.ts +18 -0
- package/src/events/agent/turn-end.ts +20 -0
- package/src/events/agent/turn-start.ts +19 -0
- package/src/events/event-deps.ts +33 -0
- package/src/events/index.ts +91 -0
- package/src/events/input/input.ts +143 -0
- package/src/events/session/context.ts +68 -0
- package/src/events/session/model-select.ts +20 -0
- package/src/events/session/session-compact.ts +20 -0
- package/src/events/session/session-shutdown.ts +26 -0
- package/src/events/session/session-start.ts +45 -0
- package/src/events/session/session-tree.ts +17 -0
- package/src/events/tool/tool-call.ts +58 -0
- package/src/events/tool/tool-result.ts +54 -0
- package/src/git/git-queries.ts +154 -0
- package/src/git/resolve-base-branch.ts +173 -0
- package/src/git/worktrees/worktree-helpers.ts +142 -0
- package/src/git/worktrees/worktree-interception.ts +66 -0
- package/src/git/worktrees/worktree-lifecycle.ts +380 -0
- package/src/guardrails/completion-gating.ts +63 -0
- package/src/guardrails/file-classifier.ts +461 -0
- package/src/guardrails/force-add-guard.ts +312 -0
- package/src/guardrails/guardrail-tracker.ts +73 -0
- package/src/guardrails/guardrail-violations.ts +47 -0
- package/src/guardrails/guardrails-engine.ts +663 -0
- package/src/guardrails/session-guardrails.ts +75 -0
- package/src/guardrails/shell-decompose.ts +285 -0
- package/src/guardrails/tdd-enforcement.ts +122 -0
- package/src/guardrails/test-output.ts +272 -0
- package/src/index.ts +598 -0
- package/src/integrations/parallel-work-guardrail-integration.ts +29 -0
- package/src/integrations/subagent-integration.ts +166 -0
- package/src/integrations/todo-integration.ts +58 -0
- package/src/kanban/auto-agent/auto-agent-cleanup.ts +37 -0
- package/src/kanban/auto-agent/auto-agent-events.ts +142 -0
- package/src/kanban/auto-agent/auto-agent-grace-period.ts +83 -0
- package/src/kanban/auto-agent/auto-agent-lifecycle.ts +497 -0
- package/src/kanban/auto-agent/auto-agent-notify.ts +43 -0
- package/src/kanban/auto-agent/auto-agent-pause.ts +44 -0
- package/src/kanban/auto-agent/auto-agent-state-machine.ts +629 -0
- package/src/kanban/data/kanban-database.ts +851 -0
- package/src/kanban/data/kanban-db-schema.ts +137 -0
- package/src/kanban/data/kanban-detect-project.ts +43 -0
- package/src/kanban/data/kanban-move-feature.ts +43 -0
- package/src/kanban/data/kanban-types.ts +64 -0
- package/src/kanban/ensure-feature.ts +226 -0
- package/src/kanban/kanban-board-ui/app.js +1125 -0
- package/src/kanban/kanban-board-ui/csv-parser.d.ts +5 -0
- package/src/kanban/kanban-board-ui/csv-parser.js +80 -0
- package/src/kanban/kanban-board-ui/index.html +127 -0
- package/src/kanban/kanban-board-ui/style.css +820 -0
- package/src/kanban/kanban-bridge.ts +216 -0
- package/src/kanban/kanban-context.ts +43 -0
- package/src/kanban/kanban-events.ts +34 -0
- package/src/kanban/kanban-generate-title.ts +259 -0
- package/src/kanban/kanban-generate-topic.ts +170 -0
- package/src/kanban/kanban-operations.ts +170 -0
- package/src/kanban/kanban-server.ts +931 -0
- package/src/kanban/kanban-turn-handlers.ts +55 -0
- package/src/kanban/model-capture.ts +34 -0
- package/src/log.ts +47 -0
- package/src/phases/env-sync.ts +64 -0
- package/src/phases/execution-mode.ts +79 -0
- package/src/phases/implement-to-verify.ts +31 -0
- package/src/phases/phase-progression.ts +249 -0
- package/src/phases/phase-transitions.ts +226 -0
- package/src/phases/workflow-router.ts +98 -0
- package/src/prompts/finish-instructions.ts +143 -0
- package/src/prompts/skill-block-builder.ts +73 -0
- package/src/prompts/skill-expansion.ts +262 -0
- package/src/prompts/task-gate-skill.ts +83 -0
- package/src/prompts/template-engine.ts +433 -0
- package/src/prompts/text-blocks.ts +279 -0
- package/src/review/review-context.ts +56 -0
- package/src/review/review-counter.ts +66 -0
- package/src/review/review-empty-loop-tracking.ts +52 -0
- package/src/review/review-loops.ts +217 -0
- package/src/review/review-report.ts +101 -0
- package/src/settings/model-overrides.ts +269 -0
- package/src/settings/settings-schema.ts +458 -0
- package/src/settings/settings-types.ts +81 -0
- package/src/settings/settings-ui.ts +222 -0
- package/src/shared/errors.ts +24 -0
- package/src/shared/global.d.ts +22 -0
- package/src/shared/session-entries.ts +24 -0
- package/src/shared/types.ts +195 -0
- package/src/shared/workflow-refs.ts +253 -0
- package/src/shared/workflow-types.ts +180 -0
- package/src/snippets/vendored/subscribe-to-dialog-coordinator.ts +98 -0
- package/src/snippets/vendored/subscribe-to-notifications.ts +150 -0
- package/src/snippets/vendored/subscribe-to-parallel-work-guardrail.ts +35 -0
- package/src/snippets/vendored/subscribe-to-subagent.ts +67 -0
- package/src/snippets/vendored/subscribe-to-todo.ts +63 -0
- package/src/state/archive-artifacts.ts +602 -0
- package/src/state/artifact-junction.ts +462 -0
- package/src/state/artifact-paths.ts +230 -0
- package/src/state/feature-activation.ts +239 -0
- package/src/state/feature-management.ts +325 -0
- package/src/state/feature-record-store.ts +39 -0
- package/src/state/feature-session.ts +263 -0
- package/src/state/feature-state.ts +439 -0
- package/src/state/post-turn-dispatch.ts +106 -0
- package/src/state/session-lifecycle.ts +346 -0
- package/src/state/state-persistence.ts +122 -0
- package/src/state/worth-notes.ts +89 -0
- package/src/tools/add-to-backlog.ts +117 -0
- package/src/tools/phase-ready.ts +742 -0
- package/src/tools/task-ready-advance.ts +235 -0
- package/src/tools/text-result.ts +15 -0
- package/src/ui/featyard-widget.ts +250 -0
- package/src/ui/manage-features-dialog.ts +246 -0
- package/src/ui/select-dialog.ts +27 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register kanban UI commands (/fy:kanban, /fy:kanban-release) + the shared kanban
|
|
6
|
+
* HTTP server management + agent-lookup helpers shared by the auto-agent loop.
|
|
7
|
+
* The add_to_backlog tool lives separately in tools/add-to-backlog.ts.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Server } from "node:http";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { basename, join } from "node:path";
|
|
13
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import type { KanbanDatabase } from "../kanban/data/kanban-database.js";
|
|
15
|
+
import type { KanbanContext } from "../kanban/kanban-context.js";
|
|
16
|
+
import { createGenerateTitleCallback } from "../kanban/kanban-generate-title.js";
|
|
17
|
+
import { createServer } from "../kanban/kanban-server.js";
|
|
18
|
+
import { log } from "../log.js";
|
|
19
|
+
import { DEFAULT_GLOBAL_DIR, getSettings, loadFeatyardConfig, NO_CWD_OVERRIDE } from "../settings/settings-ui.js";
|
|
20
|
+
|
|
21
|
+
/** AutoAgentStateMachine (type-only alias to avoid repeating the inline import across findAgent* helpers). */
|
|
22
|
+
type AutoAgent = import("../kanban/auto-agent/auto-agent-state-machine.js").AutoAgentStateMachine;
|
|
23
|
+
/** Result shape of findAgentForSlug / findAgentByStates: a matched state machine + its kanban feature id, or null. */
|
|
24
|
+
type AutoAgentMatch = { sm: AutoAgent; featureId: number } | null;
|
|
25
|
+
|
|
26
|
+
/** Shared server instance — module-level for same-factory access. */
|
|
27
|
+
let _sharedServer: { server: Server; port: number; authToken: string } | null = null;
|
|
28
|
+
|
|
29
|
+
/** Get the shared server info. */
|
|
30
|
+
export function getSharedServer(): { server: Server; port: number; authToken: string } | null {
|
|
31
|
+
return _sharedServer;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Set the shared server info. */
|
|
35
|
+
export function setSharedServer(info: { server: Server; port: number; authToken: string } | null): void {
|
|
36
|
+
_sharedServer = info;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Sentinel for setSharedServer() — tear down / clear the shared server reference. */
|
|
40
|
+
export const NO_SHARED_SERVER: { server: Server; port: number; authToken: string } | null = null;
|
|
41
|
+
|
|
42
|
+
/** Returns true if the error is an EADDRINUSE (port already in use) error. */
|
|
43
|
+
function isAddressInUseError(err: unknown): boolean {
|
|
44
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
45
|
+
const code = (err as Record<string, unknown>).code;
|
|
46
|
+
if (code === "EADDRINUSE") return true;
|
|
47
|
+
}
|
|
48
|
+
if (err && typeof err === "object" && "message" in err) {
|
|
49
|
+
const message = (err as Record<string, unknown>).message;
|
|
50
|
+
if (typeof message === "string" && message.includes("EADDRINUSE")) return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* When the kanban server port is already in use, try to connect to the
|
|
57
|
+
* existing server by reading the persisted auth token and verifying it's alive.
|
|
58
|
+
* Returns true on success (shared server is set), false on failure (user notified).
|
|
59
|
+
*/
|
|
60
|
+
async function tryConnectToExistingServer(
|
|
61
|
+
dataDir: string,
|
|
62
|
+
port: number,
|
|
63
|
+
notify: (msg: string, level: "info" | "warning" | "error") => void,
|
|
64
|
+
): Promise<boolean> {
|
|
65
|
+
log.info(`[kanban] port ${port} already in use, attempting to connect to existing server`);
|
|
66
|
+
const tokenFile = join(dataDir, "auth_token.txt");
|
|
67
|
+
const fs = await import("node:fs");
|
|
68
|
+
|
|
69
|
+
// Read auth token
|
|
70
|
+
if (!fs.existsSync(tokenFile)) {
|
|
71
|
+
notify(
|
|
72
|
+
`Kanban server on port ${port} is running but has no auth token (likely started before multi-project support). ` +
|
|
73
|
+
`Please restart the other pi session so it writes the token, or kill the process on port ${port} and retry.`,
|
|
74
|
+
"error",
|
|
75
|
+
);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
let savedToken: string;
|
|
79
|
+
try {
|
|
80
|
+
savedToken = fs.readFileSync(tokenFile, "utf-8").trim();
|
|
81
|
+
} catch (err) {
|
|
82
|
+
log.warn(`[kanban] failed to read auth token: ${err}`);
|
|
83
|
+
notify(`Failed to connect to existing kanban server: ${err}`, "error");
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Verify server is alive (5s timeout)
|
|
88
|
+
try {
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
const timeout = setTimeout(() => controller.abort(), 5_000);
|
|
91
|
+
const res = await globalThis
|
|
92
|
+
.fetch(`http://localhost:${port}/api/projects`, {
|
|
93
|
+
headers: { Authorization: `Bearer ${savedToken}` },
|
|
94
|
+
signal: controller.signal,
|
|
95
|
+
})
|
|
96
|
+
.finally(() => clearTimeout(timeout));
|
|
97
|
+
|
|
98
|
+
if (!res.ok) {
|
|
99
|
+
notify(`Kanban server on port ${port} is not responding. Try stopping the other session first.`, "error");
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setSharedServer({ server: { close: () => {} } as unknown as Server, port, authToken: savedToken });
|
|
104
|
+
log.info(`[kanban] connected to existing kanban server on port ${port}`);
|
|
105
|
+
notify(`Connected to existing kanban server on port ${port}`, "info");
|
|
106
|
+
return true;
|
|
107
|
+
} catch {
|
|
108
|
+
notify(`Kanban server on port ${port} is not reachable. Try stopping the other session first.`, "error");
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Register the kanban UI slash commands (/fy:kanban, /fy:kanban-release). */
|
|
114
|
+
export function registerKanbanCommands(pi: ExtensionAPI, ctx: KanbanContext): void {
|
|
115
|
+
const { getDatabase, getTools } = ctx;
|
|
116
|
+
|
|
117
|
+
// Register /fy:kanban command
|
|
118
|
+
pi.registerCommand("fy:kanban", {
|
|
119
|
+
description: "Open kanban board in browser (starts HTTP server if needed)",
|
|
120
|
+
async handler(_args, cmdCtx) {
|
|
121
|
+
const database = await getDatabase();
|
|
122
|
+
const config = loadFeatyardConfig(DEFAULT_GLOBAL_DIR, NO_CWD_OVERRIDE);
|
|
123
|
+
const desiredPort = config["kanban-port"] ?? 0;
|
|
124
|
+
const dataDir = join(homedir(), ".pi", "featyard", "kanban");
|
|
125
|
+
const staticDir = join(__dirname, "../kanban/kanban-board-ui");
|
|
126
|
+
|
|
127
|
+
if (!getSharedServer()) {
|
|
128
|
+
try {
|
|
129
|
+
setSharedServer(
|
|
130
|
+
await createServer(database, desiredPort, staticDir, {
|
|
131
|
+
doneHideAfterMs: getSettings().kanbanDoneHideAfterMs ?? null,
|
|
132
|
+
generateTitle: await createGenerateTitleCallback(
|
|
133
|
+
() => ctx.capturedModelRef.model,
|
|
134
|
+
() => ctx.capturedModelRef.registry,
|
|
135
|
+
await import("@earendil-works/pi-agent-core"),
|
|
136
|
+
),
|
|
137
|
+
dataDir,
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
} catch (err: unknown) {
|
|
141
|
+
if (!isAddressInUseError(err)) throw err;
|
|
142
|
+
if (!(await tryConnectToExistingServer(dataDir, desiredPort, cmdCtx.ui.notify.bind(cmdCtx.ui)))) return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Auto-detect or create project from git repo
|
|
146
|
+
const { detectProject } = await import("../kanban/data/kanban-detect-project.js");
|
|
147
|
+
let projectId = await detectProject(database, process.cwd());
|
|
148
|
+
if (!projectId) {
|
|
149
|
+
// Auto-create project for this repo
|
|
150
|
+
try {
|
|
151
|
+
const { resolveRepoPath } = await import("../kanban/data/kanban-detect-project.js");
|
|
152
|
+
const repoPath = await resolveRepoPath(process.cwd());
|
|
153
|
+
const existing = database.findProjectByRepoPath(repoPath);
|
|
154
|
+
if (existing) {
|
|
155
|
+
projectId = existing.id;
|
|
156
|
+
} else {
|
|
157
|
+
projectId = database.createProject({ name: basename(repoPath), repoPath });
|
|
158
|
+
log.info(`[kanban] auto-created project ${projectId} "${basename(repoPath)}" for repoPath=${repoPath}`);
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
log.warn(`[kanban] failed to auto-create project: ${err}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const server = getSharedServer();
|
|
165
|
+
if (!server) {
|
|
166
|
+
log.warn("[kanban] no shared server available, cannot open Kanban board");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const url = projectId
|
|
170
|
+
? `http://localhost:${server.port}?project=${projectId}`
|
|
171
|
+
: `http://localhost:${server.port}`;
|
|
172
|
+
cmdCtx.ui.notify(`Kanban board: ${url}`, "info");
|
|
173
|
+
// Open in browser (platform-specific)
|
|
174
|
+
const { execFile } = await import("node:child_process");
|
|
175
|
+
if (process.platform === "win32") {
|
|
176
|
+
execFile("cmd", ["/c", "start", "", url], () => {});
|
|
177
|
+
} else if (process.platform === "darwin") {
|
|
178
|
+
execFile("open", [url], () => {});
|
|
179
|
+
} else {
|
|
180
|
+
execFile("xdg-open", [url], () => {});
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Register /fy:kanban-release command
|
|
186
|
+
pi.registerCommand("fy:kanban-release", {
|
|
187
|
+
description: "Release a feature lock so others can pick it up",
|
|
188
|
+
async handler(args, cmdCtx) {
|
|
189
|
+
const featureId = parseInt(args.trim(), 10);
|
|
190
|
+
if (!featureId || featureId <= 0) {
|
|
191
|
+
cmdCtx.ui.notify("Usage: /fy:kanban-release <feature-id>", "warning");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const kanbanTools = await getTools();
|
|
197
|
+
kanbanTools.kanbanRelease({ featureId });
|
|
198
|
+
cmdCtx.ui.notify(`Feature ${featureId} lock released`, "info");
|
|
199
|
+
} catch (err) {
|
|
200
|
+
cmdCtx.ui.notify(`Failed to release feature ${featureId}: ${err}`, "error");
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Helper: find an agent whose current feature matches the given slug. */
|
|
207
|
+
export async function findAgentForSlug(
|
|
208
|
+
slug: string,
|
|
209
|
+
autoAgent: AutoAgent | null,
|
|
210
|
+
getDatabase: () => Promise<KanbanDatabase>,
|
|
211
|
+
options: { state: string | string[] | null } | null,
|
|
212
|
+
): Promise<AutoAgentMatch> {
|
|
213
|
+
if (!autoAgent) return null;
|
|
214
|
+
const sm = autoAgent;
|
|
215
|
+
if (options?.state) {
|
|
216
|
+
const states = Array.isArray(options.state) ? options.state : [options.state];
|
|
217
|
+
if (!states.includes(sm.getState())) {
|
|
218
|
+
log.debug(`[kanban] findAgentForSlug("${slug}"): agent state=${sm.getState()} not in [${states.join(", ")}]`);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const featureId = sm.getCurrentFeatureId();
|
|
223
|
+
if (featureId === null) return null;
|
|
224
|
+
try {
|
|
225
|
+
const db = await getDatabase();
|
|
226
|
+
const feature = db.getFeature(featureId);
|
|
227
|
+
if (!feature || feature.slug !== slug) {
|
|
228
|
+
log.info(
|
|
229
|
+
`[kanban] findAgentForSlug("${slug}"): feature ${featureId} slug=${feature?.slug ?? "null"} !== ${slug}`,
|
|
230
|
+
);
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
log.warn(`[kanban] findAgentForSlug: lookup failed for slug "${slug}", featureId=${featureId}: ${err}`);
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
return { sm, featureId };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Find an agent for the given slug whose state is in `states`. Shared backing for findAnyActiveAgent/findResumableAgent. */
|
|
241
|
+
function findAgentByStates(
|
|
242
|
+
slug: string,
|
|
243
|
+
autoAgent: AutoAgent | null,
|
|
244
|
+
getDatabase: () => Promise<KanbanDatabase>,
|
|
245
|
+
states: readonly string[],
|
|
246
|
+
): Promise<AutoAgentMatch> {
|
|
247
|
+
return findAgentForSlug(slug, autoAgent, getDatabase, { state: [...states] });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Active agent states — any agent that is currently doing work or temporarily paused. */
|
|
251
|
+
const ACTIVE_STATES = ["working", "paused", "waiting"] as const;
|
|
252
|
+
|
|
253
|
+
/** Resumable agent states — agents waiting for user input. */
|
|
254
|
+
const RESUMABLE_STATES = ["waiting"] as const;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Find any active agent for the given slug (working, paused, or waiting).
|
|
258
|
+
* Convenience wrapper around findAgentForSlug that replaces cascading nullish coalescing.
|
|
259
|
+
*/
|
|
260
|
+
export function findAnyActiveAgent(
|
|
261
|
+
slug: string,
|
|
262
|
+
autoAgent: AutoAgent | null,
|
|
263
|
+
getDatabase: () => Promise<KanbanDatabase>,
|
|
264
|
+
): Promise<AutoAgentMatch> {
|
|
265
|
+
return findAgentByStates(slug, autoAgent, getDatabase, ACTIVE_STATES);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Find a resumable agent for the given slug (waiting).
|
|
270
|
+
* Convenience wrapper around findAgentForSlug that replaces cascading nullish coalescing.
|
|
271
|
+
*/
|
|
272
|
+
export function findResumableAgent(
|
|
273
|
+
slug: string,
|
|
274
|
+
autoAgent: AutoAgent | null,
|
|
275
|
+
getDatabase: () => Promise<KanbanDatabase>,
|
|
276
|
+
): Promise<AutoAgentMatch> {
|
|
277
|
+
return findAgentByStates(slug, autoAgent, getDatabase, RESUMABLE_STATES);
|
|
278
|
+
}
|