@defend-tech/opencode-optima 0.1.57 → 0.1.59
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/workflow_product_manager.md +2 -2
- package/dist/index.js +169 -110
- package/dist/sanitize_cli.js +169 -110
- package/docs/core/agent_orchestration.md +2 -2
- package/docs/core/agent_orchestration.prompt.md +1 -1
- package/docs/core/task_model.md +1 -1
- package/docs/core/task_model.prompt.md +1 -1
- package/package.json +1 -1
|
@@ -55,8 +55,8 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
|
|
|
55
55
|
- Do not update the principal `dev` workspace `.optima/tasks/current.md` for ClickUp task planning.
|
|
56
56
|
- Unrelated active tasks in `.optima/tasks/current.md` must not block planning; move to/create the correct task worktree instead.
|
|
57
57
|
- Parent setup pulls remote once; after parent branch creation, subtasks can trust the parent local branch without continuous remote polling.
|
|
58
|
-
- Branches: parent `<clickup-task-type>/<parent-task-id>`; subtask `<clickup-task-type>/<parent-task-id
|
|
59
|
-
- PR targets: subtask -> parent branch; parent -> `dev` only after Tech Lead + Validator/QA pass and `CTO`/`PO` move to `merge`; release -> `dev` to `main` only after explicit approval.
|
|
58
|
+
- Branches: parent `<clickup-task-type>/<parent-task-id>`; subtask `<clickup-task-type>/<parent-task-id>-subtask-<subtask-id>`; pending planned subtasks `<clickup-task-type>/<parent-task-id>-pending-<title-slug>`; PoC always `poc/<clickup-task-id>` and remains there unless productized later.
|
|
59
|
+
- PR targets/start points: subtask -> parent branch and starts from the parent branch; if parent branch/worktree is missing, bootstrap the parent from `dev`/`origin/dev` first; parent -> `dev` only after Tech Lead + Validator/QA pass and `CTO`/`PO` move to `merge`; release -> `dev` to `main` only after explicit approval.
|
|
60
60
|
- Preserve user work and unrelated dirty files. Stop and ask if unexpected changes appear.
|
|
61
61
|
|
|
62
62
|
## Operating Style
|
package/dist/index.js
CHANGED
|
@@ -632,8 +632,6 @@ var require_Alias = __commonJS({
|
|
|
632
632
|
* instance of the `source` anchor before this node.
|
|
633
633
|
*/
|
|
634
634
|
resolve(doc, ctx) {
|
|
635
|
-
if (ctx?.maxAliasCount === 0)
|
|
636
|
-
throw new ReferenceError("Alias resolution is disabled");
|
|
637
635
|
let nodes;
|
|
638
636
|
if (ctx?.aliasResolveCache) {
|
|
639
637
|
nodes = ctx.aliasResolveCache;
|
|
@@ -1433,7 +1431,6 @@ var require_stringify = __commonJS({
|
|
|
1433
1431
|
nullStr: "null",
|
|
1434
1432
|
simpleKeys: false,
|
|
1435
1433
|
singleQuote: null,
|
|
1436
|
-
trailingComma: false,
|
|
1437
1434
|
trueStr: "true",
|
|
1438
1435
|
verifyAliasOrder: true
|
|
1439
1436
|
}, doc.schema.toStringOptions, options);
|
|
@@ -1706,18 +1703,18 @@ var require_merge = __commonJS({
|
|
|
1706
1703
|
};
|
|
1707
1704
|
var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default);
|
|
1708
1705
|
function addMergeToJSMap(ctx, map, value) {
|
|
1709
|
-
|
|
1710
|
-
if (identity.isSeq(
|
|
1711
|
-
for (const it of
|
|
1706
|
+
value = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
|
|
1707
|
+
if (identity.isSeq(value))
|
|
1708
|
+
for (const it of value.items)
|
|
1712
1709
|
mergeValue(ctx, map, it);
|
|
1713
|
-
else if (Array.isArray(
|
|
1714
|
-
for (const it of
|
|
1710
|
+
else if (Array.isArray(value))
|
|
1711
|
+
for (const it of value)
|
|
1715
1712
|
mergeValue(ctx, map, it);
|
|
1716
1713
|
else
|
|
1717
|
-
mergeValue(ctx, map,
|
|
1714
|
+
mergeValue(ctx, map, value);
|
|
1718
1715
|
}
|
|
1719
1716
|
function mergeValue(ctx, map, value) {
|
|
1720
|
-
const source =
|
|
1717
|
+
const source = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
|
|
1721
1718
|
if (!identity.isMap(source))
|
|
1722
1719
|
throw new Error("Merge sources must be maps or map aliases");
|
|
1723
1720
|
const srcMap = source.toJSON(null, ctx, Map);
|
|
@@ -1738,9 +1735,6 @@ var require_merge = __commonJS({
|
|
|
1738
1735
|
}
|
|
1739
1736
|
return map;
|
|
1740
1737
|
}
|
|
1741
|
-
function resolveAliasValue(ctx, value) {
|
|
1742
|
-
return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
|
|
1743
|
-
}
|
|
1744
1738
|
exports.addMergeToJSMap = addMergeToJSMap;
|
|
1745
1739
|
exports.isMergeKey = isMergeKey;
|
|
1746
1740
|
exports.merge = merge;
|
|
@@ -1954,19 +1948,12 @@ ${indent}${line}` : "\n";
|
|
|
1954
1948
|
if (comment)
|
|
1955
1949
|
reqNewline = true;
|
|
1956
1950
|
let str = stringify.stringify(item, itemCtx, () => comment = null);
|
|
1957
|
-
|
|
1958
|
-
if (i < items.length - 1) {
|
|
1951
|
+
if (i < items.length - 1)
|
|
1959
1952
|
str += ",";
|
|
1960
|
-
} else if (ctx.options.trailingComma) {
|
|
1961
|
-
if (ctx.options.lineWidth > 0) {
|
|
1962
|
-
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
1963
|
-
}
|
|
1964
|
-
if (reqNewline) {
|
|
1965
|
-
str += ",";
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
1953
|
if (comment)
|
|
1969
1954
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
1955
|
+
if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
|
|
1956
|
+
reqNewline = true;
|
|
1970
1957
|
lines.push(str);
|
|
1971
1958
|
linesAtValue = lines.length;
|
|
1972
1959
|
}
|
|
@@ -2378,7 +2365,7 @@ var require_stringifyNumber = __commonJS({
|
|
|
2378
2365
|
if (!isFinite(num))
|
|
2379
2366
|
return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
|
|
2380
2367
|
let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
2381
|
-
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") &&
|
|
2368
|
+
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^\d/.test(n)) {
|
|
2382
2369
|
let i = n.indexOf(".");
|
|
2383
2370
|
if (i < 0) {
|
|
2384
2371
|
i = n.length;
|
|
@@ -4750,7 +4737,7 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4750
4737
|
while (next === " " || next === " ")
|
|
4751
4738
|
next = source[++i + 1];
|
|
4752
4739
|
} else if (next === "x" || next === "u" || next === "U") {
|
|
4753
|
-
const length =
|
|
4740
|
+
const length = { x: 2, u: 4, U: 8 }[next];
|
|
4754
4741
|
res += parseCharCode(source, i + 1, length, onError);
|
|
4755
4742
|
i += length;
|
|
4756
4743
|
} else {
|
|
@@ -4825,13 +4812,12 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4825
4812
|
const cc = source.substr(offset, length);
|
|
4826
4813
|
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
|
|
4827
4814
|
const code = ok ? parseInt(cc, 16) : NaN;
|
|
4828
|
-
|
|
4829
|
-
return String.fromCodePoint(code);
|
|
4830
|
-
} catch {
|
|
4815
|
+
if (isNaN(code)) {
|
|
4831
4816
|
const raw = source.substr(offset - 2, length + 2);
|
|
4832
4817
|
onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
|
|
4833
4818
|
return raw;
|
|
4834
4819
|
}
|
|
4820
|
+
return String.fromCodePoint(code);
|
|
4835
4821
|
}
|
|
4836
4822
|
exports.resolveFlowScalar = resolveFlowScalar;
|
|
4837
4823
|
}
|
|
@@ -4981,22 +4967,17 @@ var require_compose_node = __commonJS({
|
|
|
4981
4967
|
case "block-map":
|
|
4982
4968
|
case "block-seq":
|
|
4983
4969
|
case "flow-collection":
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
node.anchor = anchor.source.substring(1);
|
|
4988
|
-
} catch (error) {
|
|
4989
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4990
|
-
onError(token, "RESOURCE_EXHAUSTION", message);
|
|
4991
|
-
}
|
|
4970
|
+
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
|
|
4971
|
+
if (anchor)
|
|
4972
|
+
node.anchor = anchor.source.substring(1);
|
|
4992
4973
|
break;
|
|
4993
4974
|
default: {
|
|
4994
4975
|
const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
|
|
4995
4976
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
4977
|
+
node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
|
|
4996
4978
|
isSrcToken = false;
|
|
4997
4979
|
}
|
|
4998
4980
|
}
|
|
4999
|
-
node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
|
|
5000
4981
|
if (anchor && node.anchor === "")
|
|
5001
4982
|
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
|
|
5002
4983
|
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
|
|
@@ -5181,10 +5162,8 @@ ${cb}` : comment;
|
|
|
5181
5162
|
}
|
|
5182
5163
|
}
|
|
5183
5164
|
if (afterDoc) {
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
for (let i = 0; i < this.warnings.length; ++i)
|
|
5187
|
-
doc.warnings.push(this.warnings[i]);
|
|
5165
|
+
Array.prototype.push.apply(doc.errors, this.errors);
|
|
5166
|
+
Array.prototype.push.apply(doc.warnings, this.warnings);
|
|
5188
5167
|
} else {
|
|
5189
5168
|
doc.errors = this.errors;
|
|
5190
5169
|
doc.warnings = this.warnings;
|
|
@@ -5917,7 +5896,7 @@ var require_lexer = __commonJS({
|
|
|
5917
5896
|
const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
|
|
5918
5897
|
this.indentNext = this.indentValue + 1;
|
|
5919
5898
|
this.indentValue += n;
|
|
5920
|
-
return
|
|
5899
|
+
return yield* this.parseBlockStart();
|
|
5921
5900
|
}
|
|
5922
5901
|
return "doc";
|
|
5923
5902
|
}
|
|
@@ -6216,38 +6195,28 @@ var require_lexer = __commonJS({
|
|
|
6216
6195
|
return 0;
|
|
6217
6196
|
}
|
|
6218
6197
|
*pushIndicators() {
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
|
|
6238
|
-
if (!inFlow)
|
|
6239
|
-
this.indentNext = this.indentValue + 1;
|
|
6240
|
-
else if (this.flowKey)
|
|
6241
|
-
this.flowKey = false;
|
|
6242
|
-
n += yield* this.pushCount(1);
|
|
6243
|
-
n += yield* this.pushSpaces(true);
|
|
6244
|
-
continue loop;
|
|
6245
|
-
}
|
|
6198
|
+
switch (this.charAt(0)) {
|
|
6199
|
+
case "!":
|
|
6200
|
+
return (yield* this.pushTag()) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
|
|
6201
|
+
case "&":
|
|
6202
|
+
return (yield* this.pushUntil(isNotAnchorChar)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
|
|
6203
|
+
case "-":
|
|
6204
|
+
// this is an error
|
|
6205
|
+
case "?":
|
|
6206
|
+
// this is an error outside flow collections
|
|
6207
|
+
case ":": {
|
|
6208
|
+
const inFlow = this.flowLevel > 0;
|
|
6209
|
+
const ch1 = this.charAt(1);
|
|
6210
|
+
if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
|
|
6211
|
+
if (!inFlow)
|
|
6212
|
+
this.indentNext = this.indentValue + 1;
|
|
6213
|
+
else if (this.flowKey)
|
|
6214
|
+
this.flowKey = false;
|
|
6215
|
+
return (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
|
|
6246
6216
|
}
|
|
6247
6217
|
}
|
|
6248
|
-
break loop;
|
|
6249
6218
|
}
|
|
6250
|
-
return
|
|
6219
|
+
return 0;
|
|
6251
6220
|
}
|
|
6252
6221
|
*pushTag() {
|
|
6253
6222
|
if (this.charAt(1) === "<") {
|
|
@@ -6406,13 +6375,6 @@ var require_parser = __commonJS({
|
|
|
6406
6375
|
}
|
|
6407
6376
|
return prev.splice(i, prev.length);
|
|
6408
6377
|
}
|
|
6409
|
-
function arrayPushArray(target, source) {
|
|
6410
|
-
if (source.length < 1e5)
|
|
6411
|
-
Array.prototype.push.apply(target, source);
|
|
6412
|
-
else
|
|
6413
|
-
for (let i = 0; i < source.length; ++i)
|
|
6414
|
-
target.push(source[i]);
|
|
6415
|
-
}
|
|
6416
6378
|
function fixFlowSeqItems(fc) {
|
|
6417
6379
|
if (fc.start.type === "flow-seq-start") {
|
|
6418
6380
|
for (const it of fc.items) {
|
|
@@ -6422,11 +6384,11 @@ var require_parser = __commonJS({
|
|
|
6422
6384
|
delete it.key;
|
|
6423
6385
|
if (isFlowToken(it.value)) {
|
|
6424
6386
|
if (it.value.end)
|
|
6425
|
-
|
|
6387
|
+
Array.prototype.push.apply(it.value.end, it.sep);
|
|
6426
6388
|
else
|
|
6427
6389
|
it.value.end = it.sep;
|
|
6428
6390
|
} else
|
|
6429
|
-
|
|
6391
|
+
Array.prototype.push.apply(it.start, it.sep);
|
|
6430
6392
|
delete it.sep;
|
|
6431
6393
|
}
|
|
6432
6394
|
}
|
|
@@ -6781,7 +6743,7 @@ var require_parser = __commonJS({
|
|
|
6781
6743
|
const prev = map.items[map.items.length - 2];
|
|
6782
6744
|
const end = prev?.value?.end;
|
|
6783
6745
|
if (Array.isArray(end)) {
|
|
6784
|
-
|
|
6746
|
+
Array.prototype.push.apply(end, it.start);
|
|
6785
6747
|
end.push(this.sourceToken);
|
|
6786
6748
|
map.items.pop();
|
|
6787
6749
|
return;
|
|
@@ -6969,7 +6931,7 @@ var require_parser = __commonJS({
|
|
|
6969
6931
|
const prev = seq.items[seq.items.length - 2];
|
|
6970
6932
|
const end = prev?.value?.end;
|
|
6971
6933
|
if (Array.isArray(end)) {
|
|
6972
|
-
|
|
6934
|
+
Array.prototype.push.apply(end, it.start);
|
|
6973
6935
|
end.push(this.sourceToken);
|
|
6974
6936
|
seq.items.pop();
|
|
6975
6937
|
return;
|
|
@@ -8841,7 +8803,13 @@ function deriveClickUpBranchName({ taskType, parentTaskId, subtaskId, taskId } =
|
|
|
8841
8803
|
throw new Error("taskType and parentTaskId/taskId are required to derive a ClickUp branch name.");
|
|
8842
8804
|
}
|
|
8843
8805
|
if (typeSlug === "poc") return ["poc", parentSlug].join("/");
|
|
8844
|
-
return
|
|
8806
|
+
if (subtaskSlug) return `${typeSlug}/${parentSlug}-subtask-${subtaskSlug}`;
|
|
8807
|
+
return [typeSlug, parentSlug].join("/");
|
|
8808
|
+
}
|
|
8809
|
+
function isClickUpSubtaskRoute({ taskType, parentTaskId, subtaskId, taskId } = {}) {
|
|
8810
|
+
return Boolean(
|
|
8811
|
+
normalizeClickUpTaskType(taskType) !== "poc" && subtaskId && parentTaskId && String(parentTaskId) !== String(taskId || subtaskId)
|
|
8812
|
+
);
|
|
8845
8813
|
}
|
|
8846
8814
|
function deriveClickUpPrTarget({ isRelease = false, parentTaskId, parentBranch, parentTaskType, taskType, devBranch = "dev", mainBranch = "main" } = {}) {
|
|
8847
8815
|
if (isRelease) return mainBranch;
|
|
@@ -9030,15 +8998,17 @@ function deriveClickUpPendingSubtaskBranch({ taskType, parentTaskId, title } = {
|
|
|
9030
8998
|
const parentSlug = branchSafeClickUpId(parentTaskId);
|
|
9031
8999
|
if (!typeSlug || !parentSlug) throw new Error("taskType and parentTaskId are required to derive a pending subtask branch name.");
|
|
9032
9000
|
if (typeSlug === "poc") return ["poc", parentSlug].join("/");
|
|
9033
|
-
return
|
|
9001
|
+
return `${typeSlug}/${parentSlug}-pending-${normalizeLooseToken(title) || "subtask"}`;
|
|
9034
9002
|
}
|
|
9035
|
-
function buildClickUpSubtaskAgentMetadata({ parentTaskId, subtask, branch, prTarget, titleSlug } = {}) {
|
|
9003
|
+
function buildClickUpSubtaskAgentMetadata({ parentTaskId, subtask, branch, prTarget, titleSlug, parentBranch } = {}) {
|
|
9036
9004
|
return mergeClickUpAgentMetadata("", {
|
|
9037
9005
|
subtask: {
|
|
9038
9006
|
parent_task_id: parentTaskId,
|
|
9007
|
+
parent_branch: parentBranch || prTarget,
|
|
9039
9008
|
title: subtask.title,
|
|
9040
9009
|
title_slug: titleSlug,
|
|
9041
9010
|
branch,
|
|
9011
|
+
subtask_branch: branch,
|
|
9042
9012
|
pr_target: prTarget,
|
|
9043
9013
|
pending_clickup_subtask_id: true,
|
|
9044
9014
|
owner_role: subtask.ownerRole,
|
|
@@ -9058,7 +9028,7 @@ function buildClickUpCreateSubtasksPayload({ parentTaskId, markdown = "", source
|
|
|
9058
9028
|
const titleSlug = normalizeLooseToken(subtask.title) || "subtask";
|
|
9059
9029
|
const branch = subtask.branch || deriveClickUpPendingSubtaskBranch({ taskType: subtask.type, parentTaskId: parentId, title: subtask.title });
|
|
9060
9030
|
const prTarget = normalizeClickUpTaskType(subtask.type) === "poc" ? "dev" : parentBranchValue;
|
|
9061
|
-
const agentMetadata = buildClickUpSubtaskAgentMetadata({ parentTaskId: parentId, subtask, branch, prTarget, titleSlug });
|
|
9031
|
+
const agentMetadata = buildClickUpSubtaskAgentMetadata({ parentTaskId: parentId, subtask, branch, prTarget, titleSlug, parentBranch: parentBranchValue });
|
|
9062
9032
|
const descriptionParts = [
|
|
9063
9033
|
subtask.description,
|
|
9064
9034
|
subtask.acceptanceCriteria.length ? ["Acceptance Criteria:", ...subtask.acceptanceCriteria.map((item) => `- ${item}`)].join("\n") : ""
|
|
@@ -9236,27 +9206,52 @@ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
|
|
|
9236
9206
|
return null;
|
|
9237
9207
|
}
|
|
9238
9208
|
}
|
|
9209
|
+
function clickUpGitRefExists(baseWorktree, ref, runGitFn = runGit) {
|
|
9210
|
+
try {
|
|
9211
|
+
runGitFn(baseWorktree, ["rev-parse", "--verify", ref]);
|
|
9212
|
+
return true;
|
|
9213
|
+
} catch {
|
|
9214
|
+
return false;
|
|
9215
|
+
}
|
|
9216
|
+
}
|
|
9217
|
+
function resolveClickUpDevStartPoint(baseWorktree, runGitFn = runGit) {
|
|
9218
|
+
return clickUpGitRefExists(baseWorktree, "dev", runGitFn) ? "dev" : "origin/dev";
|
|
9219
|
+
}
|
|
9220
|
+
function addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn = runGit } = {}) {
|
|
9221
|
+
if (clickUpGitRefExists(baseWorktree, branch, runGitFn)) {
|
|
9222
|
+
runGitFn(baseWorktree, ["worktree", "add", worktreePath, branch]);
|
|
9223
|
+
} else {
|
|
9224
|
+
runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9239
9227
|
function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
|
|
9240
9228
|
const effectiveParent = parentTaskId || taskId;
|
|
9229
|
+
const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9230
|
+
const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
|
|
9241
9231
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9242
9232
|
const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
|
|
9243
|
-
if (existing) return { ...existing, branch };
|
|
9233
|
+
if (existing) return { ...existing, branch, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
|
|
9244
9234
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9245
|
-
if (fs5.existsSync(worktreePath)) return { branch, worktree: path5.resolve(worktreePath), reused: true };
|
|
9235
|
+
if (fs5.existsSync(worktreePath)) return { branch, worktree: path5.resolve(worktreePath), reused: true, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
|
|
9246
9236
|
if (allowNonGitFallback) {
|
|
9247
9237
|
fs5.mkdirSync(worktreePath, { recursive: true });
|
|
9248
|
-
return { branch, worktree: path5.resolve(worktreePath), reused: false, fallback: "non_git" };
|
|
9238
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, fallback: "non_git", parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", startPoint: parentBranch || "dev" };
|
|
9249
9239
|
}
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9240
|
+
let parentBootstrap = null;
|
|
9241
|
+
if (isSubtask) {
|
|
9242
|
+
const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
|
|
9243
|
+
if (!fs5.existsSync(parentWorktree)) {
|
|
9244
|
+
const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9245
|
+
const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
|
|
9246
|
+
addClickUpWorktreeForBranch({ baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, runGitFn });
|
|
9247
|
+
parentBootstrap = { branch: parentBranch, worktree: path5.resolve(parentWorktree), startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists };
|
|
9248
|
+
} else {
|
|
9249
|
+
parentBootstrap = { branch: parentBranch, worktree: path5.resolve(parentWorktree), reused: true };
|
|
9256
9250
|
}
|
|
9257
|
-
}
|
|
9258
|
-
|
|
9259
|
-
|
|
9251
|
+
}
|
|
9252
|
+
const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9253
|
+
addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn });
|
|
9254
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", parentBootstrap: parentBootstrap || void 0 };
|
|
9260
9255
|
}
|
|
9261
9256
|
function normalizeClickUpDefinitionDocParent(parent = {}) {
|
|
9262
9257
|
const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
|
|
@@ -9278,13 +9273,22 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9278
9273
|
if (!description) errors.push("taskDescription is required for the initial ClickUp task description rewrite.");
|
|
9279
9274
|
if (errors.length > 0) return clickUpPayloadValidationError(errors);
|
|
9280
9275
|
const effectiveParent = parentTaskId || taskId;
|
|
9276
|
+
const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9277
|
+
const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
|
|
9281
9278
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9282
9279
|
const worktree = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9280
|
+
const prTarget = isSubtask ? parentBranch : "dev";
|
|
9281
|
+
const startFrom = isSubtask ? parentBranch : "dev";
|
|
9283
9282
|
const estimate = preEstimateClickUpWork({ complexity, filesChanged, acceptanceCriteria, unknowns });
|
|
9284
9283
|
const metadata = mergeClickUpAgentMetadata(existingAgentMetadata, {
|
|
9285
9284
|
task: {
|
|
9285
|
+
parent_task_id: isSubtask ? effectiveParent : void 0,
|
|
9286
|
+
parent_branch: parentBranch || void 0,
|
|
9286
9287
|
branch,
|
|
9288
|
+
subtask_branch: isSubtask ? branch : void 0,
|
|
9287
9289
|
worktree,
|
|
9290
|
+
subtask_worktree: isSubtask ? worktree : void 0,
|
|
9291
|
+
pr_target: prTarget,
|
|
9288
9292
|
mirror_task_path: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
9289
9293
|
evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
|
|
9290
9294
|
delivery_evidence_path: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
|
|
@@ -9297,17 +9301,18 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9297
9301
|
dryRun: true,
|
|
9298
9302
|
required_first_actions: [
|
|
9299
9303
|
"Create or reuse the task-specific branch/worktree before planning or writing local .optima mirrors.",
|
|
9300
|
-
`Use branch ${branch} from
|
|
9304
|
+
`Use branch ${branch} from ${startFrom} and worktree ${worktree} for this task workspace.`,
|
|
9301
9305
|
"Write planning state, .optima/tasks/current.md, task mirrors, and local evidence only inside the task worktree.",
|
|
9302
9306
|
`Write merge-trackable final evidence under ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}; .optima remains local mirror/staging.`,
|
|
9303
9307
|
"Do not update the principal dev workspace .optima/tasks/current.md for ClickUp task planning.",
|
|
9304
9308
|
"If another active task is present in current.md, move to or create this task worktree instead of stopping."
|
|
9305
9309
|
],
|
|
9306
9310
|
git: {
|
|
9307
|
-
startFrom
|
|
9311
|
+
startFrom,
|
|
9308
9312
|
branch,
|
|
9309
9313
|
worktree,
|
|
9310
|
-
|
|
9314
|
+
parentBranch: parentBranch || void 0,
|
|
9315
|
+
prTarget
|
|
9311
9316
|
},
|
|
9312
9317
|
mirror: {
|
|
9313
9318
|
taskPath: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
@@ -9319,7 +9324,7 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9319
9324
|
wouldCreate: true
|
|
9320
9325
|
},
|
|
9321
9326
|
clickup: {
|
|
9322
|
-
comment: `Starting task from
|
|
9327
|
+
comment: `Starting task from ${startFrom}. Branch: ${branch}. Worktree: ${worktree}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
|
|
9323
9328
|
description,
|
|
9324
9329
|
fields: {
|
|
9325
9330
|
Definition: definitionContent,
|
|
@@ -9592,6 +9597,8 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9592
9597
|
log: normalizeClickUpWebhookLogLevel(raw.log),
|
|
9593
9598
|
opencode: {
|
|
9594
9599
|
baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl),
|
|
9600
|
+
promptDelivery: ["http", "direct"].includes(String(opencode.prompt_delivery || opencode.promptDelivery || raw.prompt_delivery || raw.promptDelivery || "").trim().toLowerCase()) ? "http" : "sdk",
|
|
9601
|
+
acceptPromptAdmission: opencode.accept_prompt_admission === true || opencode.acceptPromptAdmission === true || raw.accept_prompt_admission === true || raw.acceptPromptAdmission === true,
|
|
9595
9602
|
startupReconciliationDelayMs: normalizeNonNegativeInteger(
|
|
9596
9603
|
opencode.startup_reconciliation_delay_ms ?? opencode.startupReconciliationDelayMs ?? raw.startup_reconciliation_delay_ms ?? raw.startupReconciliationDelayMs,
|
|
9597
9604
|
CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
|
|
@@ -10561,6 +10568,16 @@ async function probeOpenCodeSessionControl(client, { directory, agent, omitAgent
|
|
|
10561
10568
|
const rawMessages = await readOpenCodeSessionMessages(client, { sessionId: create.session_id, directory, limit: 50 });
|
|
10562
10569
|
if (!rawMessages) throw new Error("OpenCode client does not expose session.messages.");
|
|
10563
10570
|
const fullMessageText = rawMessages.map(openCodeMessageText).join("\n");
|
|
10571
|
+
const respondingAgents = [...new Set(rawMessages.filter((message) => normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant").flatMap((message) => [normalizeOpenCodeMessageAgent(message), normalizeOpenCodeMessageMode(message)]).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
10572
|
+
const requestedAgent = normalizeLooseToken(agent);
|
|
10573
|
+
const assistantVisible = rawMessages.some((message) => normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant");
|
|
10574
|
+
const markerVisible = fullMessageText.includes(marker);
|
|
10575
|
+
const deliveryVerified = markerVisible && assistantVisible;
|
|
10576
|
+
const requestedAgentVerified = requestedAgent ? deliveryVerified && rawMessages.some((message) => {
|
|
10577
|
+
const messageAgent = normalizeLooseToken(normalizeOpenCodeMessageAgent(message));
|
|
10578
|
+
const messageMode = normalizeLooseToken(normalizeOpenCodeMessageMode(message));
|
|
10579
|
+
return normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant" && (messageAgent === requestedAgent || messageMode === requestedAgent);
|
|
10580
|
+
}) : null;
|
|
10564
10581
|
const messages = {
|
|
10565
10582
|
ok: true,
|
|
10566
10583
|
session_id: create.session_id,
|
|
@@ -10578,8 +10595,11 @@ async function probeOpenCodeSessionControl(client, { directory, agent, omitAgent
|
|
|
10578
10595
|
marker_length: marker.length,
|
|
10579
10596
|
marker_truncated: requestedMarker.length > marker.length,
|
|
10580
10597
|
marker_hash: hashOpenCodeSessionText(marker),
|
|
10581
|
-
marker_visible:
|
|
10582
|
-
assistant_visible:
|
|
10598
|
+
marker_visible: markerVisible,
|
|
10599
|
+
assistant_visible: assistantVisible,
|
|
10600
|
+
delivery_verified: deliveryVerified,
|
|
10601
|
+
requested_agent_verified: requestedAgentVerified,
|
|
10602
|
+
responding_agents: respondingAgents,
|
|
10583
10603
|
create,
|
|
10584
10604
|
prompt,
|
|
10585
10605
|
messages
|
|
@@ -10647,9 +10667,9 @@ function openCodeBlockingPromptVerification(result, sessionId) {
|
|
|
10647
10667
|
if (parts.length > 0 || messageId) return { ok: true, method: parts.length > 0 ? "blocking_prompt_parts" : "blocking_prompt_message", messageId: messageId ? String(messageId) : null, sessionId: deliveredSessionId ? String(deliveredSessionId) : String(sessionId), parts: parts.length };
|
|
10648
10668
|
return null;
|
|
10649
10669
|
}
|
|
10650
|
-
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
10670
|
+
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, directPrompt = false, acceptPromptAdmission = false, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
10651
10671
|
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, directory, limit: 50 }).catch(() => null);
|
|
10652
|
-
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, allowDirectFallback:
|
|
10672
|
+
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: directPrompt, allowDirectFallback: directPrompt });
|
|
10653
10673
|
let blockingPromptVerification = null;
|
|
10654
10674
|
let admissionVerification = null;
|
|
10655
10675
|
try {
|
|
@@ -10662,6 +10682,9 @@ async function deliverClickUpSessionEventWithVerification({ openCodeClient, send
|
|
|
10662
10682
|
return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
|
|
10663
10683
|
}
|
|
10664
10684
|
if (blockingPromptVerification) return { ok: true, verification: blockingPromptVerification, admissionVerification: null, fallback: false };
|
|
10685
|
+
if (admissionVerification && acceptPromptAdmission) {
|
|
10686
|
+
return { ok: true, verification: admissionVerification, admissionVerification, fallback: false, admissionOnly: true };
|
|
10687
|
+
}
|
|
10665
10688
|
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, directory, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
10666
10689
|
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
|
|
10667
10690
|
if (admissionVerification) {
|
|
@@ -10699,6 +10722,8 @@ async function recoverClickUpPmSession({ openCodeClient, sendSessionEvent, click
|
|
|
10699
10722
|
text: prompt,
|
|
10700
10723
|
directory: taskRoute.worktree,
|
|
10701
10724
|
opencodeBaseUrl: config.opencode?.baseUrl,
|
|
10725
|
+
directPrompt: config.opencode?.promptDelivery === "http",
|
|
10726
|
+
acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true,
|
|
10702
10727
|
eventMarkers,
|
|
10703
10728
|
verifySessionEventDelivery,
|
|
10704
10729
|
applyBlockerOnFailure: false
|
|
@@ -10974,8 +10999,13 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
10974
10999
|
}
|
|
10975
11000
|
const deliveryEvidencePath = deliveryEvidencePathForClickUpTask(taskId);
|
|
10976
11001
|
const routingMetadata = {
|
|
11002
|
+
parent_task_id: subtaskId ? parentTaskId : void 0,
|
|
11003
|
+
parent_branch: taskRoute.parentBranch,
|
|
10977
11004
|
branch: taskRoute.branch,
|
|
11005
|
+
subtask_branch: subtaskId ? taskRoute.branch : void 0,
|
|
10978
11006
|
worktree: taskRoute.worktree,
|
|
11007
|
+
subtask_worktree: subtaskId ? taskRoute.worktree : void 0,
|
|
11008
|
+
pr_target: taskRoute.prTarget || (subtaskId ? taskRoute.parentBranch : "dev"),
|
|
10979
11009
|
delivery_evidence_path: deliveryEvidencePath,
|
|
10980
11010
|
evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId)}/SUMMARY.md`
|
|
10981
11011
|
};
|
|
@@ -11001,7 +11031,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11001
11031
|
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
|
|
11002
11032
|
throw error;
|
|
11003
11033
|
}
|
|
11004
|
-
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
11034
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
11005
11035
|
if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
11006
11036
|
const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
|
|
11007
11037
|
await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
|
|
@@ -11012,7 +11042,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11012
11042
|
const sessionId = String(existingSessionId);
|
|
11013
11043
|
if (await sessionExists(openCodeClient, sessionId)) {
|
|
11014
11044
|
if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
|
|
11015
|
-
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11045
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11016
11046
|
if (!delivery.ok) {
|
|
11017
11047
|
const recovery2 = await recoverClickUpPmSession({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, staleSessionId: sessionId, sessionTitle, taskRoute, metadataWithRouting, config, prompt, eventMarkers: [taskId, eventType], deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, eventKey, createSession, verifySessionEventDelivery });
|
|
11018
11048
|
return finish(recovery2);
|
|
@@ -12247,7 +12277,33 @@ function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
|
12247
12277
|
function shouldRegisterWorkflowProductManager(options = {}, worktree = process.cwd()) {
|
|
12248
12278
|
if (options.clickUpWebhookActive === true) return true;
|
|
12249
12279
|
const validation = options.clickUpWebhookValidation;
|
|
12250
|
-
|
|
12280
|
+
if (validation?.complete !== true || validation?.ok === false) return false;
|
|
12281
|
+
const basePath = validation.config?.basePath;
|
|
12282
|
+
return isSameOrNestedPath(worktree, basePath) || isClickUpDerivedWorktreeSibling(worktree, basePath);
|
|
12283
|
+
}
|
|
12284
|
+
function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
12285
|
+
if (typeof candidate !== "string" || typeof basePath !== "string" || !candidate.trim() || !basePath.trim()) return false;
|
|
12286
|
+
const resolvedCandidate = path5.resolve(candidate);
|
|
12287
|
+
const resolvedBase = path5.resolve(basePath);
|
|
12288
|
+
const baseParent = path5.dirname(resolvedBase);
|
|
12289
|
+
if (path5.dirname(resolvedCandidate) !== baseParent) return false;
|
|
12290
|
+
const baseName = path5.basename(resolvedBase);
|
|
12291
|
+
const candidateName = path5.basename(resolvedCandidate);
|
|
12292
|
+
if (!candidateName.startsWith(`${baseName}-`)) return false;
|
|
12293
|
+
const branchSlug = candidateName.slice(baseName.length + 1);
|
|
12294
|
+
const parts = branchSlug.split("-").filter(Boolean);
|
|
12295
|
+
if (parts.length < 2) return false;
|
|
12296
|
+
if (!(/* @__PURE__ */ new Set(["tarea", "bug", "doc", "poc", "idea"])).has(parts[0])) return false;
|
|
12297
|
+
if (!parts.slice(1).every((part) => /^[a-z0-9][a-z0-9-]*$/.test(part))) return false;
|
|
12298
|
+
try {
|
|
12299
|
+
const candidateStat = fs5.lstatSync(resolvedCandidate);
|
|
12300
|
+
if (!candidateStat.isDirectory() || candidateStat.isSymbolicLink()) return false;
|
|
12301
|
+
const realCandidate = fs5.realpathSync.native(resolvedCandidate);
|
|
12302
|
+
const realBaseParent = fs5.realpathSync.native(baseParent);
|
|
12303
|
+
return path5.dirname(realCandidate) === realBaseParent && path5.basename(realCandidate) === candidateName;
|
|
12304
|
+
} catch {
|
|
12305
|
+
return false;
|
|
12306
|
+
}
|
|
12251
12307
|
}
|
|
12252
12308
|
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
12253
12309
|
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
@@ -12259,6 +12315,9 @@ function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktr
|
|
|
12259
12315
|
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
12260
12316
|
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
12261
12317
|
}
|
|
12318
|
+
if (clickUpBasePath && isClickUpDerivedWorktreeSibling(requested, clickUpBasePath)) {
|
|
12319
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_derived_worktree", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
12320
|
+
}
|
|
12262
12321
|
return {
|
|
12263
12322
|
ok: false,
|
|
12264
12323
|
error: `Directory '${requested}' is outside the safe worktree '${safe.worktree}' and configured ClickUp base path.`
|
|
@@ -13090,7 +13149,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
13090
13149
|
}
|
|
13091
13150
|
};
|
|
13092
13151
|
}
|
|
13093
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
13152
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
13094
13153
|
export {
|
|
13095
13154
|
OptimaPlugin as default
|
|
13096
13155
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -633,8 +633,6 @@ var require_Alias = __commonJS({
|
|
|
633
633
|
* instance of the `source` anchor before this node.
|
|
634
634
|
*/
|
|
635
635
|
resolve(doc, ctx) {
|
|
636
|
-
if (ctx?.maxAliasCount === 0)
|
|
637
|
-
throw new ReferenceError("Alias resolution is disabled");
|
|
638
636
|
let nodes;
|
|
639
637
|
if (ctx?.aliasResolveCache) {
|
|
640
638
|
nodes = ctx.aliasResolveCache;
|
|
@@ -1434,7 +1432,6 @@ var require_stringify = __commonJS({
|
|
|
1434
1432
|
nullStr: "null",
|
|
1435
1433
|
simpleKeys: false,
|
|
1436
1434
|
singleQuote: null,
|
|
1437
|
-
trailingComma: false,
|
|
1438
1435
|
trueStr: "true",
|
|
1439
1436
|
verifyAliasOrder: true
|
|
1440
1437
|
}, doc.schema.toStringOptions, options);
|
|
@@ -1707,18 +1704,18 @@ var require_merge = __commonJS({
|
|
|
1707
1704
|
};
|
|
1708
1705
|
var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default);
|
|
1709
1706
|
function addMergeToJSMap(ctx, map, value) {
|
|
1710
|
-
|
|
1711
|
-
if (identity.isSeq(
|
|
1712
|
-
for (const it of
|
|
1707
|
+
value = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
|
|
1708
|
+
if (identity.isSeq(value))
|
|
1709
|
+
for (const it of value.items)
|
|
1713
1710
|
mergeValue(ctx, map, it);
|
|
1714
|
-
else if (Array.isArray(
|
|
1715
|
-
for (const it of
|
|
1711
|
+
else if (Array.isArray(value))
|
|
1712
|
+
for (const it of value)
|
|
1716
1713
|
mergeValue(ctx, map, it);
|
|
1717
1714
|
else
|
|
1718
|
-
mergeValue(ctx, map,
|
|
1715
|
+
mergeValue(ctx, map, value);
|
|
1719
1716
|
}
|
|
1720
1717
|
function mergeValue(ctx, map, value) {
|
|
1721
|
-
const source =
|
|
1718
|
+
const source = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
|
|
1722
1719
|
if (!identity.isMap(source))
|
|
1723
1720
|
throw new Error("Merge sources must be maps or map aliases");
|
|
1724
1721
|
const srcMap = source.toJSON(null, ctx, Map);
|
|
@@ -1739,9 +1736,6 @@ var require_merge = __commonJS({
|
|
|
1739
1736
|
}
|
|
1740
1737
|
return map;
|
|
1741
1738
|
}
|
|
1742
|
-
function resolveAliasValue(ctx, value) {
|
|
1743
|
-
return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
|
|
1744
|
-
}
|
|
1745
1739
|
exports.addMergeToJSMap = addMergeToJSMap;
|
|
1746
1740
|
exports.isMergeKey = isMergeKey;
|
|
1747
1741
|
exports.merge = merge;
|
|
@@ -1955,19 +1949,12 @@ ${indent}${line}` : "\n";
|
|
|
1955
1949
|
if (comment)
|
|
1956
1950
|
reqNewline = true;
|
|
1957
1951
|
let str = stringify.stringify(item, itemCtx, () => comment = null);
|
|
1958
|
-
|
|
1959
|
-
if (i < items.length - 1) {
|
|
1952
|
+
if (i < items.length - 1)
|
|
1960
1953
|
str += ",";
|
|
1961
|
-
} else if (ctx.options.trailingComma) {
|
|
1962
|
-
if (ctx.options.lineWidth > 0) {
|
|
1963
|
-
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
1964
|
-
}
|
|
1965
|
-
if (reqNewline) {
|
|
1966
|
-
str += ",";
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
1954
|
if (comment)
|
|
1970
1955
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
1956
|
+
if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
|
|
1957
|
+
reqNewline = true;
|
|
1971
1958
|
lines.push(str);
|
|
1972
1959
|
linesAtValue = lines.length;
|
|
1973
1960
|
}
|
|
@@ -2379,7 +2366,7 @@ var require_stringifyNumber = __commonJS({
|
|
|
2379
2366
|
if (!isFinite(num))
|
|
2380
2367
|
return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
|
|
2381
2368
|
let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
2382
|
-
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") &&
|
|
2369
|
+
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^\d/.test(n)) {
|
|
2383
2370
|
let i = n.indexOf(".");
|
|
2384
2371
|
if (i < 0) {
|
|
2385
2372
|
i = n.length;
|
|
@@ -4751,7 +4738,7 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4751
4738
|
while (next === " " || next === " ")
|
|
4752
4739
|
next = source[++i + 1];
|
|
4753
4740
|
} else if (next === "x" || next === "u" || next === "U") {
|
|
4754
|
-
const length =
|
|
4741
|
+
const length = { x: 2, u: 4, U: 8 }[next];
|
|
4755
4742
|
res += parseCharCode(source, i + 1, length, onError);
|
|
4756
4743
|
i += length;
|
|
4757
4744
|
} else {
|
|
@@ -4826,13 +4813,12 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4826
4813
|
const cc = source.substr(offset, length);
|
|
4827
4814
|
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
|
|
4828
4815
|
const code = ok ? parseInt(cc, 16) : NaN;
|
|
4829
|
-
|
|
4830
|
-
return String.fromCodePoint(code);
|
|
4831
|
-
} catch {
|
|
4816
|
+
if (isNaN(code)) {
|
|
4832
4817
|
const raw = source.substr(offset - 2, length + 2);
|
|
4833
4818
|
onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
|
|
4834
4819
|
return raw;
|
|
4835
4820
|
}
|
|
4821
|
+
return String.fromCodePoint(code);
|
|
4836
4822
|
}
|
|
4837
4823
|
exports.resolveFlowScalar = resolveFlowScalar;
|
|
4838
4824
|
}
|
|
@@ -4982,22 +4968,17 @@ var require_compose_node = __commonJS({
|
|
|
4982
4968
|
case "block-map":
|
|
4983
4969
|
case "block-seq":
|
|
4984
4970
|
case "flow-collection":
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
node.anchor = anchor.source.substring(1);
|
|
4989
|
-
} catch (error) {
|
|
4990
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4991
|
-
onError(token, "RESOURCE_EXHAUSTION", message);
|
|
4992
|
-
}
|
|
4971
|
+
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
|
|
4972
|
+
if (anchor)
|
|
4973
|
+
node.anchor = anchor.source.substring(1);
|
|
4993
4974
|
break;
|
|
4994
4975
|
default: {
|
|
4995
4976
|
const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
|
|
4996
4977
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
4978
|
+
node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
|
|
4997
4979
|
isSrcToken = false;
|
|
4998
4980
|
}
|
|
4999
4981
|
}
|
|
5000
|
-
node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
|
|
5001
4982
|
if (anchor && node.anchor === "")
|
|
5002
4983
|
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
|
|
5003
4984
|
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
|
|
@@ -5182,10 +5163,8 @@ ${cb}` : comment;
|
|
|
5182
5163
|
}
|
|
5183
5164
|
}
|
|
5184
5165
|
if (afterDoc) {
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
for (let i = 0; i < this.warnings.length; ++i)
|
|
5188
|
-
doc.warnings.push(this.warnings[i]);
|
|
5166
|
+
Array.prototype.push.apply(doc.errors, this.errors);
|
|
5167
|
+
Array.prototype.push.apply(doc.warnings, this.warnings);
|
|
5189
5168
|
} else {
|
|
5190
5169
|
doc.errors = this.errors;
|
|
5191
5170
|
doc.warnings = this.warnings;
|
|
@@ -5918,7 +5897,7 @@ var require_lexer = __commonJS({
|
|
|
5918
5897
|
const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
|
|
5919
5898
|
this.indentNext = this.indentValue + 1;
|
|
5920
5899
|
this.indentValue += n;
|
|
5921
|
-
return
|
|
5900
|
+
return yield* this.parseBlockStart();
|
|
5922
5901
|
}
|
|
5923
5902
|
return "doc";
|
|
5924
5903
|
}
|
|
@@ -6217,38 +6196,28 @@ var require_lexer = __commonJS({
|
|
|
6217
6196
|
return 0;
|
|
6218
6197
|
}
|
|
6219
6198
|
*pushIndicators() {
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
|
|
6239
|
-
if (!inFlow)
|
|
6240
|
-
this.indentNext = this.indentValue + 1;
|
|
6241
|
-
else if (this.flowKey)
|
|
6242
|
-
this.flowKey = false;
|
|
6243
|
-
n += yield* this.pushCount(1);
|
|
6244
|
-
n += yield* this.pushSpaces(true);
|
|
6245
|
-
continue loop;
|
|
6246
|
-
}
|
|
6199
|
+
switch (this.charAt(0)) {
|
|
6200
|
+
case "!":
|
|
6201
|
+
return (yield* this.pushTag()) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
|
|
6202
|
+
case "&":
|
|
6203
|
+
return (yield* this.pushUntil(isNotAnchorChar)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
|
|
6204
|
+
case "-":
|
|
6205
|
+
// this is an error
|
|
6206
|
+
case "?":
|
|
6207
|
+
// this is an error outside flow collections
|
|
6208
|
+
case ":": {
|
|
6209
|
+
const inFlow = this.flowLevel > 0;
|
|
6210
|
+
const ch1 = this.charAt(1);
|
|
6211
|
+
if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
|
|
6212
|
+
if (!inFlow)
|
|
6213
|
+
this.indentNext = this.indentValue + 1;
|
|
6214
|
+
else if (this.flowKey)
|
|
6215
|
+
this.flowKey = false;
|
|
6216
|
+
return (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
|
|
6247
6217
|
}
|
|
6248
6218
|
}
|
|
6249
|
-
break loop;
|
|
6250
6219
|
}
|
|
6251
|
-
return
|
|
6220
|
+
return 0;
|
|
6252
6221
|
}
|
|
6253
6222
|
*pushTag() {
|
|
6254
6223
|
if (this.charAt(1) === "<") {
|
|
@@ -6407,13 +6376,6 @@ var require_parser = __commonJS({
|
|
|
6407
6376
|
}
|
|
6408
6377
|
return prev.splice(i, prev.length);
|
|
6409
6378
|
}
|
|
6410
|
-
function arrayPushArray(target, source) {
|
|
6411
|
-
if (source.length < 1e5)
|
|
6412
|
-
Array.prototype.push.apply(target, source);
|
|
6413
|
-
else
|
|
6414
|
-
for (let i = 0; i < source.length; ++i)
|
|
6415
|
-
target.push(source[i]);
|
|
6416
|
-
}
|
|
6417
6379
|
function fixFlowSeqItems(fc) {
|
|
6418
6380
|
if (fc.start.type === "flow-seq-start") {
|
|
6419
6381
|
for (const it of fc.items) {
|
|
@@ -6423,11 +6385,11 @@ var require_parser = __commonJS({
|
|
|
6423
6385
|
delete it.key;
|
|
6424
6386
|
if (isFlowToken(it.value)) {
|
|
6425
6387
|
if (it.value.end)
|
|
6426
|
-
|
|
6388
|
+
Array.prototype.push.apply(it.value.end, it.sep);
|
|
6427
6389
|
else
|
|
6428
6390
|
it.value.end = it.sep;
|
|
6429
6391
|
} else
|
|
6430
|
-
|
|
6392
|
+
Array.prototype.push.apply(it.start, it.sep);
|
|
6431
6393
|
delete it.sep;
|
|
6432
6394
|
}
|
|
6433
6395
|
}
|
|
@@ -6782,7 +6744,7 @@ var require_parser = __commonJS({
|
|
|
6782
6744
|
const prev = map.items[map.items.length - 2];
|
|
6783
6745
|
const end = prev?.value?.end;
|
|
6784
6746
|
if (Array.isArray(end)) {
|
|
6785
|
-
|
|
6747
|
+
Array.prototype.push.apply(end, it.start);
|
|
6786
6748
|
end.push(this.sourceToken);
|
|
6787
6749
|
map.items.pop();
|
|
6788
6750
|
return;
|
|
@@ -6970,7 +6932,7 @@ var require_parser = __commonJS({
|
|
|
6970
6932
|
const prev = seq.items[seq.items.length - 2];
|
|
6971
6933
|
const end = prev?.value?.end;
|
|
6972
6934
|
if (Array.isArray(end)) {
|
|
6973
|
-
|
|
6935
|
+
Array.prototype.push.apply(end, it.start);
|
|
6974
6936
|
end.push(this.sourceToken);
|
|
6975
6937
|
seq.items.pop();
|
|
6976
6938
|
return;
|
|
@@ -8848,7 +8810,13 @@ function deriveClickUpBranchName({ taskType, parentTaskId, subtaskId, taskId } =
|
|
|
8848
8810
|
throw new Error("taskType and parentTaskId/taskId are required to derive a ClickUp branch name.");
|
|
8849
8811
|
}
|
|
8850
8812
|
if (typeSlug === "poc") return ["poc", parentSlug].join("/");
|
|
8851
|
-
return
|
|
8813
|
+
if (subtaskSlug) return `${typeSlug}/${parentSlug}-subtask-${subtaskSlug}`;
|
|
8814
|
+
return [typeSlug, parentSlug].join("/");
|
|
8815
|
+
}
|
|
8816
|
+
function isClickUpSubtaskRoute({ taskType, parentTaskId, subtaskId, taskId } = {}) {
|
|
8817
|
+
return Boolean(
|
|
8818
|
+
normalizeClickUpTaskType(taskType) !== "poc" && subtaskId && parentTaskId && String(parentTaskId) !== String(taskId || subtaskId)
|
|
8819
|
+
);
|
|
8852
8820
|
}
|
|
8853
8821
|
function deriveClickUpPrTarget({ isRelease = false, parentTaskId, parentBranch, parentTaskType, taskType, devBranch = "dev", mainBranch = "main" } = {}) {
|
|
8854
8822
|
if (isRelease) return mainBranch;
|
|
@@ -9037,15 +9005,17 @@ function deriveClickUpPendingSubtaskBranch({ taskType, parentTaskId, title } = {
|
|
|
9037
9005
|
const parentSlug = branchSafeClickUpId(parentTaskId);
|
|
9038
9006
|
if (!typeSlug || !parentSlug) throw new Error("taskType and parentTaskId are required to derive a pending subtask branch name.");
|
|
9039
9007
|
if (typeSlug === "poc") return ["poc", parentSlug].join("/");
|
|
9040
|
-
return
|
|
9008
|
+
return `${typeSlug}/${parentSlug}-pending-${normalizeLooseToken(title) || "subtask"}`;
|
|
9041
9009
|
}
|
|
9042
|
-
function buildClickUpSubtaskAgentMetadata({ parentTaskId, subtask, branch, prTarget, titleSlug } = {}) {
|
|
9010
|
+
function buildClickUpSubtaskAgentMetadata({ parentTaskId, subtask, branch, prTarget, titleSlug, parentBranch } = {}) {
|
|
9043
9011
|
return mergeClickUpAgentMetadata("", {
|
|
9044
9012
|
subtask: {
|
|
9045
9013
|
parent_task_id: parentTaskId,
|
|
9014
|
+
parent_branch: parentBranch || prTarget,
|
|
9046
9015
|
title: subtask.title,
|
|
9047
9016
|
title_slug: titleSlug,
|
|
9048
9017
|
branch,
|
|
9018
|
+
subtask_branch: branch,
|
|
9049
9019
|
pr_target: prTarget,
|
|
9050
9020
|
pending_clickup_subtask_id: true,
|
|
9051
9021
|
owner_role: subtask.ownerRole,
|
|
@@ -9065,7 +9035,7 @@ function buildClickUpCreateSubtasksPayload({ parentTaskId, markdown = "", source
|
|
|
9065
9035
|
const titleSlug = normalizeLooseToken(subtask.title) || "subtask";
|
|
9066
9036
|
const branch = subtask.branch || deriveClickUpPendingSubtaskBranch({ taskType: subtask.type, parentTaskId: parentId, title: subtask.title });
|
|
9067
9037
|
const prTarget = normalizeClickUpTaskType(subtask.type) === "poc" ? "dev" : parentBranchValue;
|
|
9068
|
-
const agentMetadata = buildClickUpSubtaskAgentMetadata({ parentTaskId: parentId, subtask, branch, prTarget, titleSlug });
|
|
9038
|
+
const agentMetadata = buildClickUpSubtaskAgentMetadata({ parentTaskId: parentId, subtask, branch, prTarget, titleSlug, parentBranch: parentBranchValue });
|
|
9069
9039
|
const descriptionParts = [
|
|
9070
9040
|
subtask.description,
|
|
9071
9041
|
subtask.acceptanceCriteria.length ? ["Acceptance Criteria:", ...subtask.acceptanceCriteria.map((item) => `- ${item}`)].join("\n") : ""
|
|
@@ -9243,27 +9213,52 @@ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
|
|
|
9243
9213
|
return null;
|
|
9244
9214
|
}
|
|
9245
9215
|
}
|
|
9216
|
+
function clickUpGitRefExists(baseWorktree, ref, runGitFn = runGit) {
|
|
9217
|
+
try {
|
|
9218
|
+
runGitFn(baseWorktree, ["rev-parse", "--verify", ref]);
|
|
9219
|
+
return true;
|
|
9220
|
+
} catch {
|
|
9221
|
+
return false;
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
function resolveClickUpDevStartPoint(baseWorktree, runGitFn = runGit) {
|
|
9225
|
+
return clickUpGitRefExists(baseWorktree, "dev", runGitFn) ? "dev" : "origin/dev";
|
|
9226
|
+
}
|
|
9227
|
+
function addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn = runGit } = {}) {
|
|
9228
|
+
if (clickUpGitRefExists(baseWorktree, branch, runGitFn)) {
|
|
9229
|
+
runGitFn(baseWorktree, ["worktree", "add", worktreePath, branch]);
|
|
9230
|
+
} else {
|
|
9231
|
+
runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
|
|
9232
|
+
}
|
|
9233
|
+
}
|
|
9246
9234
|
function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
|
|
9247
9235
|
const effectiveParent = parentTaskId || taskId;
|
|
9236
|
+
const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9237
|
+
const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
|
|
9248
9238
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9249
9239
|
const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
|
|
9250
|
-
if (existing) return { ...existing, branch };
|
|
9240
|
+
if (existing) return { ...existing, branch, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
|
|
9251
9241
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9252
|
-
if (fs5.existsSync(worktreePath)) return { branch, worktree: path5.resolve(worktreePath), reused: true };
|
|
9242
|
+
if (fs5.existsSync(worktreePath)) return { branch, worktree: path5.resolve(worktreePath), reused: true, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
|
|
9253
9243
|
if (allowNonGitFallback) {
|
|
9254
9244
|
fs5.mkdirSync(worktreePath, { recursive: true });
|
|
9255
|
-
return { branch, worktree: path5.resolve(worktreePath), reused: false, fallback: "non_git" };
|
|
9245
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, fallback: "non_git", parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", startPoint: parentBranch || "dev" };
|
|
9256
9246
|
}
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9247
|
+
let parentBootstrap = null;
|
|
9248
|
+
if (isSubtask) {
|
|
9249
|
+
const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
|
|
9250
|
+
if (!fs5.existsSync(parentWorktree)) {
|
|
9251
|
+
const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9252
|
+
const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
|
|
9253
|
+
addClickUpWorktreeForBranch({ baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, runGitFn });
|
|
9254
|
+
parentBootstrap = { branch: parentBranch, worktree: path5.resolve(parentWorktree), startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists };
|
|
9255
|
+
} else {
|
|
9256
|
+
parentBootstrap = { branch: parentBranch, worktree: path5.resolve(parentWorktree), reused: true };
|
|
9263
9257
|
}
|
|
9264
|
-
}
|
|
9265
|
-
|
|
9266
|
-
|
|
9258
|
+
}
|
|
9259
|
+
const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9260
|
+
addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn });
|
|
9261
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", parentBootstrap: parentBootstrap || void 0 };
|
|
9267
9262
|
}
|
|
9268
9263
|
function normalizeClickUpDefinitionDocParent(parent = {}) {
|
|
9269
9264
|
const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
|
|
@@ -9285,13 +9280,22 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9285
9280
|
if (!description) errors.push("taskDescription is required for the initial ClickUp task description rewrite.");
|
|
9286
9281
|
if (errors.length > 0) return clickUpPayloadValidationError(errors);
|
|
9287
9282
|
const effectiveParent = parentTaskId || taskId;
|
|
9283
|
+
const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9284
|
+
const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
|
|
9288
9285
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
9289
9286
|
const worktree = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9287
|
+
const prTarget = isSubtask ? parentBranch : "dev";
|
|
9288
|
+
const startFrom = isSubtask ? parentBranch : "dev";
|
|
9290
9289
|
const estimate = preEstimateClickUpWork({ complexity, filesChanged, acceptanceCriteria, unknowns });
|
|
9291
9290
|
const metadata = mergeClickUpAgentMetadata(existingAgentMetadata, {
|
|
9292
9291
|
task: {
|
|
9292
|
+
parent_task_id: isSubtask ? effectiveParent : void 0,
|
|
9293
|
+
parent_branch: parentBranch || void 0,
|
|
9293
9294
|
branch,
|
|
9295
|
+
subtask_branch: isSubtask ? branch : void 0,
|
|
9294
9296
|
worktree,
|
|
9297
|
+
subtask_worktree: isSubtask ? worktree : void 0,
|
|
9298
|
+
pr_target: prTarget,
|
|
9295
9299
|
mirror_task_path: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
9296
9300
|
evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
|
|
9297
9301
|
delivery_evidence_path: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
|
|
@@ -9304,17 +9308,18 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9304
9308
|
dryRun: true,
|
|
9305
9309
|
required_first_actions: [
|
|
9306
9310
|
"Create or reuse the task-specific branch/worktree before planning or writing local .optima mirrors.",
|
|
9307
|
-
`Use branch ${branch} from
|
|
9311
|
+
`Use branch ${branch} from ${startFrom} and worktree ${worktree} for this task workspace.`,
|
|
9308
9312
|
"Write planning state, .optima/tasks/current.md, task mirrors, and local evidence only inside the task worktree.",
|
|
9309
9313
|
`Write merge-trackable final evidence under ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}; .optima remains local mirror/staging.`,
|
|
9310
9314
|
"Do not update the principal dev workspace .optima/tasks/current.md for ClickUp task planning.",
|
|
9311
9315
|
"If another active task is present in current.md, move to or create this task worktree instead of stopping."
|
|
9312
9316
|
],
|
|
9313
9317
|
git: {
|
|
9314
|
-
startFrom
|
|
9318
|
+
startFrom,
|
|
9315
9319
|
branch,
|
|
9316
9320
|
worktree,
|
|
9317
|
-
|
|
9321
|
+
parentBranch: parentBranch || void 0,
|
|
9322
|
+
prTarget
|
|
9318
9323
|
},
|
|
9319
9324
|
mirror: {
|
|
9320
9325
|
taskPath: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
@@ -9326,7 +9331,7 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9326
9331
|
wouldCreate: true
|
|
9327
9332
|
},
|
|
9328
9333
|
clickup: {
|
|
9329
|
-
comment: `Starting task from
|
|
9334
|
+
comment: `Starting task from ${startFrom}. Branch: ${branch}. Worktree: ${worktree}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
|
|
9330
9335
|
description,
|
|
9331
9336
|
fields: {
|
|
9332
9337
|
Definition: definitionContent,
|
|
@@ -9599,6 +9604,8 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9599
9604
|
log: normalizeClickUpWebhookLogLevel(raw.log),
|
|
9600
9605
|
opencode: {
|
|
9601
9606
|
baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl),
|
|
9607
|
+
promptDelivery: ["http", "direct"].includes(String(opencode.prompt_delivery || opencode.promptDelivery || raw.prompt_delivery || raw.promptDelivery || "").trim().toLowerCase()) ? "http" : "sdk",
|
|
9608
|
+
acceptPromptAdmission: opencode.accept_prompt_admission === true || opencode.acceptPromptAdmission === true || raw.accept_prompt_admission === true || raw.acceptPromptAdmission === true,
|
|
9602
9609
|
startupReconciliationDelayMs: normalizeNonNegativeInteger(
|
|
9603
9610
|
opencode.startup_reconciliation_delay_ms ?? opencode.startupReconciliationDelayMs ?? raw.startup_reconciliation_delay_ms ?? raw.startupReconciliationDelayMs,
|
|
9604
9611
|
CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
|
|
@@ -10568,6 +10575,16 @@ async function probeOpenCodeSessionControl(client, { directory, agent, omitAgent
|
|
|
10568
10575
|
const rawMessages = await readOpenCodeSessionMessages(client, { sessionId: create.session_id, directory, limit: 50 });
|
|
10569
10576
|
if (!rawMessages) throw new Error("OpenCode client does not expose session.messages.");
|
|
10570
10577
|
const fullMessageText = rawMessages.map(openCodeMessageText).join("\n");
|
|
10578
|
+
const respondingAgents = [...new Set(rawMessages.filter((message) => normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant").flatMap((message) => [normalizeOpenCodeMessageAgent(message), normalizeOpenCodeMessageMode(message)]).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
10579
|
+
const requestedAgent = normalizeLooseToken(agent);
|
|
10580
|
+
const assistantVisible = rawMessages.some((message) => normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant");
|
|
10581
|
+
const markerVisible = fullMessageText.includes(marker);
|
|
10582
|
+
const deliveryVerified = markerVisible && assistantVisible;
|
|
10583
|
+
const requestedAgentVerified = requestedAgent ? deliveryVerified && rawMessages.some((message) => {
|
|
10584
|
+
const messageAgent = normalizeLooseToken(normalizeOpenCodeMessageAgent(message));
|
|
10585
|
+
const messageMode = normalizeLooseToken(normalizeOpenCodeMessageMode(message));
|
|
10586
|
+
return normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant" && (messageAgent === requestedAgent || messageMode === requestedAgent);
|
|
10587
|
+
}) : null;
|
|
10571
10588
|
const messages = {
|
|
10572
10589
|
ok: true,
|
|
10573
10590
|
session_id: create.session_id,
|
|
@@ -10585,8 +10602,11 @@ async function probeOpenCodeSessionControl(client, { directory, agent, omitAgent
|
|
|
10585
10602
|
marker_length: marker.length,
|
|
10586
10603
|
marker_truncated: requestedMarker.length > marker.length,
|
|
10587
10604
|
marker_hash: hashOpenCodeSessionText(marker),
|
|
10588
|
-
marker_visible:
|
|
10589
|
-
assistant_visible:
|
|
10605
|
+
marker_visible: markerVisible,
|
|
10606
|
+
assistant_visible: assistantVisible,
|
|
10607
|
+
delivery_verified: deliveryVerified,
|
|
10608
|
+
requested_agent_verified: requestedAgentVerified,
|
|
10609
|
+
responding_agents: respondingAgents,
|
|
10590
10610
|
create,
|
|
10591
10611
|
prompt,
|
|
10592
10612
|
messages
|
|
@@ -10654,9 +10674,9 @@ function openCodeBlockingPromptVerification(result, sessionId) {
|
|
|
10654
10674
|
if (parts.length > 0 || messageId) return { ok: true, method: parts.length > 0 ? "blocking_prompt_parts" : "blocking_prompt_message", messageId: messageId ? String(messageId) : null, sessionId: deliveredSessionId ? String(deliveredSessionId) : String(sessionId), parts: parts.length };
|
|
10655
10675
|
return null;
|
|
10656
10676
|
}
|
|
10657
|
-
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
10677
|
+
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, directPrompt = false, acceptPromptAdmission = false, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
10658
10678
|
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, directory, limit: 50 }).catch(() => null);
|
|
10659
|
-
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, allowDirectFallback:
|
|
10679
|
+
const sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: directPrompt, allowDirectFallback: directPrompt });
|
|
10660
10680
|
let blockingPromptVerification = null;
|
|
10661
10681
|
let admissionVerification = null;
|
|
10662
10682
|
try {
|
|
@@ -10669,6 +10689,9 @@ async function deliverClickUpSessionEventWithVerification({ openCodeClient, send
|
|
|
10669
10689
|
return { ok: false, action: "message_delivery_failed", reason: error.message, taskId, sessionId, fallbackAttempted: false, blockerTag: blocker2 };
|
|
10670
10690
|
}
|
|
10671
10691
|
if (blockingPromptVerification) return { ok: true, verification: blockingPromptVerification, admissionVerification: null, fallback: false };
|
|
10692
|
+
if (admissionVerification && acceptPromptAdmission) {
|
|
10693
|
+
return { ok: true, verification: admissionVerification, admissionVerification, fallback: false, admissionOnly: true };
|
|
10694
|
+
}
|
|
10672
10695
|
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, directory, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
10673
10696
|
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
|
|
10674
10697
|
if (admissionVerification) {
|
|
@@ -10706,6 +10729,8 @@ async function recoverClickUpPmSession({ openCodeClient, sendSessionEvent, click
|
|
|
10706
10729
|
text: prompt,
|
|
10707
10730
|
directory: taskRoute.worktree,
|
|
10708
10731
|
opencodeBaseUrl: config.opencode?.baseUrl,
|
|
10732
|
+
directPrompt: config.opencode?.promptDelivery === "http",
|
|
10733
|
+
acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true,
|
|
10709
10734
|
eventMarkers,
|
|
10710
10735
|
verifySessionEventDelivery,
|
|
10711
10736
|
applyBlockerOnFailure: false
|
|
@@ -10981,8 +11006,13 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
10981
11006
|
}
|
|
10982
11007
|
const deliveryEvidencePath = deliveryEvidencePathForClickUpTask(taskId);
|
|
10983
11008
|
const routingMetadata = {
|
|
11009
|
+
parent_task_id: subtaskId ? parentTaskId : void 0,
|
|
11010
|
+
parent_branch: taskRoute.parentBranch,
|
|
10984
11011
|
branch: taskRoute.branch,
|
|
11012
|
+
subtask_branch: subtaskId ? taskRoute.branch : void 0,
|
|
10985
11013
|
worktree: taskRoute.worktree,
|
|
11014
|
+
subtask_worktree: subtaskId ? taskRoute.worktree : void 0,
|
|
11015
|
+
pr_target: taskRoute.prTarget || (subtaskId ? taskRoute.parentBranch : "dev"),
|
|
10986
11016
|
delivery_evidence_path: deliveryEvidencePath,
|
|
10987
11017
|
evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId)}/SUMMARY.md`
|
|
10988
11018
|
};
|
|
@@ -11008,7 +11038,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11008
11038
|
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
|
|
11009
11039
|
throw error;
|
|
11010
11040
|
}
|
|
11011
|
-
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
11041
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery });
|
|
11012
11042
|
if (!delivery.ok) return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
|
|
11013
11043
|
const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
|
|
11014
11044
|
await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
|
|
@@ -11019,7 +11049,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11019
11049
|
const sessionId = String(existingSessionId);
|
|
11020
11050
|
if (await sessionExists(openCodeClient, sessionId)) {
|
|
11021
11051
|
if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
|
|
11022
|
-
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11052
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11023
11053
|
if (!delivery.ok) {
|
|
11024
11054
|
const recovery2 = await recoverClickUpPmSession({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, staleSessionId: sessionId, sessionTitle, taskRoute, metadataWithRouting, config, prompt, eventMarkers: [taskId, eventType], deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, eventKey, createSession, verifySessionEventDelivery });
|
|
11025
11055
|
return finish(recovery2);
|
|
@@ -12254,7 +12284,33 @@ function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
|
12254
12284
|
function shouldRegisterWorkflowProductManager(options = {}, worktree = process.cwd()) {
|
|
12255
12285
|
if (options.clickUpWebhookActive === true) return true;
|
|
12256
12286
|
const validation = options.clickUpWebhookValidation;
|
|
12257
|
-
|
|
12287
|
+
if (validation?.complete !== true || validation?.ok === false) return false;
|
|
12288
|
+
const basePath = validation.config?.basePath;
|
|
12289
|
+
return isSameOrNestedPath(worktree, basePath) || isClickUpDerivedWorktreeSibling(worktree, basePath);
|
|
12290
|
+
}
|
|
12291
|
+
function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
12292
|
+
if (typeof candidate !== "string" || typeof basePath !== "string" || !candidate.trim() || !basePath.trim()) return false;
|
|
12293
|
+
const resolvedCandidate = path5.resolve(candidate);
|
|
12294
|
+
const resolvedBase = path5.resolve(basePath);
|
|
12295
|
+
const baseParent = path5.dirname(resolvedBase);
|
|
12296
|
+
if (path5.dirname(resolvedCandidate) !== baseParent) return false;
|
|
12297
|
+
const baseName = path5.basename(resolvedBase);
|
|
12298
|
+
const candidateName = path5.basename(resolvedCandidate);
|
|
12299
|
+
if (!candidateName.startsWith(`${baseName}-`)) return false;
|
|
12300
|
+
const branchSlug = candidateName.slice(baseName.length + 1);
|
|
12301
|
+
const parts = branchSlug.split("-").filter(Boolean);
|
|
12302
|
+
if (parts.length < 2) return false;
|
|
12303
|
+
if (!(/* @__PURE__ */ new Set(["tarea", "bug", "doc", "poc", "idea"])).has(parts[0])) return false;
|
|
12304
|
+
if (!parts.slice(1).every((part) => /^[a-z0-9][a-z0-9-]*$/.test(part))) return false;
|
|
12305
|
+
try {
|
|
12306
|
+
const candidateStat = fs5.lstatSync(resolvedCandidate);
|
|
12307
|
+
if (!candidateStat.isDirectory() || candidateStat.isSymbolicLink()) return false;
|
|
12308
|
+
const realCandidate = fs5.realpathSync.native(resolvedCandidate);
|
|
12309
|
+
const realBaseParent = fs5.realpathSync.native(baseParent);
|
|
12310
|
+
return path5.dirname(realCandidate) === realBaseParent && path5.basename(realCandidate) === candidateName;
|
|
12311
|
+
} catch {
|
|
12312
|
+
return false;
|
|
12313
|
+
}
|
|
12258
12314
|
}
|
|
12259
12315
|
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
12260
12316
|
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
@@ -12266,6 +12322,9 @@ function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktr
|
|
|
12266
12322
|
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
12267
12323
|
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
12268
12324
|
}
|
|
12325
|
+
if (clickUpBasePath && isClickUpDerivedWorktreeSibling(requested, clickUpBasePath)) {
|
|
12326
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_derived_worktree", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
12327
|
+
}
|
|
12269
12328
|
return {
|
|
12270
12329
|
ok: false,
|
|
12271
12330
|
error: `Directory '${requested}' is outside the safe worktree '${safe.worktree}' and configured ClickUp base path.`
|
|
@@ -13097,7 +13156,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
13097
13156
|
}
|
|
13098
13157
|
};
|
|
13099
13158
|
}
|
|
13100
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
13159
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
13101
13160
|
|
|
13102
13161
|
// src/sanitize_cli.js
|
|
13103
13162
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
- Only one shared-worktree `implementation` task may be active.
|
|
34
34
|
- ClickUp-first delivery should use task-specific worktrees/branches; the principal workspace must remain on `dev` and never on `main`.
|
|
35
35
|
- Parent task start pulls remote once; after branch creation, subtasks trust the parent local branch instead of continuous remote polling.
|
|
36
|
-
- Parent branch format is `<clickup-task-type>/<parent-task-id>`; subtask branch format is `<clickup-task-type>/<parent-task-id
|
|
37
|
-
- Subtask
|
|
36
|
+
- Parent branch format is `<clickup-task-type>/<parent-task-id>`; subtask branch format is the non-nested sibling ref `<clickup-task-type>/<parent-task-id>-subtask-<subtask-id>`; pending planned subtasks use `<clickup-task-type>/<parent-task-id>-pending-<title-slug>`; PoC branch format is always `poc/<clickup-task-id>` and stays there until a later productization task.
|
|
37
|
+
- Subtask worktrees start from the parent branch and PR to the parent branch; if the parent branch/worktree is missing, bootstrap the parent from `dev`/`origin/dev` first. Parent task PRs target `dev`, and release PRs target `main` from `dev` only after explicit approval.
|
|
38
38
|
- After successful subtask validation, Validator/QA merges the subtask PR into the parent branch/workspace without `CTO`/`PO` approval.
|
|
39
39
|
- After parent Tech Lead and Validator/QA validation passes, the parent task stays in `validation`, `CTO`/`PO` are assigned and marked as approval owners, and they approve by moving it to `merge`; only then does Validator/QA attempt the parent PR merge into `dev`.
|
|
40
40
|
- If any subtask or parent merge conflicts or fails, Validator/QA returns the affected ClickUp item to `in progress` and routes it to the coding owner.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- Human role registry: resolve `CTO` and `PO` from `docs/core/humans.md` when present; if missing in a task worktree, use Optima-provided fallback role context/configured ClickUp IDs instead of blocking solely on the missing file.
|
|
17
17
|
- ClickUp-first statuses: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, test strategy, and `CTO`/`PO` assignment, `in progress` execute, `validation` split Tech Lead + Validator/QA gates; Validator/QA may merge validated subtasks directly into the parent branch; validated parents stay in `validation` for `CTO`/`PO` approval; `merge` means `CTO`/`PO` approved the parent and Validator/QA may attempt parent merge into `dev`; `completed`/`Closed` ignore unless reopened.
|
|
18
18
|
- Shared-worktree rule: one active `implementation` task at a time; isolated `investigation`/`spec` may run in parallel if non-conflicting.
|
|
19
|
-
- Git rules: principal workspace stays on `dev`, never `main`; parent task pulls remote once at start; subtasks
|
|
19
|
+
- Git rules: principal workspace stays on `dev`, never `main`; parent branches use `<type>/<parent-id>`; subtask branches use non-nested `<type>/<parent-id>-subtask-<subtask-id>` and pending subtasks use `<type>/<parent-id>-pending-<title-slug>`; parent task pulls remote once at start; subtasks start from and PR to the parent local branch, bootstrapping the parent from `dev`/`origin/dev` first when missing; PoC branches stay `poc/<clickup-task-id>`; parents PR to `dev`, releases PR `dev` -> `main`; failed/conflicted subtask or parent merges return the affected item to `in progress` for the coding owner; no direct `main` pushes.
|
|
20
20
|
- Store `agent_metadata` session JSON; `Definition` is the plan contract, final Documentation is delivered behavior docs.
|
|
21
21
|
- `workflow_product_manager` is registered only when opt-in ClickUp webhook mode is complete and active/valid.
|
|
22
22
|
- Webhook mode validates `X-Signature` HMAC SHA-256, routes only PM-assigned non-terminal status/assignee events, routes comments only on `@Defend Tech Product Manager`, writes new `ses_...` ids to `agent_metadata`, and reports stale/missing sessions to ClickUp without replacement.
|
package/docs/core/task_model.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
- `complex + implementation` normally uses `workflow_runner` in full mode.
|
|
24
24
|
- While one shared-worktree implementation task is active, parallel work is limited to non-conflicting `investigation` or `spec`.
|
|
25
25
|
- WPM estimates `Story Points` during `plan`, re-estimates on material plan changes, links the `Definition` plan contract when needed, and records `agent_metadata` session IDs.
|
|
26
|
-
- In ClickUp-first mode, work should be decomposed into parent/subtask branches: parent tasks pull remote once at start, parent branches merge to `dev`, subtasks trust the parent local branch and merge to parent branches, PoC branches stay `poc/<clickup-task-id>`, and release branches merge `dev` to `main` only after approval.
|
|
26
|
+
- In ClickUp-first mode, work should be decomposed into parent/subtask branches: parent tasks pull remote once at start, parent branches use `<type>/<parent-id>` and merge to `dev`, subtasks use non-nested `<type>/<parent-id>-subtask-<subtask-id>` branches that start from/trust the parent local branch and merge to parent branches, missing parent branches/worktrees are bootstrapped from `dev`/`origin/dev` before subtask worktree creation, PoC branches stay `poc/<clickup-task-id>`, and release branches merge `dev` to `main` only after approval.
|
|
27
27
|
- Validator/QA owns merge execution after the correct gate: validated subtask PRs merge directly into the parent branch/workspace, while parent PRs merge into `dev` only after `CTO`/`PO` move the parent task to `merge`. Merge conflicts or failed attempts return the affected task/subtask to `in progress` for the coding owner.
|
|
28
28
|
|
|
29
29
|
## Pre-Sync Defaults
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
- Routing: keep `tiny` to one slice and usually one specialist; keep `standard` bounded; decompose `complex` into slice-based subtasks.
|
|
12
12
|
- `complex + implementation` normally uses `workflow_runner` in full mode.
|
|
13
13
|
- WPM stores `agent_metadata`, re-estimates `Story Points` on material plan changes, and keeps `Definition` plan contract separate from final Documentation.
|
|
14
|
-
- Shared worktree permits only one active implementation task; isolated investigation/spec work may run in parallel; PoC branches remain `poc/<clickup-task-id>`; merge conflicts or failed merge attempts return the affected task/subtask to `in progress` for the coding owner.
|
|
14
|
+
- Shared worktree permits only one active implementation task; isolated investigation/spec work may run in parallel; parent branches use `<type>/<parent-id>`, subtasks use non-nested `<type>/<parent-id>-subtask-<subtask-id>` from the parent branch with parent bootstrap from `dev`/`origin/dev` when needed; PoC branches remain `poc/<clickup-task-id>`; merge conflicts or failed merge attempts return the affected task/subtask to `in progress` for the coding owner.
|
|
15
15
|
- Pre-sync defaults: `tiny` -> developer + tech_lead; `standard` -> business_analyst + technical_architect; `complex` -> business_analyst + technical_architect + tech_lead + Validator/QA.
|
|
16
16
|
- Add UI/UX for user-facing interface impact; add BA when product behavior, copy intent, or requirements are affected; add Tech Lead to standard work with elevated technical risk.
|