agentplane 0.1.5 → 0.1.7
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/assets/AGENTS.md +1 -1
- package/assets/agents/ORCHESTRATOR.json +1 -1
- package/assets/agents/UPGRADER.json +1 -1
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +22 -7
- package/dist/commands/branch/index.d.ts +60 -0
- package/dist/commands/branch/index.d.ts.map +1 -0
- package/dist/commands/branch/index.js +511 -0
- package/dist/commands/guard/index.d.ts +67 -0
- package/dist/commands/guard/index.d.ts.map +1 -0
- package/dist/commands/guard/index.js +367 -0
- package/dist/commands/hooks/index.d.ts +18 -0
- package/dist/commands/hooks/index.d.ts.map +1 -0
- package/dist/commands/hooks/index.js +290 -0
- package/dist/commands/pr/index.d.ts +46 -0
- package/dist/commands/pr/index.d.ts.map +1 -0
- package/dist/commands/pr/index.js +854 -0
- package/dist/commands/shared/git-diff.d.ts +9 -0
- package/dist/commands/shared/git-diff.d.ts.map +1 -0
- package/dist/commands/shared/git-diff.js +41 -0
- package/dist/commands/shared/git-ops.d.ts +24 -0
- package/dist/commands/shared/git-ops.d.ts.map +1 -0
- package/dist/commands/shared/git-ops.js +181 -0
- package/dist/commands/shared/git-worktree.d.ts +8 -0
- package/dist/commands/shared/git-worktree.d.ts.map +1 -0
- package/dist/commands/shared/git-worktree.js +48 -0
- package/dist/commands/shared/git.d.ts +4 -0
- package/dist/commands/shared/git.d.ts.map +1 -0
- package/dist/commands/shared/git.js +14 -0
- package/dist/commands/shared/path.d.ts +3 -0
- package/dist/commands/shared/path.d.ts.map +1 -0
- package/dist/commands/shared/path.js +14 -0
- package/dist/commands/shared/pr-meta.d.ts +21 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -0
- package/dist/commands/shared/pr-meta.js +72 -0
- package/dist/commands/shared/task-backend.d.ts +15 -0
- package/dist/commands/shared/task-backend.d.ts.map +1 -0
- package/dist/commands/shared/task-backend.js +55 -0
- package/dist/commands/task/add.d.ts +8 -0
- package/dist/commands/task/add.d.ts.map +1 -0
- package/dist/commands/task/add.js +164 -0
- package/dist/commands/task/block.d.ts +19 -0
- package/dist/commands/task/block.d.ts.map +1 -0
- package/dist/commands/task/block.js +86 -0
- package/dist/commands/task/comment.d.ts +8 -0
- package/dist/commands/task/comment.d.ts.map +1 -0
- package/dist/commands/task/comment.js +29 -0
- package/dist/commands/task/doc.d.ts +17 -0
- package/dist/commands/task/doc.d.ts.map +1 -0
- package/dist/commands/task/doc.js +220 -0
- package/dist/commands/task/export.d.ts +5 -0
- package/dist/commands/task/export.d.ts.map +1 -0
- package/dist/commands/task/export.js +27 -0
- package/dist/commands/task/finish.d.ts +27 -0
- package/dist/commands/task/finish.d.ts.map +1 -0
- package/dist/commands/task/finish.js +131 -0
- package/dist/commands/task/index.d.ts +23 -0
- package/dist/commands/task/index.d.ts.map +1 -0
- package/dist/commands/task/index.js +22 -0
- package/dist/commands/task/lint.d.ts +5 -0
- package/dist/commands/task/lint.d.ts.map +1 -0
- package/dist/commands/task/lint.js +22 -0
- package/dist/commands/task/list.d.ts +11 -0
- package/dist/commands/task/list.d.ts.map +1 -0
- package/dist/commands/task/list.js +54 -0
- package/dist/commands/task/migrate.d.ts +6 -0
- package/dist/commands/task/migrate.d.ts.map +1 -0
- package/dist/commands/task/migrate.js +70 -0
- package/dist/commands/task/new.d.ts +8 -0
- package/dist/commands/task/new.d.ts.map +1 -0
- package/dist/commands/task/new.js +117 -0
- package/dist/commands/task/next.d.ts +6 -0
- package/dist/commands/task/next.d.ts.map +1 -0
- package/dist/commands/task/next.js +45 -0
- package/dist/commands/task/normalize.d.ts +6 -0
- package/dist/commands/task/normalize.d.ts.map +1 -0
- package/dist/commands/task/normalize.js +46 -0
- package/dist/commands/task/ready.d.ts +6 -0
- package/dist/commands/task/ready.d.ts.map +1 -0
- package/dist/commands/task/ready.js +57 -0
- package/dist/commands/task/scaffold.d.ts +8 -0
- package/dist/commands/task/scaffold.d.ts.map +1 -0
- package/dist/commands/task/scaffold.js +131 -0
- package/dist/commands/task/scrub.d.ts +8 -0
- package/dist/commands/task/scrub.d.ts.map +1 -0
- package/dist/commands/task/scrub.js +121 -0
- package/dist/commands/task/search.d.ts +7 -0
- package/dist/commands/task/search.d.ts.map +1 -0
- package/dist/commands/task/search.js +79 -0
- package/dist/commands/task/set-status.d.ts +19 -0
- package/dist/commands/task/set-status.d.ts.map +1 -0
- package/dist/commands/task/set-status.js +123 -0
- package/dist/commands/task/shared.d.ts +46 -0
- package/dist/commands/task/shared.d.ts.map +1 -0
- package/dist/commands/task/shared.js +283 -0
- package/dist/commands/task/show.d.ts +6 -0
- package/dist/commands/task/show.d.ts.map +1 -0
- package/dist/commands/task/show.js +35 -0
- package/dist/commands/task/start.d.ts +19 -0
- package/dist/commands/task/start.d.ts.map +1 -0
- package/dist/commands/task/start.js +109 -0
- package/dist/commands/task/update.d.ts +8 -0
- package/dist/commands/task/update.d.ts.map +1 -0
- package/dist/commands/task/update.js +144 -0
- package/dist/commands/task/verify.d.ts +14 -0
- package/dist/commands/task/verify.d.ts.map +1 -0
- package/dist/commands/task/verify.js +362 -0
- package/dist/commands/workflow.d.ts +5 -364
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -4617
- package/package.json +2 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
2
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
3
|
+
import { successMessage, unknownEntityMessage, warnMessage } from "../../cli/output.js";
|
|
4
|
+
import { buildDependencyState } from "./shared.js";
|
|
5
|
+
export async function cmdReady(opts) {
|
|
6
|
+
try {
|
|
7
|
+
const { backend } = await loadTaskBackend({
|
|
8
|
+
cwd: opts.cwd,
|
|
9
|
+
rootOverride: opts.rootOverride ?? null,
|
|
10
|
+
});
|
|
11
|
+
const tasks = await backend.listTasks();
|
|
12
|
+
const depState = buildDependencyState(tasks);
|
|
13
|
+
const task = tasks.find((item) => item.id === opts.taskId);
|
|
14
|
+
const warnings = [];
|
|
15
|
+
if (task) {
|
|
16
|
+
const dep = depState.get(task.id);
|
|
17
|
+
const missing = dep?.missing ?? [];
|
|
18
|
+
const incomplete = dep?.incomplete ?? [];
|
|
19
|
+
if (missing.length > 0) {
|
|
20
|
+
warnings.push(`${task.id}: missing deps: ${missing.join(", ")}`);
|
|
21
|
+
}
|
|
22
|
+
if (incomplete.length > 0) {
|
|
23
|
+
warnings.push(`${task.id}: incomplete deps: ${incomplete.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
warnings.push(unknownEntityMessage("task id", opts.taskId));
|
|
28
|
+
}
|
|
29
|
+
for (const warning of warnings) {
|
|
30
|
+
process.stdout.write(`${warnMessage(warning)}\n`);
|
|
31
|
+
}
|
|
32
|
+
if (task) {
|
|
33
|
+
const status = String(task.status || "TODO").toUpperCase();
|
|
34
|
+
const title = task.title?.trim() || "(untitled task)";
|
|
35
|
+
const owner = task.owner?.trim() || "-";
|
|
36
|
+
const dep = depState.get(task.id);
|
|
37
|
+
const dependsOn = dep?.dependsOn ?? [];
|
|
38
|
+
process.stdout.write(`Task: ${task.id} [${status}] ${title}\n`);
|
|
39
|
+
process.stdout.write(`Owner: ${owner}\n`);
|
|
40
|
+
process.stdout.write(`Depends on: ${dependsOn.length > 0 ? dependsOn.join(", ") : "-"}\n`);
|
|
41
|
+
const missing = dep?.missing ?? [];
|
|
42
|
+
const incomplete = dep?.incomplete ?? [];
|
|
43
|
+
if (missing.length > 0) {
|
|
44
|
+
process.stdout.write(`${warnMessage(`missing deps: ${missing.join(", ")}`)}\n`);
|
|
45
|
+
}
|
|
46
|
+
if (incomplete.length > 0) {
|
|
47
|
+
process.stdout.write(`${warnMessage(`incomplete deps: ${incomplete.join(", ")}`)}\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const ready = warnings.length === 0;
|
|
51
|
+
process.stdout.write(`${ready ? successMessage("ready") : warnMessage("not ready")}` + "\n");
|
|
52
|
+
return ready ? 0 : 2;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
throw mapBackendError(err, { command: "ready", root: opts.rootOverride ?? null });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const TASK_SCAFFOLD_USAGE = "Usage: agentplane task scaffold <task-id> [--title <text>] [--overwrite] [--force]";
|
|
2
|
+
export declare const TASK_SCAFFOLD_USAGE_EXAMPLE = "agentplane task scaffold 202602030608-F1Q8AB";
|
|
3
|
+
export declare function cmdTaskScaffold(opts: {
|
|
4
|
+
cwd: string;
|
|
5
|
+
rootOverride?: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
}): Promise<number>;
|
|
8
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/commands/task/scaffold.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,mBAAmB,uFACsD,CAAC;AACvF,eAAO,MAAM,2BAA2B,iDAAiD,CAAC;AAyD1F,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ElB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureDocSections, renderTaskReadme, taskReadmePath } from "@agentplaneorg/core";
|
|
4
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
5
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
6
|
+
import { missingValueMessage, successMessage, unknownEntityMessage, usageMessage, } from "../../cli/output.js";
|
|
7
|
+
import { CliError } from "../../shared/errors.js";
|
|
8
|
+
import { taskDataToFrontmatter } from "../shared/task-backend.js";
|
|
9
|
+
import { nowIso } from "./shared.js";
|
|
10
|
+
export const TASK_SCAFFOLD_USAGE = "Usage: agentplane task scaffold <task-id> [--title <text>] [--overwrite] [--force]";
|
|
11
|
+
export const TASK_SCAFFOLD_USAGE_EXAMPLE = "agentplane task scaffold 202602030608-F1Q8AB";
|
|
12
|
+
function parseTaskScaffoldFlags(args) {
|
|
13
|
+
const [taskId, ...rest] = args;
|
|
14
|
+
if (!taskId) {
|
|
15
|
+
throw new CliError({
|
|
16
|
+
exitCode: 2,
|
|
17
|
+
code: "E_USAGE",
|
|
18
|
+
message: usageMessage(TASK_SCAFFOLD_USAGE, TASK_SCAFFOLD_USAGE_EXAMPLE),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const out = { taskId, overwrite: false, force: false, quiet: false };
|
|
22
|
+
for (let i = 0; i < rest.length; i++) {
|
|
23
|
+
const arg = rest[i];
|
|
24
|
+
if (!arg)
|
|
25
|
+
continue;
|
|
26
|
+
if (arg === "--overwrite") {
|
|
27
|
+
out.overwrite = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg === "--force") {
|
|
31
|
+
out.force = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (arg === "--quiet") {
|
|
35
|
+
out.quiet = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (arg === "--title") {
|
|
39
|
+
const next = rest[i + 1];
|
|
40
|
+
if (!next) {
|
|
41
|
+
throw new CliError({
|
|
42
|
+
exitCode: 2,
|
|
43
|
+
code: "E_USAGE",
|
|
44
|
+
message: missingValueMessage("--title"),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
out.title = next;
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
throw new CliError({
|
|
52
|
+
exitCode: 2,
|
|
53
|
+
code: "E_USAGE",
|
|
54
|
+
message: `Unknown flag: ${arg}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
export async function cmdTaskScaffold(opts) {
|
|
60
|
+
const flags = parseTaskScaffoldFlags(opts.args);
|
|
61
|
+
try {
|
|
62
|
+
const { backend, resolved, config } = await loadTaskBackend({
|
|
63
|
+
cwd: opts.cwd,
|
|
64
|
+
rootOverride: opts.rootOverride ?? null,
|
|
65
|
+
});
|
|
66
|
+
const task = await backend.getTask(flags.taskId);
|
|
67
|
+
if (!task && !flags.force) {
|
|
68
|
+
throw new CliError({
|
|
69
|
+
exitCode: 2,
|
|
70
|
+
code: "E_USAGE",
|
|
71
|
+
message: unknownEntityMessage("task id", flags.taskId),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const readmePath = taskReadmePath(path.join(resolved.gitRoot, config.paths.workflow_dir), flags.taskId);
|
|
75
|
+
try {
|
|
76
|
+
await readFile(readmePath, "utf8");
|
|
77
|
+
if (!flags.overwrite) {
|
|
78
|
+
throw new CliError({
|
|
79
|
+
exitCode: 2,
|
|
80
|
+
code: "E_USAGE",
|
|
81
|
+
message: `File already exists: ${readmePath}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const code = err?.code;
|
|
87
|
+
if (code !== "ENOENT") {
|
|
88
|
+
if (err instanceof CliError)
|
|
89
|
+
throw err;
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const baseTask = task ??
|
|
94
|
+
{
|
|
95
|
+
id: flags.taskId,
|
|
96
|
+
title: flags.title ?? "",
|
|
97
|
+
description: "",
|
|
98
|
+
status: "TODO",
|
|
99
|
+
priority: "med",
|
|
100
|
+
owner: "",
|
|
101
|
+
depends_on: [],
|
|
102
|
+
tags: [],
|
|
103
|
+
verify: [],
|
|
104
|
+
comments: [],
|
|
105
|
+
doc_version: 2,
|
|
106
|
+
doc_updated_at: nowIso(),
|
|
107
|
+
doc_updated_by: "UNKNOWN",
|
|
108
|
+
};
|
|
109
|
+
if (flags.title)
|
|
110
|
+
baseTask.title = flags.title;
|
|
111
|
+
if (typeof baseTask.doc_updated_by !== "string" ||
|
|
112
|
+
baseTask.doc_updated_by.trim().length === 0 ||
|
|
113
|
+
baseTask.doc_updated_by.trim().toLowerCase() === "agentplane") {
|
|
114
|
+
baseTask.doc_updated_by = baseTask.owner?.trim() ? baseTask.owner : "UNKNOWN";
|
|
115
|
+
}
|
|
116
|
+
const frontmatter = taskDataToFrontmatter(baseTask);
|
|
117
|
+
const body = ensureDocSections("", config.tasks.doc.required_sections);
|
|
118
|
+
const text = renderTaskReadme(frontmatter, body);
|
|
119
|
+
await mkdir(path.dirname(readmePath), { recursive: true });
|
|
120
|
+
await writeFile(readmePath, text.endsWith("\n") ? text : `${text}\n`, "utf8");
|
|
121
|
+
if (!flags.quiet) {
|
|
122
|
+
process.stdout.write(`${successMessage("wrote", path.relative(resolved.gitRoot, readmePath))}\n`);
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
if (err instanceof CliError)
|
|
128
|
+
throw err;
|
|
129
|
+
throw mapBackendError(err, { command: "task scaffold", root: opts.rootOverride ?? null });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const TASK_SCRUB_USAGE = "Usage: agentplane task scrub --find <text> --replace <text> [flags]";
|
|
2
|
+
export declare const TASK_SCRUB_USAGE_EXAMPLE = "agentplane task scrub --find \"agentctl\" --replace \"agentplane\" --dry-run";
|
|
3
|
+
export declare function cmdTaskScrub(opts: {
|
|
4
|
+
cwd: string;
|
|
5
|
+
rootOverride?: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
}): Promise<number>;
|
|
8
|
+
//# sourceMappingURL=scrub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrub.d.ts","sourceRoot":"","sources":["../../../src/commands/task/scrub.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,gBAAgB,wEAC0C,CAAC;AACxE,eAAO,MAAM,wBAAwB,iFACuC,CAAC;AAgE7E,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwDlB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
2
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
3
|
+
import { infoMessage, missingValueMessage, successMessage, usageMessage, } from "../../cli/output.js";
|
|
4
|
+
import { CliError } from "../../shared/errors.js";
|
|
5
|
+
export const TASK_SCRUB_USAGE = "Usage: agentplane task scrub --find <text> --replace <text> [flags]";
|
|
6
|
+
export const TASK_SCRUB_USAGE_EXAMPLE = 'agentplane task scrub --find "agentctl" --replace "agentplane" --dry-run';
|
|
7
|
+
function parseTaskScrubFlags(args) {
|
|
8
|
+
const out = {
|
|
9
|
+
find: "",
|
|
10
|
+
replace: "",
|
|
11
|
+
dryRun: false,
|
|
12
|
+
quiet: false,
|
|
13
|
+
};
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
const arg = args[i];
|
|
16
|
+
if (!arg)
|
|
17
|
+
continue;
|
|
18
|
+
if (arg === "--dry-run") {
|
|
19
|
+
out.dryRun = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (arg === "--quiet") {
|
|
23
|
+
out.quiet = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (!arg.startsWith("--")) {
|
|
27
|
+
throw new CliError({
|
|
28
|
+
exitCode: 2,
|
|
29
|
+
code: "E_USAGE",
|
|
30
|
+
message: usageMessage(TASK_SCRUB_USAGE, TASK_SCRUB_USAGE_EXAMPLE),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const next = args[i + 1];
|
|
34
|
+
if (!next) {
|
|
35
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
|
|
36
|
+
}
|
|
37
|
+
if (arg === "--find") {
|
|
38
|
+
out.find = next;
|
|
39
|
+
}
|
|
40
|
+
else if (arg === "--replace") {
|
|
41
|
+
out.replace = next;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unknown flag: ${arg}` });
|
|
45
|
+
}
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
function scrubValue(value, find, replace) {
|
|
51
|
+
if (typeof value === "string")
|
|
52
|
+
return value.replaceAll(find, replace);
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.map((item) => scrubValue(item, find, replace));
|
|
55
|
+
if (value && typeof value === "object") {
|
|
56
|
+
const entries = Object.entries(value);
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const [key, val] of entries) {
|
|
59
|
+
out[key] = scrubValue(val, find, replace);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
export async function cmdTaskScrub(opts) {
|
|
66
|
+
const flags = parseTaskScrubFlags(opts.args);
|
|
67
|
+
if (!flags.find) {
|
|
68
|
+
throw new CliError({
|
|
69
|
+
exitCode: 2,
|
|
70
|
+
code: "E_USAGE",
|
|
71
|
+
message: usageMessage(TASK_SCRUB_USAGE, TASK_SCRUB_USAGE_EXAMPLE),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const { backend } = await loadTaskBackend({
|
|
76
|
+
cwd: opts.cwd,
|
|
77
|
+
rootOverride: opts.rootOverride ?? null,
|
|
78
|
+
});
|
|
79
|
+
const tasks = await backend.listTasks();
|
|
80
|
+
const updated = [];
|
|
81
|
+
const changedIds = new Set();
|
|
82
|
+
for (const task of tasks) {
|
|
83
|
+
const before = JSON.stringify(task);
|
|
84
|
+
const scrubbed = scrubValue(task, flags.find, flags.replace);
|
|
85
|
+
if (scrubbed && typeof scrubbed === "object") {
|
|
86
|
+
const next = scrubbed;
|
|
87
|
+
updated.push(next);
|
|
88
|
+
const after = JSON.stringify(next);
|
|
89
|
+
if (before !== after)
|
|
90
|
+
changedIds.add(next.id);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
updated.push(task);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (flags.dryRun) {
|
|
97
|
+
if (!flags.quiet) {
|
|
98
|
+
process.stdout.write(`${infoMessage(`dry-run: would update ${changedIds.size} task(s)`)}` + "\n");
|
|
99
|
+
for (const id of [...changedIds].toSorted()) {
|
|
100
|
+
process.stdout.write(`${id}\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
if (backend.writeTasks) {
|
|
106
|
+
await backend.writeTasks(updated);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (const task of updated) {
|
|
110
|
+
await backend.writeTask(task);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!flags.quiet) {
|
|
114
|
+
process.stdout.write(`${successMessage("updated tasks", undefined, `count=${changedIds.size}`)}` + "\n");
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
throw mapBackendError(err, { command: "task scrub", root: opts.rootOverride ?? null });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/commands/task/search.ts"],"names":[],"mappings":"AAcA,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqElB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
2
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
3
|
+
import { invalidValueMessage } from "../../cli/output.js";
|
|
4
|
+
import { CliError } from "../../shared/errors.js";
|
|
5
|
+
import { buildDependencyState, dedupeStrings, formatTaskLine, parseTaskListFilters, taskTextBlob, toStringArray, } from "./shared.js";
|
|
6
|
+
export async function cmdTaskSearch(opts) {
|
|
7
|
+
const query = opts.query.trim();
|
|
8
|
+
if (!query) {
|
|
9
|
+
throw new CliError({
|
|
10
|
+
exitCode: 2,
|
|
11
|
+
code: "E_USAGE",
|
|
12
|
+
message: "Missing query (expected non-empty text)",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
let regex = false;
|
|
16
|
+
const restArgs = [...opts.args];
|
|
17
|
+
if (restArgs.includes("--regex")) {
|
|
18
|
+
regex = true;
|
|
19
|
+
restArgs.splice(restArgs.indexOf("--regex"), 1);
|
|
20
|
+
}
|
|
21
|
+
const filters = parseTaskListFilters(restArgs, { allowLimit: true });
|
|
22
|
+
try {
|
|
23
|
+
const { backend } = await loadTaskBackend({
|
|
24
|
+
cwd: opts.cwd,
|
|
25
|
+
rootOverride: opts.rootOverride ?? null,
|
|
26
|
+
});
|
|
27
|
+
const tasks = await backend.listTasks();
|
|
28
|
+
const depState = buildDependencyState(tasks);
|
|
29
|
+
let filtered = tasks;
|
|
30
|
+
if (filters.status.length > 0) {
|
|
31
|
+
const wanted = new Set(filters.status.map((s) => s.trim().toUpperCase()));
|
|
32
|
+
filtered = filtered.filter((task) => wanted.has(String(task.status || "TODO").toUpperCase()));
|
|
33
|
+
}
|
|
34
|
+
if (filters.owner.length > 0) {
|
|
35
|
+
const wanted = new Set(filters.owner.map((o) => o.trim().toUpperCase()));
|
|
36
|
+
filtered = filtered.filter((task) => wanted.has(String(task.owner || "").toUpperCase()));
|
|
37
|
+
}
|
|
38
|
+
if (filters.tag.length > 0) {
|
|
39
|
+
const wanted = new Set(filters.tag.map((t) => t.trim()).filter(Boolean));
|
|
40
|
+
filtered = filtered.filter((task) => {
|
|
41
|
+
const tags = dedupeStrings(toStringArray(task.tags));
|
|
42
|
+
return tags.some((tag) => wanted.has(tag));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
let matches = [];
|
|
46
|
+
if (regex) {
|
|
47
|
+
let pattern;
|
|
48
|
+
try {
|
|
49
|
+
pattern = new RegExp(query, "i");
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : "Invalid regex";
|
|
53
|
+
throw new CliError({
|
|
54
|
+
exitCode: 2,
|
|
55
|
+
code: "E_USAGE",
|
|
56
|
+
message: invalidValueMessage("regex", message, "valid pattern"),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
matches = filtered.filter((task) => pattern.test(taskTextBlob(task)));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const needle = query.toLowerCase();
|
|
63
|
+
matches = filtered.filter((task) => taskTextBlob(task).toLowerCase().includes(needle));
|
|
64
|
+
}
|
|
65
|
+
if (filters.limit !== undefined && filters.limit >= 0) {
|
|
66
|
+
matches = matches.slice(0, filters.limit);
|
|
67
|
+
}
|
|
68
|
+
const sorted = matches.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
69
|
+
for (const task of sorted) {
|
|
70
|
+
process.stdout.write(`${formatTaskLine(task, depState.get(task.id))}\n`);
|
|
71
|
+
}
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
if (err instanceof CliError)
|
|
76
|
+
throw err;
|
|
77
|
+
throw mapBackendError(err, { command: "task search", root: opts.rootOverride ?? null });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function cmdTaskSetStatus(opts: {
|
|
2
|
+
cwd: string;
|
|
3
|
+
rootOverride?: string;
|
|
4
|
+
taskId: string;
|
|
5
|
+
status: string;
|
|
6
|
+
author?: string;
|
|
7
|
+
body?: string;
|
|
8
|
+
commit?: string;
|
|
9
|
+
force: boolean;
|
|
10
|
+
commitFromComment: boolean;
|
|
11
|
+
commitEmoji?: string;
|
|
12
|
+
commitAllow: string[];
|
|
13
|
+
commitAutoAllow: boolean;
|
|
14
|
+
commitAllowTasks: boolean;
|
|
15
|
+
commitRequireClean: boolean;
|
|
16
|
+
confirmStatusCommit: boolean;
|
|
17
|
+
quiet: boolean;
|
|
18
|
+
}): Promise<number>;
|
|
19
|
+
//# sourceMappingURL=set-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-status.d.ts","sourceRoot":"","sources":["../../../src/commands/task/set-status.ts"],"names":[],"mappings":"AAmBA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8HlB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
2
|
+
import { successMessage, warnMessage } from "../../cli/output.js";
|
|
3
|
+
import { formatCommentBodyForCommit } from "../../shared/comment-format.js";
|
|
4
|
+
import { CliError } from "../../shared/errors.js";
|
|
5
|
+
import { commitFromComment } from "../guard/index.js";
|
|
6
|
+
import { loadBackendTask, resolveDocUpdatedBy } from "../shared/task-backend.js";
|
|
7
|
+
import { buildDependencyState, defaultCommitEmojiForStatus, enforceStatusCommitPolicy, isTransitionAllowed, normalizeTaskStatus, nowIso, readCommitInfo, } from "./shared.js";
|
|
8
|
+
export async function cmdTaskSetStatus(opts) {
|
|
9
|
+
const nextStatus = normalizeTaskStatus(opts.status);
|
|
10
|
+
if (nextStatus === "DONE" && !opts.force) {
|
|
11
|
+
throw new CliError({
|
|
12
|
+
exitCode: 2,
|
|
13
|
+
code: "E_USAGE",
|
|
14
|
+
message: "Use `agentplane finish` to mark DONE (use --force to override)",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if ((opts.author && !opts.body) || (opts.body && !opts.author)) {
|
|
18
|
+
throw new CliError({
|
|
19
|
+
exitCode: 2,
|
|
20
|
+
code: "E_USAGE",
|
|
21
|
+
message: "--author and --body must be provided together",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const { backend, task, config, resolved } = await loadBackendTask({
|
|
26
|
+
cwd: opts.cwd,
|
|
27
|
+
rootOverride: opts.rootOverride ?? null,
|
|
28
|
+
taskId: opts.taskId,
|
|
29
|
+
});
|
|
30
|
+
if (opts.commitFromComment) {
|
|
31
|
+
enforceStatusCommitPolicy({
|
|
32
|
+
policy: config.status_commit_policy,
|
|
33
|
+
action: "task set-status",
|
|
34
|
+
confirmed: opts.confirmStatusCommit,
|
|
35
|
+
quiet: opts.quiet,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const currentStatus = String(task.status || "TODO").toUpperCase();
|
|
39
|
+
if (!opts.force && !isTransitionAllowed(currentStatus, nextStatus)) {
|
|
40
|
+
throw new CliError({
|
|
41
|
+
exitCode: 2,
|
|
42
|
+
code: "E_USAGE",
|
|
43
|
+
message: `Refusing status transition ${currentStatus} -> ${nextStatus} (use --force to override)`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (!opts.force && (nextStatus === "DOING" || nextStatus === "DONE")) {
|
|
47
|
+
const allTasks = await backend.listTasks();
|
|
48
|
+
const depState = buildDependencyState(allTasks);
|
|
49
|
+
const dep = depState.get(task.id);
|
|
50
|
+
if (dep && (dep.missing.length > 0 || dep.incomplete.length > 0)) {
|
|
51
|
+
if (!opts.quiet) {
|
|
52
|
+
if (dep.missing.length > 0) {
|
|
53
|
+
process.stderr.write(`${warnMessage(`missing deps: ${dep.missing.join(", ")}`)}\n`);
|
|
54
|
+
}
|
|
55
|
+
if (dep.incomplete.length > 0) {
|
|
56
|
+
process.stderr.write(`${warnMessage(`incomplete deps: ${dep.incomplete.join(", ")}`)}\n`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw new CliError({
|
|
60
|
+
exitCode: 2,
|
|
61
|
+
code: "E_USAGE",
|
|
62
|
+
message: `Task is not ready: ${task.id} (use --force to override)`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const existingComments = Array.isArray(task.comments)
|
|
67
|
+
? task.comments.filter((item) => !!item && typeof item.author === "string" && typeof item.body === "string")
|
|
68
|
+
: [];
|
|
69
|
+
let comments = existingComments;
|
|
70
|
+
if (opts.author && opts.body) {
|
|
71
|
+
const commentBody = opts.commitFromComment
|
|
72
|
+
? formatCommentBodyForCommit(opts.body, config)
|
|
73
|
+
: opts.body;
|
|
74
|
+
comments = [...existingComments, { author: opts.author, body: commentBody }];
|
|
75
|
+
}
|
|
76
|
+
const next = {
|
|
77
|
+
...task,
|
|
78
|
+
status: nextStatus,
|
|
79
|
+
comments,
|
|
80
|
+
doc_version: 2,
|
|
81
|
+
doc_updated_at: nowIso(),
|
|
82
|
+
doc_updated_by: resolveDocUpdatedBy(task, opts.author),
|
|
83
|
+
};
|
|
84
|
+
if (opts.commit) {
|
|
85
|
+
const commitInfo = await readCommitInfo(resolved.gitRoot, opts.commit);
|
|
86
|
+
next.commit = { hash: commitInfo.hash, message: commitInfo.message };
|
|
87
|
+
}
|
|
88
|
+
await backend.writeTask(next);
|
|
89
|
+
// tasks.json is export-only; generated via `agentplane task export`.
|
|
90
|
+
if (opts.commitFromComment) {
|
|
91
|
+
if (!opts.body) {
|
|
92
|
+
throw new CliError({
|
|
93
|
+
exitCode: 2,
|
|
94
|
+
code: "E_USAGE",
|
|
95
|
+
message: "--body is required when using --commit-from-comment",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
await commitFromComment({
|
|
99
|
+
cwd: opts.cwd,
|
|
100
|
+
rootOverride: opts.rootOverride,
|
|
101
|
+
taskId: opts.taskId,
|
|
102
|
+
commentBody: opts.body,
|
|
103
|
+
formattedComment: formatCommentBodyForCommit(opts.body, config),
|
|
104
|
+
emoji: opts.commitEmoji ?? defaultCommitEmojiForStatus(nextStatus),
|
|
105
|
+
allow: opts.commitAllow,
|
|
106
|
+
autoAllow: opts.commitAutoAllow || opts.commitAllow.length === 0,
|
|
107
|
+
allowTasks: opts.commitAllowTasks,
|
|
108
|
+
requireClean: opts.commitRequireClean,
|
|
109
|
+
quiet: opts.quiet,
|
|
110
|
+
config,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (!opts.quiet) {
|
|
114
|
+
process.stdout.write(`${successMessage("status", opts.taskId, `next=${nextStatus}`)}\n`);
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (err instanceof CliError)
|
|
120
|
+
throw err;
|
|
121
|
+
throw mapBackendError(err, { command: "task set-status", root: opts.rootOverride ?? null });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { type AgentplaneConfig } from "@agentplaneorg/core";
|
|
3
|
+
import { type TaskData } from "../../backends/task-backend.js";
|
|
4
|
+
export declare const execFileAsync: typeof execFile.__promisify__;
|
|
5
|
+
export declare function nowIso(): string;
|
|
6
|
+
export declare function normalizeDependsOnInput(value: string): string[];
|
|
7
|
+
export declare function normalizeTaskStatus(value: string): string;
|
|
8
|
+
export declare function dedupeStrings(items: string[]): string[];
|
|
9
|
+
export declare function toStringArray(value: unknown): string[];
|
|
10
|
+
export declare function requiresVerify(tags: string[], requiredTags: string[]): boolean;
|
|
11
|
+
export type DependencyState = {
|
|
12
|
+
dependsOn: string[];
|
|
13
|
+
missing: string[];
|
|
14
|
+
incomplete: string[];
|
|
15
|
+
};
|
|
16
|
+
export declare function buildDependencyState(tasks: TaskData[]): Map<string, DependencyState>;
|
|
17
|
+
export declare function formatTaskLine(task: TaskData, depState?: DependencyState): string;
|
|
18
|
+
export declare function isTransitionAllowed(current: string, next: string): boolean;
|
|
19
|
+
export declare function requireStructuredComment(body: string, prefix: string, minChars: number): void;
|
|
20
|
+
export declare function readHeadCommit(cwd: string): Promise<{
|
|
21
|
+
hash: string;
|
|
22
|
+
message: string;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function enforceStatusCommitPolicy(opts: {
|
|
25
|
+
policy: AgentplaneConfig["status_commit_policy"];
|
|
26
|
+
action: string;
|
|
27
|
+
confirmed: boolean;
|
|
28
|
+
quiet: boolean;
|
|
29
|
+
}): void;
|
|
30
|
+
export declare function readCommitInfo(cwd: string, rev: string): Promise<{
|
|
31
|
+
hash: string;
|
|
32
|
+
message: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function defaultCommitEmojiForStatus(status: string): string;
|
|
35
|
+
export type TaskListFilters = {
|
|
36
|
+
status: string[];
|
|
37
|
+
owner: string[];
|
|
38
|
+
tag: string[];
|
|
39
|
+
limit?: number;
|
|
40
|
+
quiet: boolean;
|
|
41
|
+
};
|
|
42
|
+
export declare function parseTaskListFilters(args: string[], opts?: {
|
|
43
|
+
allowLimit?: boolean;
|
|
44
|
+
}): TaskListFilters;
|
|
45
|
+
export declare function taskTextBlob(task: TaskData): string;
|
|
46
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/commands/task/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQ5D,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAG/D,eAAO,MAAM,aAAa,+BAAsB,CAAC;AAEjD,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAI/D;AAID,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAczD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAWvD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAKtD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAI9E;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAqBpF;AAgBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAgBjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO1E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAgB7F;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ5F;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,gBAAgB,CAAC,sBAAsB,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,IAAI,CAqBP;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ5C;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9B,eAAe,CAsEjB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAiBnD"}
|