agentplane 0.3.10 → 0.3.11
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/policy/governance.md +3 -4
- package/assets/policy/incidents.md +19 -88
- package/assets/policy/workflow.branch_pr.md +1 -1
- package/assets/policy/workflow.direct.md +2 -2
- package/bin/agentplane.js +56 -1
- package/bin/runtime-watch.js +1 -0
- package/bin/stale-dist-policy.d.ts +1 -1
- package/bin/stale-dist-policy.js +13 -0
- package/dist/.build-manifest.json +219 -154
- package/dist/cli/bootstrap-guide.d.ts.map +1 -1
- package/dist/cli/bootstrap-guide.js +3 -2
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +2 -1
- package/dist/cli/command-invocations.d.ts.map +1 -1
- package/dist/cli/command-invocations.js +4 -1
- package/dist/cli/run-cli/command-catalog/project.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/project.js +3 -1
- package/dist/cli/run-cli/command-catalog/task.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog/task.js +10 -0
- package/dist/cli/run-cli/command-catalog.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core/preflight.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core/preflight.js +44 -1
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +12 -0
- package/dist/commands/branch/cleanup-merged.d.ts +2 -0
- package/dist/commands/branch/cleanup-merged.d.ts.map +1 -1
- package/dist/commands/branch/cleanup-merged.js +132 -28
- package/dist/commands/branch/work-start.d.ts.map +1 -1
- package/dist/commands/branch/work-start.js +60 -1
- package/dist/commands/cleanup/merged.command.d.ts +2 -0
- package/dist/commands/cleanup/merged.command.d.ts.map +1 -1
- package/dist/commands/cleanup/merged.command.js +24 -0
- package/dist/commands/doctor/branch-pr.d.ts +4 -0
- package/dist/commands/doctor/branch-pr.d.ts.map +1 -0
- package/dist/commands/doctor/branch-pr.js +96 -0
- package/dist/commands/doctor/fixes.d.ts +5 -0
- package/dist/commands/doctor/fixes.d.ts.map +1 -1
- package/dist/commands/doctor/fixes.js +70 -0
- package/dist/commands/doctor.run.d.ts.map +1 -1
- package/dist/commands/doctor.run.js +6 -1
- package/dist/commands/finish.run.d.ts.map +1 -1
- package/dist/commands/finish.run.js +11 -0
- package/dist/commands/finish.spec.d.ts +11 -0
- package/dist/commands/finish.spec.d.ts.map +1 -1
- package/dist/commands/finish.spec.js +51 -0
- package/dist/commands/guard/impl/close-message.d.ts.map +1 -1
- package/dist/commands/guard/impl/close-message.js +23 -6
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +24 -2
- package/dist/commands/guard/impl/env.d.ts +1 -0
- package/dist/commands/guard/impl/env.d.ts.map +1 -1
- package/dist/commands/guard/impl/env.js +1 -0
- package/dist/commands/hooks/index.d.ts.map +1 -1
- package/dist/commands/hooks/index.js +98 -1
- package/dist/commands/incidents/collect.command.d.ts.map +1 -1
- package/dist/commands/incidents/collect.command.js +12 -7
- package/dist/commands/incidents/incidents.command.js +1 -1
- package/dist/commands/incidents/shared.d.ts +34 -0
- package/dist/commands/incidents/shared.d.ts.map +1 -1
- package/dist/commands/incidents/shared.js +166 -12
- package/dist/commands/pr/check.d.ts.map +1 -1
- package/dist/commands/pr/check.js +238 -135
- package/dist/commands/pr/close-superseded.d.ts +9 -0
- package/dist/commands/pr/close-superseded.d.ts.map +1 -0
- package/dist/commands/pr/close-superseded.js +129 -0
- package/dist/commands/pr/close.d.ts +11 -0
- package/dist/commands/pr/close.d.ts.map +1 -0
- package/dist/commands/pr/close.js +116 -0
- package/dist/commands/pr/index.d.ts +2 -0
- package/dist/commands/pr/index.d.ts.map +1 -1
- package/dist/commands/pr/index.js +2 -0
- package/dist/commands/pr/integrate/artifacts.d.ts +7 -0
- package/dist/commands/pr/integrate/artifacts.d.ts.map +1 -1
- package/dist/commands/pr/integrate/artifacts.js +66 -1
- package/dist/commands/pr/integrate/cmd.d.ts.map +1 -1
- package/dist/commands/pr/integrate/cmd.js +16 -0
- package/dist/commands/pr/integrate/internal/bootstrap-guidance.d.ts +8 -0
- package/dist/commands/pr/integrate/internal/bootstrap-guidance.d.ts.map +1 -0
- package/dist/commands/pr/integrate/internal/bootstrap-guidance.js +59 -0
- package/dist/commands/pr/integrate/internal/finalize.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/finalize.js +40 -12
- package/dist/commands/pr/integrate/internal/merge.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/merge.js +36 -13
- package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.d.ts +13 -0
- package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.d.ts.map +1 -0
- package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.js +25 -0
- package/dist/commands/pr/integrate/internal/prepare.d.ts +3 -2
- package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/prepare.js +101 -38
- package/dist/commands/pr/internal/freshness.d.ts +20 -0
- package/dist/commands/pr/internal/freshness.d.ts.map +1 -0
- package/dist/commands/pr/internal/freshness.js +50 -0
- package/dist/commands/pr/internal/gh-api.d.ts +6 -0
- package/dist/commands/pr/internal/gh-api.d.ts.map +1 -0
- package/dist/commands/pr/internal/gh-api.js +80 -0
- package/dist/commands/pr/internal/pr-paths.d.ts +10 -0
- package/dist/commands/pr/internal/pr-paths.d.ts.map +1 -1
- package/dist/commands/pr/internal/pr-paths.js +10 -0
- package/dist/commands/pr/internal/review-template.d.ts.map +1 -1
- package/dist/commands/pr/internal/review-template.js +37 -4
- package/dist/commands/pr/internal/sync.d.ts +9 -0
- package/dist/commands/pr/internal/sync.d.ts.map +1 -1
- package/dist/commands/pr/internal/sync.js +462 -122
- package/dist/commands/pr/open.d.ts +1 -0
- package/dist/commands/pr/open.d.ts.map +1 -1
- package/dist/commands/pr/open.js +13 -2
- package/dist/commands/pr/pr.command.d.ts +15 -0
- package/dist/commands/pr/pr.command.d.ts.map +1 -1
- package/dist/commands/pr/pr.command.js +118 -2
- package/dist/commands/pr/update.d.ts.map +1 -1
- package/dist/commands/pr/update.js +59 -1
- package/dist/commands/release/apply.command.d.ts.map +1 -1
- package/dist/commands/release/apply.command.js +3 -17
- package/dist/commands/release/apply.preflight.d.ts.map +1 -1
- package/dist/commands/release/apply.preflight.js +1 -1
- package/dist/commands/shared/gh-transport.d.ts +16 -0
- package/dist/commands/shared/gh-transport.d.ts.map +1 -0
- package/dist/commands/shared/gh-transport.js +71 -0
- package/dist/commands/shared/git-diff.d.ts +3 -1
- package/dist/commands/shared/git-diff.d.ts.map +1 -1
- package/dist/commands/shared/git-diff.js +10 -2
- package/dist/commands/shared/git-ops.d.ts +1 -0
- package/dist/commands/shared/git-ops.d.ts.map +1 -1
- package/dist/commands/shared/git-ops.js +15 -0
- package/dist/commands/shared/git-worktree.d.ts +2 -0
- package/dist/commands/shared/git-worktree.d.ts.map +1 -1
- package/dist/commands/shared/git-worktree.js +22 -2
- package/dist/commands/shared/post-commit-pr-artifacts.d.ts +9 -0
- package/dist/commands/shared/post-commit-pr-artifacts.d.ts.map +1 -0
- package/dist/commands/shared/post-commit-pr-artifacts.js +22 -0
- package/dist/commands/shared/pr-meta.d.ts +20 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -1
- package/dist/commands/shared/pr-meta.js +125 -0
- package/dist/commands/shared/task-backend.d.ts +7 -0
- package/dist/commands/shared/task-backend.d.ts.map +1 -1
- package/dist/commands/shared/task-backend.js +34 -22
- package/dist/commands/task/close-duplicate.d.ts.map +1 -1
- package/dist/commands/task/close-duplicate.js +34 -1
- package/dist/commands/task/derive.js +1 -1
- package/dist/commands/task/doc-template.d.ts.map +1 -1
- package/dist/commands/task/doc-template.js +7 -11
- package/dist/commands/task/findings-add.command.d.ts +20 -0
- package/dist/commands/task/findings-add.command.d.ts.map +1 -0
- package/dist/commands/task/findings-add.command.js +165 -0
- package/dist/commands/task/findings.command.d.ts +7 -0
- package/dist/commands/task/findings.command.d.ts.map +1 -0
- package/dist/commands/task/findings.command.js +20 -0
- package/dist/commands/task/findings.d.ts +63 -0
- package/dist/commands/task/findings.d.ts.map +1 -0
- package/dist/commands/task/findings.js +188 -0
- package/dist/commands/task/finish-shared.d.ts +1 -0
- package/dist/commands/task/finish-shared.d.ts.map +1 -1
- package/dist/commands/task/finish-shared.js +55 -1
- package/dist/commands/task/finish.d.ts +10 -0
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +125 -6
- package/dist/commands/task/hosted-close-pr.command.d.ts +11 -0
- package/dist/commands/task/hosted-close-pr.command.d.ts.map +1 -0
- package/dist/commands/task/hosted-close-pr.command.js +414 -0
- package/dist/commands/task/hosted-close.command.d.ts.map +1 -1
- package/dist/commands/task/hosted-close.command.js +49 -1
- package/dist/commands/task/hosted-merge-sync.d.ts +38 -0
- package/dist/commands/task/hosted-merge-sync.d.ts.map +1 -1
- package/dist/commands/task/hosted-merge-sync.js +249 -17
- package/dist/commands/task/index.d.ts +1 -0
- package/dist/commands/task/index.d.ts.map +1 -1
- package/dist/commands/task/index.js +1 -0
- package/dist/commands/task/new.d.ts +1 -0
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +71 -1
- package/dist/commands/task/new.spec.d.ts.map +1 -1
- package/dist/commands/task/new.spec.js +7 -0
- package/dist/commands/task/normalize.command.d.ts +2 -0
- package/dist/commands/task/normalize.command.d.ts.map +1 -1
- package/dist/commands/task/normalize.command.js +45 -0
- package/dist/commands/task/normalize.d.ts +2 -0
- package/dist/commands/task/normalize.d.ts.map +1 -1
- package/dist/commands/task/normalize.js +85 -8
- package/dist/commands/task/plan.d.ts.map +1 -1
- package/dist/commands/task/plan.js +7 -10
- package/dist/commands/task/shared/docs.d.ts +6 -0
- package/dist/commands/task/shared/docs.d.ts.map +1 -1
- package/dist/commands/task/shared/docs.js +14 -0
- package/dist/commands/task/shared/transitions.d.ts.map +1 -1
- package/dist/commands/task/shared/transitions.js +11 -1
- package/dist/commands/task/shared.d.ts +1 -1
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +1 -1
- package/dist/commands/task/start-ready.d.ts.map +1 -1
- package/dist/commands/task/start-ready.js +86 -0
- package/dist/commands/task/start.d.ts.map +1 -1
- package/dist/commands/task/start.js +7 -10
- package/dist/commands/task/task.command.d.ts.map +1 -1
- package/dist/commands/task/task.command.js +4 -0
- package/dist/commands/task/verify-command-shared.d.ts +19 -0
- package/dist/commands/task/verify-command-shared.d.ts.map +1 -1
- package/dist/commands/task/verify-command-shared.js +152 -1
- package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
- package/dist/commands/task/verify-ok.command.js +15 -2
- package/dist/commands/task/verify-record.d.ts +36 -0
- package/dist/commands/task/verify-record.d.ts.map +1 -1
- package/dist/commands/task/verify-record.js +166 -11
- package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
- package/dist/commands/task/verify-rework.command.js +15 -2
- package/dist/commands/task/verify-show.command.d.ts +1 -1
- package/dist/commands/task/verify-show.command.d.ts.map +1 -1
- package/dist/commands/task/verify-show.command.js +28 -1
- package/dist/commands/verify.run.d.ts.map +1 -1
- package/dist/commands/verify.run.js +12 -0
- package/dist/commands/verify.spec.d.ts +2 -6
- package/dist/commands/verify.spec.d.ts.map +1 -1
- package/dist/commands/verify.spec.js +30 -3
- package/dist/runtime/incidents/index.d.ts +1 -1
- package/dist/runtime/incidents/index.d.ts.map +1 -1
- package/dist/runtime/incidents/resolve.d.ts.map +1 -1
- package/dist/runtime/incidents/resolve.js +319 -73
- package/dist/runtime/incidents/types.d.ts +14 -2
- package/dist/runtime/incidents/types.d.ts.map +1 -1
- package/dist/shared/env.d.ts +1 -0
- package/dist/shared/env.d.ts.map +1 -1
- package/dist/shared/env.js +22 -1
- package/dist/shared/protected-paths.d.ts +1 -1
- package/dist/shared/protected-paths.d.ts.map +1 -1
- package/dist/shared/protected-paths.js +4 -0
- package/package.json +2 -2
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { normalizeTaskDocVersion } from "@agentplaneorg/core";
|
|
3
|
+
import { normalizeTaskDocVersion, resolveBaseBranch } from "@agentplaneorg/core";
|
|
4
4
|
import { CliError } from "../../shared/errors.js";
|
|
5
5
|
import { writeJsonStableIfChanged } from "../../shared/write-if-changed.js";
|
|
6
6
|
import { execFileAsync } from "../shared/git.js";
|
|
7
|
+
import { withGhTransportRetry } from "../shared/gh-transport.js";
|
|
7
8
|
import { parseTaskIdFromBranch } from "../shared/git-worktree.js";
|
|
8
9
|
import { parsePrMeta } from "../shared/pr-meta.js";
|
|
10
|
+
import { ghEnv } from "../pr/internal/gh-api.js";
|
|
9
11
|
import { appendTaskEvent } from "./shared.js";
|
|
10
12
|
function normalizeMergedPr(value) {
|
|
11
13
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -78,6 +80,19 @@ function pickHostedMergedPr(records) {
|
|
|
78
80
|
return right.localeCompare(left);
|
|
79
81
|
})[0] ?? null);
|
|
80
82
|
}
|
|
83
|
+
export function resolveLocalMergedPrMeta(meta) {
|
|
84
|
+
const branch = meta?.branch?.trim() ?? "";
|
|
85
|
+
const mergeCommit = meta?.merge_commit?.trim() ?? "";
|
|
86
|
+
if (meta?.status !== "MERGED" || !branch || !mergeCommit)
|
|
87
|
+
return null;
|
|
88
|
+
return {
|
|
89
|
+
branch,
|
|
90
|
+
base: meta.base ?? null,
|
|
91
|
+
mergedAt: meta.merged_at ?? null,
|
|
92
|
+
mergeCommit,
|
|
93
|
+
headSha: meta.head_sha ?? null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
81
96
|
export function resolveHostedMergeTargetFromEvent(opts) {
|
|
82
97
|
if (!opts.event || typeof opts.event !== "object" || Array.isArray(opts.event))
|
|
83
98
|
return null;
|
|
@@ -94,23 +109,27 @@ export function resolveHostedMergeTargetFromEvent(opts) {
|
|
|
94
109
|
mergedPr,
|
|
95
110
|
};
|
|
96
111
|
}
|
|
97
|
-
async function resolveHostedMergedPr(opts) {
|
|
98
|
-
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
export async function resolveHostedMergedPr(opts) {
|
|
113
|
+
return await withGhTransportRetry(async () => {
|
|
114
|
+
const { stdout } = await execFileAsync("gh", [
|
|
115
|
+
"pr",
|
|
116
|
+
"list",
|
|
117
|
+
"--state",
|
|
118
|
+
"merged",
|
|
119
|
+
"--head",
|
|
120
|
+
opts.branch,
|
|
121
|
+
"--json",
|
|
122
|
+
"number,title,url,mergedAt,baseRefName,headRefName,headRefOid,mergeCommit",
|
|
123
|
+
], {
|
|
124
|
+
cwd: opts.cwd,
|
|
125
|
+
env: ghEnv(),
|
|
126
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
127
|
+
});
|
|
128
|
+
const parsed = JSON.parse(stdout);
|
|
129
|
+
return Array.isArray(parsed) ? pickHostedMergedPr(parsed) : null;
|
|
130
|
+
}, {
|
|
131
|
+
label: `looking up merged PR metadata for ${opts.branch}`,
|
|
111
132
|
});
|
|
112
|
-
const parsed = JSON.parse(stdout);
|
|
113
|
-
return Array.isArray(parsed) ? pickHostedMergedPr(parsed) : null;
|
|
114
133
|
}
|
|
115
134
|
function buildSyncedPrMeta(opts) {
|
|
116
135
|
const at = opts.mergedPr.mergedAt ?? new Date().toISOString();
|
|
@@ -160,6 +179,119 @@ function buildSyncedTask(opts) {
|
|
|
160
179
|
doc_updated_by: "INTEGRATOR",
|
|
161
180
|
};
|
|
162
181
|
}
|
|
182
|
+
function needsHostedMergeSyncFromLocalMeta(opts) {
|
|
183
|
+
const currentStatus = String(opts.task.status || "TODO").toUpperCase();
|
|
184
|
+
const expectedCommit = opts.meta.mergeCommit;
|
|
185
|
+
if (currentStatus !== "DONE")
|
|
186
|
+
return true;
|
|
187
|
+
if ((opts.task.commit?.hash ?? "") !== expectedCommit)
|
|
188
|
+
return true;
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
function buildLocallyMergedSyncedTask(opts) {
|
|
192
|
+
const at = opts.meta.mergedAt ?? new Date().toISOString();
|
|
193
|
+
const currentStatus = String(opts.task.status || "TODO").toUpperCase();
|
|
194
|
+
const note = "Local PR metadata already marks the task branch as MERGED; " +
|
|
195
|
+
"task projection reconciled without an additional GitHub lookup.";
|
|
196
|
+
const statusEvent = {
|
|
197
|
+
type: "status",
|
|
198
|
+
at,
|
|
199
|
+
author: "INTEGRATOR",
|
|
200
|
+
from: currentStatus,
|
|
201
|
+
to: "DONE",
|
|
202
|
+
note,
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
...opts.task,
|
|
206
|
+
status: "DONE",
|
|
207
|
+
result_summary: opts.task.result_summary ?? "Merged and reconciled from local PR metadata.",
|
|
208
|
+
commit: opts.task.commit?.hash?.trim()
|
|
209
|
+
? opts.task.commit
|
|
210
|
+
: {
|
|
211
|
+
hash: opts.meta.mergeCommit,
|
|
212
|
+
message: "Merged branch_pr task reconciled from local PR metadata",
|
|
213
|
+
},
|
|
214
|
+
events: appendTaskEvent(opts.task, statusEvent),
|
|
215
|
+
doc_version: normalizeTaskDocVersion(opts.task.doc_version),
|
|
216
|
+
doc_updated_at: at,
|
|
217
|
+
doc_updated_by: "INTEGRATOR",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function hasTaskVerificationForLocalSync(opts) {
|
|
221
|
+
if (opts.task.verification?.state === "ok")
|
|
222
|
+
return "task";
|
|
223
|
+
if (opts.meta?.verify?.status === "pass")
|
|
224
|
+
return "pr";
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
async function gitRefExists(opts) {
|
|
228
|
+
try {
|
|
229
|
+
await execFileAsync("git", ["rev-parse", "--verify", "--quiet", opts.ref], {
|
|
230
|
+
cwd: opts.cwd,
|
|
231
|
+
env: process.env,
|
|
232
|
+
});
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function gitIsAncestor(opts) {
|
|
240
|
+
try {
|
|
241
|
+
await execFileAsync("git", ["merge-base", "--is-ancestor", opts.ancestor, opts.descendant], {
|
|
242
|
+
cwd: opts.cwd,
|
|
243
|
+
env: process.env,
|
|
244
|
+
});
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
const code = err?.code;
|
|
249
|
+
if (code === 1)
|
|
250
|
+
return false;
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async function resolveSyncBaseBranch(opts) {
|
|
255
|
+
const fromMeta = opts.meta?.base?.trim() ?? "";
|
|
256
|
+
if (fromMeta.length > 0)
|
|
257
|
+
return fromMeta;
|
|
258
|
+
return await resolveBaseBranch({
|
|
259
|
+
cwd: opts.ctx.resolvedProject.gitRoot,
|
|
260
|
+
rootOverride: opts.ctx.resolvedProject.gitRoot,
|
|
261
|
+
cliBaseOpt: null,
|
|
262
|
+
mode: opts.ctx.config.workflow_mode,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function buildLocallySyncedTask(opts) {
|
|
266
|
+
const at = new Date().toISOString();
|
|
267
|
+
const currentStatus = String(opts.task.status || "TODO").toUpperCase();
|
|
268
|
+
const note = `Local branch_pr reconciliation detected task commit ${opts.candidate.commitHash.slice(0, 12)} ` +
|
|
269
|
+
`on base ${opts.candidate.base}; canonical task state normalized after shipment.`;
|
|
270
|
+
const statusEvent = {
|
|
271
|
+
type: "status",
|
|
272
|
+
at,
|
|
273
|
+
author: "INTEGRATOR",
|
|
274
|
+
from: currentStatus,
|
|
275
|
+
to: "DONE",
|
|
276
|
+
note,
|
|
277
|
+
};
|
|
278
|
+
return {
|
|
279
|
+
...opts.task,
|
|
280
|
+
status: "DONE",
|
|
281
|
+
result_summary: opts.task.result_summary ??
|
|
282
|
+
`Shipped on ${opts.candidate.base} and reconciled from local branch_pr state.`,
|
|
283
|
+
commit: opts.task.commit?.hash?.trim()
|
|
284
|
+
? opts.task.commit
|
|
285
|
+
: {
|
|
286
|
+
hash: opts.candidate.commitHash,
|
|
287
|
+
message: `Shipped on ${opts.candidate.base} before canonical task closure`,
|
|
288
|
+
},
|
|
289
|
+
events: appendTaskEvent(opts.task, statusEvent),
|
|
290
|
+
doc_version: normalizeTaskDocVersion(opts.task.doc_version),
|
|
291
|
+
doc_updated_at: at,
|
|
292
|
+
doc_updated_by: "INTEGRATOR",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
163
295
|
async function readPrMetaIfPresent(opts) {
|
|
164
296
|
const metaPath = path.join(opts.ctx.resolvedProject.gitRoot, opts.ctx.config.paths.workflow_dir, opts.taskId, "pr", "meta.json");
|
|
165
297
|
try {
|
|
@@ -238,6 +370,97 @@ export async function syncHostedMergedTask(opts) {
|
|
|
238
370
|
synced: 1,
|
|
239
371
|
};
|
|
240
372
|
}
|
|
373
|
+
export async function findLocallyShippedBranchPrTasks(opts) {
|
|
374
|
+
if (opts.ctx.backendId !== "local" || opts.ctx.config.workflow_mode !== "branch_pr") {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
const matches = [];
|
|
378
|
+
for (const task of opts.tasks) {
|
|
379
|
+
const currentStatus = String(task.status || "TODO").toUpperCase();
|
|
380
|
+
if (currentStatus === "DONE")
|
|
381
|
+
continue;
|
|
382
|
+
const prMetaRecord = await readPrMetaIfPresent({ ctx: opts.ctx, taskId: task.id });
|
|
383
|
+
const verificationSource = hasTaskVerificationForLocalSync({
|
|
384
|
+
task,
|
|
385
|
+
meta: prMetaRecord?.meta ?? null,
|
|
386
|
+
});
|
|
387
|
+
if (!verificationSource)
|
|
388
|
+
continue;
|
|
389
|
+
const commitHash = task.commit?.hash?.trim() ?? "";
|
|
390
|
+
if (!commitHash)
|
|
391
|
+
continue;
|
|
392
|
+
const base = await resolveSyncBaseBranch({
|
|
393
|
+
ctx: opts.ctx,
|
|
394
|
+
meta: prMetaRecord?.meta ?? null,
|
|
395
|
+
});
|
|
396
|
+
if (!base)
|
|
397
|
+
continue;
|
|
398
|
+
if (!(await gitRefExists({ cwd: opts.ctx.resolvedProject.gitRoot, ref: base })))
|
|
399
|
+
continue;
|
|
400
|
+
if (!(await gitIsAncestor({
|
|
401
|
+
cwd: opts.ctx.resolvedProject.gitRoot,
|
|
402
|
+
ancestor: commitHash,
|
|
403
|
+
descendant: base,
|
|
404
|
+
}))) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
matches.push({
|
|
408
|
+
taskId: task.id,
|
|
409
|
+
branch: prMetaRecord?.meta.branch?.trim() ?? "",
|
|
410
|
+
base,
|
|
411
|
+
commitHash,
|
|
412
|
+
verificationSource,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return matches;
|
|
416
|
+
}
|
|
417
|
+
export async function findDoneBranchPrTasksWithOpenPrArtifacts(opts) {
|
|
418
|
+
if (opts.ctx.backendId !== "local" || opts.ctx.config.workflow_mode !== "branch_pr") {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
const matches = [];
|
|
422
|
+
for (const task of opts.tasks) {
|
|
423
|
+
const currentStatus = String(task.status || "TODO").toUpperCase();
|
|
424
|
+
if (currentStatus !== "DONE")
|
|
425
|
+
continue;
|
|
426
|
+
const prMetaRecord = await readPrMetaIfPresent({ ctx: opts.ctx, taskId: task.id });
|
|
427
|
+
const meta = prMetaRecord?.meta ?? null;
|
|
428
|
+
if (!meta || meta.status === "MERGED")
|
|
429
|
+
continue;
|
|
430
|
+
const branch = meta.branch?.trim() ?? "";
|
|
431
|
+
if (!branch)
|
|
432
|
+
continue;
|
|
433
|
+
const commitHash = task.commit?.hash?.trim() ?? meta.head_sha?.trim() ?? "";
|
|
434
|
+
if (!commitHash)
|
|
435
|
+
continue;
|
|
436
|
+
const base = await resolveSyncBaseBranch({
|
|
437
|
+
ctx: opts.ctx,
|
|
438
|
+
meta,
|
|
439
|
+
});
|
|
440
|
+
if (!base)
|
|
441
|
+
continue;
|
|
442
|
+
matches.push({
|
|
443
|
+
taskId: task.id,
|
|
444
|
+
branch,
|
|
445
|
+
base,
|
|
446
|
+
commitHash,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
return matches;
|
|
450
|
+
}
|
|
451
|
+
export async function syncLocallyShippedBranchPrTasks(opts) {
|
|
452
|
+
const matches = await findLocallyShippedBranchPrTasks(opts);
|
|
453
|
+
if (matches.length === 0)
|
|
454
|
+
return { tasks: opts.tasks, synced: 0 };
|
|
455
|
+
const byTaskId = new Map(matches.map((entry) => [entry.taskId, entry]));
|
|
456
|
+
return {
|
|
457
|
+
tasks: opts.tasks.map((task) => {
|
|
458
|
+
const candidate = byTaskId.get(task.id);
|
|
459
|
+
return candidate ? buildLocallySyncedTask({ task, candidate }) : task;
|
|
460
|
+
}),
|
|
461
|
+
synced: matches.length,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
241
464
|
export async function syncHostedMergedTasks(opts) {
|
|
242
465
|
if (opts.ctx.backendId !== "local" || opts.ctx.config.workflow_mode !== "branch_pr") {
|
|
243
466
|
return { tasks: opts.tasks, synced: 0 };
|
|
@@ -255,6 +478,15 @@ export async function syncHostedMergedTasks(opts) {
|
|
|
255
478
|
nextTasks.push(task);
|
|
256
479
|
continue;
|
|
257
480
|
}
|
|
481
|
+
const localMergedMeta = resolveLocalMergedPrMeta(prMetaRecord.meta);
|
|
482
|
+
if (localMergedMeta) {
|
|
483
|
+
nextTasks.push(needsHostedMergeSyncFromLocalMeta({ task, meta: localMergedMeta })
|
|
484
|
+
? buildLocallyMergedSyncedTask({ task, meta: localMergedMeta })
|
|
485
|
+
: task);
|
|
486
|
+
if (needsHostedMergeSyncFromLocalMeta({ task, meta: localMergedMeta }))
|
|
487
|
+
synced += 1;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
258
490
|
const mergedPr = await resolveHostedMergedPr({
|
|
259
491
|
cwd: opts.ctx.resolvedProject.gitRoot,
|
|
260
492
|
branch,
|
|
@@ -18,6 +18,7 @@ export { cmdTaskDerive } from "./derive.js";
|
|
|
18
18
|
export { cmdTaskCloseDuplicate } from "./close-duplicate.js";
|
|
19
19
|
export { cmdTaskStartReady } from "./start-ready.js";
|
|
20
20
|
export { cmdTaskCloseNoop } from "./close-noop.js";
|
|
21
|
+
export { makeRunTaskHostedClosePrHandler, taskHostedClosePrSpec, } from "./hosted-close-pr.command.js";
|
|
21
22
|
export { cmdTaskExport } from "./export.js";
|
|
22
23
|
export { cmdTaskLint } from "./lint.js";
|
|
23
24
|
export { cmdTaskPlanSet, cmdTaskPlanApprove, cmdTaskPlanReject } from "./plan.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/task/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/task/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,+BAA+B,EAC/B,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAElF,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -17,6 +17,7 @@ export { cmdTaskDerive } from "./derive.js";
|
|
|
17
17
|
export { cmdTaskCloseDuplicate } from "./close-duplicate.js";
|
|
18
18
|
export { cmdTaskStartReady } from "./start-ready.js";
|
|
19
19
|
export { cmdTaskCloseNoop } from "./close-noop.js";
|
|
20
|
+
export { makeRunTaskHostedClosePrHandler, taskHostedClosePrSpec, } from "./hosted-close-pr.command.js";
|
|
20
21
|
export { cmdTaskExport } from "./export.js";
|
|
21
22
|
export { cmdTaskLint } from "./lint.js";
|
|
22
23
|
export { cmdTaskPlanSet, cmdTaskPlanApprove, cmdTaskPlanReject } from "./plan.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.ts"],"names":[],"mappings":"AAaA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.ts"],"names":[],"mappings":"AAaA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAepF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IAC5C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAiIF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuJlB"}
|
|
@@ -8,6 +8,22 @@ import { makeReadOnlyUsecaseContext } from "../../usecases/context/resolve-conte
|
|
|
8
8
|
import { loadCommandContext } from "../shared/task-backend.js";
|
|
9
9
|
import { ensureTaskDependsOnGraphIsAcyclic, nowIso, requiresVerifyStepsByPrimary, resolvePrimaryTag, warnIfUnknownOwner, } from "./shared.js";
|
|
10
10
|
import { buildDefaultVerifyStepsSection, defaultTaskDocV3, TASK_DOC_VERSION_V3, } from "./doc-template.js";
|
|
11
|
+
const TASK_NEW_DUPLICATE_THRESHOLD = 0.75;
|
|
12
|
+
const TASK_NEW_DUPLICATE_STOPWORDS = new Set([
|
|
13
|
+
"a",
|
|
14
|
+
"an",
|
|
15
|
+
"and",
|
|
16
|
+
"for",
|
|
17
|
+
"from",
|
|
18
|
+
"in",
|
|
19
|
+
"is",
|
|
20
|
+
"of",
|
|
21
|
+
"on",
|
|
22
|
+
"the",
|
|
23
|
+
"to",
|
|
24
|
+
"when",
|
|
25
|
+
"with",
|
|
26
|
+
]);
|
|
11
27
|
function dedupeTrimmed(values) {
|
|
12
28
|
const seen = new Set();
|
|
13
29
|
const out = [];
|
|
@@ -55,11 +71,65 @@ function sanitizeTaskNewParsed(p) {
|
|
|
55
71
|
const verify = dedupeTrimmed(p.verify);
|
|
56
72
|
return { ...p, title, description, owner, tags, dependsOn, verify };
|
|
57
73
|
}
|
|
74
|
+
function normalizeDuplicateTitleTokens(value) {
|
|
75
|
+
return value
|
|
76
|
+
.trim()
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.replaceAll(/[^a-z0-9]+/g, " ")
|
|
79
|
+
.split(/\s+/)
|
|
80
|
+
.map((token) => token.trim())
|
|
81
|
+
.filter((token) => token.length > 0 &&
|
|
82
|
+
(token.length > 2 || /\d/.test(token)) &&
|
|
83
|
+
!TASK_NEW_DUPLICATE_STOPWORDS.has(token));
|
|
84
|
+
}
|
|
85
|
+
function duplicateSimilarity(left, right) {
|
|
86
|
+
const leftTokens = normalizeDuplicateTitleTokens(left);
|
|
87
|
+
const rightTokens = normalizeDuplicateTitleTokens(right);
|
|
88
|
+
if (leftTokens.length === 0 || rightTokens.length === 0) {
|
|
89
|
+
return left.trim().toLowerCase() === right.trim().toLowerCase() ? 1 : 0;
|
|
90
|
+
}
|
|
91
|
+
const leftSet = new Set(leftTokens);
|
|
92
|
+
const rightSet = new Set(rightTokens);
|
|
93
|
+
const intersection = [...leftSet].filter((token) => rightSet.has(token)).length;
|
|
94
|
+
const union = new Set([...leftSet, ...rightSet]).size;
|
|
95
|
+
return union === 0 ? 0 : intersection / union;
|
|
96
|
+
}
|
|
97
|
+
function listOpenTaskDuplicates(tasks, title) {
|
|
98
|
+
return tasks
|
|
99
|
+
.filter((task) => String(task.status ?? "").toUpperCase() !== "DONE")
|
|
100
|
+
.map((task) => ({
|
|
101
|
+
task,
|
|
102
|
+
score: duplicateSimilarity(task.title ?? "", title),
|
|
103
|
+
}))
|
|
104
|
+
.filter(({ score }) => score >= TASK_NEW_DUPLICATE_THRESHOLD)
|
|
105
|
+
.toSorted((left, right) => right.score - left.score || left.task.id.localeCompare(right.task.id));
|
|
106
|
+
}
|
|
107
|
+
function formatDuplicateTaskMessage(duplicates, allowDuplicate) {
|
|
108
|
+
const summary = duplicates
|
|
109
|
+
.slice(0, 3)
|
|
110
|
+
.map(({ task, score }) => `${task.id} (${Math.round(score * 100)}% title overlap, status=${String(task.status || "TODO").toUpperCase()}): ${task.title}`)
|
|
111
|
+
.join("; ");
|
|
112
|
+
const tail = allowDuplicate
|
|
113
|
+
? "creating a duplicate because --allow-duplicate was passed"
|
|
114
|
+
: "rerun with --allow-duplicate only when intentional or close the extra task with `agentplane task close-duplicate`";
|
|
115
|
+
return `potential duplicate open task detected: ${summary}; ${tail}.`;
|
|
116
|
+
}
|
|
58
117
|
export async function runTaskNewParsed(opts) {
|
|
59
118
|
const p = sanitizeTaskNewParsed(opts.parsed);
|
|
60
119
|
try {
|
|
61
120
|
const ctx = opts.ctx ??
|
|
62
121
|
(await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
|
|
122
|
+
const duplicateTasks = listOpenTaskDuplicates(await ctx.taskBackend.listTasks(), p.title);
|
|
123
|
+
if (duplicateTasks.length > 0 && !p.allowDuplicate) {
|
|
124
|
+
throw new CliError({
|
|
125
|
+
exitCode: 3,
|
|
126
|
+
code: "E_VALIDATION",
|
|
127
|
+
message: formatDuplicateTaskMessage(duplicateTasks, false),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (duplicateTasks.length > 0 && p.allowDuplicate) {
|
|
131
|
+
process.stderr.write(`${warnMessage(formatDuplicateTaskMessage(duplicateTasks, true))}\n`);
|
|
132
|
+
}
|
|
63
133
|
const suffixLength = ctx.config.tasks.id_suffix_length_default;
|
|
64
134
|
if (!ctx.taskBackend.generateTaskId) {
|
|
65
135
|
throw new CliError({
|
|
@@ -96,7 +166,7 @@ export async function runTaskNewParsed(opts) {
|
|
|
96
166
|
primary: primary.primary,
|
|
97
167
|
verifyCommands: p.verify,
|
|
98
168
|
}));
|
|
99
|
-
process.stderr.write(`${warnMessage("task requires Verify Steps by primary tag; seeded a
|
|
169
|
+
process.stderr.write(`${warnMessage("task requires Verify Steps by primary tag; seeded a concrete ## Verify Steps section in README (refine it only if the task needs stricter acceptance coverage)")}\n`);
|
|
100
170
|
}
|
|
101
171
|
const hasSpike = p.tags.some((tag) => tag.trim().toLowerCase() === spikeTag);
|
|
102
172
|
const hasImplementationTags = requiresVerifyStepsByPrimary(p.tags, ctx.config);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.spec.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,aAAa,
|
|
1
|
+
{"version":3,"file":"new.spec.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,aAAa,CAuFlD,CAAC"}
|
|
@@ -57,6 +57,12 @@ export const taskNewSpec = {
|
|
|
57
57
|
repeatable: true,
|
|
58
58
|
description: "Repeatable. Verification commands/checks to run for this task.",
|
|
59
59
|
},
|
|
60
|
+
{
|
|
61
|
+
kind: "boolean",
|
|
62
|
+
name: "allow-duplicate",
|
|
63
|
+
default: false,
|
|
64
|
+
description: "Allow creating a task even when an open task with a highly similar title already exists.",
|
|
65
|
+
},
|
|
60
66
|
],
|
|
61
67
|
examples: [
|
|
62
68
|
{
|
|
@@ -76,5 +82,6 @@ export const taskNewSpec = {
|
|
|
76
82
|
tags: (raw.opts.tag ?? []),
|
|
77
83
|
dependsOn: (raw.opts["depends-on"] ?? []),
|
|
78
84
|
verify: (raw.opts.verify ?? []),
|
|
85
|
+
allowDuplicate: raw.opts["allow-duplicate"] === true,
|
|
79
86
|
}),
|
|
80
87
|
};
|
|
@@ -5,6 +5,8 @@ export type TaskNormalizeParsed = {
|
|
|
5
5
|
force: boolean;
|
|
6
6
|
yes: boolean;
|
|
7
7
|
syncHostedMerges: boolean;
|
|
8
|
+
syncBranchPrState: boolean;
|
|
9
|
+
taskIds: string[];
|
|
8
10
|
};
|
|
9
11
|
export declare const taskNormalizeSpec: CommandSpec<TaskNormalizeParsed>;
|
|
10
12
|
export declare function makeRunTaskNormalizeHandler(getCtx: (cmd: string) => Promise<CommandContext>): (ctx: CommandCtx, p: TaskNormalizeParsed) => Promise<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/normalize.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"normalize.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/normalize.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,mBAAmB,CA2F9D,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC5E,KAAK,UAAU,EAAE,GAAG,mBAAmB,KAAG,OAAO,CAAC,MAAM,CAAC,CAaxE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { usageError } from "../../cli/spec/errors.js";
|
|
1
2
|
import { cmdTaskNormalize } from "./normalize.js";
|
|
2
3
|
export const taskNormalizeSpec = {
|
|
3
4
|
id: ["task", "normalize"],
|
|
@@ -28,6 +29,19 @@ export const taskNormalizeSpec = {
|
|
|
28
29
|
default: false,
|
|
29
30
|
description: "Query GitHub for merged hosted PRs referenced by local PR artifacts and reconcile stale branch_pr task state before normalization.",
|
|
30
31
|
},
|
|
32
|
+
{
|
|
33
|
+
kind: "boolean",
|
|
34
|
+
name: "sync-branch-pr-state",
|
|
35
|
+
default: false,
|
|
36
|
+
description: "Reconcile stale local branch_pr task state when verified task commits already landed on the base branch.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
kind: "string",
|
|
40
|
+
name: "task-id",
|
|
41
|
+
valueHint: "<task-id>",
|
|
42
|
+
repeatable: true,
|
|
43
|
+
description: "Repeatable. Limit reconcile modes to explicit task ids instead of scanning every task.",
|
|
44
|
+
},
|
|
31
45
|
],
|
|
32
46
|
examples: [
|
|
33
47
|
{ cmd: "agentplane task normalize", why: "Normalize tasks and print a short summary." },
|
|
@@ -36,12 +50,41 @@ export const taskNormalizeSpec = {
|
|
|
36
50
|
cmd: "agentplane task normalize --sync-hosted-merges",
|
|
37
51
|
why: "Reconcile stale branch_pr task state from hosted PR merges, then normalize the local projection.",
|
|
38
52
|
},
|
|
53
|
+
{
|
|
54
|
+
cmd: "agentplane task normalize --sync-branch-pr-state",
|
|
55
|
+
why: "Reconcile locally shipped branch_pr task state when task commits already reached the base branch.",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
cmd: "agentplane task normalize --sync-hosted-merges --task-id 202604071853-XGX2YJ",
|
|
59
|
+
why: "Reconcile only known stale tasks instead of scanning unrelated historical PR metadata.",
|
|
60
|
+
},
|
|
39
61
|
],
|
|
62
|
+
validateRaw: (raw) => {
|
|
63
|
+
const taskIds = raw.opts["task-id"] ?? [];
|
|
64
|
+
if (taskIds.length > 0 &&
|
|
65
|
+
raw.opts["sync-hosted-merges"] !== true &&
|
|
66
|
+
raw.opts["sync-branch-pr-state"] !== true) {
|
|
67
|
+
throw usageError({
|
|
68
|
+
spec: taskNormalizeSpec,
|
|
69
|
+
message: "--task-id requires --sync-hosted-merges and/or --sync-branch-pr-state.",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
for (const taskId of taskIds) {
|
|
73
|
+
if (typeof taskId !== "string" || taskId.trim().length === 0) {
|
|
74
|
+
throw usageError({
|
|
75
|
+
spec: taskNormalizeSpec,
|
|
76
|
+
message: "Invalid value for --task-id: empty.",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
40
81
|
parse: (raw) => ({
|
|
41
82
|
quiet: raw.opts.quiet === true,
|
|
42
83
|
force: raw.opts.force === true,
|
|
43
84
|
yes: raw.opts.yes === true,
|
|
44
85
|
syncHostedMerges: raw.opts["sync-hosted-merges"] === true,
|
|
86
|
+
syncBranchPrState: raw.opts["sync-branch-pr-state"] === true,
|
|
87
|
+
taskIds: (raw.opts["task-id"] ?? []).map((taskId) => taskId.trim()),
|
|
45
88
|
}),
|
|
46
89
|
};
|
|
47
90
|
export function makeRunTaskNormalizeHandler(getCtx) {
|
|
@@ -54,6 +97,8 @@ export function makeRunTaskNormalizeHandler(getCtx) {
|
|
|
54
97
|
force: p.force,
|
|
55
98
|
yes: p.yes,
|
|
56
99
|
syncHostedMerges: p.syncHostedMerges,
|
|
100
|
+
syncBranchPrState: p.syncBranchPrState,
|
|
101
|
+
taskIds: p.taskIds,
|
|
57
102
|
});
|
|
58
103
|
};
|
|
59
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/commands/task/normalize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/commands/task/normalize.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAgDpF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqGlB"}
|