@ai-setting/roy-plugin-task-show 0.6.12 → 0.8.5

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,143 @@
1
+ /**
2
+ * @fileoverview CLI adapter for `roy-agent tasks tree [--json] [--root-id N]`.
3
+ *
4
+ * Used by the Web homepage to render a hierarchical task list instead of
5
+ * the flat per-task tool-call view. Mirrors the design of
6
+ * `cli-tasks-adapter.ts` (typed errors, timeouts, byte caps, injectable
7
+ * runner) so the HTTP layer can map errors the same way.
8
+ *
9
+ * The CLI's `--json` output for `tasks tree` is:
10
+ * {
11
+ * "total": <number>, // total tasks in the filtered set
12
+ * "rootCount": <number>, // number of root nodes in the returned tree
13
+ * "tree": [ // array of root nodes (recursively nested)
14
+ * {
15
+ * "task": { id, title, status, priority, type, progress, ... },
16
+ * "children": [ /* same shape, recursively *\/ ]
17
+ * },
18
+ * ...
19
+ * ]
20
+ * }
21
+ *
22
+ * If `--root-id N` is provided, the response is a 1-element array whose
23
+ * only root is task #N, and the rest of the tree hangs below it. We
24
+ * transparently pass `--root-id` through to the CLI — callers don't have
25
+ * to filter the result themselves.
26
+ *
27
+ * Design goals (matching cli-tasks-adapter.ts):
28
+ * - Spawn the host CLI as a discrete arg array — never via shell string.
29
+ * - Enforce a wall-clock timeout and a maximum stdout size.
30
+ * - Locate the leading `{` in stdout to skip INFO/log lines the real
31
+ * CLI emits before its JSON envelope.
32
+ * - Validate the parsed envelope against a stable schema.
33
+ * - Surface typed errors so callers can map them to HTTP status codes.
34
+ *
35
+ * The adapter is intentionally narrow: it knows nothing about HTTP, the
36
+ * server, or the frontend. The `runner` is injectable so tests never
37
+ * need a real subprocess.
38
+ */
39
+ import type { TaskShowConfig } from "./types.js";
40
+ /** Subset of the CLI's `task` payload that we surface to the UI. */
41
+ export interface TaskTreeNodeTask {
42
+ id: number;
43
+ title: string;
44
+ description?: string;
45
+ status: string;
46
+ priority: string;
47
+ type: string;
48
+ progress?: number;
49
+ current_status?: string;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ tags: string[];
53
+ project_path?: string;
54
+ parent_task_id?: number;
55
+ }
56
+ /** One node in the returned tree (recursive). */
57
+ export interface TaskTreeNode {
58
+ task: TaskTreeNodeTask;
59
+ children: TaskTreeNode[];
60
+ }
61
+ /** Successful parse result. */
62
+ export interface TasksTreeEnvelope {
63
+ total: number;
64
+ rootCount: number;
65
+ tree: TaskTreeNode[];
66
+ /** When the source data was last fetched (ISO 8601). */
67
+ fetchedAt: string;
68
+ /** True if this entry is past TTL but still served. */
69
+ stale: boolean;
70
+ }
71
+ /** Filter parameters understood by the underlying CLI. */
72
+ export interface TasksTreeFilter {
73
+ status?: "todo" | "active" | "completed" | "paused" | "cancelled";
74
+ priority?: "low" | "medium" | "high";
75
+ type?: "normal" | "cycle" | "longterm";
76
+ /** Restrict the tree to one root (and its descendants). */
77
+ rootId?: number;
78
+ includeArchived?: boolean;
79
+ }
80
+ export { AdapterError, TimeoutError, ParseError, SchemaError, } from "./cli-tasks-adapter.js";
81
+ export interface AdapterRunnerResult {
82
+ stdout: string;
83
+ stderr: string;
84
+ exitCode: number;
85
+ }
86
+ export type AdapterRunner = (args: string[]) => Promise<AdapterRunnerResult>;
87
+ export interface TasksTreeAdapterOptions {
88
+ /** Absolute path to the `roy-agent` executable. */
89
+ cliPath: string;
90
+ /** Mockable subprocess runner (defaults to `defaultRunner`). */
91
+ runner?: AdapterRunner;
92
+ /** Wall-clock timeout (default 30000 ms — the tree JSON is large and the CLI runs migrations). */
93
+ timeoutMs?: number;
94
+ /** Maximum stdout bytes to keep (default 8 MiB). */
95
+ maxBytes?: number;
96
+ /** TaskShowConfig for sharing limits / defaults. */
97
+ cfg: TaskShowConfig;
98
+ }
99
+ export interface TasksTreeSource {
100
+ getTasksTree(filter: TasksTreeFilter): Promise<TasksTreeEnvelope>;
101
+ }
102
+ /**
103
+ * Default subprocess runner. We deliberately avoid `shell: true` to keep
104
+ * argv as a literal array — no shell metacharacter interpretation.
105
+ *
106
+ * Two adaptations for Bun / large outputs:
107
+ * 1. File-descriptor stdio: under Bun, piping ≥1 MB via
108
+ * `stdio: ["ignore", "pipe", "pipe"]` drops data because the
109
+ * `data` and `end` events fire before every chunk has been
110
+ * delivered. Redirecting stdout/stderr to temp files captures the
111
+ * full output reliably.
112
+ * 2. Poll-until-stable read: on Bun, the child process's `close` event
113
+ * can fire BEFORE all writes have been flushed to the file
114
+ * descriptor. We poll the file size until it stabilizes, then
115
+ * read with createReadStream (more robust than readFileSync for
116
+ * large files). This guarantees we don't return truncated output.
117
+ *
118
+ * Both adaptations are no-ops under Node.js (no observable behavior
119
+ * change), so the same code runs reliably on either runtime.
120
+ *
121
+ * Temp files are cleaned up in `finally` so a crash doesn't leak dirs.
122
+ */
123
+ export declare const defaultRunner: AdapterRunner;
124
+ /**
125
+ * Build the argv array for `roy-agent tasks tree [--status] [--priority]
126
+ * [--type] [--root-id] [--include-archived] --json`. Pure function — easy
127
+ * to unit-test.
128
+ */
129
+ export declare function buildTasksTreeArgs(cliPath: string, filter: TasksTreeFilter): string[];
130
+ /**
131
+ * Run `roy-agent tasks tree [--json] [--filter...]` and return a parsed
132
+ * envelope. Errors are typed so the HTTP layer can map them to status
133
+ * codes.
134
+ *
135
+ * Pass `runner: defaultRunner` in production. Tests pass a `fixedRunner`.
136
+ */
137
+ export declare function runTasksTree(filter: TasksTreeFilter, options: TasksTreeAdapterOptions): Promise<TasksTreeEnvelope>;
138
+ /**
139
+ * Convenience wrapper used by TasksTreeCache: a `TasksTreeSource` whose
140
+ * `getTasksTree(filter)` returns the parsed envelope or throws.
141
+ */
142
+ export declare function makeTasksTreeSource(opts: TasksTreeAdapterOptions): TasksTreeSource;
143
+ //# sourceMappingURL=cli-tasks-tree-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-tasks-tree-adapter.d.ts","sourceRoot":"","sources":["../src/cli-tasks-tree-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,400 @@
1
+ /**
2
+ * @fileoverview CLI adapter for `roy-agent tasks tree [--json] [--root-id N]`.
3
+ *
4
+ * Used by the Web homepage to render a hierarchical task list instead of
5
+ * the flat per-task tool-call view. Mirrors the design of
6
+ * `cli-tasks-adapter.ts` (typed errors, timeouts, byte caps, injectable
7
+ * runner) so the HTTP layer can map errors the same way.
8
+ *
9
+ * The CLI's `--json` output for `tasks tree` is:
10
+ * {
11
+ * "total": <number>, // total tasks in the filtered set
12
+ * "rootCount": <number>, // number of root nodes in the returned tree
13
+ * "tree": [ // array of root nodes (recursively nested)
14
+ * {
15
+ * "task": { id, title, status, priority, type, progress, ... },
16
+ * "children": [ /* same shape, recursively *\/ ]
17
+ * },
18
+ * ...
19
+ * ]
20
+ * }
21
+ *
22
+ * If `--root-id N` is provided, the response is a 1-element array whose
23
+ * only root is task #N, and the rest of the tree hangs below it. We
24
+ * transparently pass `--root-id` through to the CLI — callers don't have
25
+ * to filter the result themselves.
26
+ *
27
+ * Design goals (matching cli-tasks-adapter.ts):
28
+ * - Spawn the host CLI as a discrete arg array — never via shell string.
29
+ * - Enforce a wall-clock timeout and a maximum stdout size.
30
+ * - Locate the leading `{` in stdout to skip INFO/log lines the real
31
+ * CLI emits before its JSON envelope.
32
+ * - Validate the parsed envelope against a stable schema.
33
+ * - Surface typed errors so callers can map them to HTTP status codes.
34
+ *
35
+ * The adapter is intentionally narrow: it knows nothing about HTTP, the
36
+ * server, or the frontend. The `runner` is injectable so tests never
37
+ * need a real subprocess.
38
+ */
39
+ // Re-exports so the HTTP layer can use the same error classes without
40
+ // importing cli-tasks-adapter.ts directly (avoids a circular import).
41
+ export { AdapterError, TimeoutError, ParseError, SchemaError, } from "./cli-tasks-adapter.js";
42
+ import { AdapterError, TimeoutError, ParseError, SchemaError } from "./cli-tasks-adapter.js";
43
+ // ---------------------------------------------------------------------------
44
+ // Default runner — spawns the CLI as discrete args with timeout & byte cap
45
+ // ---------------------------------------------------------------------------
46
+ /**
47
+ * Default subprocess runner. We deliberately avoid `shell: true` to keep
48
+ * argv as a literal array — no shell metacharacter interpretation.
49
+ *
50
+ * Two adaptations for Bun / large outputs:
51
+ * 1. File-descriptor stdio: under Bun, piping ≥1 MB via
52
+ * `stdio: ["ignore", "pipe", "pipe"]` drops data because the
53
+ * `data` and `end` events fire before every chunk has been
54
+ * delivered. Redirecting stdout/stderr to temp files captures the
55
+ * full output reliably.
56
+ * 2. Poll-until-stable read: on Bun, the child process's `close` event
57
+ * can fire BEFORE all writes have been flushed to the file
58
+ * descriptor. We poll the file size until it stabilizes, then
59
+ * read with createReadStream (more robust than readFileSync for
60
+ * large files). This guarantees we don't return truncated output.
61
+ *
62
+ * Both adaptations are no-ops under Node.js (no observable behavior
63
+ * change), so the same code runs reliably on either runtime.
64
+ *
65
+ * Temp files are cleaned up in `finally` so a crash doesn't leak dirs.
66
+ */
67
+ export const defaultRunner = async (args) => {
68
+ const { spawn } = await import("node:child_process");
69
+ const { mkdtempSync, openSync, rmSync, statSync, createReadStream } = await import("node:fs");
70
+ const { tmpdir } = await import("node:os");
71
+ const { join } = await import("node:path");
72
+ const executable = args[0];
73
+ const argv = args.slice(1);
74
+ const dir = mkdtempSync(join(tmpdir(), "roy-task-tree-"));
75
+ const stdoutPath = join(dir, "stdout");
76
+ const stderrPath = join(dir, "stderr");
77
+ const stdoutFd = openSync(stdoutPath, "w");
78
+ const stderrFd = openSync(stderrPath, "w");
79
+ return new Promise((resolve, reject) => {
80
+ let settled = false;
81
+ const MAX_BYTES_DEFAULT = 8 * 1024 * 1024;
82
+ const child = spawn(executable, argv, {
83
+ stdio: ["ignore", stdoutFd, stderrFd],
84
+ env: process.env,
85
+ });
86
+ const timer = setTimeout(() => {
87
+ if (settled)
88
+ return;
89
+ settled = true;
90
+ try {
91
+ child.kill("SIGKILL");
92
+ }
93
+ catch {
94
+ /* ignore */
95
+ }
96
+ const err = new Error("CLI spawn timed out");
97
+ err.name = "AbortError";
98
+ reject(err);
99
+ }, 30_000);
100
+ child.on("error", (err) => {
101
+ if (settled)
102
+ return;
103
+ settled = true;
104
+ clearTimeout(timer);
105
+ reject(err);
106
+ });
107
+ child.on("close", (code) => {
108
+ if (settled)
109
+ return;
110
+ settled = true;
111
+ clearTimeout(timer);
112
+ // Poll the stdout file size until it stops growing. The Bun
113
+ // runtime can fire `close` before the child's buffered stdout
114
+ // has been fully written to the file descriptor, so we must wait
115
+ // for the writer side to flush before reading.
116
+ const waitForFlush = () => new Promise((done) => {
117
+ const deadline = Date.now() + 5000; // 5s safety net
118
+ let lastSize = -1;
119
+ let stableTicks = 0;
120
+ const tick = () => {
121
+ let curSize = 0;
122
+ try {
123
+ curSize = statSync(stdoutPath).size;
124
+ }
125
+ catch {
126
+ // file may have been removed by something — bail
127
+ done();
128
+ return;
129
+ }
130
+ if (curSize === lastSize) {
131
+ stableTicks += 1;
132
+ if (stableTicks >= 3) {
133
+ done();
134
+ return;
135
+ }
136
+ }
137
+ else {
138
+ stableTicks = 0;
139
+ lastSize = curSize;
140
+ }
141
+ if (Date.now() >= deadline) {
142
+ done();
143
+ return;
144
+ }
145
+ setTimeout(tick, 25);
146
+ };
147
+ tick();
148
+ });
149
+ waitForFlush().then(() => {
150
+ try {
151
+ const readUtf8 = (filePath) => new Promise((res, rej) => {
152
+ const chunks = [];
153
+ const stream = createReadStream(filePath);
154
+ stream.on("data", (c) => chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)));
155
+ stream.on("end", () => res(Buffer.concat(chunks).toString("utf8")));
156
+ stream.on("error", rej);
157
+ });
158
+ Promise.all([readUtf8(stdoutPath), readUtf8(stderrPath)]).then(([stdout, stderr]) => {
159
+ if (stdout.length > MAX_BYTES_DEFAULT) {
160
+ reject(new AdapterError(`CLI stdout exceeded maxBytes=${MAX_BYTES_DEFAULT} (got ${stdout.length})`, { code: "oversize" }));
161
+ return;
162
+ }
163
+ resolve({
164
+ stdout,
165
+ stderr,
166
+ exitCode: code ?? 0,
167
+ });
168
+ }, (err) => reject(err));
169
+ }
170
+ catch (err) {
171
+ reject(err);
172
+ }
173
+ });
174
+ });
175
+ }).finally(() => {
176
+ try {
177
+ rmSync(dir, { recursive: true, force: true });
178
+ }
179
+ catch {
180
+ /* ignore — best-effort cleanup */
181
+ }
182
+ });
183
+ };
184
+ // ---------------------------------------------------------------------------
185
+ // Arg builder
186
+ // ---------------------------------------------------------------------------
187
+ /**
188
+ * Build the argv array for `roy-agent tasks tree [--status] [--priority]
189
+ * [--type] [--root-id] [--include-archived] --json`. Pure function — easy
190
+ * to unit-test.
191
+ */
192
+ export function buildTasksTreeArgs(cliPath, filter) {
193
+ const args = [cliPath, "tasks", "tree"];
194
+ if (filter.status)
195
+ args.push("--status", filter.status);
196
+ if (filter.priority)
197
+ args.push("--priority", filter.priority);
198
+ if (filter.type)
199
+ args.push("--type", filter.type);
200
+ if (typeof filter.rootId === "number" && Number.isFinite(filter.rootId) && filter.rootId > 0) {
201
+ args.push("--root-id", String(Math.trunc(filter.rootId)));
202
+ }
203
+ if (filter.includeArchived)
204
+ args.push("--include-archived");
205
+ args.push("--json");
206
+ // The CLI is noisy on stdout by default — force quiet mode so we only
207
+ // get the JSON envelope. Newer builds accept `--quiet`; older builds
208
+ // (default quiet=true) ignore it harmlessly.
209
+ args.push("--quiet");
210
+ return args;
211
+ }
212
+ // ---------------------------------------------------------------------------
213
+ // Parsing helpers
214
+ // ---------------------------------------------------------------------------
215
+ /**
216
+ * Locate the JSON envelope in stdout. The CLI emits migration / OTel
217
+ * log lines before the envelope, and some runtimes (notably Bun) have
218
+ * been observed to append unrelated output (e.g. other plugins'
219
+ * `dispose` banners) AFTER the envelope's closing brace.
220
+ *
221
+ * Strategy:
222
+ * 1. Locate the leading `{` and skip the leading log lines.
223
+ * 2. Walk forward, tracking brace depth, to find the matching `}`.
224
+ * This is robust against trailing content that isn't part of the
225
+ * envelope — anything after the balanced close brace is dropped.
226
+ * 3. Bail with a ParseError if the brace depth never returns to 0.
227
+ *
228
+ * The implementation treats `{` and `}` that appear inside JSON strings
229
+ * conservatively (a real string can contain an unescaped brace in some
230
+ * shapes, but our CLI's envelope never does). This keeps the extractor
231
+ * simple while still handling the observed trailing-noise case.
232
+ */
233
+ function extractJsonEnvelope(stdout) {
234
+ const start = stdout.indexOf("{");
235
+ if (start < 0) {
236
+ throw new ParseError("No JSON envelope found in CLI stdout (no leading `{`)");
237
+ }
238
+ let depth = 0;
239
+ let inString = false;
240
+ let escape = false;
241
+ let end = -1;
242
+ for (let i = start; i < stdout.length; i++) {
243
+ const ch = stdout[i];
244
+ if (inString) {
245
+ if (escape) {
246
+ escape = false;
247
+ }
248
+ else if (ch === "\\") {
249
+ escape = true;
250
+ }
251
+ else if (ch === '"') {
252
+ inString = false;
253
+ }
254
+ continue;
255
+ }
256
+ if (ch === '"') {
257
+ inString = true;
258
+ continue;
259
+ }
260
+ if (ch === "{") {
261
+ depth += 1;
262
+ }
263
+ else if (ch === "}") {
264
+ depth -= 1;
265
+ if (depth === 0) {
266
+ end = i;
267
+ break;
268
+ }
269
+ }
270
+ }
271
+ if (end < 0) {
272
+ throw new ParseError("Unterminated JSON envelope (brace depth never returned to 0)");
273
+ }
274
+ return stdout.slice(start, end + 1);
275
+ }
276
+ /**
277
+ * Recursively validate + normalize a tree node. Throws SchemaError on the
278
+ * first structural problem so the caller can surface a 502-style error.
279
+ */
280
+ function normalizeNode(raw, depth) {
281
+ if (depth > 16) {
282
+ // Defensive: a malicious / buggy CLI emitting 17+ levels deep would
283
+ // blow the stack on recursive normalizeNode. Bail with a clear error.
284
+ throw new SchemaError(`tree node depth > 16 (recursion guard)`);
285
+ }
286
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
287
+ throw new SchemaError("tree node is not an object");
288
+ }
289
+ if (!raw.task || typeof raw.task !== "object") {
290
+ throw new SchemaError("tree node missing `task` object");
291
+ }
292
+ const t = raw.task;
293
+ const task = {
294
+ id: Number(t.id),
295
+ title: String(t.title ?? ""),
296
+ description: typeof t.description === "string" ? t.description : undefined,
297
+ status: String(t.status ?? "unknown"),
298
+ priority: String(t.priority ?? "medium"),
299
+ type: String(t.type ?? "normal"),
300
+ progress: typeof t.progress === "number" ? t.progress : undefined,
301
+ current_status: typeof t.current_status === "string" ? t.current_status : undefined,
302
+ createdAt: String(t.createdAt ?? ""),
303
+ updatedAt: String(t.updatedAt ?? ""),
304
+ tags: Array.isArray(t.tags) ? t.tags.map((x) => String(x)) : [],
305
+ project_path: typeof t.project_path === "string" ? t.project_path : undefined,
306
+ parent_task_id: typeof t.parent_task_id === "number" ? t.parent_task_id : undefined,
307
+ };
308
+ if (!Number.isInteger(task.id) || task.id <= 0) {
309
+ throw new SchemaError(`tree node task.id is invalid: ${String(task.id)}`);
310
+ }
311
+ const children = Array.isArray(raw.children) ? raw.children : [];
312
+ return { task, children: children.map((c) => normalizeNode(c, depth + 1)) };
313
+ }
314
+ // ---------------------------------------------------------------------------
315
+ // Public entry point
316
+ // ---------------------------------------------------------------------------
317
+ /**
318
+ * Run `roy-agent tasks tree [--json] [--filter...]` and return a parsed
319
+ * envelope. Errors are typed so the HTTP layer can map them to status
320
+ * codes.
321
+ *
322
+ * Pass `runner: defaultRunner` in production. Tests pass a `fixedRunner`.
323
+ */
324
+ export async function runTasksTree(filter, options) {
325
+ const runner = options.runner ?? defaultRunner;
326
+ const args = buildTasksTreeArgs(options.cliPath, filter);
327
+ const timeoutMs = options.timeoutMs ?? 30_000;
328
+ const maxBytes = options.maxBytes ?? 8 * 1024 * 1024;
329
+ let raw;
330
+ try {
331
+ raw = await withTimeout(runner(args), timeoutMs);
332
+ }
333
+ catch (err) {
334
+ if (err?.name === "AbortError" || /timeout|abort/i.test(String(err?.message ?? ""))) {
335
+ throw new TimeoutError(`CLI timeout after ${timeoutMs}ms`, err);
336
+ }
337
+ if (err instanceof AdapterError)
338
+ throw err;
339
+ throw new AdapterError(`CLI runner failed: ${err?.message ?? String(err)}`, { cause: err });
340
+ }
341
+ if (raw.stdout.length > maxBytes) {
342
+ throw new AdapterError(`CLI stdout exceeded maxBytes=${maxBytes} (got ${raw.stdout.length})`, { code: "oversize" });
343
+ }
344
+ let parsed;
345
+ try {
346
+ const jsonText = extractJsonEnvelope(raw.stdout);
347
+ parsed = JSON.parse(jsonText);
348
+ }
349
+ catch (err) {
350
+ if (raw.exitCode !== 0) {
351
+ throw new AdapterError(`CLI exited with code ${raw.exitCode} and no parseable JSON envelope`, { code: "cli_failed", exitCode: raw.exitCode });
352
+ }
353
+ if (err instanceof ParseError || err instanceof SchemaError)
354
+ throw err;
355
+ throw new ParseError("Failed to parse CLI JSON envelope", err);
356
+ }
357
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
358
+ throw new SchemaError("envelope root is not an object");
359
+ }
360
+ if (!Array.isArray(parsed.tree)) {
361
+ throw new SchemaError("envelope missing `tree` array");
362
+ }
363
+ const tree = parsed.tree.map((n) => normalizeNode(n, 0));
364
+ const total = Number(parsed.total ?? tree.length);
365
+ const rootCount = Number(parsed.rootCount ?? tree.length);
366
+ return {
367
+ total,
368
+ rootCount,
369
+ tree,
370
+ fetchedAt: new Date().toISOString(),
371
+ stale: false,
372
+ };
373
+ }
374
+ /** Wrap a promise with a wall-clock timeout. */
375
+ function withTimeout(p, ms) {
376
+ let timer;
377
+ const timeout = new Promise((_, reject) => {
378
+ timer = setTimeout(() => {
379
+ const err = new Error(`timed out after ${ms}ms`);
380
+ err.name = "AbortError";
381
+ reject(err);
382
+ }, ms);
383
+ });
384
+ return Promise.race([p, timeout]).finally(() => {
385
+ if (timer)
386
+ clearTimeout(timer);
387
+ });
388
+ }
389
+ /**
390
+ * Convenience wrapper used by TasksTreeCache: a `TasksTreeSource` whose
391
+ * `getTasksTree(filter)` returns the parsed envelope or throws.
392
+ */
393
+ export function makeTasksTreeSource(opts) {
394
+ return {
395
+ async getTasksTree(filter) {
396
+ return runTasksTree(filter, opts);
397
+ },
398
+ };
399
+ }
400
+ //# sourceMappingURL=cli-tasks-tree-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-tasks-tree-adapter.js","sourceRoot":"","sources":["../src/cli-tasks-tree-adapter.ts"],"names":[],"mappings":"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"}
package/dist/plugin.d.ts CHANGED
@@ -104,6 +104,61 @@ export declare function resolveToolName(ctx: any, logger?: ResolveToolNameLogger
104
104
  * reach for it.
105
105
  */
106
106
  export declare function __resetResolveToolNameWarnDedupForTests(): void;
107
+ /**
108
+ * Best-effort lookup of the `roy-agent` executable on the OS PATH.
109
+ *
110
+ * Implementation: shell out to the platform's native resolver:
111
+ * - POSIX (Linux / macOS): `which roy-agent`
112
+ * - Windows: `where roy-agent`
113
+ *
114
+ * We avoid hand-rolling a `node:fs` + `process.env.PATH` parser because
115
+ * Windows PATH uses `;` (not `:`), `.bat` / `.cmd` shims need to be
116
+ * resolved, and PowerShell vs cmd semantics differ. The shell utils
117
+ * handle those edge cases correctly for us.
118
+ *
119
+ * Returns the absolute path of the first match, or `null` if:
120
+ * - the binary is not installed
121
+ * - the resolver command (`which` / `where`) is unavailable
122
+ * - any unexpected error happens (caught silently — best-effort)
123
+ *
124
+ * v0.8.5+ — new export. Was previously inlined inside the class method.
125
+ */
126
+ export declare function findRoyAgentOnPath(): string | null;
127
+ /**
128
+ * Resolve the `roy-agent` executable to use for the operations pipeline.
129
+ *
130
+ * v0.8.5+ priority order (changed from v0.8.4):
131
+ * 1. Explicit override (`cfg.royAgentCliPath`) — used as-is regardless
132
+ * of whether it exists on disk. The caller knows what they want.
133
+ * 2. `$ROY_AGENT_CLI` env var — same semantics: trust the operator.
134
+ * 3. **`roy-agent` on PATH** (the new priority-3 step). This is the
135
+ * preferred choice in production because it always tracks the
136
+ * latest globally-installed version, regardless of where the
137
+ * plugin itself lives on disk.
138
+ * 4. Sibling-repo `.js` script:
139
+ * `<cwd>/../roy-agent/packages/cli/dist/bin/roy-agent.js`
140
+ * (typical when both repos live side-by-side — dev layout)
141
+ * 5. Monorepo `.js` script:
142
+ * `<plugin>/../../../../packages/cli/dist/bin/roy-agent.js`
143
+ * (dev layout when the plugin is checked out from a monorepo)
144
+ * 6. Final fallback: the literal string `"roy-agent"`. The OS spawn
145
+ * will resolve it via PATH one more time as a last resort.
146
+ *
147
+ * **Why the priority changed in v0.8.5:**
148
+ * In v0.8.4 the function preferred the sibling-repo / monorepo `.js`
149
+ * scripts over the system PATH. That worked fine during local dev
150
+ * but, after publishing the plugin globally via npm, it meant:
151
+ * - The plugin would keep using the stale `.js` left in a sibling
152
+ * checkout (because `fs.existsSync` returned true even though
153
+ * `npm install -g @ai-setting/roy-agent-cli` had updated the
154
+ * canonical copy).
155
+ * - Users couldn't pick up CLI bug fixes without also reinstalling
156
+ * the plugin or cleaning up the stale `.js`.
157
+ * v0.8.5 flips the order: PATH first, `.js` files only as dev-time
158
+ * fallbacks. The result is that the globally-installed CLI is
159
+ * always the source of truth.
160
+ */
161
+ export declare function resolveRoyAgentCliPath(override: string | undefined): string;
107
162
  /**
108
163
  * Public, friendly type used in README + tests.
109
164
  *
@@ -145,6 +200,8 @@ export declare class TaskShowPlugin implements TaskShowPluginInterface {
145
200
  private readonly eventBus;
146
201
  /** v0.7.0+: cache for parsed task operations envelopes. */
147
202
  private readonly operationsCache;
203
+ /** v0.8.0+: cache for parsed task tree envelopes (used by /api/tasks/tree). */
204
+ private readonly tasksTreeCache;
148
205
  private env;
149
206
  private disposed;
150
207
  /**
@@ -170,12 +227,11 @@ export declare class TaskShowPlugin implements TaskShowPluginInterface {
170
227
  constructor(config?: Partial<TaskShowConfig>);
171
228
  /**
172
229
  * Resolve the `roy-agent` executable to use for the operations pipeline.
173
- * Resolution order:
174
- * 1. Explicit override (`cfg.royAgentCliPath`) if it exists
175
- * 2. `$ROY_AGENT_CLI` env var if it exists
176
- * 3. `<cwd>/../roy-agent/packages/cli/dist/bin/roy-agent.js`
177
- * (the typical layout when both repos live side-by-side)
178
- * 4. The global `roy-agent` on PATH (best-effort)
230
+ *
231
+ * v0.8.5+: delegates to the free function `resolveRoyAgentCliPath()`
232
+ * (also exported from this module) so the priority order is
233
+ * unit-testable in isolation. Kept as a method on the class so existing
234
+ * internal callers keep their semantics unchanged.
179
235
  */
180
236
  private resolveRoyAgentCliPath;
181
237
  /**