@defend-tech/opencode-optima 0.1.67 → 0.1.69
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/Agents_Common.md +3 -1
- package/Agents_Common.prompt.md +3 -1
- package/assets/agents/workflow_product_manager.md +6 -4
- package/dist/index.js +426 -279
- package/dist/sanitize_cli.js +449 -302
- package/package.json +1 -1
package/dist/sanitize_cli.js
CHANGED
|
@@ -109,17 +109,17 @@ var require_visit = __commonJS({
|
|
|
109
109
|
visit.BREAK = BREAK;
|
|
110
110
|
visit.SKIP = SKIP;
|
|
111
111
|
visit.REMOVE = REMOVE;
|
|
112
|
-
function visit_(key, node, visitor,
|
|
113
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
112
|
+
function visit_(key, node, visitor, path8) {
|
|
113
|
+
const ctrl = callVisitor(key, node, visitor, path8);
|
|
114
114
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
115
|
-
replaceNode(key,
|
|
116
|
-
return visit_(key, ctrl, visitor,
|
|
115
|
+
replaceNode(key, path8, ctrl);
|
|
116
|
+
return visit_(key, ctrl, visitor, path8);
|
|
117
117
|
}
|
|
118
118
|
if (typeof ctrl !== "symbol") {
|
|
119
119
|
if (identity.isCollection(node)) {
|
|
120
|
-
|
|
120
|
+
path8 = Object.freeze(path8.concat(node));
|
|
121
121
|
for (let i = 0; i < node.items.length; ++i) {
|
|
122
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
122
|
+
const ci = visit_(i, node.items[i], visitor, path8);
|
|
123
123
|
if (typeof ci === "number")
|
|
124
124
|
i = ci - 1;
|
|
125
125
|
else if (ci === BREAK)
|
|
@@ -130,13 +130,13 @@ var require_visit = __commonJS({
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
} else if (identity.isPair(node)) {
|
|
133
|
-
|
|
134
|
-
const ck = visit_("key", node.key, visitor,
|
|
133
|
+
path8 = Object.freeze(path8.concat(node));
|
|
134
|
+
const ck = visit_("key", node.key, visitor, path8);
|
|
135
135
|
if (ck === BREAK)
|
|
136
136
|
return BREAK;
|
|
137
137
|
else if (ck === REMOVE)
|
|
138
138
|
node.key = null;
|
|
139
|
-
const cv = visit_("value", node.value, visitor,
|
|
139
|
+
const cv = visit_("value", node.value, visitor, path8);
|
|
140
140
|
if (cv === BREAK)
|
|
141
141
|
return BREAK;
|
|
142
142
|
else if (cv === REMOVE)
|
|
@@ -157,17 +157,17 @@ var require_visit = __commonJS({
|
|
|
157
157
|
visitAsync.BREAK = BREAK;
|
|
158
158
|
visitAsync.SKIP = SKIP;
|
|
159
159
|
visitAsync.REMOVE = REMOVE;
|
|
160
|
-
async function visitAsync_(key, node, visitor,
|
|
161
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
160
|
+
async function visitAsync_(key, node, visitor, path8) {
|
|
161
|
+
const ctrl = await callVisitor(key, node, visitor, path8);
|
|
162
162
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
163
|
-
replaceNode(key,
|
|
164
|
-
return visitAsync_(key, ctrl, visitor,
|
|
163
|
+
replaceNode(key, path8, ctrl);
|
|
164
|
+
return visitAsync_(key, ctrl, visitor, path8);
|
|
165
165
|
}
|
|
166
166
|
if (typeof ctrl !== "symbol") {
|
|
167
167
|
if (identity.isCollection(node)) {
|
|
168
|
-
|
|
168
|
+
path8 = Object.freeze(path8.concat(node));
|
|
169
169
|
for (let i = 0; i < node.items.length; ++i) {
|
|
170
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
170
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path8);
|
|
171
171
|
if (typeof ci === "number")
|
|
172
172
|
i = ci - 1;
|
|
173
173
|
else if (ci === BREAK)
|
|
@@ -178,13 +178,13 @@ var require_visit = __commonJS({
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
} else if (identity.isPair(node)) {
|
|
181
|
-
|
|
182
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
181
|
+
path8 = Object.freeze(path8.concat(node));
|
|
182
|
+
const ck = await visitAsync_("key", node.key, visitor, path8);
|
|
183
183
|
if (ck === BREAK)
|
|
184
184
|
return BREAK;
|
|
185
185
|
else if (ck === REMOVE)
|
|
186
186
|
node.key = null;
|
|
187
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
187
|
+
const cv = await visitAsync_("value", node.value, visitor, path8);
|
|
188
188
|
if (cv === BREAK)
|
|
189
189
|
return BREAK;
|
|
190
190
|
else if (cv === REMOVE)
|
|
@@ -211,23 +211,23 @@ var require_visit = __commonJS({
|
|
|
211
211
|
}
|
|
212
212
|
return visitor;
|
|
213
213
|
}
|
|
214
|
-
function callVisitor(key, node, visitor,
|
|
214
|
+
function callVisitor(key, node, visitor, path8) {
|
|
215
215
|
if (typeof visitor === "function")
|
|
216
|
-
return visitor(key, node,
|
|
216
|
+
return visitor(key, node, path8);
|
|
217
217
|
if (identity.isMap(node))
|
|
218
|
-
return visitor.Map?.(key, node,
|
|
218
|
+
return visitor.Map?.(key, node, path8);
|
|
219
219
|
if (identity.isSeq(node))
|
|
220
|
-
return visitor.Seq?.(key, node,
|
|
220
|
+
return visitor.Seq?.(key, node, path8);
|
|
221
221
|
if (identity.isPair(node))
|
|
222
|
-
return visitor.Pair?.(key, node,
|
|
222
|
+
return visitor.Pair?.(key, node, path8);
|
|
223
223
|
if (identity.isScalar(node))
|
|
224
|
-
return visitor.Scalar?.(key, node,
|
|
224
|
+
return visitor.Scalar?.(key, node, path8);
|
|
225
225
|
if (identity.isAlias(node))
|
|
226
|
-
return visitor.Alias?.(key, node,
|
|
226
|
+
return visitor.Alias?.(key, node, path8);
|
|
227
227
|
return void 0;
|
|
228
228
|
}
|
|
229
|
-
function replaceNode(key,
|
|
230
|
-
const parent =
|
|
229
|
+
function replaceNode(key, path8, node) {
|
|
230
|
+
const parent = path8[path8.length - 1];
|
|
231
231
|
if (identity.isCollection(parent)) {
|
|
232
232
|
parent.items[key] = node;
|
|
233
233
|
} else if (identity.isPair(parent)) {
|
|
@@ -835,10 +835,10 @@ var require_Collection = __commonJS({
|
|
|
835
835
|
var createNode = require_createNode();
|
|
836
836
|
var identity = require_identity();
|
|
837
837
|
var Node = require_Node();
|
|
838
|
-
function collectionFromPath(schema,
|
|
838
|
+
function collectionFromPath(schema, path8, value) {
|
|
839
839
|
let v = value;
|
|
840
|
-
for (let i =
|
|
841
|
-
const k =
|
|
840
|
+
for (let i = path8.length - 1; i >= 0; --i) {
|
|
841
|
+
const k = path8[i];
|
|
842
842
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
843
843
|
const a = [];
|
|
844
844
|
a[k] = v;
|
|
@@ -857,7 +857,7 @@ var require_Collection = __commonJS({
|
|
|
857
857
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
858
858
|
});
|
|
859
859
|
}
|
|
860
|
-
var isEmptyPath = (
|
|
860
|
+
var isEmptyPath = (path8) => path8 == null || typeof path8 === "object" && !!path8[Symbol.iterator]().next().done;
|
|
861
861
|
var Collection = class extends Node.NodeBase {
|
|
862
862
|
constructor(type, schema) {
|
|
863
863
|
super(type);
|
|
@@ -887,11 +887,11 @@ var require_Collection = __commonJS({
|
|
|
887
887
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
888
888
|
* that already exists in the map.
|
|
889
889
|
*/
|
|
890
|
-
addIn(
|
|
891
|
-
if (isEmptyPath(
|
|
890
|
+
addIn(path8, value) {
|
|
891
|
+
if (isEmptyPath(path8))
|
|
892
892
|
this.add(value);
|
|
893
893
|
else {
|
|
894
|
-
const [key, ...rest] =
|
|
894
|
+
const [key, ...rest] = path8;
|
|
895
895
|
const node = this.get(key, true);
|
|
896
896
|
if (identity.isCollection(node))
|
|
897
897
|
node.addIn(rest, value);
|
|
@@ -905,8 +905,8 @@ var require_Collection = __commonJS({
|
|
|
905
905
|
* Removes a value from the collection.
|
|
906
906
|
* @returns `true` if the item was found and removed.
|
|
907
907
|
*/
|
|
908
|
-
deleteIn(
|
|
909
|
-
const [key, ...rest] =
|
|
908
|
+
deleteIn(path8) {
|
|
909
|
+
const [key, ...rest] = path8;
|
|
910
910
|
if (rest.length === 0)
|
|
911
911
|
return this.delete(key);
|
|
912
912
|
const node = this.get(key, true);
|
|
@@ -920,8 +920,8 @@ var require_Collection = __commonJS({
|
|
|
920
920
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
921
921
|
* `true` (collections are always returned intact).
|
|
922
922
|
*/
|
|
923
|
-
getIn(
|
|
924
|
-
const [key, ...rest] =
|
|
923
|
+
getIn(path8, keepScalar) {
|
|
924
|
+
const [key, ...rest] = path8;
|
|
925
925
|
const node = this.get(key, true);
|
|
926
926
|
if (rest.length === 0)
|
|
927
927
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -939,8 +939,8 @@ var require_Collection = __commonJS({
|
|
|
939
939
|
/**
|
|
940
940
|
* Checks if the collection includes a value with the key `key`.
|
|
941
941
|
*/
|
|
942
|
-
hasIn(
|
|
943
|
-
const [key, ...rest] =
|
|
942
|
+
hasIn(path8) {
|
|
943
|
+
const [key, ...rest] = path8;
|
|
944
944
|
if (rest.length === 0)
|
|
945
945
|
return this.has(key);
|
|
946
946
|
const node = this.get(key, true);
|
|
@@ -950,8 +950,8 @@ var require_Collection = __commonJS({
|
|
|
950
950
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
951
951
|
* boolean to add/remove the item from the set.
|
|
952
952
|
*/
|
|
953
|
-
setIn(
|
|
954
|
-
const [key, ...rest] =
|
|
953
|
+
setIn(path8, value) {
|
|
954
|
+
const [key, ...rest] = path8;
|
|
955
955
|
if (rest.length === 0) {
|
|
956
956
|
this.set(key, value);
|
|
957
957
|
} else {
|
|
@@ -3455,9 +3455,9 @@ var require_Document = __commonJS({
|
|
|
3455
3455
|
this.contents.add(value);
|
|
3456
3456
|
}
|
|
3457
3457
|
/** Adds a value to the document. */
|
|
3458
|
-
addIn(
|
|
3458
|
+
addIn(path8, value) {
|
|
3459
3459
|
if (assertCollection(this.contents))
|
|
3460
|
-
this.contents.addIn(
|
|
3460
|
+
this.contents.addIn(path8, value);
|
|
3461
3461
|
}
|
|
3462
3462
|
/**
|
|
3463
3463
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -3532,14 +3532,14 @@ var require_Document = __commonJS({
|
|
|
3532
3532
|
* Removes a value from the document.
|
|
3533
3533
|
* @returns `true` if the item was found and removed.
|
|
3534
3534
|
*/
|
|
3535
|
-
deleteIn(
|
|
3536
|
-
if (Collection.isEmptyPath(
|
|
3535
|
+
deleteIn(path8) {
|
|
3536
|
+
if (Collection.isEmptyPath(path8)) {
|
|
3537
3537
|
if (this.contents == null)
|
|
3538
3538
|
return false;
|
|
3539
3539
|
this.contents = null;
|
|
3540
3540
|
return true;
|
|
3541
3541
|
}
|
|
3542
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
3542
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path8) : false;
|
|
3543
3543
|
}
|
|
3544
3544
|
/**
|
|
3545
3545
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -3554,10 +3554,10 @@ var require_Document = __commonJS({
|
|
|
3554
3554
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
3555
3555
|
* `true` (collections are always returned intact).
|
|
3556
3556
|
*/
|
|
3557
|
-
getIn(
|
|
3558
|
-
if (Collection.isEmptyPath(
|
|
3557
|
+
getIn(path8, keepScalar) {
|
|
3558
|
+
if (Collection.isEmptyPath(path8))
|
|
3559
3559
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
3560
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
3560
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path8, keepScalar) : void 0;
|
|
3561
3561
|
}
|
|
3562
3562
|
/**
|
|
3563
3563
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -3568,10 +3568,10 @@ var require_Document = __commonJS({
|
|
|
3568
3568
|
/**
|
|
3569
3569
|
* Checks if the document includes a value at `path`.
|
|
3570
3570
|
*/
|
|
3571
|
-
hasIn(
|
|
3572
|
-
if (Collection.isEmptyPath(
|
|
3571
|
+
hasIn(path8) {
|
|
3572
|
+
if (Collection.isEmptyPath(path8))
|
|
3573
3573
|
return this.contents !== void 0;
|
|
3574
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
3574
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path8) : false;
|
|
3575
3575
|
}
|
|
3576
3576
|
/**
|
|
3577
3577
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -3588,13 +3588,13 @@ var require_Document = __commonJS({
|
|
|
3588
3588
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
3589
3589
|
* boolean to add/remove the item from the set.
|
|
3590
3590
|
*/
|
|
3591
|
-
setIn(
|
|
3592
|
-
if (Collection.isEmptyPath(
|
|
3591
|
+
setIn(path8, value) {
|
|
3592
|
+
if (Collection.isEmptyPath(path8)) {
|
|
3593
3593
|
this.contents = value;
|
|
3594
3594
|
} else if (this.contents == null) {
|
|
3595
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
3595
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path8), value);
|
|
3596
3596
|
} else if (assertCollection(this.contents)) {
|
|
3597
|
-
this.contents.setIn(
|
|
3597
|
+
this.contents.setIn(path8, value);
|
|
3598
3598
|
}
|
|
3599
3599
|
}
|
|
3600
3600
|
/**
|
|
@@ -5546,9 +5546,9 @@ var require_cst_visit = __commonJS({
|
|
|
5546
5546
|
visit.BREAK = BREAK;
|
|
5547
5547
|
visit.SKIP = SKIP;
|
|
5548
5548
|
visit.REMOVE = REMOVE;
|
|
5549
|
-
visit.itemAtPath = (cst,
|
|
5549
|
+
visit.itemAtPath = (cst, path8) => {
|
|
5550
5550
|
let item = cst;
|
|
5551
|
-
for (const [field, index] of
|
|
5551
|
+
for (const [field, index] of path8) {
|
|
5552
5552
|
const tok = item?.[field];
|
|
5553
5553
|
if (tok && "items" in tok) {
|
|
5554
5554
|
item = tok.items[index];
|
|
@@ -5557,23 +5557,23 @@ var require_cst_visit = __commonJS({
|
|
|
5557
5557
|
}
|
|
5558
5558
|
return item;
|
|
5559
5559
|
};
|
|
5560
|
-
visit.parentCollection = (cst,
|
|
5561
|
-
const parent = visit.itemAtPath(cst,
|
|
5562
|
-
const field =
|
|
5560
|
+
visit.parentCollection = (cst, path8) => {
|
|
5561
|
+
const parent = visit.itemAtPath(cst, path8.slice(0, -1));
|
|
5562
|
+
const field = path8[path8.length - 1][0];
|
|
5563
5563
|
const coll = parent?.[field];
|
|
5564
5564
|
if (coll && "items" in coll)
|
|
5565
5565
|
return coll;
|
|
5566
5566
|
throw new Error("Parent collection not found");
|
|
5567
5567
|
};
|
|
5568
|
-
function _visit(
|
|
5569
|
-
let ctrl = visitor(item,
|
|
5568
|
+
function _visit(path8, item, visitor) {
|
|
5569
|
+
let ctrl = visitor(item, path8);
|
|
5570
5570
|
if (typeof ctrl === "symbol")
|
|
5571
5571
|
return ctrl;
|
|
5572
5572
|
for (const field of ["key", "value"]) {
|
|
5573
5573
|
const token = item[field];
|
|
5574
5574
|
if (token && "items" in token) {
|
|
5575
5575
|
for (let i = 0; i < token.items.length; ++i) {
|
|
5576
|
-
const ci = _visit(Object.freeze(
|
|
5576
|
+
const ci = _visit(Object.freeze(path8.concat([[field, i]])), token.items[i], visitor);
|
|
5577
5577
|
if (typeof ci === "number")
|
|
5578
5578
|
i = ci - 1;
|
|
5579
5579
|
else if (ci === BREAK)
|
|
@@ -5584,10 +5584,10 @@ var require_cst_visit = __commonJS({
|
|
|
5584
5584
|
}
|
|
5585
5585
|
}
|
|
5586
5586
|
if (typeof ctrl === "function" && field === "key")
|
|
5587
|
-
ctrl = ctrl(item,
|
|
5587
|
+
ctrl = ctrl(item, path8);
|
|
5588
5588
|
}
|
|
5589
5589
|
}
|
|
5590
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5590
|
+
return typeof ctrl === "function" ? ctrl(item, path8) : ctrl;
|
|
5591
5591
|
}
|
|
5592
5592
|
exports.visit = visit;
|
|
5593
5593
|
}
|
|
@@ -7551,17 +7551,17 @@ var require_ignore = __commonJS({
|
|
|
7551
7551
|
var throwError = (message, Ctor) => {
|
|
7552
7552
|
throw new Ctor(message);
|
|
7553
7553
|
};
|
|
7554
|
-
var checkPath = (
|
|
7555
|
-
if (!isString(
|
|
7554
|
+
var checkPath = (path8, originalPath, doThrow) => {
|
|
7555
|
+
if (!isString(path8)) {
|
|
7556
7556
|
return doThrow(
|
|
7557
7557
|
`path must be a string, but got \`${originalPath}\``,
|
|
7558
7558
|
TypeError
|
|
7559
7559
|
);
|
|
7560
7560
|
}
|
|
7561
|
-
if (!
|
|
7561
|
+
if (!path8) {
|
|
7562
7562
|
return doThrow(`path must not be empty`, TypeError);
|
|
7563
7563
|
}
|
|
7564
|
-
if (checkPath.isNotRelative(
|
|
7564
|
+
if (checkPath.isNotRelative(path8)) {
|
|
7565
7565
|
const r = "`path.relative()`d";
|
|
7566
7566
|
return doThrow(
|
|
7567
7567
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -7570,7 +7570,7 @@ var require_ignore = __commonJS({
|
|
|
7570
7570
|
}
|
|
7571
7571
|
return true;
|
|
7572
7572
|
};
|
|
7573
|
-
var isNotRelative = (
|
|
7573
|
+
var isNotRelative = (path8) => REGEX_TEST_INVALID_PATH.test(path8);
|
|
7574
7574
|
checkPath.isNotRelative = isNotRelative;
|
|
7575
7575
|
checkPath.convert = (p) => p;
|
|
7576
7576
|
var Ignore = class {
|
|
@@ -7629,7 +7629,7 @@ var require_ignore = __commonJS({
|
|
|
7629
7629
|
// setting `checkUnignored` to `false` could reduce additional
|
|
7630
7630
|
// path matching.
|
|
7631
7631
|
// @returns {TestResult} true if a file is ignored
|
|
7632
|
-
_testOne(
|
|
7632
|
+
_testOne(path8, checkUnignored) {
|
|
7633
7633
|
let ignored = false;
|
|
7634
7634
|
let unignored = false;
|
|
7635
7635
|
this._rules.forEach((rule) => {
|
|
@@ -7637,7 +7637,7 @@ var require_ignore = __commonJS({
|
|
|
7637
7637
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
7638
7638
|
return;
|
|
7639
7639
|
}
|
|
7640
|
-
const matched = rule.regex.test(
|
|
7640
|
+
const matched = rule.regex.test(path8);
|
|
7641
7641
|
if (matched) {
|
|
7642
7642
|
ignored = !negative;
|
|
7643
7643
|
unignored = negative;
|
|
@@ -7650,24 +7650,24 @@ var require_ignore = __commonJS({
|
|
|
7650
7650
|
}
|
|
7651
7651
|
// @returns {TestResult}
|
|
7652
7652
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
7653
|
-
const
|
|
7653
|
+
const path8 = originalPath && checkPath.convert(originalPath);
|
|
7654
7654
|
checkPath(
|
|
7655
|
-
|
|
7655
|
+
path8,
|
|
7656
7656
|
originalPath,
|
|
7657
7657
|
this._allowRelativePaths ? RETURN_FALSE : throwError
|
|
7658
7658
|
);
|
|
7659
|
-
return this._t(
|
|
7659
|
+
return this._t(path8, cache, checkUnignored, slices);
|
|
7660
7660
|
}
|
|
7661
|
-
_t(
|
|
7662
|
-
if (
|
|
7663
|
-
return cache[
|
|
7661
|
+
_t(path8, cache, checkUnignored, slices) {
|
|
7662
|
+
if (path8 in cache) {
|
|
7663
|
+
return cache[path8];
|
|
7664
7664
|
}
|
|
7665
7665
|
if (!slices) {
|
|
7666
|
-
slices =
|
|
7666
|
+
slices = path8.split(SLASH);
|
|
7667
7667
|
}
|
|
7668
7668
|
slices.pop();
|
|
7669
7669
|
if (!slices.length) {
|
|
7670
|
-
return cache[
|
|
7670
|
+
return cache[path8] = this._testOne(path8, checkUnignored);
|
|
7671
7671
|
}
|
|
7672
7672
|
const parent = this._t(
|
|
7673
7673
|
slices.join(SLASH) + SLASH,
|
|
@@ -7675,24 +7675,24 @@ var require_ignore = __commonJS({
|
|
|
7675
7675
|
checkUnignored,
|
|
7676
7676
|
slices
|
|
7677
7677
|
);
|
|
7678
|
-
return cache[
|
|
7678
|
+
return cache[path8] = parent.ignored ? parent : this._testOne(path8, checkUnignored);
|
|
7679
7679
|
}
|
|
7680
|
-
ignores(
|
|
7681
|
-
return this._test(
|
|
7680
|
+
ignores(path8) {
|
|
7681
|
+
return this._test(path8, this._ignoreCache, false).ignored;
|
|
7682
7682
|
}
|
|
7683
7683
|
createFilter() {
|
|
7684
|
-
return (
|
|
7684
|
+
return (path8) => !this.ignores(path8);
|
|
7685
7685
|
}
|
|
7686
7686
|
filter(paths) {
|
|
7687
7687
|
return makeArray(paths).filter(this.createFilter());
|
|
7688
7688
|
}
|
|
7689
7689
|
// @returns {TestResult}
|
|
7690
|
-
test(
|
|
7691
|
-
return this._test(
|
|
7690
|
+
test(path8) {
|
|
7691
|
+
return this._test(path8, this._testCache, true);
|
|
7692
7692
|
}
|
|
7693
7693
|
};
|
|
7694
7694
|
var factory = (options) => new Ignore(options);
|
|
7695
|
-
var isPathValid = (
|
|
7695
|
+
var isPathValid = (path8) => checkPath(path8 && checkPath.convert(path8), path8, RETURN_FALSE);
|
|
7696
7696
|
factory.isPathValid = isPathValid;
|
|
7697
7697
|
factory.default = factory;
|
|
7698
7698
|
module.exports = factory;
|
|
@@ -7703,7 +7703,7 @@ var require_ignore = __commonJS({
|
|
|
7703
7703
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
7704
7704
|
checkPath.convert = makePosix;
|
|
7705
7705
|
const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
7706
|
-
checkPath.isNotRelative = (
|
|
7706
|
+
checkPath.isNotRelative = (path8) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path8) || isNotRelative(path8);
|
|
7707
7707
|
}
|
|
7708
7708
|
}
|
|
7709
7709
|
});
|
|
@@ -7711,7 +7711,7 @@ var require_ignore = __commonJS({
|
|
|
7711
7711
|
// src/sanitize_cli.js
|
|
7712
7712
|
import fs6 from "node:fs";
|
|
7713
7713
|
import os2 from "node:os";
|
|
7714
|
-
import
|
|
7714
|
+
import path7 from "node:path";
|
|
7715
7715
|
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
7716
7716
|
|
|
7717
7717
|
// src/index.js
|
|
@@ -7721,8 +7721,7 @@ import crypto from "node:crypto";
|
|
|
7721
7721
|
import fs5 from "node:fs";
|
|
7722
7722
|
import http from "node:http";
|
|
7723
7723
|
import os from "node:os";
|
|
7724
|
-
import
|
|
7725
|
-
import { fileURLToPath } from "node:url";
|
|
7724
|
+
import path6 from "node:path";
|
|
7726
7725
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
7727
7726
|
|
|
7728
7727
|
// src/git_utils.js
|
|
@@ -8493,7 +8492,9 @@ async function optima_validate_logic(worktree) {
|
|
|
8493
8492
|
};
|
|
8494
8493
|
}
|
|
8495
8494
|
|
|
8496
|
-
// src/
|
|
8495
|
+
// src/constants.js
|
|
8496
|
+
import path5 from "node:path";
|
|
8497
|
+
import { fileURLToPath } from "node:url";
|
|
8497
8498
|
var PKG_ROOT = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8498
8499
|
var BUNDLE_ASSETS_DIR = path5.join(PKG_ROOT, "assets");
|
|
8499
8500
|
var BUNDLE_AGENTS_DIR = path5.join(BUNDLE_ASSETS_DIR, "agents");
|
|
@@ -8534,6 +8535,8 @@ var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
|
8534
8535
|
var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
|
|
8535
8536
|
var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
|
|
8536
8537
|
var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
|
|
8538
|
+
var CLICKUP_WEBHOOK_STARTUP_COMMENT_LOOKBACK_MS = 6 * 60 * 60 * 1e3;
|
|
8539
|
+
var CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
|
|
8537
8540
|
var CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS = 10 * 60 * 1e3;
|
|
8538
8541
|
var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
|
|
8539
8542
|
var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
|
|
@@ -8622,20 +8625,22 @@ var REPO_LOCAL_POLICIES_README = [
|
|
|
8622
8625
|
"Only files in `.optima/policies/` affect runtime prompt behavior.",
|
|
8623
8626
|
""
|
|
8624
8627
|
].join("\n");
|
|
8628
|
+
var CLICKUP_WEBHOOK_CLEANUP_TIMEOUT_MS = 8e3;
|
|
8629
|
+
var CLICKUP_WEBHOOK_SIGNAL_STATE = Symbol.for("opencode-optima.clickup-webhook.signal-state");
|
|
8630
|
+
|
|
8631
|
+
// src/index.js
|
|
8625
8632
|
var activeWorkflows = /* @__PURE__ */ new Map();
|
|
8626
8633
|
var activeClickUpWebhookListeners = /* @__PURE__ */ new Map();
|
|
8627
8634
|
var activeClickUpWebhookLifecycleRegistry = /* @__PURE__ */ new Map();
|
|
8628
|
-
var CLICKUP_WEBHOOK_CLEANUP_TIMEOUT_MS = 8e3;
|
|
8629
|
-
var CLICKUP_WEBHOOK_SIGNAL_STATE = Symbol.for("opencode-optima.clickup-webhook.signal-state");
|
|
8630
8635
|
var activeClickUpTaskRoutes = /* @__PURE__ */ new Map();
|
|
8631
8636
|
function isRootDirectory(candidate) {
|
|
8632
|
-
const resolved =
|
|
8633
|
-
return resolved ===
|
|
8637
|
+
const resolved = path6.resolve(candidate);
|
|
8638
|
+
return resolved === path6.parse(resolved).root;
|
|
8634
8639
|
}
|
|
8635
8640
|
function isSafeWritableDirectory(candidate) {
|
|
8636
8641
|
if (typeof candidate !== "string" || !candidate.trim()) return false;
|
|
8637
|
-
if (!
|
|
8638
|
-
const resolved =
|
|
8642
|
+
if (!path6.isAbsolute(candidate)) return false;
|
|
8643
|
+
const resolved = path6.resolve(candidate);
|
|
8639
8644
|
if (isRootDirectory(resolved)) return false;
|
|
8640
8645
|
try {
|
|
8641
8646
|
const stat = fs5.statSync(resolved);
|
|
@@ -8656,7 +8661,7 @@ function resolveSafeWorktree(context = {}, pluginWorktree = null) {
|
|
|
8656
8661
|
];
|
|
8657
8662
|
for (const candidate of candidates) {
|
|
8658
8663
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8659
|
-
const resolved =
|
|
8664
|
+
const resolved = path6.resolve(candidate);
|
|
8660
8665
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8661
8666
|
}
|
|
8662
8667
|
throw new Error(SAFE_WORKTREE_FAILURE);
|
|
@@ -8671,13 +8676,13 @@ function safeWorktreeOrFailure(context, pluginWorktree) {
|
|
|
8671
8676
|
function explicitSafeInputWorktree(input = {}) {
|
|
8672
8677
|
for (const candidate of [input?.worktree, input?.directory]) {
|
|
8673
8678
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8674
|
-
const resolved =
|
|
8679
|
+
const resolved = path6.resolve(candidate);
|
|
8675
8680
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8676
8681
|
}
|
|
8677
8682
|
return null;
|
|
8678
8683
|
}
|
|
8679
8684
|
function isGitRepository(worktree) {
|
|
8680
|
-
return fs5.existsSync(
|
|
8685
|
+
return fs5.existsSync(path6.join(worktree, ".git"));
|
|
8681
8686
|
}
|
|
8682
8687
|
function normalizeLooseToken(value) {
|
|
8683
8688
|
return String(value ?? "").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -9172,7 +9177,7 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
|
|
|
9172
9177
|
function deriveClickUpWorktree({ baseWorktree = "", taskId, taskType, parentTaskId, subtaskId } = {}) {
|
|
9173
9178
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId, subtaskId, taskId });
|
|
9174
9179
|
const root = baseWorktree || process.cwd();
|
|
9175
|
-
return
|
|
9180
|
+
return path6.join(path6.dirname(root), `${path6.basename(root)}-${branch.replace(/\//g, "-")}`);
|
|
9176
9181
|
}
|
|
9177
9182
|
function clickUpCustomFieldValue(task = {}, names = []) {
|
|
9178
9183
|
const fields = Array.isArray(task.custom_fields) ? task.custom_fields : [];
|
|
@@ -9196,12 +9201,12 @@ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
|
|
|
9196
9201
|
const taskMetadata = metadataTaskRouting(metadata);
|
|
9197
9202
|
const existingWorktree = typeof taskMetadata.worktree === "string" ? taskMetadata.worktree.trim() : "";
|
|
9198
9203
|
const existingBranch = typeof taskMetadata.branch === "string" ? taskMetadata.branch.trim() : "";
|
|
9199
|
-
if (!existingWorktree || !
|
|
9204
|
+
if (!existingWorktree || !path6.isAbsolute(existingWorktree) || !fs5.existsSync(existingWorktree)) return null;
|
|
9200
9205
|
try {
|
|
9201
9206
|
const stat = fs5.statSync(existingWorktree);
|
|
9202
9207
|
if (!stat.isDirectory()) return null;
|
|
9203
9208
|
if (branch && existingBranch && existingBranch !== branch) return null;
|
|
9204
|
-
return { branch: existingBranch || branch, worktree:
|
|
9209
|
+
return { branch: existingBranch || branch, worktree: path6.resolve(existingWorktree), reused: true };
|
|
9205
9210
|
} catch {
|
|
9206
9211
|
return null;
|
|
9207
9212
|
}
|
|
@@ -9287,17 +9292,17 @@ function openChamberEntryBranch(entry) {
|
|
|
9287
9292
|
return String(entry?.branch || entry?.branchName || entry?.branch_name || entry?.worktree?.branch || "").replace(/^refs\/heads\//, "");
|
|
9288
9293
|
}
|
|
9289
9294
|
function openChamberListIncludesDirectory(list, directory) {
|
|
9290
|
-
const resolved =
|
|
9295
|
+
const resolved = path6.resolve(directory);
|
|
9291
9296
|
return normalizeOpenChamberCollection(list).some((entry) => {
|
|
9292
9297
|
const entryDirectory = openChamberEntryDirectory(entry);
|
|
9293
|
-
return entryDirectory &&
|
|
9298
|
+
return entryDirectory && path6.resolve(entryDirectory) === resolved;
|
|
9294
9299
|
});
|
|
9295
9300
|
}
|
|
9296
9301
|
function openChamberListIncludesBranch(list, directory, branch) {
|
|
9297
|
-
const resolved =
|
|
9302
|
+
const resolved = path6.resolve(directory);
|
|
9298
9303
|
return normalizeOpenChamberCollection(list).some((entry) => {
|
|
9299
9304
|
const entryDirectory = openChamberEntryDirectory(entry);
|
|
9300
|
-
if (!entryDirectory ||
|
|
9305
|
+
if (!entryDirectory || path6.resolve(entryDirectory) !== resolved) return false;
|
|
9301
9306
|
const entryBranch = openChamberEntryBranch(entry);
|
|
9302
9307
|
return !entryBranch || entryBranch === branch;
|
|
9303
9308
|
});
|
|
@@ -9305,8 +9310,8 @@ function openChamberListIncludesBranch(list, directory, branch) {
|
|
|
9305
9310
|
async function findOpenChamberProject({ opencodeBaseUrl, baseWorktree, fetchImpl = globalThis.fetch } = {}) {
|
|
9306
9311
|
const projects = await requestOpenCodeJson({ baseUrl: opencodeBaseUrl, endpoint: "/project", directory: baseWorktree, fetchImpl });
|
|
9307
9312
|
if (!Array.isArray(projects)) return null;
|
|
9308
|
-
const resolvedBase =
|
|
9309
|
-
return projects.find((project) =>
|
|
9313
|
+
const resolvedBase = path6.resolve(baseWorktree);
|
|
9314
|
+
return projects.find((project) => path6.resolve(String(project?.worktree || project?.path || "")) === resolvedBase) || null;
|
|
9310
9315
|
}
|
|
9311
9316
|
async function refreshOpenChamberProjectCopy({ opencodeBaseUrl, projectId, fetchImpl = globalThis.fetch } = {}) {
|
|
9312
9317
|
if (!projectId) return { refreshed: false, reason: "project_id_unavailable" };
|
|
@@ -9361,18 +9366,18 @@ async function createOpenChamberClickUpWorktree({ openchamberBaseUrl, baseUrl, b
|
|
|
9361
9366
|
}
|
|
9362
9367
|
const createdDirectory = openChamberEntryDirectory(created);
|
|
9363
9368
|
const createdBranch = openChamberEntryBranch(created);
|
|
9364
|
-
if (!createdDirectory || !
|
|
9369
|
+
if (!createdDirectory || !path6.isAbsolute(createdDirectory)) {
|
|
9365
9370
|
throw new Error(`OpenChamber did not return an absolute worktree path for ${branch}.`);
|
|
9366
9371
|
}
|
|
9367
9372
|
if (createdBranch !== branch) {
|
|
9368
9373
|
throw new Error(`OpenChamber created unexpected branch ${createdBranch || "<unknown>"}; expected ${branch}.`);
|
|
9369
9374
|
}
|
|
9370
9375
|
const verified = await verifyOpenChamberGitWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, worktreePath: createdDirectory, branch, fetchImpl });
|
|
9371
|
-
return { created, worktree:
|
|
9376
|
+
return { created, worktree: path6.resolve(createdDirectory), branch: createdBranch, verified };
|
|
9372
9377
|
}
|
|
9373
9378
|
async function registerOpenChamberClickUpWorktree({ openchamberBaseUrl, opencodeBaseUrl, baseUrl, baseWorktree, branch, worktreePath, fetchImpl = globalThis.fetch, source = "reuse" } = {}) {
|
|
9374
9379
|
const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, opencodeBaseUrl: opencodeBaseUrl || baseUrl, baseWorktree, worktreePath, branch, fetchImpl });
|
|
9375
|
-
return { branch, worktree:
|
|
9380
|
+
return { branch, worktree: path6.resolve(worktreePath), reused: true, provider: "openchamber", openChamber: { source, visibility } };
|
|
9376
9381
|
}
|
|
9377
9382
|
async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, openchamberBaseUrl = "", opencodeBaseUrl = "", baseUrl = "", fetchImpl = globalThis.fetch, log = null } = {}) {
|
|
9378
9383
|
const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
|
|
@@ -9435,10 +9440,10 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
9435
9440
|
const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
|
|
9436
9441
|
if (existing) return { ...existing, branch, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
|
|
9437
9442
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9438
|
-
if (fs5.existsSync(worktreePath)) return { branch, worktree:
|
|
9443
|
+
if (fs5.existsSync(worktreePath)) return { branch, worktree: path6.resolve(worktreePath), reused: true, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
|
|
9439
9444
|
if (allowNonGitFallback) {
|
|
9440
9445
|
fs5.mkdirSync(worktreePath, { recursive: true });
|
|
9441
|
-
return { branch, worktree:
|
|
9446
|
+
return { branch, worktree: path6.resolve(worktreePath), reused: false, fallback: "non_git", parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", startPoint: parentBranch || "dev" };
|
|
9442
9447
|
}
|
|
9443
9448
|
let parentBootstrap = null;
|
|
9444
9449
|
if (isSubtask) {
|
|
@@ -9447,14 +9452,14 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
9447
9452
|
const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9448
9453
|
const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
|
|
9449
9454
|
addClickUpWorktreeForBranch({ baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, runGitFn });
|
|
9450
|
-
parentBootstrap = { branch: parentBranch, worktree:
|
|
9455
|
+
parentBootstrap = { branch: parentBranch, worktree: path6.resolve(parentWorktree), startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists };
|
|
9451
9456
|
} else {
|
|
9452
|
-
parentBootstrap = { branch: parentBranch, worktree:
|
|
9457
|
+
parentBootstrap = { branch: parentBranch, worktree: path6.resolve(parentWorktree), reused: true };
|
|
9453
9458
|
}
|
|
9454
9459
|
}
|
|
9455
9460
|
const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9456
9461
|
addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn });
|
|
9457
|
-
return { branch, worktree:
|
|
9462
|
+
return { branch, worktree: path6.resolve(worktreePath), reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", parentBootstrap: parentBootstrap || void 0 };
|
|
9458
9463
|
}
|
|
9459
9464
|
function normalizeClickUpDefinitionDocParent(parent = {}) {
|
|
9460
9465
|
const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
|
|
@@ -9604,7 +9609,7 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
9604
9609
|
}
|
|
9605
9610
|
function ensureOptimaGitignoreRules(worktree) {
|
|
9606
9611
|
if (!isGitRepository(worktree)) return { touched: false, added: [] };
|
|
9607
|
-
const gitignorePath =
|
|
9612
|
+
const gitignorePath = path6.join(worktree, ".gitignore");
|
|
9608
9613
|
const existing = fs5.existsSync(gitignorePath) ? fs5.readFileSync(gitignorePath, "utf8") : "";
|
|
9609
9614
|
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
9610
9615
|
const missingRules = OPTIMA_GITIGNORE_RULES.filter((rule) => !existingRules.has(rule));
|
|
@@ -9620,40 +9625,40 @@ function ensureOptimaGitignoreRules(worktree) {
|
|
|
9620
9625
|
return { touched: true, added: missingRules };
|
|
9621
9626
|
}
|
|
9622
9627
|
function optimaDir(worktree) {
|
|
9623
|
-
return
|
|
9628
|
+
return path6.join(worktree, OPTIMA_DIRNAME);
|
|
9624
9629
|
}
|
|
9625
9630
|
function optimaLocalConfigDir(worktree) {
|
|
9626
|
-
return
|
|
9631
|
+
return path6.join(optimaDir(worktree), ".config");
|
|
9627
9632
|
}
|
|
9628
9633
|
function optimaConfigDir(worktree) {
|
|
9629
9634
|
return optimaLocalConfigDir(worktree);
|
|
9630
9635
|
}
|
|
9631
9636
|
function optimaCodemapPath(worktree) {
|
|
9632
|
-
return
|
|
9637
|
+
return path6.join(optimaDir(worktree), "codemap.yml");
|
|
9633
9638
|
}
|
|
9634
9639
|
function optimaTasksDir(worktree) {
|
|
9635
|
-
return
|
|
9640
|
+
return path6.join(optimaDir(worktree), "tasks");
|
|
9636
9641
|
}
|
|
9637
9642
|
function optimaEvidencesDir(worktree) {
|
|
9638
|
-
return
|
|
9643
|
+
return path6.join(optimaDir(worktree), "evidences");
|
|
9639
9644
|
}
|
|
9640
9645
|
function optimaScrsDir(worktree) {
|
|
9641
|
-
return
|
|
9646
|
+
return path6.join(optimaDir(worktree), "docs", "scrs");
|
|
9642
9647
|
}
|
|
9643
9648
|
function legacyNomadworkDir(worktree) {
|
|
9644
|
-
return
|
|
9649
|
+
return path6.join(worktree, LEGACY_NOMADWORK_DIRNAME);
|
|
9645
9650
|
}
|
|
9646
9651
|
function legacyNomadworksDir(worktree) {
|
|
9647
|
-
return
|
|
9652
|
+
return path6.join(worktree, LEGACY_NOMADWORKS_DIRNAME);
|
|
9648
9653
|
}
|
|
9649
9654
|
function legacyOrbitaDir(worktree) {
|
|
9650
|
-
return
|
|
9655
|
+
return path6.join(worktree, LEGACY_ORBITA_DIRNAME);
|
|
9651
9656
|
}
|
|
9652
9657
|
function legacyStaticEngDir(worktree) {
|
|
9653
|
-
return
|
|
9658
|
+
return path6.join(worktree, LEGACY_STATICENG_DIRNAME);
|
|
9654
9659
|
}
|
|
9655
9660
|
function repoConfigPath(worktree) {
|
|
9656
|
-
return
|
|
9661
|
+
return path6.join(optimaConfigDir(worktree), "optima.yaml");
|
|
9657
9662
|
}
|
|
9658
9663
|
function normalizeLegacyDiscussionEntry(entry) {
|
|
9659
9664
|
if (!entry || typeof entry !== "object") return entry;
|
|
@@ -9666,25 +9671,25 @@ function normalizeLegacyDiscussionEntry(entry) {
|
|
|
9666
9671
|
return next;
|
|
9667
9672
|
}
|
|
9668
9673
|
function repoPoliciesDir(worktree) {
|
|
9669
|
-
return
|
|
9674
|
+
return path6.join(optimaDir(worktree), "policies");
|
|
9670
9675
|
}
|
|
9671
9676
|
function generatedPoliciesDir(worktree) {
|
|
9672
|
-
return
|
|
9677
|
+
return path6.join(optimaLocalConfigDir(worktree), "generated", "policies");
|
|
9673
9678
|
}
|
|
9674
9679
|
function generatedAgentsDir(worktree) {
|
|
9675
|
-
return
|
|
9680
|
+
return path6.join(optimaLocalConfigDir(worktree), "generated", "agents");
|
|
9676
9681
|
}
|
|
9677
9682
|
function repoAgentsDir(worktree) {
|
|
9678
|
-
return
|
|
9683
|
+
return path6.join(optimaDir(worktree), "agents");
|
|
9679
9684
|
}
|
|
9680
9685
|
function repoAgentAdditionsDir(worktree) {
|
|
9681
|
-
return
|
|
9686
|
+
return path6.join(optimaDir(worktree), "agent-additions");
|
|
9682
9687
|
}
|
|
9683
9688
|
function legacyRepoAgentsDir(worktree) {
|
|
9684
|
-
return
|
|
9689
|
+
return path6.join(legacyNomadworksDir(worktree), "agents");
|
|
9685
9690
|
}
|
|
9686
9691
|
function runtimeDiscussionRegistryPath(worktree) {
|
|
9687
|
-
return
|
|
9692
|
+
return path6.join(optimaLocalConfigDir(worktree), "runtime", "discussions.json");
|
|
9688
9693
|
}
|
|
9689
9694
|
function resolveConfigPath(worktree) {
|
|
9690
9695
|
return repoConfigPath(worktree);
|
|
@@ -9727,32 +9732,32 @@ function mergeClickUpAgentMetadata(existing, update = {}) {
|
|
|
9727
9732
|
return JSON.stringify(sortJsonValue(merged), null, 2);
|
|
9728
9733
|
}
|
|
9729
9734
|
function optimaRuntimeDir(worktree) {
|
|
9730
|
-
return
|
|
9735
|
+
return path6.join(optimaLocalConfigDir(worktree), "runtime");
|
|
9731
9736
|
}
|
|
9732
9737
|
function clickUpWebhookStatePath(worktree) {
|
|
9733
|
-
return
|
|
9738
|
+
return path6.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
|
|
9734
9739
|
}
|
|
9735
9740
|
function clickUpCommentLedgerPath(worktree) {
|
|
9736
|
-
return
|
|
9741
|
+
return path6.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
|
|
9737
9742
|
}
|
|
9738
9743
|
function clickUpWebhookLogPath(worktree) {
|
|
9739
|
-
return
|
|
9744
|
+
return path6.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
9740
9745
|
}
|
|
9741
9746
|
function normalizeClickUpWebhookLogLevel(value) {
|
|
9742
9747
|
const level = String(value || "info").trim().toLowerCase();
|
|
9743
9748
|
return CLICKUP_WEBHOOK_LOG_LEVELS.has(level) ? level : "info";
|
|
9744
9749
|
}
|
|
9745
9750
|
function clickUpWebhookAuditLogDir() {
|
|
9746
|
-
const dataHome = process.env.XDG_DATA_HOME &&
|
|
9747
|
-
return
|
|
9751
|
+
const dataHome = process.env.XDG_DATA_HOME && path6.isAbsolute(process.env.XDG_DATA_HOME) ? process.env.XDG_DATA_HOME : path6.join(os.homedir(), ".local", "share", "opencode");
|
|
9752
|
+
return path6.join(dataHome, "opencode-optima");
|
|
9748
9753
|
}
|
|
9749
9754
|
function clickUpWebhookAuditLogPath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
9750
|
-
return
|
|
9755
|
+
return path6.join(logDir, `clickup-webhook-${at.toISOString().slice(0, 10)}.jsonl`);
|
|
9751
9756
|
}
|
|
9752
9757
|
function clickUpWebhookRequestFilePath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
9753
9758
|
const stamp = at.toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
9754
9759
|
const shortId = crypto.randomBytes(4).toString("hex");
|
|
9755
|
-
return
|
|
9760
|
+
return path6.join(logDir, "requests", `clickup-webhook-${stamp}-${shortId}.json`);
|
|
9756
9761
|
}
|
|
9757
9762
|
function findOptimaPluginTupleOptions(pluginEntries = []) {
|
|
9758
9763
|
if (!Array.isArray(pluginEntries)) return null;
|
|
@@ -9814,6 +9819,10 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9814
9819
|
startupReconciliationDelayMs: normalizeNonNegativeInteger(
|
|
9815
9820
|
opencode.startup_reconciliation_delay_ms ?? opencode.startupReconciliationDelayMs ?? raw.startup_reconciliation_delay_ms ?? raw.startupReconciliationDelayMs,
|
|
9816
9821
|
CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
|
|
9822
|
+
),
|
|
9823
|
+
assignmentWatchdogIntervalMs: normalizeNonNegativeInteger(
|
|
9824
|
+
opencode.assignment_watchdog_interval_ms ?? opencode.assignmentWatchdogIntervalMs ?? raw.assignment_watchdog_interval_ms ?? raw.assignmentWatchdogIntervalMs,
|
|
9825
|
+
CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS
|
|
9817
9826
|
)
|
|
9818
9827
|
},
|
|
9819
9828
|
openchamber: {
|
|
@@ -9847,7 +9856,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9847
9856
|
};
|
|
9848
9857
|
const errors = [];
|
|
9849
9858
|
if (!config.enabled) errors.push("clickup.enabled must be true");
|
|
9850
|
-
if (!config.basePath || !
|
|
9859
|
+
if (!config.basePath || !path6.isAbsolute(config.basePath)) errors.push("clickup.base_path must be an absolute path");
|
|
9851
9860
|
if (!config.teamId) errors.push("clickup.team_id is required");
|
|
9852
9861
|
if (!config.apiToken) errors.push("clickup.api_token is required");
|
|
9853
9862
|
if (!config.webhook.publicUrl) errors.push("clickup.webhook.public_url is required");
|
|
@@ -9905,7 +9914,7 @@ function readClickUpWebhookState(worktree, config = null) {
|
|
|
9905
9914
|
}
|
|
9906
9915
|
function writeClickUpWebhookState(worktree, state, config = null) {
|
|
9907
9916
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9908
|
-
fs5.mkdirSync(
|
|
9917
|
+
fs5.mkdirSync(path6.dirname(statePath), { recursive: true, mode: 448 });
|
|
9909
9918
|
const next = sanitizeClickUpWebhookState(state, config);
|
|
9910
9919
|
fs5.writeFileSync(statePath, `${JSON.stringify(next, null, 2)}
|
|
9911
9920
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -9926,7 +9935,7 @@ function isClickUpWebhookStateActive(state, config) {
|
|
|
9926
9935
|
function expandHomePath(value = "") {
|
|
9927
9936
|
const input = String(value || "").trim();
|
|
9928
9937
|
if (input === "~") return os.homedir();
|
|
9929
|
-
if (input.startsWith("~/")) return
|
|
9938
|
+
if (input.startsWith("~/")) return path6.join(os.homedir(), input.slice(2));
|
|
9930
9939
|
return input;
|
|
9931
9940
|
}
|
|
9932
9941
|
function resolveSecretReference(value = "") {
|
|
@@ -10302,7 +10311,7 @@ function readClickUpCommentLedger(ledgerPath) {
|
|
|
10302
10311
|
}
|
|
10303
10312
|
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
10304
10313
|
if (!ledgerPath || !entry.key) return;
|
|
10305
|
-
fs5.mkdirSync(
|
|
10314
|
+
fs5.mkdirSync(path6.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
10306
10315
|
fs5.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
10307
10316
|
`, { encoding: "utf8", mode: 384 });
|
|
10308
10317
|
try {
|
|
@@ -10779,17 +10788,87 @@ function openCodeResultSummary(result) {
|
|
|
10779
10788
|
status: result?.status || result?.response?.status || null
|
|
10780
10789
|
};
|
|
10781
10790
|
}
|
|
10791
|
+
function openCodeMessagePayload(message = {}) {
|
|
10792
|
+
if (!isPlainObject(message)) return {};
|
|
10793
|
+
const rawData = message.data;
|
|
10794
|
+
if (isPlainObject(rawData)) {
|
|
10795
|
+
return {
|
|
10796
|
+
...rawData,
|
|
10797
|
+
id: rawData.id || message.id,
|
|
10798
|
+
time_created: rawData.time_created ?? message.time_created,
|
|
10799
|
+
time_updated: rawData.time_updated ?? message.time_updated
|
|
10800
|
+
};
|
|
10801
|
+
}
|
|
10802
|
+
if (typeof rawData === "string" && rawData.trim()) {
|
|
10803
|
+
try {
|
|
10804
|
+
const parsed = JSON.parse(rawData);
|
|
10805
|
+
if (isPlainObject(parsed)) {
|
|
10806
|
+
return {
|
|
10807
|
+
...parsed,
|
|
10808
|
+
id: parsed.id || message.id,
|
|
10809
|
+
time_created: parsed.time_created ?? message.time_created,
|
|
10810
|
+
time_updated: parsed.time_updated ?? message.time_updated
|
|
10811
|
+
};
|
|
10812
|
+
}
|
|
10813
|
+
} catch {
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
return message;
|
|
10817
|
+
}
|
|
10782
10818
|
function normalizeOpenCodeMessageRole(message = {}) {
|
|
10783
|
-
|
|
10819
|
+
const payload = openCodeMessagePayload(message);
|
|
10820
|
+
return payload.role || payload.info?.role || payload.author?.role || payload.type || "";
|
|
10784
10821
|
}
|
|
10785
10822
|
function normalizeOpenCodeMessageAgent(message = {}) {
|
|
10786
|
-
|
|
10823
|
+
const payload = openCodeMessagePayload(message);
|
|
10824
|
+
return payload.agent || payload.info?.agent || payload.mode?.agent || payload.author?.agent || "";
|
|
10787
10825
|
}
|
|
10788
10826
|
function normalizeOpenCodeMessageMode(message = {}) {
|
|
10789
|
-
|
|
10827
|
+
const payload = openCodeMessagePayload(message);
|
|
10828
|
+
return payload.mode || payload.info?.mode || payload.metadata?.mode || "";
|
|
10790
10829
|
}
|
|
10791
10830
|
function normalizeOpenCodeMessageId(message = {}) {
|
|
10792
|
-
|
|
10831
|
+
const payload = openCodeMessagePayload(message);
|
|
10832
|
+
return payload.id || payload.messageID || payload.messageId || payload.info?.id || payload.data?.id || message.id || null;
|
|
10833
|
+
}
|
|
10834
|
+
function openCodeMessageTimestampMs(message = {}, key = "updated") {
|
|
10835
|
+
const payload = openCodeMessagePayload(message);
|
|
10836
|
+
const time = payload.time || payload.info?.time || {};
|
|
10837
|
+
const value = key === "completed" ? payload.time_completed ?? payload.timeCompleted ?? time.completed : key === "created" ? payload.time_created ?? payload.timeCreated ?? time.created : payload.time_updated ?? payload.timeUpdated ?? time.updated ?? payload.time_created ?? payload.timeCreated ?? time.created;
|
|
10838
|
+
const number = Number(value);
|
|
10839
|
+
return Number.isFinite(number) && number > 0 ? number : 0;
|
|
10840
|
+
}
|
|
10841
|
+
function isOpenCodeAssistantMessageRunning(message = {}) {
|
|
10842
|
+
if (normalizeLooseToken(normalizeOpenCodeMessageRole(message)) !== "assistant") return false;
|
|
10843
|
+
const started = openCodeMessageTimestampMs(message, "created") > 0;
|
|
10844
|
+
if (!started) return false;
|
|
10845
|
+
return openCodeMessageTimestampMs(message, "completed") === 0;
|
|
10846
|
+
}
|
|
10847
|
+
async function inspectOpenCodeSessionActivity(client, { sessionId, directory, limit = 10 } = {}) {
|
|
10848
|
+
const messages = await readOpenCodeSessionMessages(client, { sessionId, directory, limit });
|
|
10849
|
+
if (!messages) return { ok: false, reason: "message_inspection_unavailable", sessionId, directory };
|
|
10850
|
+
const enriched = messages.map((message, index) => ({
|
|
10851
|
+
message,
|
|
10852
|
+
index,
|
|
10853
|
+
sortTime: openCodeMessageTimestampMs(message, "updated") || index,
|
|
10854
|
+
createdAt: openCodeMessageTimestampMs(message, "created"),
|
|
10855
|
+
completedAt: openCodeMessageTimestampMs(message, "completed")
|
|
10856
|
+
})).sort((a, b) => a.sortTime - b.sortTime || a.index - b.index);
|
|
10857
|
+
const assistantMessages = enriched.filter((entry) => normalizeLooseToken(normalizeOpenCodeMessageRole(entry.message)) === "assistant");
|
|
10858
|
+
const latestAssistant = assistantMessages.at(-1) || null;
|
|
10859
|
+
const latest = enriched.at(-1) || null;
|
|
10860
|
+
const running = latestAssistant ? isOpenCodeAssistantMessageRunning(latestAssistant.message) : false;
|
|
10861
|
+
return {
|
|
10862
|
+
ok: true,
|
|
10863
|
+
sessionId,
|
|
10864
|
+
directory,
|
|
10865
|
+
count: messages.length,
|
|
10866
|
+
running,
|
|
10867
|
+
latestMessageId: latest ? normalizeOpenCodeMessageId(latest.message) : null,
|
|
10868
|
+
latestAssistantMessageId: latestAssistant ? normalizeOpenCodeMessageId(latestAssistant.message) : null,
|
|
10869
|
+
latestAssistantCreatedAt: latestAssistant?.createdAt || 0,
|
|
10870
|
+
latestAssistantCompletedAt: latestAssistant?.completedAt || 0
|
|
10871
|
+
};
|
|
10793
10872
|
}
|
|
10794
10873
|
function summarizeOpenCodeMessages(messages = [], { snippetLength = 160, maxMessages = 50 } = {}) {
|
|
10795
10874
|
const list = Array.isArray(messages) ? messages.slice(0, maxMessages) : [];
|
|
@@ -10929,8 +11008,9 @@ async function probeOpenCodeSessionControl(client, { directory, agent, omitAgent
|
|
|
10929
11008
|
};
|
|
10930
11009
|
}
|
|
10931
11010
|
function openCodeMessageText(message) {
|
|
10932
|
-
const
|
|
10933
|
-
const
|
|
11011
|
+
const payload = openCodeMessagePayload(message);
|
|
11012
|
+
const parts = [payload?.text, payload?.content, payload?.message, payload?.body?.text, payload?.data?.text];
|
|
11013
|
+
const partList = Array.isArray(payload?.parts) ? payload.parts : Array.isArray(payload?.body?.parts) ? payload.body.parts : [];
|
|
10934
11014
|
for (const part of partList) parts.push(part?.text, part?.content);
|
|
10935
11015
|
return parts.filter((value) => typeof value === "string").join("\n");
|
|
10936
11016
|
}
|
|
@@ -11094,7 +11174,7 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
11094
11174
|
}
|
|
11095
11175
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
11096
11176
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
11097
|
-
fs5.mkdirSync(
|
|
11177
|
+
fs5.mkdirSync(path6.dirname(logPath), { recursive: true });
|
|
11098
11178
|
const safeEntry = { ...entry, at: entry.at || (/* @__PURE__ */ new Date()).toISOString() };
|
|
11099
11179
|
fs5.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
11100
11180
|
`, "utf8");
|
|
@@ -11125,7 +11205,7 @@ function closeClickUpWebhookServer(server) {
|
|
|
11125
11205
|
});
|
|
11126
11206
|
}
|
|
11127
11207
|
function managedClickUpWebhookKey({ worktree, state, config } = {}) {
|
|
11128
|
-
return [
|
|
11208
|
+
return [path6.resolve(worktree || process.cwd()), state?.webhookId || "", config?.webhook?.publicUrl || ""].join("|");
|
|
11129
11209
|
}
|
|
11130
11210
|
async function deleteClickUpWebhookBestEffort({ webhookId, clickupClient, worktree, reason = "cleanup" } = {}) {
|
|
11131
11211
|
const id = String(webhookId || "").trim();
|
|
@@ -11148,6 +11228,7 @@ async function cleanupManagedClickUpWebhook(entry = {}, { timeoutMs = CLICKUP_WE
|
|
|
11148
11228
|
entry.cleaning = true;
|
|
11149
11229
|
const cleanup = (async () => {
|
|
11150
11230
|
clickUpWebhookLifecycleLog(entry.worktree, { type: "cleanup_started", reason, key: entry.key, webhookId: entry.state?.webhookId });
|
|
11231
|
+
if (entry.assignmentWatchdog?.interval) clearInterval(entry.assignmentWatchdog.interval);
|
|
11151
11232
|
const closeResult = await closeClickUpWebhookServer(entry.listener?.server);
|
|
11152
11233
|
if (entry.listenerRegistry && entry.listener?.key) entry.listenerRegistry.delete(entry.listener.key);
|
|
11153
11234
|
const preservedResult = { ok: true, skipped: true, reason: "remote_webhook_preserved" };
|
|
@@ -11161,10 +11242,10 @@ async function cleanupManagedClickUpWebhook(entry = {}, { timeoutMs = CLICKUP_WE
|
|
|
11161
11242
|
if (result?.timeout) clickUpWebhookLifecycleLog(entry.worktree, { type: "cleanup_timeout", reason, webhookId: entry.state?.webhookId });
|
|
11162
11243
|
return result;
|
|
11163
11244
|
}
|
|
11164
|
-
function registerClickUpWebhookLifecycle({ config, state, worktree, clickupClient, listener, listenerRegistry = activeClickUpWebhookListeners } = {}) {
|
|
11245
|
+
function registerClickUpWebhookLifecycle({ config, state, worktree, clickupClient, listener, listenerRegistry = activeClickUpWebhookListeners, assignmentWatchdog = null } = {}) {
|
|
11165
11246
|
if (!isClickUpWebhookStateActive(state, config)) return null;
|
|
11166
11247
|
const key = managedClickUpWebhookKey({ worktree, state, config });
|
|
11167
|
-
const entry = { key, config, state, worktree, clickupClient, listener, listenerRegistry, registeredAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
11248
|
+
const entry = { key, config, state, worktree, clickupClient, listener, listenerRegistry, assignmentWatchdog, registeredAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
11168
11249
|
activeClickUpWebhookLifecycleRegistry.set(key, entry);
|
|
11169
11250
|
installClickUpWebhookSignalHandlers();
|
|
11170
11251
|
clickUpWebhookLifecycleLog(worktree, { type: "lifecycle_registered", key, webhookId: state.webhookId });
|
|
@@ -11229,8 +11310,8 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
11229
11310
|
let requestFile;
|
|
11230
11311
|
if (level === "verbose") {
|
|
11231
11312
|
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
11232
|
-
fs5.mkdirSync(
|
|
11233
|
-
requestFile =
|
|
11313
|
+
fs5.mkdirSync(path6.dirname(absoluteRequestFile), { recursive: true });
|
|
11314
|
+
requestFile = path6.relative(logDir, absoluteRequestFile).split(path6.sep).join("/");
|
|
11234
11315
|
const parsedBody = payload || (() => {
|
|
11235
11316
|
try {
|
|
11236
11317
|
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
@@ -11454,19 +11535,54 @@ function scheduleClickUpStartupReconciliation({ config, state = {}, worktree = p
|
|
|
11454
11535
|
timer?.unref?.();
|
|
11455
11536
|
return { scheduled: true, delayMs: normalizedDelayMs, timer };
|
|
11456
11537
|
}
|
|
11457
|
-
|
|
11458
|
-
|
|
11538
|
+
function scheduleClickUpAssignmentWatchdog({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, scheduler = setInterval, intervalMs = config?.opencode?.assignmentWatchdogIntervalMs ?? CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS, saveState = null, reconcile = reconcileClickUpStartup } = {}) {
|
|
11539
|
+
const normalizedIntervalMs = normalizeNonNegativeInteger(intervalMs, CLICKUP_ASSIGNMENT_WATCHDOG_INTERVAL_MS);
|
|
11540
|
+
if (!normalizedIntervalMs) {
|
|
11541
|
+
clickUpWebhookLifecycleLog(worktree, { type: "assignment_watchdog_disabled", webhookId: state?.webhookId || null });
|
|
11542
|
+
return { scheduled: false, intervalMs: 0, reason: "disabled" };
|
|
11543
|
+
}
|
|
11544
|
+
let running = false;
|
|
11545
|
+
const run = () => {
|
|
11546
|
+
if (running) {
|
|
11547
|
+
appendClickUpWebhookLocalLog(worktree, { type: "assignment_watchdog_tick_skipped", reason: "previous_tick_running" });
|
|
11548
|
+
return;
|
|
11549
|
+
}
|
|
11550
|
+
running = true;
|
|
11551
|
+
const latestState = readClickUpWebhookState(worktree, config);
|
|
11552
|
+
Promise.resolve().then(() => reconcile({
|
|
11553
|
+
config,
|
|
11554
|
+
state: latestState?.webhookId ? latestState : state,
|
|
11555
|
+
worktree,
|
|
11556
|
+
clickupClient,
|
|
11557
|
+
openCodeClient,
|
|
11558
|
+
saveState,
|
|
11559
|
+
assignmentMode: "watchdog"
|
|
11560
|
+
})).catch((error) => {
|
|
11561
|
+
clickUpWebhookLifecycleLog(worktree, { type: "assignment_watchdog_failed", message: error.message });
|
|
11562
|
+
appendClickUpWebhookLocalLog(worktree, { type: "assignment_watchdog_failed", message: error.message });
|
|
11563
|
+
}).finally(() => {
|
|
11564
|
+
running = false;
|
|
11565
|
+
});
|
|
11566
|
+
};
|
|
11567
|
+
const interval = scheduler(run, normalizedIntervalMs);
|
|
11568
|
+
interval?.unref?.();
|
|
11569
|
+
clickUpWebhookLifecycleLog(worktree, { type: "assignment_watchdog_scheduled", intervalMs: normalizedIntervalMs, webhookId: state?.webhookId || null });
|
|
11570
|
+
return { scheduled: true, intervalMs: normalizedIntervalMs, interval };
|
|
11571
|
+
}
|
|
11572
|
+
async function reconcileClickUpStartup({ config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, waitForReadiness = waitForOpenCodeReadiness, saveState = null, now = () => /* @__PURE__ */ new Date(), limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT, assignmentMode = "startup" } = {}) {
|
|
11573
|
+
const watchdogMode = assignmentMode === "watchdog";
|
|
11574
|
+
clickUpWebhookLifecycleLog(worktree, { type: watchdogMode ? "assignment_watchdog_started" : "startup_reconciliation_started", lastWebhookAt: state.lastWebhookAt || null });
|
|
11459
11575
|
if (!config || !clickupClient?.listAssignedTasks) {
|
|
11460
11576
|
const skipped = { ok: true, skipped: true, reason: "clickup_task_listing_unavailable", assigned: 0, comments: 0 };
|
|
11461
|
-
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
11577
|
+
clickUpWebhookLifecycleLog(worktree, { type: watchdogMode ? "assignment_watchdog_finished" : "startup_reconciliation_finished", ...skipped });
|
|
11462
11578
|
return skipped;
|
|
11463
11579
|
}
|
|
11464
11580
|
const readiness = await waitForReadiness(openCodeClient, { worktree, now, opencodeBaseUrl: config.opencode?.baseUrl });
|
|
11465
11581
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
11466
11582
|
if (!readiness.ok) {
|
|
11467
11583
|
const failed = { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
|
|
11468
|
-
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_failed", reason: failed.reason, readiness });
|
|
11469
|
-
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...failed });
|
|
11584
|
+
clickUpWebhookLifecycleLog(worktree, { type: watchdogMode ? "assignment_watchdog_failed" : "startup_reconciliation_failed", reason: failed.reason, readiness });
|
|
11585
|
+
clickUpWebhookLifecycleLog(worktree, { type: watchdogMode ? "assignment_watchdog_finished" : "startup_reconciliation_finished", ...failed });
|
|
11470
11586
|
return failed;
|
|
11471
11587
|
}
|
|
11472
11588
|
let authorizedUserId = "";
|
|
@@ -11484,6 +11600,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11484
11600
|
if (saveState) saveState(nextState);
|
|
11485
11601
|
};
|
|
11486
11602
|
const lastWebhookMs = clickUpTimestampMs(mutableState.lastWebhookAt);
|
|
11603
|
+
const startupCommentStartMs = lastWebhookMs || Math.max(0, now().getTime() - CLICKUP_WEBHOOK_STARTUP_COMMENT_LOOKBACK_MS);
|
|
11487
11604
|
const ignored = new Set((config.routing?.ignoredStatuses || CLICKUP_WEBHOOK_TERMINAL_STATUSES).map(normalizeClickUpStatus));
|
|
11488
11605
|
const routed = { assigned: 0, comments: 0, ignored: 0, errors: 0, undelivered: 0, tasks: [] };
|
|
11489
11606
|
const listed = await clickupClient.listAssignedTasks({ assigneeId: config.routing.productManagerAssigneeId, limit });
|
|
@@ -11502,9 +11619,29 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11502
11619
|
routed.ignored += 1;
|
|
11503
11620
|
continue;
|
|
11504
11621
|
}
|
|
11622
|
+
if (watchdogMode) {
|
|
11623
|
+
const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
|
|
11624
|
+
const metadata = normalizeAgentMetadataJson(existingMetadata);
|
|
11625
|
+
const rawSessionId = getNestedMetadataValue(metadata, config.routing.metadataKey);
|
|
11626
|
+
const sessionId = typeof rawSessionId === "string" && rawSessionId.startsWith("ses_") ? rawSessionId : "";
|
|
11627
|
+
const directory = String(getNestedMetadataValue(metadata, "task.worktree") || "").trim();
|
|
11628
|
+
if (sessionId) {
|
|
11629
|
+
try {
|
|
11630
|
+
const activity = await inspectOpenCodeSessionActivity(openCodeClient, { sessionId, directory });
|
|
11631
|
+
appendClickUpWebhookLocalLog(worktree, { type: "assignment_watchdog_session_activity", taskId, sessionId, directory: directory || null, ...activity });
|
|
11632
|
+
if (activity.running) {
|
|
11633
|
+
routed.ignored += 1;
|
|
11634
|
+
routed.tasks.push({ taskId, action: "ignored", ok: true, sessionId, branch: getNestedMetadataValue(metadata, "task.branch") || null, worktree: directory || null, verification: "session_running", delivered: false });
|
|
11635
|
+
continue;
|
|
11636
|
+
}
|
|
11637
|
+
} catch (error) {
|
|
11638
|
+
appendClickUpWebhookLocalLog(worktree, { type: "assignment_watchdog_session_activity_failed", taskId, sessionId, directory: directory || null, message: error.message });
|
|
11639
|
+
}
|
|
11640
|
+
}
|
|
11641
|
+
}
|
|
11505
11642
|
try {
|
|
11506
11643
|
const result = await routeClickUpWebhookEvent({
|
|
11507
|
-
payload: { webhook_id: mutableState.webhookId || "startup", event: "taskAssigneeUpdated", task_id: taskId, task, startup_reconciliation: true },
|
|
11644
|
+
payload: { webhook_id: mutableState.webhookId || "startup", event: "taskAssigneeUpdated", task_id: taskId, task, startup_reconciliation: true, assignment_watchdog: watchdogMode },
|
|
11508
11645
|
config,
|
|
11509
11646
|
state: mutableState,
|
|
11510
11647
|
worktree,
|
|
@@ -11554,12 +11691,12 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11554
11691
|
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_undelivered", taskId, action: "error", sessionId: null, reason: "startup_reconciliation_task_failed" });
|
|
11555
11692
|
await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "startup_reconciliation_task_failed", source: "startup_reconciliation_task" });
|
|
11556
11693
|
}
|
|
11557
|
-
if (!
|
|
11694
|
+
if (!startupCommentStartMs || !clickupClient?.getTaskComments) continue;
|
|
11558
11695
|
try {
|
|
11559
|
-
const commentsResponse = await clickupClient.getTaskComments({ taskId, start:
|
|
11696
|
+
const commentsResponse = await clickupClient.getTaskComments({ taskId, start: startupCommentStartMs, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
|
|
11560
11697
|
for (const comment of clickUpCommentListItems(commentsResponse).slice(0, CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT)) {
|
|
11561
11698
|
const commentMs = clickUpCommentUpdatedMs(comment);
|
|
11562
|
-
if (!commentMs || commentMs <=
|
|
11699
|
+
if (!commentMs || commentMs <= startupCommentStartMs) continue;
|
|
11563
11700
|
const authorId = clickUpCommentAuthorId(comment);
|
|
11564
11701
|
if (authorId && (authorId === config.routing.ignoredCommentAuthorId || authorId === authorizedUserId)) continue;
|
|
11565
11702
|
if (!clickUpCommentMentionsProductManager(comment, config.routing)) continue;
|
|
@@ -11586,11 +11723,11 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11586
11723
|
await applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason: "startup_reconciliation_comments_failed", source: "startup_reconciliation_comments" });
|
|
11587
11724
|
}
|
|
11588
11725
|
}
|
|
11589
|
-
const emptyPromptSessions = routed.tasks.filter((task) => task.delivered === false && task.sessionId);
|
|
11726
|
+
const emptyPromptSessions = routed.tasks.filter((task) => task.delivered === false && task.sessionId && task.verification !== "session_running");
|
|
11590
11727
|
if (emptyPromptSessions.length > 0) {
|
|
11591
11728
|
appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_validation_failed", tasks: emptyPromptSessions });
|
|
11592
11729
|
}
|
|
11593
|
-
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...routed });
|
|
11730
|
+
clickUpWebhookLifecycleLog(worktree, { type: watchdogMode ? "assignment_watchdog_finished" : "startup_reconciliation_finished", ...routed });
|
|
11594
11731
|
return { ok: routed.errors === 0 && routed.undelivered === 0, ...routed, validation: { undelivered: routed.undelivered, emptyPromptSessions } };
|
|
11595
11732
|
}
|
|
11596
11733
|
function clickUpWebhookExpectedPath(config) {
|
|
@@ -11785,17 +11922,17 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, o
|
|
|
11785
11922
|
return result;
|
|
11786
11923
|
}
|
|
11787
11924
|
function legacyVariantPath(destinationPath) {
|
|
11788
|
-
const parsed =
|
|
11925
|
+
const parsed = path6.parse(destinationPath);
|
|
11789
11926
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
11790
|
-
if (parsed.ext) return
|
|
11791
|
-
return
|
|
11927
|
+
if (parsed.ext) return path6.join(parsed.dir, `${parsed.name}.legacy-${stamp}${parsed.ext}`);
|
|
11928
|
+
return path6.join(parsed.dir, `${parsed.base}.legacy-${stamp}`);
|
|
11792
11929
|
}
|
|
11793
11930
|
function normalizeWorkflowTaskPath(taskPath) {
|
|
11794
11931
|
if (typeof taskPath !== "string") return { ok: false, message: "Error: task_path is required." };
|
|
11795
11932
|
const trimmed = taskPath.trim();
|
|
11796
11933
|
if (!trimmed) return { ok: false, message: "Error: task_path is required." };
|
|
11797
11934
|
const normalized = trimmed.replace(/\\/g, "/");
|
|
11798
|
-
if (
|
|
11935
|
+
if (path6.isAbsolute(trimmed)) return { ok: true, taskPath: trimmed };
|
|
11799
11936
|
if (normalized === "tasks" || normalized.startsWith("tasks/")) {
|
|
11800
11937
|
return {
|
|
11801
11938
|
ok: false,
|
|
@@ -11814,12 +11951,12 @@ function mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, g
|
|
|
11814
11951
|
const sourceWasTracked = isGitTracked(gitState, sourcePath);
|
|
11815
11952
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
11816
11953
|
if (!fs5.existsSync(destinationPath)) {
|
|
11817
|
-
fs5.mkdirSync(
|
|
11954
|
+
fs5.mkdirSync(path6.dirname(destinationPath), { recursive: true });
|
|
11818
11955
|
fs5.renameSync(sourcePath, destinationPath);
|
|
11819
11956
|
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
11820
11957
|
return;
|
|
11821
11958
|
}
|
|
11822
|
-
const ext =
|
|
11959
|
+
const ext = path6.extname(destinationPath).toLowerCase();
|
|
11823
11960
|
if ([".yaml", ".yml", ".json"].includes(ext)) {
|
|
11824
11961
|
const sourceRaw = fs5.readFileSync(sourcePath, "utf8");
|
|
11825
11962
|
const destRaw = fs5.readFileSync(destinationPath, "utf8");
|
|
@@ -11854,14 +11991,14 @@ ${sourceRaw}
|
|
|
11854
11991
|
fs5.renameSync(sourcePath, preservedPath);
|
|
11855
11992
|
stageGitAwareMerge(sourcePath, preservedPath, gitState);
|
|
11856
11993
|
}
|
|
11857
|
-
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
11994
|
+
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource = path6.basename(sourcePath), gitState = null) {
|
|
11858
11995
|
if (!fs5.existsSync(sourcePath)) return;
|
|
11859
11996
|
const stat = fs5.statSync(sourcePath);
|
|
11860
11997
|
if (stat.isDirectory()) {
|
|
11861
11998
|
const sourceWasTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
11862
11999
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
11863
12000
|
if (!fs5.existsSync(destinationPath)) {
|
|
11864
|
-
fs5.mkdirSync(
|
|
12001
|
+
fs5.mkdirSync(path6.dirname(destinationPath), { recursive: true });
|
|
11865
12002
|
fs5.renameSync(sourcePath, destinationPath);
|
|
11866
12003
|
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
11867
12004
|
return;
|
|
@@ -11869,9 +12006,9 @@ function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
|
11869
12006
|
fs5.mkdirSync(destinationPath, { recursive: true });
|
|
11870
12007
|
for (const entry of fs5.readdirSync(sourcePath)) {
|
|
11871
12008
|
mergePathIntoDestination(
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
12009
|
+
path6.join(sourcePath, entry),
|
|
12010
|
+
path6.join(destinationPath, entry),
|
|
12011
|
+
path6.join(relativeSource, entry),
|
|
11875
12012
|
gitState
|
|
11876
12013
|
);
|
|
11877
12014
|
}
|
|
@@ -11881,7 +12018,7 @@ function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
|
11881
12018
|
mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState);
|
|
11882
12019
|
}
|
|
11883
12020
|
function isOptimaPluginPackageWorktree(worktree) {
|
|
11884
|
-
const packageJsonPath =
|
|
12021
|
+
const packageJsonPath = path6.join(worktree, "package.json");
|
|
11885
12022
|
if (!fs5.existsSync(packageJsonPath)) return false;
|
|
11886
12023
|
try {
|
|
11887
12024
|
const pkg = JSON.parse(fs5.readFileSync(packageJsonPath, "utf8"));
|
|
@@ -11893,51 +12030,51 @@ function isOptimaPluginPackageWorktree(worktree) {
|
|
|
11893
12030
|
function migrateLegacyOptimaLayout(worktree) {
|
|
11894
12031
|
const gitState = gitMigrationState(worktree);
|
|
11895
12032
|
const migrations = [
|
|
11896
|
-
[
|
|
11897
|
-
[
|
|
11898
|
-
[
|
|
11899
|
-
[
|
|
11900
|
-
[
|
|
11901
|
-
[
|
|
12033
|
+
[path6.join(legacyOrbitaDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path6.join(".orbita", "orbita.yaml")],
|
|
12034
|
+
[path6.join(legacyOrbitaDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path6.join(".orbita", "staticeng.yaml")],
|
|
12035
|
+
[path6.join(legacyOrbitaDir(worktree), ".config"), optimaLocalConfigDir(worktree), path6.join(".orbita", ".config")],
|
|
12036
|
+
[path6.join(legacyOrbitaDir(worktree), "config"), optimaLocalConfigDir(worktree), path6.join(".orbita", "config")],
|
|
12037
|
+
[path6.join(legacyOrbitaDir(worktree), "runtime"), path6.join(optimaLocalConfigDir(worktree), "runtime"), path6.join(".orbita", "runtime")],
|
|
12038
|
+
[path6.join(legacyOrbitaDir(worktree), "generated"), path6.join(optimaLocalConfigDir(worktree), "generated"), path6.join(".orbita", "generated")],
|
|
11902
12039
|
[legacyOrbitaDir(worktree), optimaDir(worktree), ".orbita"],
|
|
11903
|
-
[
|
|
11904
|
-
[
|
|
11905
|
-
[
|
|
11906
|
-
[
|
|
11907
|
-
[
|
|
11908
|
-
[
|
|
12040
|
+
[path6.join(legacyStaticEngDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path6.join(".staticeng", "staticeng.yaml")],
|
|
12041
|
+
[path6.join(legacyStaticEngDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path6.join(".staticeng", "orbita.yaml")],
|
|
12042
|
+
[path6.join(legacyStaticEngDir(worktree), ".config"), optimaLocalConfigDir(worktree), path6.join(".staticeng", ".config")],
|
|
12043
|
+
[path6.join(legacyStaticEngDir(worktree), "config"), optimaLocalConfigDir(worktree), path6.join(".staticeng", "config")],
|
|
12044
|
+
[path6.join(legacyStaticEngDir(worktree), "runtime"), path6.join(optimaLocalConfigDir(worktree), "runtime"), path6.join(".staticeng", "runtime")],
|
|
12045
|
+
[path6.join(legacyStaticEngDir(worktree), "generated"), path6.join(optimaLocalConfigDir(worktree), "generated"), path6.join(".staticeng", "generated")],
|
|
11909
12046
|
[legacyStaticEngDir(worktree), optimaDir(worktree), ".staticeng"],
|
|
11910
|
-
[
|
|
11911
|
-
[
|
|
11912
|
-
[
|
|
11913
|
-
[
|
|
11914
|
-
[
|
|
11915
|
-
[
|
|
11916
|
-
[
|
|
11917
|
-
[
|
|
11918
|
-
[
|
|
11919
|
-
[
|
|
11920
|
-
[
|
|
11921
|
-
[
|
|
12047
|
+
[path6.join(legacyNomadworkDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path6.join(".nomadwork", "nomadworks.yaml")],
|
|
12048
|
+
[path6.join(legacyNomadworksDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path6.join(".nomadworks", "nomadworks.yaml")],
|
|
12049
|
+
[path6.join(legacyNomadworkDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path6.join(".nomadwork", "staticeng.yaml")],
|
|
12050
|
+
[path6.join(legacyNomadworksDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path6.join(".nomadworks", "staticeng.yaml")],
|
|
12051
|
+
[path6.join(legacyNomadworkDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path6.join(".nomadwork", "orbita.yaml")],
|
|
12052
|
+
[path6.join(legacyNomadworksDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path6.join(".nomadworks", "orbita.yaml")],
|
|
12053
|
+
[path6.join(legacyNomadworkDir(worktree), "runtime"), path6.join(optimaLocalConfigDir(worktree), "runtime"), path6.join(".nomadwork", "runtime")],
|
|
12054
|
+
[path6.join(legacyNomadworksDir(worktree), "runtime"), path6.join(optimaLocalConfigDir(worktree), "runtime"), path6.join(".nomadworks", "runtime")],
|
|
12055
|
+
[path6.join(legacyNomadworkDir(worktree), "generated"), path6.join(optimaLocalConfigDir(worktree), "generated"), path6.join(".nomadwork", "generated")],
|
|
12056
|
+
[path6.join(legacyNomadworksDir(worktree), "generated"), path6.join(optimaLocalConfigDir(worktree), "generated"), path6.join(".nomadworks", "generated")],
|
|
12057
|
+
[path6.join(legacyNomadworkDir(worktree), "config"), optimaLocalConfigDir(worktree), path6.join(".nomadwork", "config")],
|
|
12058
|
+
[path6.join(legacyNomadworksDir(worktree), "config"), optimaLocalConfigDir(worktree), path6.join(".nomadworks", "config")],
|
|
11922
12059
|
[legacyNomadworkDir(worktree), optimaDir(worktree), ".nomadwork"],
|
|
11923
12060
|
[legacyNomadworksDir(worktree), optimaDir(worktree), ".nomadworks"],
|
|
11924
|
-
[
|
|
11925
|
-
[
|
|
11926
|
-
[
|
|
11927
|
-
[
|
|
11928
|
-
[
|
|
12061
|
+
[path6.join(worktree, "tasks"), optimaTasksDir(worktree), "tasks"],
|
|
12062
|
+
[path6.join(worktree, "evidences"), optimaEvidencesDir(worktree), "evidences"],
|
|
12063
|
+
[path6.join(worktree, "docs", "scrs"), optimaScrsDir(worktree), path6.join("docs", "scrs")],
|
|
12064
|
+
[path6.join(worktree, "codemap.yml"), optimaCodemapPath(worktree), "codemap.yml"],
|
|
12065
|
+
[path6.join(worktree, "codemap.yaml"), optimaCodemapPath(worktree), "codemap.yaml"]
|
|
11929
12066
|
];
|
|
11930
12067
|
if (!isOptimaPluginPackageWorktree(worktree)) {
|
|
11931
|
-
migrations.push([
|
|
12068
|
+
migrations.push([path6.join(worktree, "policies"), repoPoliciesDir(worktree), "policies"]);
|
|
11932
12069
|
}
|
|
11933
12070
|
for (const [source, destination, relativeSource] of migrations) {
|
|
11934
12071
|
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
11935
12072
|
}
|
|
11936
12073
|
for (const [source, destination, relativeSource] of [
|
|
11937
|
-
[
|
|
11938
|
-
[
|
|
11939
|
-
[
|
|
11940
|
-
[
|
|
12074
|
+
[path6.join(optimaDir(worktree), "optima.yaml"), repoConfigPath(worktree), path6.join(".optima", "optima.yaml")],
|
|
12075
|
+
[path6.join(optimaDir(worktree), "config"), optimaLocalConfigDir(worktree), path6.join(".optima", "config")],
|
|
12076
|
+
[path6.join(optimaDir(worktree), "runtime"), path6.join(optimaLocalConfigDir(worktree), "runtime"), path6.join(".optima", "runtime")],
|
|
12077
|
+
[path6.join(optimaDir(worktree), "generated"), path6.join(optimaLocalConfigDir(worktree), "generated"), path6.join(".optima", "generated")]
|
|
11941
12078
|
]) {
|
|
11942
12079
|
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
11943
12080
|
}
|
|
@@ -11986,7 +12123,7 @@ function toModelString(provider, model) {
|
|
|
11986
12123
|
}
|
|
11987
12124
|
function readTaskMetadata(taskPath, worktree) {
|
|
11988
12125
|
if (!taskPath) return {};
|
|
11989
|
-
const absoluteTaskPath =
|
|
12126
|
+
const absoluteTaskPath = path6.isAbsolute(taskPath) ? taskPath : path6.join(worktree, taskPath);
|
|
11990
12127
|
if (!fs5.existsSync(absoluteTaskPath)) return {};
|
|
11991
12128
|
try {
|
|
11992
12129
|
const raw = fs5.readFileSync(absoluteTaskPath, "utf8");
|
|
@@ -12031,18 +12168,18 @@ function loadDiscussionRegistry(worktree) {
|
|
|
12031
12168
|
}
|
|
12032
12169
|
function saveDiscussionRegistry(worktree, registry) {
|
|
12033
12170
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
12034
|
-
const runtimeDir =
|
|
12171
|
+
const runtimeDir = path6.dirname(registryPath);
|
|
12035
12172
|
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
12036
12173
|
fs5.writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf8");
|
|
12037
12174
|
}
|
|
12038
12175
|
function runtimeDiscussionsDir(worktree) {
|
|
12039
|
-
return
|
|
12176
|
+
return path6.join(optimaLocalConfigDir(worktree), "runtime", "discussions");
|
|
12040
12177
|
}
|
|
12041
12178
|
function archivedRuntimeDiscussionsDir(worktree) {
|
|
12042
|
-
return
|
|
12179
|
+
return path6.join(runtimeDiscussionsDir(worktree), "archive");
|
|
12043
12180
|
}
|
|
12044
12181
|
function finalDiscussionsDir(worktree) {
|
|
12045
|
-
return
|
|
12182
|
+
return path6.join(optimaTasksDir(worktree), "discussions");
|
|
12046
12183
|
}
|
|
12047
12184
|
function nextDiscussionIdentity(worktree, title) {
|
|
12048
12185
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
@@ -12053,11 +12190,11 @@ function nextDiscussionIdentity(worktree, title) {
|
|
|
12053
12190
|
while (true) {
|
|
12054
12191
|
const id = `DISCUSSION-${String(sequence).padStart(3, "0")}`;
|
|
12055
12192
|
const filename = `${id}-${slugifyTitle(title)}.md`;
|
|
12056
|
-
const summaryRelativePath =
|
|
12057
|
-
const summaryAbsolutePath =
|
|
12193
|
+
const summaryRelativePath = path6.join(".optima", "tasks", "discussions", filename);
|
|
12194
|
+
const summaryAbsolutePath = path6.join(worktree, summaryRelativePath);
|
|
12058
12195
|
const transcriptFilename = `${id}-transcript.md`;
|
|
12059
|
-
const transcriptRelativePath =
|
|
12060
|
-
const transcriptAbsolutePath =
|
|
12196
|
+
const transcriptRelativePath = path6.join(".optima", ".config", "runtime", "discussions", transcriptFilename);
|
|
12197
|
+
const transcriptAbsolutePath = path6.join(worktree, transcriptRelativePath);
|
|
12061
12198
|
if (!fs5.existsSync(summaryAbsolutePath) && !fs5.existsSync(transcriptAbsolutePath)) {
|
|
12062
12199
|
return {
|
|
12063
12200
|
id,
|
|
@@ -12082,11 +12219,11 @@ function findDiscussionById(worktree, discussionID) {
|
|
|
12082
12219
|
return {
|
|
12083
12220
|
id: discussionID,
|
|
12084
12221
|
filename,
|
|
12085
|
-
summaryRelativePath:
|
|
12086
|
-
summaryAbsolutePath:
|
|
12222
|
+
summaryRelativePath: path6.join(".optima", "tasks", "discussions", filename),
|
|
12223
|
+
summaryAbsolutePath: path6.join(discussionsDir, filename),
|
|
12087
12224
|
transcriptFilename,
|
|
12088
|
-
transcriptRelativePath:
|
|
12089
|
-
transcriptAbsolutePath:
|
|
12225
|
+
transcriptRelativePath: path6.join(".optima", ".config", "runtime", "discussions", transcriptFilename),
|
|
12226
|
+
transcriptAbsolutePath: path6.join(runtimeDiscussionsDir(worktree), transcriptFilename)
|
|
12090
12227
|
};
|
|
12091
12228
|
}
|
|
12092
12229
|
function parseDiscussionFile(filePath) {
|
|
@@ -12169,15 +12306,15 @@ async function appendMessageIfNeeded(client, worktree, registry, sessionID, mess
|
|
|
12169
12306
|
});
|
|
12170
12307
|
const text = extractTextParts(response.data.parts || []);
|
|
12171
12308
|
if (!text) return;
|
|
12172
|
-
appendDiscussionMessage(
|
|
12309
|
+
appendDiscussionMessage(path6.join(worktree, discussion.transcriptPath), speaker, text, messageID);
|
|
12173
12310
|
discussion.appendedMessageIDs ??= [];
|
|
12174
12311
|
discussion.appendedMessageIDs.push(messageID);
|
|
12175
12312
|
saveDiscussionRegistry(worktree, registry);
|
|
12176
12313
|
}
|
|
12177
12314
|
async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
12178
|
-
const transcriptPath =
|
|
12179
|
-
const summaryPath =
|
|
12180
|
-
const summaryDir =
|
|
12315
|
+
const transcriptPath = path6.join(worktree, discussion.transcriptPath);
|
|
12316
|
+
const summaryPath = path6.join(worktree, discussion.summaryPath);
|
|
12317
|
+
const summaryDir = path6.dirname(summaryPath);
|
|
12181
12318
|
if (!fs5.existsSync(summaryDir)) fs5.mkdirSync(summaryDir, { recursive: true });
|
|
12182
12319
|
const hasExistingSummary = fs5.existsSync(summaryPath);
|
|
12183
12320
|
const priorMtimeMs = hasExistingSummary ? fs5.statSync(summaryPath).mtimeMs : null;
|
|
@@ -12280,11 +12417,11 @@ async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
|
12280
12417
|
return { confirmation, summaryPath, transcriptPath, hasExistingSummary, priorMtimeMs };
|
|
12281
12418
|
}
|
|
12282
12419
|
function archiveDiscussionTranscript(worktree, transcriptRelativePath) {
|
|
12283
|
-
const sourcePath =
|
|
12420
|
+
const sourcePath = path6.join(worktree, transcriptRelativePath);
|
|
12284
12421
|
if (!fs5.existsSync(sourcePath)) return null;
|
|
12285
12422
|
const archiveDir = archivedRuntimeDiscussionsDir(worktree);
|
|
12286
12423
|
if (!fs5.existsSync(archiveDir)) fs5.mkdirSync(archiveDir, { recursive: true });
|
|
12287
|
-
const targetPath =
|
|
12424
|
+
const targetPath = path6.join(archiveDir, path6.basename(sourcePath));
|
|
12288
12425
|
fs5.renameSync(sourcePath, targetPath);
|
|
12289
12426
|
return targetPath;
|
|
12290
12427
|
}
|
|
@@ -12303,7 +12440,7 @@ async function finalizeClosingDiscussion(client, worktree, registry, sessionID,
|
|
|
12303
12440
|
if (!summaryContent) {
|
|
12304
12441
|
throw new Error(`Discussion summary file is empty at ${discussion.summaryPath}`);
|
|
12305
12442
|
}
|
|
12306
|
-
const transcriptPath =
|
|
12443
|
+
const transcriptPath = path6.join(worktree, discussion.transcriptPath);
|
|
12307
12444
|
setDiscussionStatus(transcriptPath, "closed");
|
|
12308
12445
|
const archivedTranscriptPath = archiveDiscussionTranscript(worktree, discussion.transcriptPath);
|
|
12309
12446
|
delete registry.active[sessionID];
|
|
@@ -12311,7 +12448,7 @@ async function finalizeClosingDiscussion(client, worktree, registry, sessionID,
|
|
|
12311
12448
|
return {
|
|
12312
12449
|
confirmation,
|
|
12313
12450
|
summaryPath: discussion.summaryPath,
|
|
12314
|
-
archivedTranscriptPath: archivedTranscriptPath ?
|
|
12451
|
+
archivedTranscriptPath: archivedTranscriptPath ? path6.relative(worktree, archivedTranscriptPath) : path6.join(".optima", ".config", "runtime", "discussions", "archive", path6.basename(discussion.transcriptPath))
|
|
12315
12452
|
};
|
|
12316
12453
|
}
|
|
12317
12454
|
function normalizeTeamMode(value) {
|
|
@@ -12383,7 +12520,7 @@ function syncGeneratedPolicies(worktree, repoCfg) {
|
|
|
12383
12520
|
if (!fs5.existsSync(generatedDir)) fs5.mkdirSync(generatedDir, { recursive: true });
|
|
12384
12521
|
const policyFiles = fs5.readdirSync(BUNDLE_POLICIES_DIR).filter((file) => file.endsWith(".md") && file !== "README.md");
|
|
12385
12522
|
for (const file of policyFiles) {
|
|
12386
|
-
const sourcePath =
|
|
12523
|
+
const sourcePath = path6.join(BUNDLE_POLICIES_DIR, file);
|
|
12387
12524
|
const source = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
12388
12525
|
const generated = [
|
|
12389
12526
|
"<!--",
|
|
@@ -12395,18 +12532,18 @@ function syncGeneratedPolicies(worktree, repoCfg) {
|
|
|
12395
12532
|
source,
|
|
12396
12533
|
""
|
|
12397
12534
|
].join("\n");
|
|
12398
|
-
fs5.writeFileSync(
|
|
12535
|
+
fs5.writeFileSync(path6.join(generatedDir, file), generated, "utf8");
|
|
12399
12536
|
}
|
|
12400
12537
|
}
|
|
12401
12538
|
function ensureReadmeFile(dirPath, content) {
|
|
12402
12539
|
if (!fs5.existsSync(dirPath)) fs5.mkdirSync(dirPath, { recursive: true });
|
|
12403
|
-
const readmePath =
|
|
12540
|
+
const readmePath = path6.join(dirPath, "README.md");
|
|
12404
12541
|
if (!fs5.existsSync(readmePath)) {
|
|
12405
12542
|
fs5.writeFileSync(readmePath, content, "utf8");
|
|
12406
12543
|
}
|
|
12407
12544
|
}
|
|
12408
12545
|
function ensureFileIfMissing(filePath, content) {
|
|
12409
|
-
if (!fs5.existsSync(
|
|
12546
|
+
if (!fs5.existsSync(path6.dirname(filePath))) fs5.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
12410
12547
|
if (!fs5.existsSync(filePath)) fs5.writeFileSync(filePath, content, "utf8");
|
|
12411
12548
|
}
|
|
12412
12549
|
function currentTasksRegistryContent() {
|
|
@@ -12493,13 +12630,13 @@ Never place implementation evidence under root \`evidences/\`.
|
|
|
12493
12630
|
}
|
|
12494
12631
|
function ensureOptimaTaskTemplates(worktree) {
|
|
12495
12632
|
const tasksDir = optimaTasksDir(worktree);
|
|
12496
|
-
ensureFileIfMissing(
|
|
12497
|
-
ensureFileIfMissing(
|
|
12633
|
+
ensureFileIfMissing(path6.join(tasksDir, "task-template.md"), taskTemplateContent());
|
|
12634
|
+
ensureFileIfMissing(path6.join(tasksDir, "subtask-template.md"), subtaskTemplateContent());
|
|
12498
12635
|
}
|
|
12499
12636
|
function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
12500
12637
|
const configPath = repoConfigPath(worktree);
|
|
12501
12638
|
if (fs5.existsSync(configPath)) return false;
|
|
12502
|
-
const templatePath =
|
|
12639
|
+
const templatePath = path6.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12503
12640
|
if (!fs5.existsSync(templatePath)) return false;
|
|
12504
12641
|
const agentIds = fs5.existsSync(BUNDLE_AGENTS_DIR) ? fs5.readdirSync(BUNDLE_AGENTS_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")) : [];
|
|
12505
12642
|
let optimaConfig = fs5.readFileSync(templatePath, "utf8");
|
|
@@ -12518,9 +12655,9 @@ function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
|
12518
12655
|
function scaffoldOptimaRootCodemap(worktree) {
|
|
12519
12656
|
const rootCodemapPath = optimaCodemapPath(worktree);
|
|
12520
12657
|
if (fs5.existsSync(rootCodemapPath)) return false;
|
|
12521
|
-
const templatePath =
|
|
12658
|
+
const templatePath = path6.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12522
12659
|
if (!fs5.existsSync(templatePath)) return false;
|
|
12523
|
-
const codemapConfig = fs5.readFileSync(templatePath, "utf8").replace("{{projectName}}",
|
|
12660
|
+
const codemapConfig = fs5.readFileSync(templatePath, "utf8").replace("{{projectName}}", path6.basename(worktree));
|
|
12524
12661
|
ensureFileIfMissing(rootCodemapPath, codemapConfig);
|
|
12525
12662
|
return fs5.existsSync(rootCodemapPath);
|
|
12526
12663
|
}
|
|
@@ -12529,10 +12666,10 @@ function ensureOptimaRegistries(worktree) {
|
|
|
12529
12666
|
const scrsDir = optimaScrsDir(worktree);
|
|
12530
12667
|
if (!fs5.existsSync(tasksDir)) fs5.mkdirSync(tasksDir, { recursive: true });
|
|
12531
12668
|
if (!fs5.existsSync(scrsDir)) fs5.mkdirSync(scrsDir, { recursive: true });
|
|
12532
|
-
ensureFileIfMissing(
|
|
12533
|
-
ensureFileIfMissing(
|
|
12534
|
-
ensureFileIfMissing(
|
|
12535
|
-
ensureFileIfMissing(
|
|
12669
|
+
ensureFileIfMissing(path6.join(tasksDir, "current.md"), currentTasksRegistryContent());
|
|
12670
|
+
ensureFileIfMissing(path6.join(tasksDir, "done.md"), doneTasksRegistryContent());
|
|
12671
|
+
ensureFileIfMissing(path6.join(scrsDir, "current.md"), currentScrRegistryContent());
|
|
12672
|
+
ensureFileIfMissing(path6.join(scrsDir, "done.md"), doneScrRegistryContent());
|
|
12536
12673
|
}
|
|
12537
12674
|
function scaffoldOptimaReadmes(worktree) {
|
|
12538
12675
|
ensureReadmeFile(repoPoliciesDir(worktree), REPO_LOCAL_POLICIES_README);
|
|
@@ -12694,12 +12831,12 @@ function shouldRegisterWorkflowProductManager(options = {}, worktree = process.c
|
|
|
12694
12831
|
}
|
|
12695
12832
|
function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
12696
12833
|
if (typeof candidate !== "string" || typeof basePath !== "string" || !candidate.trim() || !basePath.trim()) return false;
|
|
12697
|
-
const resolvedCandidate =
|
|
12698
|
-
const resolvedBase =
|
|
12699
|
-
const baseParent =
|
|
12700
|
-
if (
|
|
12701
|
-
const baseName =
|
|
12702
|
-
const candidateName =
|
|
12834
|
+
const resolvedCandidate = path6.resolve(candidate);
|
|
12835
|
+
const resolvedBase = path6.resolve(basePath);
|
|
12836
|
+
const baseParent = path6.dirname(resolvedBase);
|
|
12837
|
+
if (path6.dirname(resolvedCandidate) !== baseParent) return false;
|
|
12838
|
+
const baseName = path6.basename(resolvedBase);
|
|
12839
|
+
const candidateName = path6.basename(resolvedCandidate);
|
|
12703
12840
|
if (!candidateName.startsWith(`${baseName}-`)) return false;
|
|
12704
12841
|
const branchSlug = candidateName.slice(baseName.length + 1);
|
|
12705
12842
|
const parts = branchSlug.split("-").filter(Boolean);
|
|
@@ -12711,7 +12848,7 @@ function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
|
12711
12848
|
if (!candidateStat.isDirectory() || candidateStat.isSymbolicLink()) return false;
|
|
12712
12849
|
const realCandidate = fs5.realpathSync.native(resolvedCandidate);
|
|
12713
12850
|
const realBaseParent = fs5.realpathSync.native(baseParent);
|
|
12714
|
-
return
|
|
12851
|
+
return path6.dirname(realCandidate) === realBaseParent && path6.basename(realCandidate) === candidateName;
|
|
12715
12852
|
} catch {
|
|
12716
12853
|
return false;
|
|
12717
12854
|
}
|
|
@@ -12719,15 +12856,15 @@ function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
|
12719
12856
|
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
12720
12857
|
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
12721
12858
|
if (!safe.ok) return { ok: false, error: safe.message };
|
|
12722
|
-
const requested = String(requestedDirectory || "").trim() ?
|
|
12859
|
+
const requested = String(requestedDirectory || "").trim() ? path6.resolve(safe.worktree, String(requestedDirectory).trim()) : safe.worktree;
|
|
12723
12860
|
if (!isSafeWritableDirectory(requested)) return { ok: false, error: `Directory is not a safe writable directory: ${requested}` };
|
|
12724
12861
|
if (isSameOrNestedPath(requested, safe.worktree)) return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "context_worktree" };
|
|
12725
12862
|
const clickUpBasePath = clickUpWebhookValidation?.complete === true && clickUpWebhookValidation?.ok !== false ? clickUpWebhookValidation.config?.basePath : "";
|
|
12726
12863
|
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
12727
|
-
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath:
|
|
12864
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path6.resolve(clickUpBasePath) };
|
|
12728
12865
|
}
|
|
12729
12866
|
if (clickUpBasePath && isClickUpDerivedWorktreeSibling(requested, clickUpBasePath)) {
|
|
12730
|
-
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_derived_worktree", clickupBasePath:
|
|
12867
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_derived_worktree", clickupBasePath: path6.resolve(clickUpBasePath) };
|
|
12731
12868
|
}
|
|
12732
12869
|
return {
|
|
12733
12870
|
ok: false,
|
|
@@ -12761,8 +12898,8 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
12761
12898
|
if (!enabled) continue;
|
|
12762
12899
|
}
|
|
12763
12900
|
const promptOptions = { preferCompactPromptDocs: repoCfg.features?.compact_prompt_docs !== false };
|
|
12764
|
-
const bundledDefinition = loadAgentDefinition(
|
|
12765
|
-
const repoDefinition = loadAgentDefinition(
|
|
12901
|
+
const bundledDefinition = loadAgentDefinition(path6.join(BUNDLE_AGENTS_DIR, file), worktree, promptOptions);
|
|
12902
|
+
const repoDefinition = loadAgentDefinition(path6.join(repoAgentDefinitions, file), worktree, promptOptions) || loadAgentDefinition(path6.join(legacyAgentsDir, file), worktree, promptOptions);
|
|
12766
12903
|
const activeDefinition = repoDefinition || bundledDefinition;
|
|
12767
12904
|
if (!activeDefinition) continue;
|
|
12768
12905
|
const { data } = activeDefinition;
|
|
@@ -12771,7 +12908,7 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
12771
12908
|
if (modePromptFragment) finalPrompt = `${finalPrompt}
|
|
12772
12909
|
|
|
12773
12910
|
${modePromptFragment}`;
|
|
12774
|
-
const additionFragment = loadMarkdownFragment(
|
|
12911
|
+
const additionFragment = loadMarkdownFragment(path6.join(repoAgentAdditions, file), worktree);
|
|
12775
12912
|
if (additionFragment) {
|
|
12776
12913
|
finalPrompt = `${finalPrompt}
|
|
12777
12914
|
|
|
@@ -12824,7 +12961,7 @@ Use this Optima-provided fallback when the current task worktree lacks docs/core
|
|
|
12824
12961
|
}
|
|
12825
12962
|
ourAgents[id] = agentConfig;
|
|
12826
12963
|
if (repoCfg.features?.debug_dumps !== false) {
|
|
12827
|
-
const debugPath =
|
|
12964
|
+
const debugPath = path6.join(debugDir, `${id}.md`);
|
|
12828
12965
|
const { prompt, ...dumpConfig } = agentConfig;
|
|
12829
12966
|
const debugHeader = `---
|
|
12830
12967
|
${import_yaml3.default.stringify(dumpConfig).trim()}
|
|
@@ -12904,7 +13041,17 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
12904
13041
|
...listenerState,
|
|
12905
13042
|
listener: { bindHost: clickUpWebhookValidation.config.webhook.bindHost, bindPort: clickUpWebhookValidation.config.webhook.bindPort, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
12906
13043
|
}, clickUpWebhookValidation.config);
|
|
12907
|
-
|
|
13044
|
+
const assignmentWatchdog = scheduleClickUpAssignmentWatchdog({
|
|
13045
|
+
config: clickUpWebhookValidation.config,
|
|
13046
|
+
state: activeState,
|
|
13047
|
+
worktree,
|
|
13048
|
+
clickupClient: lifecycleClickUpClient,
|
|
13049
|
+
openCodeClient: input.client,
|
|
13050
|
+
scheduler: input.assignmentWatchdogScheduler,
|
|
13051
|
+
intervalMs: input.assignmentWatchdogIntervalMs,
|
|
13052
|
+
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, clickUpWebhookValidation.config)
|
|
13053
|
+
});
|
|
13054
|
+
registerClickUpWebhookLifecycle({ config: clickUpWebhookValidation.config, state: activeState, worktree, clickupClient: lifecycleClickUpClient, listener: readyListener, listenerRegistry, assignmentWatchdog });
|
|
12908
13055
|
scheduleClickUpStartupReconciliation({
|
|
12909
13056
|
config: clickUpWebhookValidation.config,
|
|
12910
13057
|
state: activeState,
|
|
@@ -12966,8 +13113,8 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
12966
13113
|
migrateLegacyOptimaLayout(toolWorktree);
|
|
12967
13114
|
const cfgDir = optimaConfigDir(toolWorktree);
|
|
12968
13115
|
if (!fs5.existsSync(cfgDir)) fs5.mkdirSync(cfgDir, { recursive: true });
|
|
12969
|
-
const optimaTmplPath =
|
|
12970
|
-
const codemapTmplPath =
|
|
13116
|
+
const optimaTmplPath = path6.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
13117
|
+
const codemapTmplPath = path6.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12971
13118
|
if (!fs5.existsSync(optimaTmplPath) || !fs5.existsSync(codemapTmplPath)) {
|
|
12972
13119
|
return "Error: Initialization templates not found in plugin.";
|
|
12973
13120
|
}
|
|
@@ -13089,14 +13236,14 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
13089
13236
|
async execute(args, context) {
|
|
13090
13237
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
13091
13238
|
if (!safe.ok) return safe.message;
|
|
13092
|
-
const summaryPath =
|
|
13239
|
+
const summaryPath = path6.resolve(safe.worktree, args.summary_path || "");
|
|
13093
13240
|
if (!fs5.existsSync(summaryPath)) return `FAIL: summary_path not found: ${summaryPath}`;
|
|
13094
|
-
const taskPath = args.task_path ?
|
|
13241
|
+
const taskPath = args.task_path ? path6.resolve(safe.worktree, args.task_path) : "";
|
|
13095
13242
|
const payload = buildClickUpSummaryPayload({
|
|
13096
13243
|
summaryMarkdown: fs5.readFileSync(summaryPath, "utf8"),
|
|
13097
|
-
summaryPath:
|
|
13244
|
+
summaryPath: path6.relative(safe.worktree, summaryPath),
|
|
13098
13245
|
taskMarkdown: taskPath && fs5.existsSync(taskPath) ? fs5.readFileSync(taskPath, "utf8") : "",
|
|
13099
|
-
taskPath: taskPath ?
|
|
13246
|
+
taskPath: taskPath ? path6.relative(safe.worktree, taskPath) : "",
|
|
13100
13247
|
branch: args.branch,
|
|
13101
13248
|
worktree: args.worktree,
|
|
13102
13249
|
pr: args.pr
|
|
@@ -13175,12 +13322,12 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
13175
13322
|
async execute(args, context) {
|
|
13176
13323
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
13177
13324
|
if (!safe.ok) return safe.message;
|
|
13178
|
-
const markdownPath =
|
|
13325
|
+
const markdownPath = path6.resolve(safe.worktree, args.markdown_path || "");
|
|
13179
13326
|
if (!fs5.existsSync(markdownPath)) return `FAIL: markdown_path not found: ${markdownPath}`;
|
|
13180
13327
|
const payload = buildClickUpCreateSubtasksPayload({
|
|
13181
13328
|
parentTaskId: args.parent_task_id,
|
|
13182
13329
|
markdown: fs5.readFileSync(markdownPath, "utf8"),
|
|
13183
|
-
sourcePath:
|
|
13330
|
+
sourcePath: path6.relative(safe.worktree, markdownPath),
|
|
13184
13331
|
parentBranch: args.parent_branch,
|
|
13185
13332
|
parentTaskType: args.parent_task_type || "Tarea",
|
|
13186
13333
|
apply: String(args.apply || "").toLowerCase() === "true"
|
|
@@ -13379,7 +13526,7 @@ Backfilled messages: ${backfilled}`;
|
|
|
13379
13526
|
if (!existing) {
|
|
13380
13527
|
return "FAIL: No active discussion exists for this session.";
|
|
13381
13528
|
}
|
|
13382
|
-
const discussionPath =
|
|
13529
|
+
const discussionPath = path6.join(toolWorktree, existing.transcriptPath);
|
|
13383
13530
|
setDiscussionStatus(discussionPath, "summarizing");
|
|
13384
13531
|
existing.status = "summarizing";
|
|
13385
13532
|
saveDiscussionRegistry(toolWorktree, toolRegistry);
|
|
@@ -13431,7 +13578,7 @@ Reason: ${err.message}`;
|
|
|
13431
13578
|
try {
|
|
13432
13579
|
const sessionResult = await client.session.create({
|
|
13433
13580
|
query: { directory: workflowDirectory },
|
|
13434
|
-
body: { title: `Workflow Run: ${
|
|
13581
|
+
body: { title: `Workflow Run: ${path6.basename(workflowTaskPath)}` }
|
|
13435
13582
|
});
|
|
13436
13583
|
const sessionId = sessionResult.data.id;
|
|
13437
13584
|
activeWorkflows.set(sessionId, { pmaSessionId, taskPath: workflowTaskPath, track: workflowTrack, directory: workflowDirectory });
|
|
@@ -13560,7 +13707,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
13560
13707
|
}
|
|
13561
13708
|
};
|
|
13562
13709
|
}
|
|
13563
|
-
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, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, 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 };
|
|
13710
|
+
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, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, 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 };
|
|
13564
13711
|
|
|
13565
13712
|
// src/sanitize_cli.js
|
|
13566
13713
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|
|
@@ -13616,13 +13763,13 @@ function usage() {
|
|
|
13616
13763
|
}
|
|
13617
13764
|
function expandHome(inputPath) {
|
|
13618
13765
|
if (!inputPath || inputPath === "~") return os2.homedir();
|
|
13619
|
-
if (inputPath.startsWith("~/")) return
|
|
13766
|
+
if (inputPath.startsWith("~/")) return path7.join(os2.homedir(), inputPath.slice(2));
|
|
13620
13767
|
return inputPath;
|
|
13621
13768
|
}
|
|
13622
13769
|
function resolveOpenCodeDbPath(inputPath = null) {
|
|
13623
13770
|
const expanded = expandHome(inputPath || process.env.OPTIMA_OPENCODE_DB_PATH || DEFAULT_DB_PATH);
|
|
13624
13771
|
try {
|
|
13625
|
-
if (fs6.existsSync(expanded) && fs6.statSync(expanded).isDirectory()) return
|
|
13772
|
+
if (fs6.existsSync(expanded) && fs6.statSync(expanded).isDirectory()) return path7.join(expanded, "opencode.db");
|
|
13626
13773
|
} catch {
|
|
13627
13774
|
return expanded;
|
|
13628
13775
|
}
|
|
@@ -13686,8 +13833,8 @@ function extractOpenedPathValue(value) {
|
|
|
13686
13833
|
}
|
|
13687
13834
|
function normalizePathCandidate(candidate) {
|
|
13688
13835
|
const expanded = expandHome(candidate);
|
|
13689
|
-
if (!
|
|
13690
|
-
return
|
|
13836
|
+
if (!path7.isAbsolute(expanded)) return null;
|
|
13837
|
+
return path7.resolve(expanded);
|
|
13691
13838
|
}
|
|
13692
13839
|
function discoverOpenCodePaths(dbPath) {
|
|
13693
13840
|
const resolvedDb = resolveOpenCodeDbPath(dbPath);
|
|
@@ -13716,27 +13863,27 @@ function discoverOpenCodePaths(dbPath) {
|
|
|
13716
13863
|
function collectMarkdownOverridesFrom(baseDir) {
|
|
13717
13864
|
const files = [];
|
|
13718
13865
|
for (const dirName of OVERRIDE_DIRS) {
|
|
13719
|
-
const dirPath =
|
|
13866
|
+
const dirPath = path7.join(baseDir, dirName);
|
|
13720
13867
|
if (!fs6.existsSync(dirPath)) continue;
|
|
13721
13868
|
for (const entry of fs6.readdirSync(dirPath, { withFileTypes: true })) {
|
|
13722
13869
|
if (!entry.isFile()) continue;
|
|
13723
13870
|
if (!entry.name.endsWith(".md")) continue;
|
|
13724
13871
|
if (entry.name.toLowerCase() === "readme.md") continue;
|
|
13725
|
-
files.push(
|
|
13872
|
+
files.push(path7.join(dirPath, entry.name));
|
|
13726
13873
|
}
|
|
13727
13874
|
}
|
|
13728
13875
|
return files;
|
|
13729
13876
|
}
|
|
13730
13877
|
function collectOverrideFiles(worktree) {
|
|
13731
|
-
return collectMarkdownOverridesFrom(
|
|
13878
|
+
return collectMarkdownOverridesFrom(path7.join(worktree, ".optima")).sort();
|
|
13732
13879
|
}
|
|
13733
13880
|
function collectPlannedOverrideFiles(worktree) {
|
|
13734
13881
|
return [
|
|
13735
13882
|
...collectOverrideFiles(worktree),
|
|
13736
|
-
...collectMarkdownOverridesFrom(
|
|
13737
|
-
...collectMarkdownOverridesFrom(
|
|
13738
|
-
...collectMarkdownOverridesFrom(
|
|
13739
|
-
...collectMarkdownOverridesFrom(
|
|
13883
|
+
...collectMarkdownOverridesFrom(path7.join(worktree, ".staticeng")),
|
|
13884
|
+
...collectMarkdownOverridesFrom(path7.join(worktree, ".orbita")),
|
|
13885
|
+
...collectMarkdownOverridesFrom(path7.join(worktree, ".nomadwork")),
|
|
13886
|
+
...collectMarkdownOverridesFrom(path7.join(worktree, ".nomadworks"))
|
|
13740
13887
|
].sort();
|
|
13741
13888
|
}
|
|
13742
13889
|
var CRC_TABLE = (() => {
|
|
@@ -13769,14 +13916,14 @@ function writeUInt16LE(value) {
|
|
|
13769
13916
|
return buffer;
|
|
13770
13917
|
}
|
|
13771
13918
|
function createZipBackup(files, backupPath) {
|
|
13772
|
-
fs6.mkdirSync(
|
|
13919
|
+
fs6.mkdirSync(path7.dirname(backupPath), { recursive: true });
|
|
13773
13920
|
const localParts = [];
|
|
13774
13921
|
const centralParts = [];
|
|
13775
13922
|
let offset = 0;
|
|
13776
13923
|
const now = dosTimeDate();
|
|
13777
13924
|
for (const filePath of files) {
|
|
13778
13925
|
const data = fs6.readFileSync(filePath);
|
|
13779
|
-
const name =
|
|
13926
|
+
const name = path7.resolve(filePath).replace(/^\//, "").split(path7.sep).join("/");
|
|
13780
13927
|
const nameBuffer = Buffer.from(name, "utf8");
|
|
13781
13928
|
const crc = crc32(data);
|
|
13782
13929
|
const local = Buffer.concat([
|
|
@@ -13838,15 +13985,15 @@ function validationStatus(result) {
|
|
|
13838
13985
|
return result.ok ? "passed" : "failed";
|
|
13839
13986
|
}
|
|
13840
13987
|
function hasLegacyMarker(worktree) {
|
|
13841
|
-
return fs6.existsSync(
|
|
13988
|
+
return fs6.existsSync(path7.join(worktree, ".staticeng")) || fs6.existsSync(path7.join(worktree, ".orbita")) || fs6.existsSync(path7.join(worktree, ".nomadwork")) || fs6.existsSync(path7.join(worktree, ".nomadworks"));
|
|
13842
13989
|
}
|
|
13843
13990
|
function rootOptimaArtifacts(worktree) {
|
|
13844
13991
|
const artifacts = [
|
|
13845
|
-
["tasks/",
|
|
13846
|
-
["evidences/",
|
|
13847
|
-
["docs/scrs/",
|
|
13848
|
-
["codemap.yml",
|
|
13849
|
-
["codemap.yaml",
|
|
13992
|
+
["tasks/", path7.join(worktree, "tasks")],
|
|
13993
|
+
["evidences/", path7.join(worktree, "evidences")],
|
|
13994
|
+
["docs/scrs/", path7.join(worktree, "docs", "scrs")],
|
|
13995
|
+
["codemap.yml", path7.join(worktree, "codemap.yml")],
|
|
13996
|
+
["codemap.yaml", path7.join(worktree, "codemap.yaml")]
|
|
13850
13997
|
];
|
|
13851
13998
|
return artifacts.map(([display, artifactPath]) => ({ display, path: artifactPath })).filter((item) => fs6.existsSync(item.path));
|
|
13852
13999
|
}
|
|
@@ -13874,7 +14021,7 @@ function planOptimaMigration(worktree, dryRun) {
|
|
|
13874
14021
|
...overrides.map((file) => ({
|
|
13875
14022
|
category: "override",
|
|
13876
14023
|
action: dryRun ? "would_remove_after_backup" : "removed_after_backup",
|
|
13877
|
-
path:
|
|
14024
|
+
path: path7.relative(worktree, file).split(path7.sep).join("/"),
|
|
13878
14025
|
detail: "Remove implicit agent/policy override after host-level backup."
|
|
13879
14026
|
}))
|
|
13880
14027
|
],
|
|
@@ -13922,7 +14069,7 @@ async function sanitizeHost(options = {}) {
|
|
|
13922
14069
|
const overrides = repoStates.flatMap((state) => state.overrideFiles.map((file) => ({ repo: state.repo, file })));
|
|
13923
14070
|
report.totals.overrideFiles = overrides.length;
|
|
13924
14071
|
if (!dryRun && overrides.length > 0) {
|
|
13925
|
-
const backupPath =
|
|
14072
|
+
const backupPath = path7.join(os2.homedir(), BACKUP_DIRNAME, `${timestamp()}.zip`);
|
|
13926
14073
|
try {
|
|
13927
14074
|
createZipBackup(overrides.map((item) => item.file), backupPath);
|
|
13928
14075
|
report.backupPath = backupPath;
|
|
@@ -13951,7 +14098,7 @@ async function sanitizeHost(options = {}) {
|
|
|
13951
14098
|
repairActions: state.plan.actions.length,
|
|
13952
14099
|
repairSkipped: state.plan.skippedRepair === true,
|
|
13953
14100
|
overrideFiles: state.overrideFiles.length,
|
|
13954
|
-
overridePaths: state.overrideFiles.map((file) =>
|
|
14101
|
+
overridePaths: state.overrideFiles.map((file) => path7.relative(state.repo, file).split(path7.sep).join("/")),
|
|
13955
14102
|
validation: validation ? validationStatus(validation) : "skipped",
|
|
13956
14103
|
unresolved: state.plan.unresolved
|
|
13957
14104
|
});
|