@defend-tech/opencode-optima 0.1.75 → 0.1.77
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 +2 -1
- package/Agents_Common.prompt.md +2 -1
- package/assets/agents/tech_lead.md +2 -0
- package/assets/agents/workflow_product_manager.md +5 -4
- package/dist/index.js +1085 -817
- package/dist/sanitize_cli.js +1120 -852
- package/docs/core/agent_orchestration.md +3 -3
- package/docs/core/agent_orchestration.prompt.md +2 -2
- package/docs/core/role_contracts.md +2 -2
- package/docs/core/role_contracts.prompt.md +2 -2
- package/docs/core/task_model.md +1 -1
- package/docs/core/task_model.prompt.md +1 -1
- package/docs/core/testing_strategy.md +1 -1
- package/docs/guides/AGENTS.md +1 -0
- 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, path10) {
|
|
113
|
+
const ctrl = callVisitor(key, node, visitor, path10);
|
|
114
114
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
115
|
-
replaceNode(key,
|
|
116
|
-
return visit_(key, ctrl, visitor,
|
|
115
|
+
replaceNode(key, path10, ctrl);
|
|
116
|
+
return visit_(key, ctrl, visitor, path10);
|
|
117
117
|
}
|
|
118
118
|
if (typeof ctrl !== "symbol") {
|
|
119
119
|
if (identity.isCollection(node)) {
|
|
120
|
-
|
|
120
|
+
path10 = Object.freeze(path10.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, path10);
|
|
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
|
+
path10 = Object.freeze(path10.concat(node));
|
|
134
|
+
const ck = visit_("key", node.key, visitor, path10);
|
|
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, path10);
|
|
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, path10) {
|
|
161
|
+
const ctrl = await callVisitor(key, node, visitor, path10);
|
|
162
162
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
163
|
-
replaceNode(key,
|
|
164
|
-
return visitAsync_(key, ctrl, visitor,
|
|
163
|
+
replaceNode(key, path10, ctrl);
|
|
164
|
+
return visitAsync_(key, ctrl, visitor, path10);
|
|
165
165
|
}
|
|
166
166
|
if (typeof ctrl !== "symbol") {
|
|
167
167
|
if (identity.isCollection(node)) {
|
|
168
|
-
|
|
168
|
+
path10 = Object.freeze(path10.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, path10);
|
|
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
|
+
path10 = Object.freeze(path10.concat(node));
|
|
182
|
+
const ck = await visitAsync_("key", node.key, visitor, path10);
|
|
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, path10);
|
|
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, path10) {
|
|
215
215
|
if (typeof visitor === "function")
|
|
216
|
-
return visitor(key, node,
|
|
216
|
+
return visitor(key, node, path10);
|
|
217
217
|
if (identity.isMap(node))
|
|
218
|
-
return visitor.Map?.(key, node,
|
|
218
|
+
return visitor.Map?.(key, node, path10);
|
|
219
219
|
if (identity.isSeq(node))
|
|
220
|
-
return visitor.Seq?.(key, node,
|
|
220
|
+
return visitor.Seq?.(key, node, path10);
|
|
221
221
|
if (identity.isPair(node))
|
|
222
|
-
return visitor.Pair?.(key, node,
|
|
222
|
+
return visitor.Pair?.(key, node, path10);
|
|
223
223
|
if (identity.isScalar(node))
|
|
224
|
-
return visitor.Scalar?.(key, node,
|
|
224
|
+
return visitor.Scalar?.(key, node, path10);
|
|
225
225
|
if (identity.isAlias(node))
|
|
226
|
-
return visitor.Alias?.(key, node,
|
|
226
|
+
return visitor.Alias?.(key, node, path10);
|
|
227
227
|
return void 0;
|
|
228
228
|
}
|
|
229
|
-
function replaceNode(key,
|
|
230
|
-
const parent =
|
|
229
|
+
function replaceNode(key, path10, node) {
|
|
230
|
+
const parent = path10[path10.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, path10, value) {
|
|
839
839
|
let v = value;
|
|
840
|
-
for (let i =
|
|
841
|
-
const k =
|
|
840
|
+
for (let i = path10.length - 1; i >= 0; --i) {
|
|
841
|
+
const k = path10[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 = (path10) => path10 == null || typeof path10 === "object" && !!path10[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(path10, value) {
|
|
891
|
+
if (isEmptyPath(path10))
|
|
892
892
|
this.add(value);
|
|
893
893
|
else {
|
|
894
|
-
const [key, ...rest] =
|
|
894
|
+
const [key, ...rest] = path10;
|
|
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(path10) {
|
|
909
|
+
const [key, ...rest] = path10;
|
|
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(path10, keepScalar) {
|
|
924
|
+
const [key, ...rest] = path10;
|
|
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(path10) {
|
|
943
|
+
const [key, ...rest] = path10;
|
|
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(path10, value) {
|
|
954
|
+
const [key, ...rest] = path10;
|
|
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(path10, value) {
|
|
3459
3459
|
if (assertCollection(this.contents))
|
|
3460
|
-
this.contents.addIn(
|
|
3460
|
+
this.contents.addIn(path10, 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(path10) {
|
|
3536
|
+
if (Collection.isEmptyPath(path10)) {
|
|
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(path10) : 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(path10, keepScalar) {
|
|
3558
|
+
if (Collection.isEmptyPath(path10))
|
|
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(path10, 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(path10) {
|
|
3572
|
+
if (Collection.isEmptyPath(path10))
|
|
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(path10) : 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(path10, value) {
|
|
3592
|
+
if (Collection.isEmptyPath(path10)) {
|
|
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(path10), value);
|
|
3596
3596
|
} else if (assertCollection(this.contents)) {
|
|
3597
|
-
this.contents.setIn(
|
|
3597
|
+
this.contents.setIn(path10, 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, path10) => {
|
|
5550
5550
|
let item = cst;
|
|
5551
|
-
for (const [field, index] of
|
|
5551
|
+
for (const [field, index] of path10) {
|
|
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, path10) => {
|
|
5561
|
+
const parent = visit.itemAtPath(cst, path10.slice(0, -1));
|
|
5562
|
+
const field = path10[path10.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(path10, item, visitor) {
|
|
5569
|
+
let ctrl = visitor(item, path10);
|
|
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(path10.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, path10);
|
|
5588
5588
|
}
|
|
5589
5589
|
}
|
|
5590
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5590
|
+
return typeof ctrl === "function" ? ctrl(item, path10) : ctrl;
|
|
5591
5591
|
}
|
|
5592
5592
|
exports.visit = visit;
|
|
5593
5593
|
}
|
|
@@ -6872,14 +6872,14 @@ var require_parser = __commonJS({
|
|
|
6872
6872
|
case "scalar":
|
|
6873
6873
|
case "single-quoted-scalar":
|
|
6874
6874
|
case "double-quoted-scalar": {
|
|
6875
|
-
const
|
|
6875
|
+
const fs10 = this.flowScalar(this.type);
|
|
6876
6876
|
if (atNextItem || it.value) {
|
|
6877
|
-
map.items.push({ start, key:
|
|
6877
|
+
map.items.push({ start, key: fs10, sep: [] });
|
|
6878
6878
|
this.onKeyLine = true;
|
|
6879
6879
|
} else if (it.sep) {
|
|
6880
|
-
this.stack.push(
|
|
6880
|
+
this.stack.push(fs10);
|
|
6881
6881
|
} else {
|
|
6882
|
-
Object.assign(it, { key:
|
|
6882
|
+
Object.assign(it, { key: fs10, sep: [] });
|
|
6883
6883
|
this.onKeyLine = true;
|
|
6884
6884
|
}
|
|
6885
6885
|
return;
|
|
@@ -7007,13 +7007,13 @@ var require_parser = __commonJS({
|
|
|
7007
7007
|
case "scalar":
|
|
7008
7008
|
case "single-quoted-scalar":
|
|
7009
7009
|
case "double-quoted-scalar": {
|
|
7010
|
-
const
|
|
7010
|
+
const fs10 = this.flowScalar(this.type);
|
|
7011
7011
|
if (!it || it.value)
|
|
7012
|
-
fc.items.push({ start: [], key:
|
|
7012
|
+
fc.items.push({ start: [], key: fs10, sep: [] });
|
|
7013
7013
|
else if (it.sep)
|
|
7014
|
-
this.stack.push(
|
|
7014
|
+
this.stack.push(fs10);
|
|
7015
7015
|
else
|
|
7016
|
-
Object.assign(it, { key:
|
|
7016
|
+
Object.assign(it, { key: fs10, sep: [] });
|
|
7017
7017
|
return;
|
|
7018
7018
|
}
|
|
7019
7019
|
case "flow-map-end":
|
|
@@ -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 = (path10, originalPath, doThrow) => {
|
|
7555
|
+
if (!isString(path10)) {
|
|
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 (!path10) {
|
|
7562
7562
|
return doThrow(`path must not be empty`, TypeError);
|
|
7563
7563
|
}
|
|
7564
|
-
if (checkPath.isNotRelative(
|
|
7564
|
+
if (checkPath.isNotRelative(path10)) {
|
|
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 = (path10) => REGEX_TEST_INVALID_PATH.test(path10);
|
|
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(path10, 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(path10);
|
|
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 path10 = originalPath && checkPath.convert(originalPath);
|
|
7654
7654
|
checkPath(
|
|
7655
|
-
|
|
7655
|
+
path10,
|
|
7656
7656
|
originalPath,
|
|
7657
7657
|
this._allowRelativePaths ? RETURN_FALSE : throwError
|
|
7658
7658
|
);
|
|
7659
|
-
return this._t(
|
|
7659
|
+
return this._t(path10, cache, checkUnignored, slices);
|
|
7660
7660
|
}
|
|
7661
|
-
_t(
|
|
7662
|
-
if (
|
|
7663
|
-
return cache[
|
|
7661
|
+
_t(path10, cache, checkUnignored, slices) {
|
|
7662
|
+
if (path10 in cache) {
|
|
7663
|
+
return cache[path10];
|
|
7664
7664
|
}
|
|
7665
7665
|
if (!slices) {
|
|
7666
|
-
slices =
|
|
7666
|
+
slices = path10.split(SLASH);
|
|
7667
7667
|
}
|
|
7668
7668
|
slices.pop();
|
|
7669
7669
|
if (!slices.length) {
|
|
7670
|
-
return cache[
|
|
7670
|
+
return cache[path10] = this._testOne(path10, 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[path10] = parent.ignored ? parent : this._testOne(path10, checkUnignored);
|
|
7679
7679
|
}
|
|
7680
|
-
ignores(
|
|
7681
|
-
return this._test(
|
|
7680
|
+
ignores(path10) {
|
|
7681
|
+
return this._test(path10, this._ignoreCache, false).ignored;
|
|
7682
7682
|
}
|
|
7683
7683
|
createFilter() {
|
|
7684
|
-
return (
|
|
7684
|
+
return (path10) => !this.ignores(path10);
|
|
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(path10) {
|
|
7691
|
+
return this._test(path10, this._testCache, true);
|
|
7692
7692
|
}
|
|
7693
7693
|
};
|
|
7694
7694
|
var factory = (options) => new Ignore(options);
|
|
7695
|
-
var isPathValid = (
|
|
7695
|
+
var isPathValid = (path10) => checkPath(path10 && checkPath.convert(path10), path10, RETURN_FALSE);
|
|
7696
7696
|
factory.isPathValid = isPathValid;
|
|
7697
7697
|
factory.default = factory;
|
|
7698
7698
|
module.exports = factory;
|
|
@@ -7703,27 +7703,59 @@ 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 = (path10) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path10) || isNotRelative(path10);
|
|
7707
7707
|
}
|
|
7708
7708
|
}
|
|
7709
7709
|
});
|
|
7710
7710
|
|
|
7711
7711
|
// src/sanitize_cli.js
|
|
7712
|
-
import
|
|
7713
|
-
import
|
|
7714
|
-
import
|
|
7712
|
+
import fs9 from "node:fs";
|
|
7713
|
+
import os3 from "node:os";
|
|
7714
|
+
import path9 from "node:path";
|
|
7715
7715
|
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
7716
7716
|
|
|
7717
7717
|
// src/index.js
|
|
7718
7718
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
7719
7719
|
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
7720
|
-
import
|
|
7721
|
-
import
|
|
7720
|
+
import crypto2 from "node:crypto";
|
|
7721
|
+
import fs8 from "node:fs";
|
|
7722
7722
|
import http from "node:http";
|
|
7723
|
-
import
|
|
7724
|
-
import
|
|
7723
|
+
import os2 from "node:os";
|
|
7724
|
+
import path8 from "node:path";
|
|
7725
7725
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
7726
7726
|
|
|
7727
|
+
// src/clickup_comments.js
|
|
7728
|
+
var COMMENT_TEXT_KEYS = /* @__PURE__ */ new Set(["comment", "body", "description"]);
|
|
7729
|
+
function normalizeClickUpMarkdown(value = "") {
|
|
7730
|
+
return String(value ?? "").replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
7731
|
+
}
|
|
7732
|
+
function formatClickUpStatusComment({ title = "", summary = "", sections = [], context = [] } = {}) {
|
|
7733
|
+
const lines = [];
|
|
7734
|
+
const heading = normalizeClickUpMarkdown(title);
|
|
7735
|
+
if (heading) lines.push(`## ${heading}`, "");
|
|
7736
|
+
const intro = normalizeClickUpMarkdown(summary);
|
|
7737
|
+
if (intro) lines.push(intro, "");
|
|
7738
|
+
for (const section of sections) {
|
|
7739
|
+
const sectionTitle = normalizeClickUpMarkdown(section?.title);
|
|
7740
|
+
const sectionBody = normalizeClickUpMarkdown(section?.body);
|
|
7741
|
+
if (!sectionTitle || !sectionBody) continue;
|
|
7742
|
+
lines.push(`### ${sectionTitle}`, sectionBody, "");
|
|
7743
|
+
}
|
|
7744
|
+
const contextLines = context.map(normalizeClickUpMarkdown).filter(Boolean);
|
|
7745
|
+
if (contextLines.length > 0) lines.push("### Context", contextLines.map((item) => item.startsWith("- ") ? item : `- ${item}`).join("\n"));
|
|
7746
|
+
return normalizeClickUpMarkdown(lines.join("\n"));
|
|
7747
|
+
}
|
|
7748
|
+
function normalizeClickUpPayloadComments(value) {
|
|
7749
|
+
if (Array.isArray(value)) return value.map(normalizeClickUpPayloadComments);
|
|
7750
|
+
if (!value || typeof value !== "object") return value;
|
|
7751
|
+
return Object.fromEntries(
|
|
7752
|
+
Object.entries(value).map(([key, entry]) => [
|
|
7753
|
+
key,
|
|
7754
|
+
COMMENT_TEXT_KEYS.has(key) && typeof entry === "string" ? normalizeClickUpMarkdown(entry) : normalizeClickUpPayloadComments(entry)
|
|
7755
|
+
])
|
|
7756
|
+
);
|
|
7757
|
+
}
|
|
7758
|
+
|
|
7727
7759
|
// src/git_utils.js
|
|
7728
7760
|
import fs from "node:fs";
|
|
7729
7761
|
import path from "node:path";
|
|
@@ -7802,24 +7834,438 @@ function stageGitAwareMerge(sourcePath, destinationPath, gitState) {
|
|
|
7802
7834
|
}
|
|
7803
7835
|
}
|
|
7804
7836
|
|
|
7805
|
-
// src/
|
|
7837
|
+
// src/github.js
|
|
7838
|
+
import crypto from "node:crypto";
|
|
7839
|
+
import fs3 from "node:fs";
|
|
7840
|
+
import path3 from "node:path";
|
|
7841
|
+
|
|
7842
|
+
// src/secrets.js
|
|
7806
7843
|
import fs2 from "node:fs";
|
|
7844
|
+
import os from "node:os";
|
|
7807
7845
|
import path2 from "node:path";
|
|
7846
|
+
function expandHomePath(value = "") {
|
|
7847
|
+
const input = String(value || "").trim();
|
|
7848
|
+
if (input === "~") return os.homedir();
|
|
7849
|
+
if (input.startsWith("~/")) return path2.join(os.homedir(), input.slice(2));
|
|
7850
|
+
return input;
|
|
7851
|
+
}
|
|
7852
|
+
function resolveSecretReference(value = "") {
|
|
7853
|
+
const raw = String(value || "").trim();
|
|
7854
|
+
const envMatch = raw.match(/^\{env:([A-Za-z_][A-Za-z0-9_]*)\}$/);
|
|
7855
|
+
if (envMatch) return process.env[envMatch[1]] || "";
|
|
7856
|
+
const fileMatch = raw.match(/^\{file:(.+)\}$/);
|
|
7857
|
+
if (fileMatch) {
|
|
7858
|
+
try {
|
|
7859
|
+
return fs2.readFileSync(expandHomePath(fileMatch[1]), "utf8").trim();
|
|
7860
|
+
} catch {
|
|
7861
|
+
return "";
|
|
7862
|
+
}
|
|
7863
|
+
}
|
|
7864
|
+
return raw;
|
|
7865
|
+
}
|
|
7866
|
+
|
|
7867
|
+
// src/github.js
|
|
7868
|
+
function base64UrlJson(value) {
|
|
7869
|
+
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
7870
|
+
}
|
|
7871
|
+
function resolveGitHubAppPrivateKey(app = {}) {
|
|
7872
|
+
const direct = resolveSecretReference(app.privateKey || "");
|
|
7873
|
+
if (direct) return direct.replace(/\\n/g, "\n");
|
|
7874
|
+
const file = expandHomePath(app.privateKeyFile || "");
|
|
7875
|
+
if (!file) return "";
|
|
7876
|
+
try {
|
|
7877
|
+
return fs3.readFileSync(file, "utf8").trim().replace(/\\n/g, "\n");
|
|
7878
|
+
} catch {
|
|
7879
|
+
return "";
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
function createGitHubAppJwt({ appId, privateKey, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
7883
|
+
const key = String(privateKey || "").trim();
|
|
7884
|
+
const issuer = String(appId || "").trim();
|
|
7885
|
+
if (!issuer || !key) throw new Error("GitHub App app_id and private key are required");
|
|
7886
|
+
const nowSeconds = Math.floor(now().getTime() / 1e3);
|
|
7887
|
+
const header = base64UrlJson({ alg: "RS256", typ: "JWT" });
|
|
7888
|
+
const payload = base64UrlJson({ iat: nowSeconds - 60, exp: nowSeconds + 540, iss: issuer });
|
|
7889
|
+
const unsigned = `${header}.${payload}`;
|
|
7890
|
+
const signature = crypto.createSign("RSA-SHA256").update(unsigned).end().sign(key, "base64url");
|
|
7891
|
+
return `${unsigned}.${signature}`;
|
|
7892
|
+
}
|
|
7893
|
+
function encodeGitHubPathSegment(value) {
|
|
7894
|
+
return encodeURIComponent(String(value || ""));
|
|
7895
|
+
}
|
|
7896
|
+
function encodeGitHubBranchRef(branch = "") {
|
|
7897
|
+
return String(branch || "").split("/").map(encodeGitHubPathSegment).join("/");
|
|
7898
|
+
}
|
|
7899
|
+
function parseNullSeparatedGitOutput(output = "") {
|
|
7900
|
+
return String(output || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
7901
|
+
}
|
|
7902
|
+
function listWorktreeChangedPaths(worktree, runGitFn = runGit) {
|
|
7903
|
+
const tracked = parseNullSeparatedGitOutput(runGitFn(worktree, ["diff", "--name-only", "-z", "HEAD", "--"]));
|
|
7904
|
+
const untracked = parseNullSeparatedGitOutput(runGitFn(worktree, ["ls-files", "--others", "--exclude-standard", "-z", "--"]));
|
|
7905
|
+
return [.../* @__PURE__ */ new Set([...tracked, ...untracked])].filter((item) => item && !item.startsWith("../") && !path3.isAbsolute(item));
|
|
7906
|
+
}
|
|
7907
|
+
function currentGitBranch(worktree, runGitFn = runGit) {
|
|
7908
|
+
return String(runGitFn(worktree, ["rev-parse", "--abbrev-ref", "HEAD"]) || "").trim();
|
|
7909
|
+
}
|
|
7910
|
+
function treeEntryForWorktreePath(worktree, gitPath) {
|
|
7911
|
+
const absolutePath = path3.join(worktree, ...String(gitPath || "").split("/"));
|
|
7912
|
+
if (!fs3.existsSync(absolutePath)) return { path: gitPath, mode: "100644", type: "blob", sha: null, deleted: true };
|
|
7913
|
+
const stat = fs3.lstatSync(absolutePath);
|
|
7914
|
+
if (stat.isDirectory()) return null;
|
|
7915
|
+
if (stat.isSymbolicLink()) {
|
|
7916
|
+
return {
|
|
7917
|
+
path: gitPath,
|
|
7918
|
+
mode: "120000",
|
|
7919
|
+
type: "blob",
|
|
7920
|
+
content: fs3.readlinkSync(absolutePath),
|
|
7921
|
+
encoding: "utf-8"
|
|
7922
|
+
};
|
|
7923
|
+
}
|
|
7924
|
+
return {
|
|
7925
|
+
path: gitPath,
|
|
7926
|
+
mode: stat.mode & 73 ? "100755" : "100644",
|
|
7927
|
+
type: "blob",
|
|
7928
|
+
content: fs3.readFileSync(absolutePath).toString("base64"),
|
|
7929
|
+
encoding: "base64"
|
|
7930
|
+
};
|
|
7931
|
+
}
|
|
7932
|
+
function appendGitHubQuery(pathname, params = {}) {
|
|
7933
|
+
const query = new URLSearchParams();
|
|
7934
|
+
for (const [key, value] of Object.entries(params || {})) {
|
|
7935
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
7936
|
+
query.set(key, String(value));
|
|
7937
|
+
}
|
|
7938
|
+
const suffix = query.toString();
|
|
7939
|
+
return suffix ? `${pathname}?${suffix}` : pathname;
|
|
7940
|
+
}
|
|
7941
|
+
function timestampMs(value = "") {
|
|
7942
|
+
const parsed = Date.parse(String(value || ""));
|
|
7943
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
7944
|
+
}
|
|
7945
|
+
function sortNewestFirst(items = []) {
|
|
7946
|
+
return [...items].sort((a, b) => {
|
|
7947
|
+
const bTime = Math.max(timestampMs(b?.updated_at), timestampMs(b?.created_at));
|
|
7948
|
+
const aTime = Math.max(timestampMs(a?.updated_at), timestampMs(a?.created_at));
|
|
7949
|
+
return bTime - aTime;
|
|
7950
|
+
});
|
|
7951
|
+
}
|
|
7952
|
+
function matchesVercelStatusContext(context = "", expected = "") {
|
|
7953
|
+
const actual = String(context || "").trim();
|
|
7954
|
+
const wanted = String(expected || "").trim();
|
|
7955
|
+
if (wanted && actual === wanted) return true;
|
|
7956
|
+
const lower = actual.toLowerCase();
|
|
7957
|
+
if (!lower.includes("vercel")) return false;
|
|
7958
|
+
if (!wanted) return lower.includes("preproduction") || lower.includes("defend-preproduction");
|
|
7959
|
+
const wantedLower = wanted.toLowerCase();
|
|
7960
|
+
return wantedLower.split(/\s+|–|-/).filter((part) => part && part !== "vercel").every((part) => lower.includes(part));
|
|
7961
|
+
}
|
|
7962
|
+
function selectFunctionalDeploymentUrl(status = {}, deployment = {}) {
|
|
7963
|
+
for (const candidate of [
|
|
7964
|
+
status?.environment_url,
|
|
7965
|
+
deployment?.environment_url,
|
|
7966
|
+
status?.target_url,
|
|
7967
|
+
deployment?.target_url
|
|
7968
|
+
]) {
|
|
7969
|
+
const value = String(candidate || "").trim();
|
|
7970
|
+
if (!value) continue;
|
|
7971
|
+
try {
|
|
7972
|
+
const url = new URL(value);
|
|
7973
|
+
if (url.protocol === "http:" || url.protocol === "https:") return value;
|
|
7974
|
+
} catch {
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
return "";
|
|
7978
|
+
}
|
|
7979
|
+
async function checkFunctionalDeploymentUrl(url, fetchImpl = globalThis.fetch) {
|
|
7980
|
+
const target = String(url || "").trim();
|
|
7981
|
+
if (!target) return { ok: false, reason: "url_missing" };
|
|
7982
|
+
if (typeof fetchImpl !== "function") return { ok: false, reason: "fetch_unavailable", url: target };
|
|
7983
|
+
const signal = typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(8e3) : void 0;
|
|
7984
|
+
const probe = async (method) => {
|
|
7985
|
+
const response = await fetchImpl(target, { method, redirect: "follow", signal });
|
|
7986
|
+
return {
|
|
7987
|
+
ok: response.status >= 200 && response.status < 400,
|
|
7988
|
+
status: response.status,
|
|
7989
|
+
statusText: response.statusText || "",
|
|
7990
|
+
url: response.url || target,
|
|
7991
|
+
method
|
|
7992
|
+
};
|
|
7993
|
+
};
|
|
7994
|
+
try {
|
|
7995
|
+
const head = await probe("HEAD");
|
|
7996
|
+
if (head.ok) return head;
|
|
7997
|
+
const get = await probe("GET");
|
|
7998
|
+
return get.ok ? get : { ...get, reason: "http_not_ok" };
|
|
7999
|
+
} catch (error) {
|
|
8000
|
+
try {
|
|
8001
|
+
const get = await probe("GET");
|
|
8002
|
+
return get.ok ? get : { ...get, reason: "http_not_ok" };
|
|
8003
|
+
} catch (secondError) {
|
|
8004
|
+
return { ok: false, reason: "request_failed", url: target, error: secondError.message || error.message };
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
function createGitHubApiClient(config = {}, fetchImpl = globalThis.fetch) {
|
|
8009
|
+
const staticToken = resolveSecretReference(config?.apiToken);
|
|
8010
|
+
const appConfig = typeof config?.app === "object" && config.app !== null ? config.app : {};
|
|
8011
|
+
const appId = resolveSecretReference(appConfig.appId || appConfig.app_id || "");
|
|
8012
|
+
const installationId = resolveSecretReference(appConfig.installationId || appConfig.installation_id || "");
|
|
8013
|
+
const appEnabled = appConfig.enabled === true || Boolean(appId && installationId && (appConfig.privateKey || appConfig.privateKeyFile));
|
|
8014
|
+
const privateKey = resolveGitHubAppPrivateKey(appConfig);
|
|
8015
|
+
const owner = String(config?.owner || "").trim();
|
|
8016
|
+
const repo = String(config?.repo || "").trim();
|
|
8017
|
+
let installationToken = null;
|
|
8018
|
+
let installationTokenExpiresAt = 0;
|
|
8019
|
+
const requestJson = async (url, { method = "GET", headers = {}, body = void 0 } = {}) => {
|
|
8020
|
+
if (typeof fetchImpl !== "function") throw new Error("fetch is unavailable; inject a GitHub client for live PR lookup");
|
|
8021
|
+
const response = await fetchImpl(url, {
|
|
8022
|
+
method,
|
|
8023
|
+
headers: {
|
|
8024
|
+
Accept: "application/vnd.github+json",
|
|
8025
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
8026
|
+
...body === void 0 ? {} : { "Content-Type": "application/json" },
|
|
8027
|
+
...headers
|
|
8028
|
+
},
|
|
8029
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
8030
|
+
});
|
|
8031
|
+
if (!response.ok) throw new Error(`GitHub API request failed: ${response.status}`);
|
|
8032
|
+
return response.status === 204 ? null : response.json();
|
|
8033
|
+
};
|
|
8034
|
+
const getAuthToken = async () => {
|
|
8035
|
+
if (!appEnabled) return staticToken || "";
|
|
8036
|
+
const nowMs = Date.now();
|
|
8037
|
+
if (installationToken && installationTokenExpiresAt - 6e4 > nowMs) return installationToken;
|
|
8038
|
+
const jwt = createGitHubAppJwt({ appId, privateKey });
|
|
8039
|
+
const installation = await requestJson(`https://api.github.com/app/installations/${encodeURIComponent(installationId)}/access_tokens`, {
|
|
8040
|
+
method: "POST",
|
|
8041
|
+
headers: { Authorization: `Bearer ${jwt}` }
|
|
8042
|
+
});
|
|
8043
|
+
installationToken = String(installation?.token || "").trim();
|
|
8044
|
+
installationTokenExpiresAt = Date.parse(installation?.expires_at || "") || nowMs + 3e6;
|
|
8045
|
+
if (!installationToken) throw new Error("GitHub App installation token response did not include a token");
|
|
8046
|
+
return installationToken;
|
|
8047
|
+
};
|
|
8048
|
+
const request = async (pathname, { method = "GET", body = void 0 } = {}) => {
|
|
8049
|
+
if (!owner || !repo) throw new Error("GitHub repository owner/repo is not configured");
|
|
8050
|
+
const token = await getAuthToken();
|
|
8051
|
+
return requestJson(`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}${pathname}`, {
|
|
8052
|
+
method,
|
|
8053
|
+
body,
|
|
8054
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
8055
|
+
});
|
|
8056
|
+
};
|
|
8057
|
+
const getRef = async (branch) => request(`/git/ref/heads/${encodeGitHubBranchRef(branch)}`);
|
|
8058
|
+
const getCommitObject = async (sha) => request(`/git/commits/${encodeURIComponent(sha)}`);
|
|
8059
|
+
const createBlob = async ({ content, encoding }) => request("/git/blobs", { method: "POST", body: { content, encoding } });
|
|
8060
|
+
const createTree = async ({ baseTree, tree }) => request("/git/trees", { method: "POST", body: { base_tree: baseTree, tree } });
|
|
8061
|
+
const createCommit = async ({ message, treeSha, parents }) => request("/git/commits", { method: "POST", body: { message, tree: treeSha, parents } });
|
|
8062
|
+
const updateRef = async ({ branch, sha, force = false }) => request(`/git/refs/heads/${encodeGitHubBranchRef(branch)}`, { method: "PATCH", body: { sha, force } });
|
|
8063
|
+
return {
|
|
8064
|
+
async getPullRequest(number) {
|
|
8065
|
+
return request(`/pulls/${encodeURIComponent(number)}`);
|
|
8066
|
+
},
|
|
8067
|
+
async createPullRequest({ title, head, base, body = "", draft = false, maintainerCanModify = true }) {
|
|
8068
|
+
return request("/pulls", {
|
|
8069
|
+
method: "POST",
|
|
8070
|
+
body: {
|
|
8071
|
+
title: String(title || ""),
|
|
8072
|
+
head: String(head || ""),
|
|
8073
|
+
base: String(base || ""),
|
|
8074
|
+
body: String(body || ""),
|
|
8075
|
+
draft: draft === true,
|
|
8076
|
+
maintainer_can_modify: maintainerCanModify !== false
|
|
8077
|
+
}
|
|
8078
|
+
});
|
|
8079
|
+
},
|
|
8080
|
+
async createIssueComment({ issueNumber, body }) {
|
|
8081
|
+
return request(`/issues/${encodeURIComponent(issueNumber)}/comments`, { method: "POST", body: { body: String(body || "") } });
|
|
8082
|
+
},
|
|
8083
|
+
async replyToReviewComment({ commentId, body }) {
|
|
8084
|
+
return request(`/pulls/comments/${encodeURIComponent(commentId)}/replies`, { method: "POST", body: { body: String(body || "") } });
|
|
8085
|
+
},
|
|
8086
|
+
async createPullRequestReview({ pullNumber, body, event = "COMMENT" }) {
|
|
8087
|
+
return request(`/pulls/${encodeURIComponent(pullNumber)}/reviews`, { method: "POST", body: { body: String(body || ""), event: String(event || "COMMENT").toUpperCase() } });
|
|
8088
|
+
},
|
|
8089
|
+
async mergePullRequest({ pullNumber, commitTitle = "", commitMessage = "", mergeMethod = "squash" }) {
|
|
8090
|
+
const body = { merge_method: String(mergeMethod || "squash") };
|
|
8091
|
+
if (commitTitle) body.commit_title = String(commitTitle);
|
|
8092
|
+
if (commitMessage) body.commit_message = String(commitMessage);
|
|
8093
|
+
return request(`/pulls/${encodeURIComponent(pullNumber)}/merge`, { method: "PUT", body });
|
|
8094
|
+
},
|
|
8095
|
+
async getCombinedStatus(ref) {
|
|
8096
|
+
return request(`/commits/${encodeURIComponent(String(ref || ""))}/status`);
|
|
8097
|
+
},
|
|
8098
|
+
async listDeployments({ sha = "", ref = "", environment = "", perPage = 30 } = {}) {
|
|
8099
|
+
return request(appendGitHubQuery("/deployments", { sha, ref, environment, per_page: perPage }));
|
|
8100
|
+
},
|
|
8101
|
+
async listDeploymentStatuses(deploymentId) {
|
|
8102
|
+
return request(`/deployments/${encodeURIComponent(deploymentId)}/statuses`);
|
|
8103
|
+
},
|
|
8104
|
+
async verifyVercelPullRequestDeployment({
|
|
8105
|
+
pullNumber,
|
|
8106
|
+
context = "Vercel \u2013 defend-preproduction",
|
|
8107
|
+
environment = "Preview \u2013 defend-preproduction",
|
|
8108
|
+
requireFunctionalUrl = true
|
|
8109
|
+
} = {}) {
|
|
8110
|
+
const pr = await this.getPullRequest(pullNumber);
|
|
8111
|
+
const headSha = String(pr?.head?.sha || "").trim();
|
|
8112
|
+
if (!headSha) return { ok: true, ready: false, reason: "pr_head_sha_missing", pull_request: { number: pullNumber } };
|
|
8113
|
+
const combined = await this.getCombinedStatus(headSha);
|
|
8114
|
+
const statuses = Array.isArray(combined?.statuses) ? combined.statuses : [];
|
|
8115
|
+
const selectedStatus = sortNewestFirst(statuses.filter((status) => matchesVercelStatusContext(status?.context, context)))[0] || null;
|
|
8116
|
+
if (!selectedStatus) {
|
|
8117
|
+
return {
|
|
8118
|
+
ok: true,
|
|
8119
|
+
ready: false,
|
|
8120
|
+
reason: "vercel_status_missing",
|
|
8121
|
+
pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
|
|
8122
|
+
required_context: context,
|
|
8123
|
+
combined_state: combined?.state || null
|
|
8124
|
+
};
|
|
8125
|
+
}
|
|
8126
|
+
if (String(selectedStatus.state || "").toLowerCase() !== "success") {
|
|
8127
|
+
return {
|
|
8128
|
+
ok: true,
|
|
8129
|
+
ready: false,
|
|
8130
|
+
reason: "vercel_status_not_success",
|
|
8131
|
+
pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
|
|
8132
|
+
required_context: context,
|
|
8133
|
+
status: selectedStatus,
|
|
8134
|
+
combined_state: combined?.state || null
|
|
8135
|
+
};
|
|
8136
|
+
}
|
|
8137
|
+
let deployments = await this.listDeployments({ sha: headSha, environment });
|
|
8138
|
+
if (!Array.isArray(deployments) || deployments.length === 0) deployments = await this.listDeployments({ sha: headSha });
|
|
8139
|
+
const newestDeployments = sortNewestFirst(Array.isArray(deployments) ? deployments : []);
|
|
8140
|
+
const selectedDeployment = newestDeployments.find((deployment) => !environment || String(deployment?.environment || "") === environment) || newestDeployments[0] || null;
|
|
8141
|
+
if (!selectedDeployment?.id) {
|
|
8142
|
+
return {
|
|
8143
|
+
ok: true,
|
|
8144
|
+
ready: false,
|
|
8145
|
+
reason: "vercel_deployment_missing",
|
|
8146
|
+
pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
|
|
8147
|
+
required_environment: environment,
|
|
8148
|
+
status: selectedStatus
|
|
8149
|
+
};
|
|
8150
|
+
}
|
|
8151
|
+
const deploymentStatuses = await this.listDeploymentStatuses(selectedDeployment.id);
|
|
8152
|
+
const selectedDeploymentStatus = sortNewestFirst(Array.isArray(deploymentStatuses) ? deploymentStatuses : [])[0] || null;
|
|
8153
|
+
if (!selectedDeploymentStatus || String(selectedDeploymentStatus.state || "").toLowerCase() !== "success") {
|
|
8154
|
+
return {
|
|
8155
|
+
ok: true,
|
|
8156
|
+
ready: false,
|
|
8157
|
+
reason: "vercel_deployment_not_success",
|
|
8158
|
+
pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
|
|
8159
|
+
required_environment: environment,
|
|
8160
|
+
status: selectedStatus,
|
|
8161
|
+
deployment: selectedDeployment,
|
|
8162
|
+
deployment_status: selectedDeploymentStatus
|
|
8163
|
+
};
|
|
8164
|
+
}
|
|
8165
|
+
const url = selectFunctionalDeploymentUrl(selectedDeploymentStatus, selectedDeployment);
|
|
8166
|
+
const urlCheck = requireFunctionalUrl ? await checkFunctionalDeploymentUrl(url, fetchImpl) : { ok: Boolean(url), reason: url ? void 0 : "url_missing", url };
|
|
8167
|
+
if (requireFunctionalUrl && !urlCheck.ok) {
|
|
8168
|
+
return {
|
|
8169
|
+
ok: true,
|
|
8170
|
+
ready: false,
|
|
8171
|
+
reason: "vercel_url_not_functional",
|
|
8172
|
+
pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
|
|
8173
|
+
required_environment: environment,
|
|
8174
|
+
status: selectedStatus,
|
|
8175
|
+
deployment: selectedDeployment,
|
|
8176
|
+
deployment_status: selectedDeploymentStatus,
|
|
8177
|
+
url,
|
|
8178
|
+
url_check: urlCheck
|
|
8179
|
+
};
|
|
8180
|
+
}
|
|
8181
|
+
return {
|
|
8182
|
+
ok: true,
|
|
8183
|
+
ready: true,
|
|
8184
|
+
reason: "vercel_pr_deployment_ready",
|
|
8185
|
+
pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
|
|
8186
|
+
required_context: context,
|
|
8187
|
+
required_environment: environment,
|
|
8188
|
+
status: selectedStatus,
|
|
8189
|
+
deployment: selectedDeployment,
|
|
8190
|
+
deployment_status: selectedDeploymentStatus,
|
|
8191
|
+
url,
|
|
8192
|
+
url_check: urlCheck
|
|
8193
|
+
};
|
|
8194
|
+
},
|
|
8195
|
+
async commitWorktree({ worktree, branch = "", message = "", runGitFn = runGit, syncLocal = false } = {}) {
|
|
8196
|
+
const directory = path3.resolve(String(worktree || ""));
|
|
8197
|
+
if (!directory || !fs3.existsSync(directory)) throw new Error("worktree does not exist");
|
|
8198
|
+
const targetBranch = String(branch || currentGitBranch(directory, runGitFn)).trim();
|
|
8199
|
+
if (!targetBranch || targetBranch === "HEAD") throw new Error("target branch is required for GitHub API commit");
|
|
8200
|
+
const commitMessage = String(message || "").trim();
|
|
8201
|
+
if (!commitMessage) throw new Error("commit message is required");
|
|
8202
|
+
const changedPaths = listWorktreeChangedPaths(directory, runGitFn);
|
|
8203
|
+
if (changedPaths.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
|
|
8204
|
+
const ref = await getRef(targetBranch);
|
|
8205
|
+
const headSha = ref?.object?.sha;
|
|
8206
|
+
if (!headSha) throw new Error(`GitHub ref for ${targetBranch} did not include a head sha`);
|
|
8207
|
+
const headCommit = await getCommitObject(headSha);
|
|
8208
|
+
const baseTree = headCommit?.tree?.sha;
|
|
8209
|
+
if (!baseTree) throw new Error(`GitHub commit ${headSha} did not include a tree sha`);
|
|
8210
|
+
const tree = [];
|
|
8211
|
+
for (const gitPath of changedPaths) {
|
|
8212
|
+
const entry = treeEntryForWorktreePath(directory, gitPath);
|
|
8213
|
+
if (!entry) continue;
|
|
8214
|
+
if (entry.deleted) {
|
|
8215
|
+
tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: null });
|
|
8216
|
+
continue;
|
|
8217
|
+
}
|
|
8218
|
+
const blob = await createBlob({ content: entry.content, encoding: entry.encoding });
|
|
8219
|
+
if (!blob?.sha) throw new Error(`GitHub blob creation failed for ${gitPath}`);
|
|
8220
|
+
tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: blob.sha });
|
|
8221
|
+
}
|
|
8222
|
+
if (tree.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
|
|
8223
|
+
const nextTree = await createTree({ baseTree, tree });
|
|
8224
|
+
const nextCommit = await createCommit({ message: commitMessage, treeSha: nextTree.sha, parents: [headSha] });
|
|
8225
|
+
if (!nextCommit?.sha) throw new Error("GitHub commit creation did not return a sha");
|
|
8226
|
+
const updatedRef = await updateRef({ branch: targetBranch, sha: nextCommit.sha, force: false });
|
|
8227
|
+
if (syncLocal) {
|
|
8228
|
+
runGitFn(directory, ["fetch", "origin", targetBranch]);
|
|
8229
|
+
runGitFn(directory, ["reset", "--hard", "FETCH_HEAD"]);
|
|
8230
|
+
}
|
|
8231
|
+
return {
|
|
8232
|
+
ok: true,
|
|
8233
|
+
action: "committed",
|
|
8234
|
+
branch: targetBranch,
|
|
8235
|
+
before: headSha,
|
|
8236
|
+
after: nextCommit.sha,
|
|
8237
|
+
changedPaths,
|
|
8238
|
+
treeEntries: tree.length,
|
|
8239
|
+
verification: nextCommit.verification || null,
|
|
8240
|
+
ref: updatedRef
|
|
8241
|
+
};
|
|
8242
|
+
},
|
|
8243
|
+
async authMode() {
|
|
8244
|
+
if (appEnabled) return { mode: "github_app", appId, installationId };
|
|
8245
|
+
if (staticToken) return { mode: "token" };
|
|
8246
|
+
return { mode: "anonymous" };
|
|
8247
|
+
}
|
|
8248
|
+
};
|
|
8249
|
+
}
|
|
8250
|
+
|
|
8251
|
+
// src/include_resolver.js
|
|
8252
|
+
import fs4 from "node:fs";
|
|
8253
|
+
import path4 from "node:path";
|
|
7808
8254
|
function compactPromptPath(filePath) {
|
|
7809
8255
|
if (!filePath.endsWith(".md") || filePath.endsWith(".prompt.md")) return null;
|
|
7810
8256
|
return filePath.replace(/\.md$/, ".prompt.md");
|
|
7811
8257
|
}
|
|
7812
8258
|
function isSameOrNestedPath(candidate, root) {
|
|
7813
8259
|
if (typeof candidate !== "string" || typeof root !== "string" || !candidate.trim() || !root.trim()) return false;
|
|
7814
|
-
const resolvedCandidate =
|
|
7815
|
-
const resolvedRoot =
|
|
7816
|
-
const relative =
|
|
7817
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !
|
|
8260
|
+
const resolvedCandidate = path4.resolve(candidate);
|
|
8261
|
+
const resolvedRoot = path4.resolve(root);
|
|
8262
|
+
const relative = path4.relative(resolvedRoot, resolvedCandidate);
|
|
8263
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path4.isAbsolute(relative);
|
|
7818
8264
|
}
|
|
7819
8265
|
function resolveWithin(baseDir, relativePath) {
|
|
7820
8266
|
if (!relativePath) return null;
|
|
7821
|
-
if (
|
|
7822
|
-
const resolved =
|
|
8267
|
+
if (path4.isAbsolute(relativePath)) return null;
|
|
8268
|
+
const resolved = path4.resolve(baseDir, relativePath);
|
|
7823
8269
|
return isSameOrNestedPath(resolved, baseDir) ? resolved : null;
|
|
7824
8270
|
}
|
|
7825
8271
|
function withCompactPreference(paths, options = {}) {
|
|
@@ -7832,18 +8278,18 @@ function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
|
7832
8278
|
const scopedMatch = trimmed.match(/^([a-z]+):(.*)$/i);
|
|
7833
8279
|
const scope = scopedMatch?.[1]?.toLowerCase();
|
|
7834
8280
|
const target = scopedMatch ? scopedMatch[2].trim() : trimmed;
|
|
7835
|
-
const optimaRoot = options.optimaRoot ||
|
|
7836
|
-
const repoPolicyRoot = options.repoPolicyRoot ||
|
|
7837
|
-
const bundlePolicyRoot = options.bundlePolicyRoot ||
|
|
8281
|
+
const optimaRoot = options.optimaRoot || path4.join(repoRoot, ".optima");
|
|
8282
|
+
const repoPolicyRoot = options.repoPolicyRoot || path4.join(optimaRoot, "policies");
|
|
8283
|
+
const bundlePolicyRoot = options.bundlePolicyRoot || path4.join(bundleRoot, "assets", "policies");
|
|
7838
8284
|
if (scope === "plugin") {
|
|
7839
8285
|
for (const filePath of withCompactPreference([resolveWithin(bundleRoot, target)], options)) {
|
|
7840
|
-
if (filePath &&
|
|
8286
|
+
if (filePath && fs4.existsSync(filePath)) return filePath;
|
|
7841
8287
|
}
|
|
7842
8288
|
return null;
|
|
7843
8289
|
}
|
|
7844
8290
|
if (scope === "repo") {
|
|
7845
8291
|
for (const filePath of withCompactPreference([resolveWithin(optimaRoot, target)], options)) {
|
|
7846
|
-
if (filePath &&
|
|
8292
|
+
if (filePath && fs4.existsSync(filePath)) return filePath;
|
|
7847
8293
|
}
|
|
7848
8294
|
return null;
|
|
7849
8295
|
}
|
|
@@ -7853,7 +8299,7 @@ function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
|
7853
8299
|
resolveWithin(bundlePolicyRoot, target)
|
|
7854
8300
|
], options);
|
|
7855
8301
|
for (const filePath of candidates2) {
|
|
7856
|
-
if (filePath &&
|
|
8302
|
+
if (filePath && fs4.existsSync(filePath)) return filePath;
|
|
7857
8303
|
}
|
|
7858
8304
|
return null;
|
|
7859
8305
|
}
|
|
@@ -7862,7 +8308,7 @@ function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
|
7862
8308
|
resolveWithin(bundleRoot, target)
|
|
7863
8309
|
], options);
|
|
7864
8310
|
for (const filePath of candidates) {
|
|
7865
|
-
if (filePath &&
|
|
8311
|
+
if (filePath && fs4.existsSync(filePath)) return filePath;
|
|
7866
8312
|
}
|
|
7867
8313
|
return null;
|
|
7868
8314
|
}
|
|
@@ -7883,16 +8329,69 @@ function resolveIncludes(text, repoRoot, bundleRoot, options = {}) {
|
|
|
7883
8329
|
|
|
7884
8330
|
`;
|
|
7885
8331
|
}
|
|
7886
|
-
const content =
|
|
8332
|
+
const content = fs4.readFileSync(filePath, "utf8");
|
|
7887
8333
|
return resolveIncludes(content, repoRoot, bundleRoot, { ...options, includeDepth: includeDepth + 1, maxIncludeDepth });
|
|
7888
8334
|
});
|
|
7889
8335
|
}
|
|
7890
8336
|
|
|
8337
|
+
// src/markdown_artifacts.js
|
|
8338
|
+
import fs5 from "node:fs";
|
|
8339
|
+
var CLICKUP_REQUIRED_SUMMARY_SECTIONS = [
|
|
8340
|
+
"Summary",
|
|
8341
|
+
"Work Performed",
|
|
8342
|
+
"AC Coverage",
|
|
8343
|
+
"Verification Results",
|
|
8344
|
+
"Documentation Impact",
|
|
8345
|
+
"Open Risks",
|
|
8346
|
+
"Recommended Next Step"
|
|
8347
|
+
];
|
|
8348
|
+
var RAW_LOG_SECTION_NAMES = /* @__PURE__ */ new Set(["Raw Logs", "Logs", "Full Logs", "Command Output", "Transcript"]);
|
|
8349
|
+
function parseMarkdownSections(markdown = "") {
|
|
8350
|
+
const sections = {};
|
|
8351
|
+
let current = null;
|
|
8352
|
+
let buffer = [];
|
|
8353
|
+
const flush = () => {
|
|
8354
|
+
if (!current) return;
|
|
8355
|
+
sections[current] = buffer.join("\n").trim();
|
|
8356
|
+
};
|
|
8357
|
+
for (const line of String(markdown).split(/\r?\n/)) {
|
|
8358
|
+
const heading = /^(#{2,3})\s+(.+?)\s*$/.exec(line);
|
|
8359
|
+
if (heading) {
|
|
8360
|
+
flush();
|
|
8361
|
+
current = heading[2].trim();
|
|
8362
|
+
buffer = [];
|
|
8363
|
+
continue;
|
|
8364
|
+
}
|
|
8365
|
+
if (current) buffer.push(line);
|
|
8366
|
+
}
|
|
8367
|
+
flush();
|
|
8368
|
+
return sections;
|
|
8369
|
+
}
|
|
8370
|
+
function parseMarkdownArtifact(markdown = "", { requiredSections = [] } = {}) {
|
|
8371
|
+
const sections = parseMarkdownSections(markdown);
|
|
8372
|
+
const missing = requiredSections.filter((section) => !sections[section]);
|
|
8373
|
+
return {
|
|
8374
|
+
ok: missing.length === 0,
|
|
8375
|
+
sections,
|
|
8376
|
+
missing,
|
|
8377
|
+
message: missing.length ? `Missing required section(s): ${missing.join(", ")}` : "ok"
|
|
8378
|
+
};
|
|
8379
|
+
}
|
|
8380
|
+
function readMarkdownArtifact(filePath, options = {}) {
|
|
8381
|
+
const markdown = fs5.readFileSync(filePath, "utf8");
|
|
8382
|
+
return parseMarkdownArtifact(markdown, options);
|
|
8383
|
+
}
|
|
8384
|
+
function stripRawLogSections(sections = {}) {
|
|
8385
|
+
return Object.fromEntries(
|
|
8386
|
+
Object.entries(sections).filter(([name]) => !RAW_LOG_SECTION_NAMES.has(name))
|
|
8387
|
+
);
|
|
8388
|
+
}
|
|
8389
|
+
|
|
7891
8390
|
// src/repair.js
|
|
7892
8391
|
var import_yaml = __toESM(require_dist(), 1);
|
|
7893
8392
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
7894
|
-
import
|
|
7895
|
-
import
|
|
8393
|
+
import fs6 from "node:fs";
|
|
8394
|
+
import path5 from "node:path";
|
|
7896
8395
|
var CODEMAP_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7897
8396
|
".js",
|
|
7898
8397
|
".ts",
|
|
@@ -7930,10 +8429,10 @@ var LEGACY_OPERATIONAL_ROOTS = /* @__PURE__ */ new Set([
|
|
|
7930
8429
|
]);
|
|
7931
8430
|
var OPTIMA_OPERATIONAL_FOLDERS = [".optima", "templates", "dist"];
|
|
7932
8431
|
function relPath(worktree, targetPath) {
|
|
7933
|
-
return
|
|
8432
|
+
return path5.relative(worktree, targetPath).split(path5.sep).join("/") || ".";
|
|
7934
8433
|
}
|
|
7935
8434
|
function normalizeCodemapRelPath(relPathValue) {
|
|
7936
|
-
return
|
|
8435
|
+
return path5.normalize(relPathValue).replace(/\\/g, "/").replace(/\/$/, "");
|
|
7937
8436
|
}
|
|
7938
8437
|
function firstPathSegment(relPathValue) {
|
|
7939
8438
|
return normalizeCodemapRelPath(relPathValue).split("/").filter(Boolean)[0] || "";
|
|
@@ -7952,8 +8451,8 @@ function isHiddenTree(relPathValue) {
|
|
|
7952
8451
|
function loadGitIgnoreMatcher(worktree) {
|
|
7953
8452
|
const ig = (0, import_ignore.default)();
|
|
7954
8453
|
ig.add(".git");
|
|
7955
|
-
const gitignorePath =
|
|
7956
|
-
if (
|
|
8454
|
+
const gitignorePath = path5.join(worktree, ".gitignore");
|
|
8455
|
+
if (fs6.existsSync(gitignorePath)) ig.add(fs6.readFileSync(gitignorePath, "utf8"));
|
|
7957
8456
|
return ig;
|
|
7958
8457
|
}
|
|
7959
8458
|
function codemapSectionEntries(value) {
|
|
@@ -7972,7 +8471,7 @@ function codemapIndexedPaths(map) {
|
|
|
7972
8471
|
}
|
|
7973
8472
|
function readCodemap(filePath, unresolved, worktree) {
|
|
7974
8473
|
try {
|
|
7975
|
-
const parsed = import_yaml.default.parse(
|
|
8474
|
+
const parsed = import_yaml.default.parse(fs6.readFileSync(filePath, "utf8"));
|
|
7976
8475
|
if (!parsed || typeof parsed !== "object") {
|
|
7977
8476
|
unresolved.push({ category: "codemap", path: relPath(worktree, filePath), message: "CodeMap is empty or invalid; manual repair required." });
|
|
7978
8477
|
return null;
|
|
@@ -7984,42 +8483,42 @@ function readCodemap(filePath, unresolved, worktree) {
|
|
|
7984
8483
|
}
|
|
7985
8484
|
}
|
|
7986
8485
|
function writeCodemap(filePath, map) {
|
|
7987
|
-
|
|
8486
|
+
fs6.writeFileSync(filePath, import_yaml.default.stringify(map), "utf8");
|
|
7988
8487
|
}
|
|
7989
8488
|
function isIgnoredPath(ig, relativePath) {
|
|
7990
8489
|
const normalized = normalizeCodemapRelPath(relativePath);
|
|
7991
8490
|
return Boolean(normalized) && ig.ignores(normalized);
|
|
7992
8491
|
}
|
|
7993
8492
|
function hasImmediateSourceFile(dirPath, ig, worktree) {
|
|
7994
|
-
if (!
|
|
7995
|
-
for (const item of
|
|
7996
|
-
const childPath =
|
|
7997
|
-
const relative =
|
|
8493
|
+
if (!fs6.existsSync(dirPath)) return false;
|
|
8494
|
+
for (const item of fs6.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8495
|
+
const childPath = path5.join(dirPath, item.name);
|
|
8496
|
+
const relative = path5.relative(worktree, childPath);
|
|
7998
8497
|
if (isIgnoredPath(ig, relative) || isOperationalRelPath(relative)) continue;
|
|
7999
|
-
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(
|
|
8498
|
+
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(path5.extname(item.name))) return true;
|
|
8000
8499
|
}
|
|
8001
8500
|
return false;
|
|
8002
8501
|
}
|
|
8003
8502
|
function containsSource(dirPath, ig, worktree) {
|
|
8004
|
-
if (!
|
|
8005
|
-
const dirRelPath =
|
|
8503
|
+
if (!fs6.existsSync(dirPath)) return false;
|
|
8504
|
+
const dirRelPath = path5.relative(worktree, dirPath);
|
|
8006
8505
|
if (isOperationalRelPath(dirRelPath) || isHiddenTree(dirRelPath)) return false;
|
|
8007
|
-
for (const item of
|
|
8008
|
-
const childPath =
|
|
8009
|
-
const relative =
|
|
8506
|
+
for (const item of fs6.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8507
|
+
const childPath = path5.join(dirPath, item.name);
|
|
8508
|
+
const relative = path5.relative(worktree, childPath);
|
|
8010
8509
|
if (isIgnoredPath(ig, relative) || isOperationalRelPath(relative) || isHiddenTree(relative)) continue;
|
|
8011
|
-
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(
|
|
8510
|
+
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(path5.extname(item.name))) return true;
|
|
8012
8511
|
if (item.isDirectory() && containsSource(childPath, ig, worktree)) return true;
|
|
8013
8512
|
}
|
|
8014
8513
|
return false;
|
|
8015
8514
|
}
|
|
8016
8515
|
function createModuleCodemap(dirPath, worktree, deps) {
|
|
8017
|
-
const entries =
|
|
8516
|
+
const entries = fs6.readdirSync(dirPath, { withFileTypes: true });
|
|
8018
8517
|
const entrypoints = [];
|
|
8019
8518
|
const internals = [];
|
|
8020
|
-
const relToRoot =
|
|
8519
|
+
const relToRoot = path5.relative(dirPath, deps.optimaCodemapPath(worktree)).split(path5.sep).join("/");
|
|
8021
8520
|
for (const item of entries) {
|
|
8022
|
-
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(
|
|
8521
|
+
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(path5.extname(item.name))) continue;
|
|
8023
8522
|
const bucket = /^index\.(js|ts|tsx|jsx)$|^main\.(js|ts|tsx|jsx|py|go|rs|java)$/.test(item.name) ? entrypoints : internals;
|
|
8024
8523
|
bucket.push({ path: item.name });
|
|
8025
8524
|
}
|
|
@@ -8032,7 +8531,7 @@ function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unres
|
|
|
8032
8531
|
const map = readCodemap(codemapPath, unresolved, worktree);
|
|
8033
8532
|
if (!map) return;
|
|
8034
8533
|
const isRootMap = codemapPath === deps.optimaCodemapPath(worktree);
|
|
8035
|
-
const mapDir =
|
|
8534
|
+
const mapDir = path5.dirname(codemapPath);
|
|
8036
8535
|
const pathBase = isRootMap ? worktree : mapDir;
|
|
8037
8536
|
let changed = false;
|
|
8038
8537
|
for (const section of ["modules", "entrypoints", "sources_of_truth", "links", "internals"]) {
|
|
@@ -8043,13 +8542,13 @@ function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unres
|
|
|
8043
8542
|
}
|
|
8044
8543
|
const nextEntries = [];
|
|
8045
8544
|
for (const entry of originalEntries) {
|
|
8046
|
-
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://") ||
|
|
8545
|
+
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://") || path5.isAbsolute(entry.path)) {
|
|
8047
8546
|
nextEntries.push(entry);
|
|
8048
8547
|
continue;
|
|
8049
8548
|
}
|
|
8050
8549
|
const normalizedPath = normalizeCodemapRelPath(entry.path);
|
|
8051
|
-
const absPath =
|
|
8052
|
-
if (!
|
|
8550
|
+
const absPath = path5.join(pathBase, normalizedPath);
|
|
8551
|
+
if (!fs6.existsSync(absPath)) {
|
|
8053
8552
|
registerAction({ category: "codemap", action: apply ? "removed" : "would_remove", path: relPath(worktree, codemapPath), detail: `Remove broken ${section.slice(0, -1)} reference '${entry.path}'.` });
|
|
8054
8553
|
changed = true;
|
|
8055
8554
|
continue;
|
|
@@ -8058,8 +8557,8 @@ function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unres
|
|
|
8058
8557
|
const maxParts = isRootMap ? 2 : 1;
|
|
8059
8558
|
if (parts.length > maxParts) {
|
|
8060
8559
|
const localPath = isRootMap ? parts.slice(0, 2).join("/") : parts[0];
|
|
8061
|
-
const localAbs =
|
|
8062
|
-
if (
|
|
8560
|
+
const localAbs = path5.join(pathBase, localPath);
|
|
8561
|
+
if (fs6.existsSync(localAbs)) {
|
|
8063
8562
|
nextEntries.push({ ...entry, path: localPath });
|
|
8064
8563
|
changed = true;
|
|
8065
8564
|
registerAction({ category: "codemap", action: apply ? "updated" : "would_update", path: relPath(worktree, codemapPath), detail: `Shorten '${entry.path}' to local path '${localPath}'.` });
|
|
@@ -8073,11 +8572,11 @@ function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unres
|
|
|
8073
8572
|
}
|
|
8074
8573
|
if (Array.isArray(map[section])) map[section] = nextEntries;
|
|
8075
8574
|
}
|
|
8076
|
-
if (map.scope === "module" && !isOperationalRelPath(
|
|
8575
|
+
if (map.scope === "module" && !isOperationalRelPath(path5.relative(worktree, mapDir))) {
|
|
8077
8576
|
const indexed = codemapIndexedPaths(map);
|
|
8078
8577
|
const internals = Array.isArray(map.internals) ? map.internals : [];
|
|
8079
|
-
for (const item of
|
|
8080
|
-
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(
|
|
8578
|
+
for (const item of fs6.readdirSync(mapDir, { withFileTypes: true })) {
|
|
8579
|
+
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(path5.extname(item.name))) continue;
|
|
8081
8580
|
if (indexed.has(item.name)) continue;
|
|
8082
8581
|
internals.push({ path: item.name });
|
|
8083
8582
|
indexed.add(item.name);
|
|
@@ -8091,13 +8590,13 @@ function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unres
|
|
|
8091
8590
|
function repairCodemaps(worktree, apply, actions, unresolved, deps) {
|
|
8092
8591
|
const ig = loadGitIgnoreMatcher(worktree);
|
|
8093
8592
|
const rootCodemapPath = deps.optimaCodemapPath(worktree);
|
|
8094
|
-
const rootCodemapMissing = !
|
|
8593
|
+
const rootCodemapMissing = !fs6.existsSync(rootCodemapPath);
|
|
8095
8594
|
if (rootCodemapMissing) {
|
|
8096
8595
|
actions.push({ category: "codemap", action: apply ? "created" : "would_create", path: ".optima/codemap.yml", detail: "Create missing root CodeMap from template." });
|
|
8097
8596
|
if (apply) deps.scaffoldOptimaRootCodemap(worktree);
|
|
8098
8597
|
}
|
|
8099
|
-
if (
|
|
8100
|
-
const rootMap =
|
|
8598
|
+
if (fs6.existsSync(rootCodemapPath)) repairSingleCodemap(rootCodemapPath, worktree, apply, (action) => actions.push(action), unresolved, deps);
|
|
8599
|
+
const rootMap = fs6.existsSync(rootCodemapPath) ? readCodemap(rootCodemapPath, unresolved, worktree) : null;
|
|
8101
8600
|
let rootChanged = false;
|
|
8102
8601
|
const rootModulesRepairable = rootMap && (rootMap.modules === void 0 || Array.isArray(rootMap.modules));
|
|
8103
8602
|
if (rootMap) {
|
|
@@ -8109,10 +8608,10 @@ function repairCodemaps(worktree, apply, actions, unresolved, deps) {
|
|
|
8109
8608
|
}
|
|
8110
8609
|
}
|
|
8111
8610
|
function walk(dirPath) {
|
|
8112
|
-
const relative =
|
|
8611
|
+
const relative = path5.relative(worktree, dirPath);
|
|
8113
8612
|
if (relative && (isIgnoredPath(ig, relative) || isOperationalRelPath(relative) || isHiddenTree(relative))) return;
|
|
8114
|
-
const codemapPath =
|
|
8115
|
-
const hasCodemap =
|
|
8613
|
+
const codemapPath = path5.join(dirPath, "codemap.yml");
|
|
8614
|
+
const hasCodemap = fs6.existsSync(codemapPath);
|
|
8116
8615
|
const sourceDir = relative && containsSource(dirPath, ig, worktree);
|
|
8117
8616
|
if (sourceDir && !hasCodemap && dirPath !== worktree) {
|
|
8118
8617
|
if (hasImmediateSourceFile(dirPath, ig, worktree)) {
|
|
@@ -8131,19 +8630,19 @@ function repairCodemaps(worktree, apply, actions, unresolved, deps) {
|
|
|
8131
8630
|
actions.push({ category: "codemap", action: apply ? "updated" : "would_update", path: ".optima/codemap.yml", detail: `Register top-level source module '${normalizeCodemapRelPath(relative)}'.` });
|
|
8132
8631
|
}
|
|
8133
8632
|
}
|
|
8134
|
-
if (
|
|
8135
|
-
for (const item of
|
|
8136
|
-
if (item.isDirectory()) walk(
|
|
8633
|
+
if (fs6.existsSync(codemapPath) && codemapPath !== rootCodemapPath) repairSingleCodemap(codemapPath, worktree, apply, (action) => actions.push(action), unresolved, deps);
|
|
8634
|
+
for (const item of fs6.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8635
|
+
if (item.isDirectory()) walk(path5.join(dirPath, item.name));
|
|
8137
8636
|
}
|
|
8138
8637
|
}
|
|
8139
|
-
if (
|
|
8638
|
+
if (fs6.existsSync(worktree)) walk(worktree);
|
|
8140
8639
|
if (rootMap && rootChanged && apply) writeCodemap(rootCodemapPath, rootMap);
|
|
8141
8640
|
}
|
|
8142
8641
|
function walkMarkdownFiles(dirPath) {
|
|
8143
|
-
if (!
|
|
8642
|
+
if (!fs6.existsSync(dirPath)) return [];
|
|
8144
8643
|
const files = [];
|
|
8145
|
-
for (const entry of
|
|
8146
|
-
const entryPath =
|
|
8644
|
+
for (const entry of fs6.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8645
|
+
const entryPath = path5.join(dirPath, entry.name);
|
|
8147
8646
|
if (entry.isDirectory()) {
|
|
8148
8647
|
files.push(...walkMarkdownFiles(entryPath));
|
|
8149
8648
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -8182,7 +8681,7 @@ function rewriteOptimaMarkdownReferences(content, evidenceBasePath = null) {
|
|
|
8182
8681
|
}
|
|
8183
8682
|
function optimaEvidenceBaseForFile(worktree, filePath, deps) {
|
|
8184
8683
|
const evidenceRoot = deps.optimaEvidencesDir(worktree);
|
|
8185
|
-
const relative =
|
|
8684
|
+
const relative = path5.relative(evidenceRoot, filePath).split(path5.sep).join("/");
|
|
8186
8685
|
const [taskId] = relative.split("/");
|
|
8187
8686
|
if (!taskId || taskId === ".." || relative.startsWith("../")) return null;
|
|
8188
8687
|
return `.optima/evidences/${taskId}`;
|
|
@@ -8195,11 +8694,11 @@ function normalizeOptimaMarkdownReferences(worktree, deps) {
|
|
|
8195
8694
|
];
|
|
8196
8695
|
const changed = [];
|
|
8197
8696
|
for (const filePath of scanRoots.flatMap(walkMarkdownFiles)) {
|
|
8198
|
-
const raw =
|
|
8199
|
-
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) +
|
|
8697
|
+
const raw = fs6.readFileSync(filePath, "utf8");
|
|
8698
|
+
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) + path5.sep) ? optimaEvidenceBaseForFile(worktree, filePath, deps) : null;
|
|
8200
8699
|
const rewritten = rewriteOptimaMarkdownReferences(raw, evidenceBasePath);
|
|
8201
8700
|
if (rewritten !== raw) {
|
|
8202
|
-
|
|
8701
|
+
fs6.writeFileSync(filePath, rewritten, "utf8");
|
|
8203
8702
|
changed.push(relPath(worktree, filePath));
|
|
8204
8703
|
}
|
|
8205
8704
|
}
|
|
@@ -8207,8 +8706,8 @@ function normalizeOptimaMarkdownReferences(worktree, deps) {
|
|
|
8207
8706
|
}
|
|
8208
8707
|
function missingOptimaGitignoreRules(worktree, deps) {
|
|
8209
8708
|
if (!deps.isGitRepository(worktree)) return [];
|
|
8210
|
-
const gitignorePath =
|
|
8211
|
-
const existing =
|
|
8709
|
+
const gitignorePath = path5.join(worktree, ".gitignore");
|
|
8710
|
+
const existing = fs6.existsSync(gitignorePath) ? fs6.readFileSync(gitignorePath, "utf8") : "";
|
|
8212
8711
|
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
8213
8712
|
return deps.optimaGitignoreRules.filter((rule) => !existingRules.has(rule));
|
|
8214
8713
|
}
|
|
@@ -8225,11 +8724,11 @@ function planOptimaRepair(worktree, args = {}, deps) {
|
|
|
8225
8724
|
const apply = mode === "apply";
|
|
8226
8725
|
const actions = [];
|
|
8227
8726
|
const unresolved = [];
|
|
8228
|
-
const hadOptima =
|
|
8727
|
+
const hadOptima = fs6.existsSync(deps.optimaDir(worktree));
|
|
8229
8728
|
const missingGitignoreRules = missingOptimaGitignoreRules(worktree, deps);
|
|
8230
8729
|
const markdownBefore = apply ? [] : walkMarkdownFiles(deps.optimaDir(worktree)).filter((filePath) => {
|
|
8231
|
-
const raw =
|
|
8232
|
-
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) +
|
|
8730
|
+
const raw = fs6.readFileSync(filePath, "utf8");
|
|
8731
|
+
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) + path5.sep) ? optimaEvidenceBaseForFile(worktree, filePath, deps) : null;
|
|
8233
8732
|
return rewriteOptimaMarkdownReferences(raw, evidenceBasePath) !== raw;
|
|
8234
8733
|
}).map((filePath) => relPath(worktree, filePath));
|
|
8235
8734
|
if (!hadOptima) pushRepairAction(actions, "operational", apply ? "created" : "would_create", ".optima/", "Create Optima operational root.");
|
|
@@ -8238,31 +8737,31 @@ function planOptimaRepair(worktree, args = {}, deps) {
|
|
|
8238
8737
|
[".staticeng/", deps.legacyStaticEngDir(worktree)],
|
|
8239
8738
|
[".nomadwork/", deps.legacyNomadworkDir(worktree)],
|
|
8240
8739
|
[".nomadworks/", deps.legacyNomadworksDir(worktree)],
|
|
8241
|
-
["tasks/",
|
|
8242
|
-
["evidences/",
|
|
8243
|
-
["docs/scrs/",
|
|
8244
|
-
["codemap.yml",
|
|
8245
|
-
["codemap.yaml",
|
|
8740
|
+
["tasks/", path5.join(worktree, "tasks")],
|
|
8741
|
+
["evidences/", path5.join(worktree, "evidences")],
|
|
8742
|
+
["docs/scrs/", path5.join(worktree, "docs", "scrs")],
|
|
8743
|
+
["codemap.yml", path5.join(worktree, "codemap.yml")],
|
|
8744
|
+
["codemap.yaml", path5.join(worktree, "codemap.yaml")]
|
|
8246
8745
|
];
|
|
8247
|
-
if (!deps.isOptimaPluginPackageWorktree(worktree)) legacySources.push(["policies/",
|
|
8746
|
+
if (!deps.isOptimaPluginPackageWorktree(worktree)) legacySources.push(["policies/", path5.join(worktree, "policies")]);
|
|
8248
8747
|
for (const [display, sourcePath] of legacySources) {
|
|
8249
|
-
if (
|
|
8748
|
+
if (fs6.existsSync(sourcePath)) pushRepairAction(actions, "legacy", apply ? "migrated" : "would_migrate", display, "Move or merge legacy/root Optima artifact into .optima using preservation safeguards.");
|
|
8250
8749
|
}
|
|
8251
8750
|
if (apply) deps.migrateLegacyOptimaLayout(worktree);
|
|
8252
|
-
const configMissing = !
|
|
8751
|
+
const configMissing = !fs6.existsSync(deps.repoConfigPath(worktree));
|
|
8253
8752
|
if (configMissing) pushRepairAction(actions, "config", apply ? "created" : "would_create", ".optima/.config/optima.yaml", "Create missing local Optima config from template.");
|
|
8254
8753
|
if (apply) deps.scaffoldOptimaConfig(worktree, args.team_mode || "full");
|
|
8255
8754
|
const requiredFiles = [
|
|
8256
|
-
[
|
|
8257
|
-
[
|
|
8258
|
-
[
|
|
8259
|
-
[
|
|
8260
|
-
[
|
|
8261
|
-
[
|
|
8262
|
-
[
|
|
8755
|
+
[path5.join(deps.optimaTasksDir(worktree), "current.md"), "registry", ".optima/tasks/current.md", deps.currentTasksRegistryContent()],
|
|
8756
|
+
[path5.join(deps.optimaTasksDir(worktree), "done.md"), "registry", ".optima/tasks/done.md", deps.doneTasksRegistryContent()],
|
|
8757
|
+
[path5.join(deps.optimaScrsDir(worktree), "current.md"), "registry", ".optima/docs/scrs/current.md", deps.currentScrRegistryContent()],
|
|
8758
|
+
[path5.join(deps.optimaScrsDir(worktree), "done.md"), "registry", ".optima/docs/scrs/done.md", deps.doneScrRegistryContent()],
|
|
8759
|
+
[path5.join(deps.optimaTasksDir(worktree), "task-template.md"), "template", ".optima/tasks/task-template.md", deps.taskTemplateContent()],
|
|
8760
|
+
[path5.join(deps.optimaTasksDir(worktree), "subtask-template.md"), "template", ".optima/tasks/subtask-template.md", deps.subtaskTemplateContent()],
|
|
8761
|
+
[path5.join(deps.repoPoliciesDir(worktree), "README.md"), "policy", ".optima/policies/README.md", deps.repoLocalPoliciesReadme]
|
|
8263
8762
|
];
|
|
8264
8763
|
for (const [filePath, category, displayPath, content] of requiredFiles) {
|
|
8265
|
-
if (!
|
|
8764
|
+
if (!fs6.existsSync(filePath)) {
|
|
8266
8765
|
pushRepairAction(actions, category, apply ? "created" : "would_create", displayPath, "Create missing Optima scaffold file without overwriting existing content.");
|
|
8267
8766
|
if (apply) deps.ensureFileIfMissing(filePath, content);
|
|
8268
8767
|
}
|
|
@@ -8316,18 +8815,18 @@ function formatRepairResult(plan, validationResult = null, formatValidationResul
|
|
|
8316
8815
|
// src/validate_logic.js
|
|
8317
8816
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
8318
8817
|
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
8319
|
-
import
|
|
8320
|
-
import
|
|
8818
|
+
import fs7 from "node:fs";
|
|
8819
|
+
import path6 from "node:path";
|
|
8321
8820
|
async function optima_validate_logic(worktree) {
|
|
8322
|
-
const rootCodemapPath =
|
|
8323
|
-
if (!
|
|
8821
|
+
const rootCodemapPath = path6.join(worktree, ".optima", "codemap.yml");
|
|
8822
|
+
if (!fs7.existsSync(rootCodemapPath)) return { ok: false, errors: [".optima/codemap.yml not found."], warnings: [] };
|
|
8324
8823
|
const errors = [];
|
|
8325
8824
|
const warnings = [];
|
|
8326
8825
|
const ig = (0, import_ignore2.default)();
|
|
8327
8826
|
ig.add(".git");
|
|
8328
|
-
const gitignorePath =
|
|
8329
|
-
if (
|
|
8330
|
-
ig.add(
|
|
8827
|
+
const gitignorePath = path6.join(worktree, ".gitignore");
|
|
8828
|
+
if (fs7.existsSync(gitignorePath)) {
|
|
8829
|
+
ig.add(fs7.readFileSync(gitignorePath, "utf8"));
|
|
8331
8830
|
}
|
|
8332
8831
|
const sourceExtensions = [
|
|
8333
8832
|
".js",
|
|
@@ -8367,30 +8866,30 @@ async function optima_validate_logic(worktree) {
|
|
|
8367
8866
|
const operationalFolders = [".optima", "templates", "dist"];
|
|
8368
8867
|
const isHiddenTree2 = (relPath2) => {
|
|
8369
8868
|
if (!relPath2) return false;
|
|
8370
|
-
return relPath2.split(
|
|
8869
|
+
return relPath2.split(path6.sep).some((part) => part.startsWith("."));
|
|
8371
8870
|
};
|
|
8372
|
-
const firstPathSegment2 = (relPath2) => relPath2.split(
|
|
8871
|
+
const firstPathSegment2 = (relPath2) => relPath2.split(path6.sep).filter(Boolean)[0] || "";
|
|
8373
8872
|
const isOperationalRelPath2 = (relPath2) => {
|
|
8374
8873
|
if (!relPath2) return false;
|
|
8375
8874
|
const firstSegment = firstPathSegment2(relPath2);
|
|
8376
8875
|
if (legacyOperationalRoots.has(firstSegment)) return true;
|
|
8377
|
-
return operationalFolders.some((f) => relPath2 === f || relPath2.startsWith(f +
|
|
8876
|
+
return operationalFolders.some((f) => relPath2 === f || relPath2.startsWith(f + path6.sep));
|
|
8378
8877
|
};
|
|
8379
8878
|
const getSectionEntries = (value) => {
|
|
8380
8879
|
if (Array.isArray(value)) return value;
|
|
8381
8880
|
if (value && typeof value === "object") return Object.values(value);
|
|
8382
8881
|
return [];
|
|
8383
8882
|
};
|
|
8384
|
-
const normalizeRelativePath = (relPath2) =>
|
|
8883
|
+
const normalizeRelativePath = (relPath2) => path6.normalize(relPath2).replace(/\\/g, "/").replace(/\/$/, "");
|
|
8385
8884
|
const isSourceDir = (dirPath) => {
|
|
8386
|
-
const dirRelPath =
|
|
8885
|
+
const dirRelPath = path6.relative(worktree, dirPath);
|
|
8387
8886
|
if (isOperationalRelPath2(dirRelPath)) return false;
|
|
8388
|
-
const items =
|
|
8887
|
+
const items = fs7.readdirSync(dirPath, { withFileTypes: true });
|
|
8389
8888
|
for (const item of items) {
|
|
8390
|
-
const childPath =
|
|
8391
|
-
const relPath2 =
|
|
8889
|
+
const childPath = path6.join(dirPath, item.name);
|
|
8890
|
+
const relPath2 = path6.relative(worktree, childPath);
|
|
8392
8891
|
if (ig.ignores(relPath2) || isOperationalRelPath2(relPath2)) continue;
|
|
8393
|
-
if (item.isFile() && sourceExtensions.includes(
|
|
8892
|
+
if (item.isFile() && sourceExtensions.includes(path6.extname(item.name))) return true;
|
|
8394
8893
|
if (item.isDirectory()) {
|
|
8395
8894
|
if (isSourceDir(childPath)) return true;
|
|
8396
8895
|
}
|
|
@@ -8398,7 +8897,7 @@ async function optima_validate_logic(worktree) {
|
|
|
8398
8897
|
return false;
|
|
8399
8898
|
};
|
|
8400
8899
|
function validateMap(filePath) {
|
|
8401
|
-
const content =
|
|
8900
|
+
const content = fs7.readFileSync(filePath, "utf8");
|
|
8402
8901
|
let map;
|
|
8403
8902
|
try {
|
|
8404
8903
|
map = import_yaml2.default.parse(content);
|
|
@@ -8410,32 +8909,32 @@ async function optima_validate_logic(worktree) {
|
|
|
8410
8909
|
errors.push(`${filePath}: Invalid YAML.`);
|
|
8411
8910
|
return;
|
|
8412
8911
|
}
|
|
8413
|
-
const dir =
|
|
8912
|
+
const dir = path6.dirname(filePath);
|
|
8414
8913
|
const pathBase = filePath === rootCodemapPath ? worktree : dir;
|
|
8415
8914
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
8416
8915
|
const sectionsToVerify = ["modules", "entrypoints", "sources_of_truth", "links", "internals"];
|
|
8417
8916
|
for (const section of sectionsToVerify) {
|
|
8418
8917
|
for (const item of getSectionEntries(map[section])) {
|
|
8419
8918
|
if (item?.path) {
|
|
8420
|
-
indexedPaths.add(
|
|
8919
|
+
indexedPaths.add(path6.normalize(item.path));
|
|
8421
8920
|
if (section === "links" && (item.path.startsWith("http://") || item.path.startsWith("https://"))) {
|
|
8422
8921
|
continue;
|
|
8423
8922
|
}
|
|
8424
|
-
const absPath =
|
|
8425
|
-
if (!
|
|
8923
|
+
const absPath = path6.isAbsolute(item.path) ? item.path : path6.join(pathBase, item.path);
|
|
8924
|
+
if (!fs7.existsSync(absPath)) {
|
|
8426
8925
|
errors.push(`${filePath}: ${section.slice(0, -1)} path does not exist: ${item.path}`);
|
|
8427
8926
|
}
|
|
8428
8927
|
}
|
|
8429
8928
|
}
|
|
8430
8929
|
}
|
|
8431
|
-
const relDir =
|
|
8930
|
+
const relDir = path6.relative(worktree, dir);
|
|
8432
8931
|
const isOperational = isOperationalRelPath2(relDir);
|
|
8433
8932
|
if (map.scope === "module" && !isOperational) {
|
|
8434
|
-
const items =
|
|
8933
|
+
const items = fs7.readdirSync(dir, { withFileTypes: true });
|
|
8435
8934
|
for (const item of items) {
|
|
8436
|
-
if (item.isFile() && sourceExtensions.includes(
|
|
8935
|
+
if (item.isFile() && sourceExtensions.includes(path6.extname(item.name))) {
|
|
8437
8936
|
if (item.name === "codemap.yml") continue;
|
|
8438
|
-
if (!indexedPaths.has(
|
|
8937
|
+
if (!indexedPaths.has(path6.normalize(item.name))) {
|
|
8439
8938
|
errors.push(`${filePath}: Unindexed source file found: '${item.name}'. Every source file must be categorized in a section (e.g., 'internals').`);
|
|
8440
8939
|
}
|
|
8441
8940
|
}
|
|
@@ -8445,7 +8944,7 @@ async function optima_validate_logic(worktree) {
|
|
|
8445
8944
|
for (const key of pathKeys) {
|
|
8446
8945
|
for (const entry of getSectionEntries(map[key])) {
|
|
8447
8946
|
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://")) continue;
|
|
8448
|
-
if (
|
|
8947
|
+
if (path6.isAbsolute(entry.path)) continue;
|
|
8449
8948
|
const normalizedPath = normalizeRelativePath(entry.path);
|
|
8450
8949
|
if (!normalizedPath || normalizedPath === ".") continue;
|
|
8451
8950
|
const parts = normalizedPath.split("/").filter((p) => p && p !== ".");
|
|
@@ -8457,18 +8956,18 @@ async function optima_validate_logic(worktree) {
|
|
|
8457
8956
|
}
|
|
8458
8957
|
}
|
|
8459
8958
|
const walk = (dir) => {
|
|
8460
|
-
const relDir =
|
|
8959
|
+
const relDir = path6.relative(worktree, dir);
|
|
8461
8960
|
if (relDir && ig.ignores(relDir)) return;
|
|
8462
8961
|
if (isOperationalRelPath2(relDir)) return;
|
|
8463
8962
|
if (isHiddenTree2(relDir)) return;
|
|
8464
|
-
const hasCodemap =
|
|
8465
|
-
const items =
|
|
8466
|
-
if (!relDir.startsWith(
|
|
8963
|
+
const hasCodemap = fs7.existsSync(path6.join(dir, "codemap.yml"));
|
|
8964
|
+
const items = fs7.readdirSync(dir, { withFileTypes: true });
|
|
8965
|
+
if (!relDir.startsWith(path6.join(".optima", "tasks", "done"))) {
|
|
8467
8966
|
for (const item of items) {
|
|
8468
8967
|
if (item.isFile() && item.name.endsWith(".md")) {
|
|
8469
|
-
const content =
|
|
8968
|
+
const content = fs7.readFileSync(path6.join(dir, item.name), "utf8");
|
|
8470
8969
|
if (content.includes("[To be defined]") || content.includes("[Insert ")) {
|
|
8471
|
-
const relFilePath =
|
|
8970
|
+
const relFilePath = path6.join(relDir, item.name);
|
|
8472
8971
|
errors.push(`Documentation Placeholder found: '${relFilePath}' still contains [To be defined] or [Insert ...] placeholders.`);
|
|
8473
8972
|
}
|
|
8474
8973
|
}
|
|
@@ -8478,9 +8977,9 @@ async function optima_validate_logic(worktree) {
|
|
|
8478
8977
|
if (relDir !== "" && !hasCodemap && isSourceDir(dir) && !isOperational) {
|
|
8479
8978
|
errors.push(`Missing CodeMap: Directory '${relDir}' contains source but has no codemap.yml.`);
|
|
8480
8979
|
}
|
|
8481
|
-
if (hasCodemap) validateMap(
|
|
8980
|
+
if (hasCodemap) validateMap(path6.join(dir, "codemap.yml"));
|
|
8482
8981
|
for (const item of items) {
|
|
8483
|
-
if (item.isDirectory()) walk(
|
|
8982
|
+
if (item.isDirectory()) walk(path6.join(dir, item.name));
|
|
8484
8983
|
}
|
|
8485
8984
|
};
|
|
8486
8985
|
validateMap(rootCodemapPath);
|
|
@@ -8493,14 +8992,14 @@ async function optima_validate_logic(worktree) {
|
|
|
8493
8992
|
}
|
|
8494
8993
|
|
|
8495
8994
|
// src/constants.js
|
|
8496
|
-
import
|
|
8995
|
+
import path7 from "node:path";
|
|
8497
8996
|
import { fileURLToPath } from "node:url";
|
|
8498
|
-
var PKG_ROOT =
|
|
8499
|
-
var BUNDLE_ASSETS_DIR =
|
|
8500
|
-
var BUNDLE_AGENTS_DIR =
|
|
8501
|
-
var BUNDLE_POLICIES_DIR =
|
|
8502
|
-
var TEMPLATES_DIR =
|
|
8503
|
-
var HUMANS_REGISTRY_PATH =
|
|
8997
|
+
var PKG_ROOT = path7.resolve(path7.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8998
|
+
var BUNDLE_ASSETS_DIR = path7.join(PKG_ROOT, "assets");
|
|
8999
|
+
var BUNDLE_AGENTS_DIR = path7.join(BUNDLE_ASSETS_DIR, "agents");
|
|
9000
|
+
var BUNDLE_POLICIES_DIR = path7.join(BUNDLE_ASSETS_DIR, "policies");
|
|
9001
|
+
var TEMPLATES_DIR = path7.join(PKG_ROOT, "templates");
|
|
9002
|
+
var HUMANS_REGISTRY_PATH = path7.join(PKG_ROOT, "docs", "core", "humans.md");
|
|
8504
9003
|
var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
8505
9004
|
var MINI_MODE_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
8506
9005
|
var CLICKUP_IGNORED_TASK_TYPES = ["Idea", "Backlog", "Hito", "Nota de reuni\xF3n", "Respuesta del formulario"];
|
|
@@ -8638,18 +9137,18 @@ var GITHUB_WEBHOOK_EVENTS = ["pull_request", "pull_request_review", "pull_reques
|
|
|
8638
9137
|
var OPTIMA_GITHUB_COMMITTER_NAME = "Optima Product Manager";
|
|
8639
9138
|
var OPTIMA_GITHUB_COMMITTER_EMAIL = "optima-product-manager[bot]@users.noreply.github.com";
|
|
8640
9139
|
function isRootDirectory(candidate) {
|
|
8641
|
-
const resolved =
|
|
8642
|
-
return resolved ===
|
|
9140
|
+
const resolved = path8.resolve(candidate);
|
|
9141
|
+
return resolved === path8.parse(resolved).root;
|
|
8643
9142
|
}
|
|
8644
9143
|
function isSafeWritableDirectory(candidate) {
|
|
8645
9144
|
if (typeof candidate !== "string" || !candidate.trim()) return false;
|
|
8646
|
-
if (!
|
|
8647
|
-
const resolved =
|
|
9145
|
+
if (!path8.isAbsolute(candidate)) return false;
|
|
9146
|
+
const resolved = path8.resolve(candidate);
|
|
8648
9147
|
if (isRootDirectory(resolved)) return false;
|
|
8649
9148
|
try {
|
|
8650
|
-
const stat =
|
|
9149
|
+
const stat = fs8.statSync(resolved);
|
|
8651
9150
|
if (!stat.isDirectory()) return false;
|
|
8652
|
-
|
|
9151
|
+
fs8.accessSync(resolved, fs8.constants.W_OK);
|
|
8653
9152
|
return true;
|
|
8654
9153
|
} catch {
|
|
8655
9154
|
return false;
|
|
@@ -8661,11 +9160,11 @@ function resolveSafeWorktree(context = {}, pluginWorktree = null) {
|
|
|
8661
9160
|
context?.directory,
|
|
8662
9161
|
pluginWorktree,
|
|
8663
9162
|
process.cwd(),
|
|
8664
|
-
|
|
9163
|
+
os2.homedir()
|
|
8665
9164
|
];
|
|
8666
9165
|
for (const candidate of candidates) {
|
|
8667
9166
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8668
|
-
const resolved =
|
|
9167
|
+
const resolved = path8.resolve(candidate);
|
|
8669
9168
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8670
9169
|
}
|
|
8671
9170
|
throw new Error(SAFE_WORKTREE_FAILURE);
|
|
@@ -8680,13 +9179,13 @@ function safeWorktreeOrFailure(context, pluginWorktree) {
|
|
|
8680
9179
|
function explicitSafeInputWorktree(input = {}) {
|
|
8681
9180
|
for (const candidate of [input?.worktree, input?.directory]) {
|
|
8682
9181
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8683
|
-
const resolved =
|
|
9182
|
+
const resolved = path8.resolve(candidate);
|
|
8684
9183
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8685
9184
|
}
|
|
8686
9185
|
return null;
|
|
8687
9186
|
}
|
|
8688
9187
|
function isGitRepository(worktree) {
|
|
8689
|
-
return
|
|
9188
|
+
return fs8.existsSync(path8.join(worktree, ".git"));
|
|
8690
9189
|
}
|
|
8691
9190
|
function normalizeLooseToken(value) {
|
|
8692
9191
|
return String(value ?? "").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -8720,8 +9219,8 @@ function parseHumansRegistry(markdown = "") {
|
|
|
8720
9219
|
return roles;
|
|
8721
9220
|
}
|
|
8722
9221
|
function loadHumansRegistry(registryPath = HUMANS_REGISTRY_PATH) {
|
|
8723
|
-
if (!
|
|
8724
|
-
return parseHumansRegistry(
|
|
9222
|
+
if (!fs8.existsSync(registryPath)) return {};
|
|
9223
|
+
return parseHumansRegistry(fs8.readFileSync(registryPath, "utf8"));
|
|
8725
9224
|
}
|
|
8726
9225
|
function resolveHumanRoles(roles = CLICKUP_FINAL_APPROVER_ROLES, registry = loadHumansRegistry()) {
|
|
8727
9226
|
return [...new Set(roles.map((role) => registry[String(role ?? "").trim()] || "").filter(Boolean))];
|
|
@@ -8893,78 +9392,28 @@ function buildClickUpReviewAutomationPlan({ isSubtask = false, humanApprover = "
|
|
|
8893
9392
|
}
|
|
8894
9393
|
};
|
|
8895
9394
|
}
|
|
8896
|
-
function validateMainWorkspaceBranchSafety({ currentBranch, requiredBranch = "dev", forbiddenBranch = "main" } = {}) {
|
|
8897
|
-
const normalized = String(currentBranch ?? "").trim();
|
|
8898
|
-
if (!normalized) {
|
|
8899
|
-
return { ok: false, branch: normalized, message: "Unable to determine the current branch; expected main workspace on dev." };
|
|
8900
|
-
}
|
|
8901
|
-
if (normalized === forbiddenBranch) {
|
|
8902
|
-
return { ok: false, branch: normalized, message: "Unsafe workspace branch: main is never allowed for Optima delivery work." };
|
|
8903
|
-
}
|
|
8904
|
-
if (normalized !== requiredBranch) {
|
|
8905
|
-
return { ok: false, branch: normalized, message: `Unsafe workspace branch: expected ${requiredBranch}, got ${normalized}.` };
|
|
8906
|
-
}
|
|
8907
|
-
return { ok: true, branch: normalized, message: `Workspace branch is safe on ${requiredBranch}.` };
|
|
8908
|
-
}
|
|
8909
|
-
var CLICKUP_REQUIRED_SUMMARY_SECTIONS = [
|
|
8910
|
-
"Summary",
|
|
8911
|
-
"Work Performed",
|
|
8912
|
-
"AC Coverage",
|
|
8913
|
-
"Verification Results",
|
|
8914
|
-
"Documentation Impact",
|
|
8915
|
-
"Open Risks",
|
|
8916
|
-
"Recommended Next Step"
|
|
8917
|
-
];
|
|
8918
|
-
var CLICKUP_RAW_LOG_SECTION_NAMES = /* @__PURE__ */ new Set(["Raw Logs", "Logs", "Full Logs", "Command Output", "Transcript"]);
|
|
8919
|
-
var CLICKUP_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
8920
|
-
["plan->in progress", { status: "in progress", comment: "Plan complete; moving to implementation without generic CTO/PO assignment." }],
|
|
8921
|
-
["in progress->validation", { status: "validation", comment: "Implementation complete; ready for validation." }],
|
|
8922
|
-
["validation->merge", { status: "merge", assignFinalApprovers: true, parentOnlyFinalApproval: true, comment: "Parent validation passed with a functional preview URL; ready for CTO/PO approval flow." }],
|
|
8923
|
-
["validation->in progress", { status: "in progress", comment: "Validation failed; returning to implementation." }],
|
|
8924
|
-
["merge->completed", { status: "completed", comment: "Merge complete; closing delivery task." }],
|
|
8925
|
-
["completed->in progress", { status: "in progress", comment: "Task reopened; returning to implementation." }],
|
|
8926
|
-
["closed->in progress", { status: "in progress", comment: "Task reopened; returning to implementation." }]
|
|
8927
|
-
]);
|
|
8928
|
-
function parseMarkdownSections(markdown = "") {
|
|
8929
|
-
const sections = {};
|
|
8930
|
-
let current = null;
|
|
8931
|
-
let buffer = [];
|
|
8932
|
-
const flush = () => {
|
|
8933
|
-
if (!current) return;
|
|
8934
|
-
sections[current] = buffer.join("\n").trim();
|
|
8935
|
-
};
|
|
8936
|
-
for (const line of String(markdown).split(/\r?\n/)) {
|
|
8937
|
-
const heading = /^(#{2,3})\s+(.+?)\s*$/.exec(line);
|
|
8938
|
-
if (heading) {
|
|
8939
|
-
flush();
|
|
8940
|
-
current = heading[2].trim();
|
|
8941
|
-
buffer = [];
|
|
8942
|
-
continue;
|
|
8943
|
-
}
|
|
8944
|
-
if (current) buffer.push(line);
|
|
8945
|
-
}
|
|
8946
|
-
flush();
|
|
8947
|
-
return sections;
|
|
8948
|
-
}
|
|
8949
|
-
function parseMarkdownArtifact(markdown = "", { requiredSections = [] } = {}) {
|
|
8950
|
-
const sections = parseMarkdownSections(markdown);
|
|
8951
|
-
const missing = requiredSections.filter((section) => !sections[section]);
|
|
8952
|
-
return {
|
|
8953
|
-
ok: missing.length === 0,
|
|
8954
|
-
sections,
|
|
8955
|
-
missing,
|
|
8956
|
-
message: missing.length ? `Missing required section(s): ${missing.join(", ")}` : "ok"
|
|
8957
|
-
};
|
|
8958
|
-
}
|
|
8959
|
-
function readMarkdownArtifact(filePath, options = {}) {
|
|
8960
|
-
const markdown = fs5.readFileSync(filePath, "utf8");
|
|
8961
|
-
return parseMarkdownArtifact(markdown, options);
|
|
8962
|
-
}
|
|
8963
|
-
function stripRawLogSections(sections = {}) {
|
|
8964
|
-
return Object.fromEntries(
|
|
8965
|
-
Object.entries(sections).filter(([name]) => !CLICKUP_RAW_LOG_SECTION_NAMES.has(name))
|
|
8966
|
-
);
|
|
9395
|
+
function validateMainWorkspaceBranchSafety({ currentBranch, requiredBranch = "dev", forbiddenBranch = "main" } = {}) {
|
|
9396
|
+
const normalized = String(currentBranch ?? "").trim();
|
|
9397
|
+
if (!normalized) {
|
|
9398
|
+
return { ok: false, branch: normalized, message: "Unable to determine the current branch; expected main workspace on dev." };
|
|
9399
|
+
}
|
|
9400
|
+
if (normalized === forbiddenBranch) {
|
|
9401
|
+
return { ok: false, branch: normalized, message: "Unsafe workspace branch: main is never allowed for Optima delivery work." };
|
|
9402
|
+
}
|
|
9403
|
+
if (normalized !== requiredBranch) {
|
|
9404
|
+
return { ok: false, branch: normalized, message: `Unsafe workspace branch: expected ${requiredBranch}, got ${normalized}.` };
|
|
9405
|
+
}
|
|
9406
|
+
return { ok: true, branch: normalized, message: `Workspace branch is safe on ${requiredBranch}.` };
|
|
8967
9407
|
}
|
|
9408
|
+
var CLICKUP_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
9409
|
+
["plan->in progress", { status: "in progress", comment: "Plan complete; moving to implementation without generic CTO/PO assignment." }],
|
|
9410
|
+
["in progress->validation", { status: "validation", comment: "Implementation complete; ready for validation." }],
|
|
9411
|
+
["validation->merge", { status: "merge", assignFinalApprovers: true, parentOnlyFinalApproval: true, comment: "Parent validation passed with a functional preview URL; ready for CTO/PO approval flow." }],
|
|
9412
|
+
["validation->in progress", { status: "in progress", comment: "Validation failed; returning to implementation." }],
|
|
9413
|
+
["merge->completed", { status: "completed", comment: "Merge complete; closing delivery task." }],
|
|
9414
|
+
["completed->in progress", { status: "in progress", comment: "Task reopened; returning to implementation." }],
|
|
9415
|
+
["closed->in progress", { status: "in progress", comment: "Task reopened; returning to implementation." }]
|
|
9416
|
+
]);
|
|
8968
9417
|
var CLICKUP_SUBTASK_REQUIRED_FIELDS = ["Type", "Owner Role", "Story Points", "Slice", "Acceptance Criteria"];
|
|
8969
9418
|
var CLICKUP_SUBTASK_OPTIONAL_FIELDS = ["Depends On", "Branch", "Description", "Definition", "Documentation"];
|
|
8970
9419
|
var CLICKUP_SUBTASK_KNOWN_FIELDS = /* @__PURE__ */ new Set([...CLICKUP_SUBTASK_REQUIRED_FIELDS, ...CLICKUP_SUBTASK_OPTIONAL_FIELDS]);
|
|
@@ -9168,7 +9617,7 @@ function buildClickUpApplyPayloadResult({ payload, apply = false } = {}) {
|
|
|
9168
9617
|
noop: true,
|
|
9169
9618
|
applied: false,
|
|
9170
9619
|
message: "Dry-run only. Payload validated; no ClickUp calls were made.",
|
|
9171
|
-
payload: parsed
|
|
9620
|
+
payload: normalizeClickUpPayloadComments(parsed)
|
|
9172
9621
|
};
|
|
9173
9622
|
}
|
|
9174
9623
|
return {
|
|
@@ -9178,7 +9627,7 @@ function buildClickUpApplyPayloadResult({ payload, apply = false } = {}) {
|
|
|
9178
9627
|
applyRequested: true,
|
|
9179
9628
|
applied: false,
|
|
9180
9629
|
message: "Live ClickUp execution is not implemented/configured in this safe boundary; no ClickUp calls were made.",
|
|
9181
|
-
payload: parsed
|
|
9630
|
+
payload: normalizeClickUpPayloadComments(parsed)
|
|
9182
9631
|
};
|
|
9183
9632
|
}
|
|
9184
9633
|
function sectionValue(sections, names) {
|
|
@@ -9188,7 +9637,7 @@ function sectionValue(sections, names) {
|
|
|
9188
9637
|
return "";
|
|
9189
9638
|
}
|
|
9190
9639
|
function compactMarkdownValue(value = "") {
|
|
9191
|
-
return
|
|
9640
|
+
return normalizeClickUpMarkdown(value);
|
|
9192
9641
|
}
|
|
9193
9642
|
function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", taskMarkdown = "", taskPath = "", branch = "", worktree = "", pr = "" } = {}) {
|
|
9194
9643
|
const parsedSummary = parseMarkdownArtifact(summaryMarkdown, { requiredSections: CLICKUP_REQUIRED_SUMMARY_SECTIONS });
|
|
@@ -9213,13 +9662,12 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
|
|
|
9213
9662
|
deliveryPath ? `- Delivery evidence: ${compactMarkdownValue(deliveryPath)}` : null,
|
|
9214
9663
|
prValue ? `- PR: ${compactMarkdownValue(prValue)}` : null
|
|
9215
9664
|
].filter(Boolean);
|
|
9216
|
-
const comment =
|
|
9217
|
-
"
|
|
9218
|
-
"",
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
].filter((part) => part !== null).join("\n").trim();
|
|
9665
|
+
const comment = formatClickUpStatusComment({
|
|
9666
|
+
title: "Optima Delivery Summary",
|
|
9667
|
+
summary: "ClickUp sync payload generated from Optima Markdown artifacts.",
|
|
9668
|
+
sections: commentParts.map(([title, value]) => ({ title, body: compactMarkdownValue(value) || "Not specified." })),
|
|
9669
|
+
context: contextParts
|
|
9670
|
+
});
|
|
9223
9671
|
return {
|
|
9224
9672
|
ok: true,
|
|
9225
9673
|
mode: "payload",
|
|
@@ -9245,7 +9693,7 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
|
|
|
9245
9693
|
function deriveClickUpWorktree({ baseWorktree = "", taskId, taskType, parentTaskId, subtaskId } = {}) {
|
|
9246
9694
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId, subtaskId, taskId });
|
|
9247
9695
|
const root = baseWorktree || process.cwd();
|
|
9248
|
-
return
|
|
9696
|
+
return path8.join(path8.dirname(root), `${path8.basename(root)}-${branch.replace(/\//g, "-")}`);
|
|
9249
9697
|
}
|
|
9250
9698
|
function clickUpCustomFieldValue(task = {}, names = []) {
|
|
9251
9699
|
const fields = Array.isArray(task.custom_fields) ? task.custom_fields : [];
|
|
@@ -9269,12 +9717,12 @@ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
|
|
|
9269
9717
|
const taskMetadata = metadataTaskRouting(metadata);
|
|
9270
9718
|
const existingWorktree = typeof taskMetadata.worktree === "string" ? taskMetadata.worktree.trim() : "";
|
|
9271
9719
|
const existingBranch = typeof taskMetadata.branch === "string" ? taskMetadata.branch.trim() : "";
|
|
9272
|
-
if (!existingWorktree || !
|
|
9720
|
+
if (!existingWorktree || !path8.isAbsolute(existingWorktree) || !fs8.existsSync(existingWorktree)) return null;
|
|
9273
9721
|
try {
|
|
9274
|
-
const stat =
|
|
9722
|
+
const stat = fs8.statSync(existingWorktree);
|
|
9275
9723
|
if (!stat.isDirectory()) return null;
|
|
9276
9724
|
if (branch && existingBranch && existingBranch !== branch) return null;
|
|
9277
|
-
return { branch: existingBranch || branch, worktree:
|
|
9725
|
+
return { branch: existingBranch || branch, worktree: path8.resolve(existingWorktree), reused: true };
|
|
9278
9726
|
} catch {
|
|
9279
9727
|
return null;
|
|
9280
9728
|
}
|
|
@@ -9308,7 +9756,7 @@ function normalizeOptimaGitIdentity(identity = {}) {
|
|
|
9308
9756
|
};
|
|
9309
9757
|
}
|
|
9310
9758
|
function configureOptimaWorktreeGitIdentity({ worktreePath, identity = {}, runGitFn = runGit } = {}) {
|
|
9311
|
-
if (!worktreePath || !
|
|
9759
|
+
if (!worktreePath || !fs8.existsSync(worktreePath)) return { configured: false, reason: "worktree_missing" };
|
|
9312
9760
|
const normalized = normalizeOptimaGitIdentity(identity);
|
|
9313
9761
|
if (!normalized.name || !normalized.email) return { configured: false, reason: "identity_missing" };
|
|
9314
9762
|
try {
|
|
@@ -9394,17 +9842,17 @@ function openChamberEntryBranch(entry) {
|
|
|
9394
9842
|
return String(entry?.branch || entry?.branchName || entry?.branch_name || entry?.worktree?.branch || "").replace(/^refs\/heads\//, "");
|
|
9395
9843
|
}
|
|
9396
9844
|
function openChamberListIncludesDirectory(list, directory) {
|
|
9397
|
-
const resolved =
|
|
9845
|
+
const resolved = path8.resolve(directory);
|
|
9398
9846
|
return normalizeOpenChamberCollection(list).some((entry) => {
|
|
9399
9847
|
const entryDirectory = openChamberEntryDirectory(entry);
|
|
9400
|
-
return entryDirectory &&
|
|
9848
|
+
return entryDirectory && path8.resolve(entryDirectory) === resolved;
|
|
9401
9849
|
});
|
|
9402
9850
|
}
|
|
9403
9851
|
function openChamberListIncludesBranch(list, directory, branch) {
|
|
9404
|
-
const resolved =
|
|
9852
|
+
const resolved = path8.resolve(directory);
|
|
9405
9853
|
return normalizeOpenChamberCollection(list).some((entry) => {
|
|
9406
9854
|
const entryDirectory = openChamberEntryDirectory(entry);
|
|
9407
|
-
if (!entryDirectory ||
|
|
9855
|
+
if (!entryDirectory || path8.resolve(entryDirectory) !== resolved) return false;
|
|
9408
9856
|
const entryBranch = openChamberEntryBranch(entry);
|
|
9409
9857
|
return !entryBranch || entryBranch === branch;
|
|
9410
9858
|
});
|
|
@@ -9412,8 +9860,8 @@ function openChamberListIncludesBranch(list, directory, branch) {
|
|
|
9412
9860
|
async function findOpenChamberProject({ opencodeBaseUrl, baseWorktree, fetchImpl = globalThis.fetch } = {}) {
|
|
9413
9861
|
const projects = await requestOpenCodeJson({ baseUrl: opencodeBaseUrl, endpoint: "/project", directory: baseWorktree, fetchImpl });
|
|
9414
9862
|
if (!Array.isArray(projects)) return null;
|
|
9415
|
-
const resolvedBase =
|
|
9416
|
-
return projects.find((project) =>
|
|
9863
|
+
const resolvedBase = path8.resolve(baseWorktree);
|
|
9864
|
+
return projects.find((project) => path8.resolve(String(project?.worktree || project?.path || "")) === resolvedBase) || null;
|
|
9417
9865
|
}
|
|
9418
9866
|
async function refreshOpenChamberProjectCopy({ opencodeBaseUrl, projectId, fetchImpl = globalThis.fetch } = {}) {
|
|
9419
9867
|
if (!projectId) return { refreshed: false, reason: "project_id_unavailable" };
|
|
@@ -9468,18 +9916,18 @@ async function createOpenChamberClickUpWorktree({ openchamberBaseUrl, baseUrl, b
|
|
|
9468
9916
|
}
|
|
9469
9917
|
const createdDirectory = openChamberEntryDirectory(created);
|
|
9470
9918
|
const createdBranch = openChamberEntryBranch(created);
|
|
9471
|
-
if (!createdDirectory || !
|
|
9919
|
+
if (!createdDirectory || !path8.isAbsolute(createdDirectory)) {
|
|
9472
9920
|
throw new Error(`OpenChamber did not return an absolute worktree path for ${branch}.`);
|
|
9473
9921
|
}
|
|
9474
9922
|
if (createdBranch !== branch) {
|
|
9475
9923
|
throw new Error(`OpenChamber created unexpected branch ${createdBranch || "<unknown>"}; expected ${branch}.`);
|
|
9476
9924
|
}
|
|
9477
9925
|
const verified = await verifyOpenChamberGitWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, worktreePath: createdDirectory, branch, fetchImpl });
|
|
9478
|
-
return { created, worktree:
|
|
9926
|
+
return { created, worktree: path8.resolve(createdDirectory), branch: createdBranch, verified };
|
|
9479
9927
|
}
|
|
9480
9928
|
async function registerOpenChamberClickUpWorktree({ openchamberBaseUrl, opencodeBaseUrl, baseUrl, baseWorktree, branch, worktreePath, fetchImpl = globalThis.fetch, source = "reuse" } = {}) {
|
|
9481
9929
|
const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, opencodeBaseUrl: opencodeBaseUrl || baseUrl, baseWorktree, worktreePath, branch, fetchImpl });
|
|
9482
|
-
return { branch, worktree:
|
|
9930
|
+
return { branch, worktree: path8.resolve(worktreePath), reused: true, provider: "openchamber", openChamber: { source, visibility } };
|
|
9483
9931
|
}
|
|
9484
9932
|
async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, openchamberBaseUrl = "", opencodeBaseUrl = "", baseUrl = "", fetchImpl = globalThis.fetch, log = null, gitIdentity = {} } = {}) {
|
|
9485
9933
|
const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
|
|
@@ -9497,7 +9945,7 @@ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId,
|
|
|
9497
9945
|
return { ...registered, parentBranch: parentBranch || void 0, prTarget, gitIdentity: identity2 };
|
|
9498
9946
|
}
|
|
9499
9947
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9500
|
-
if (
|
|
9948
|
+
if (fs8.existsSync(worktreePath)) {
|
|
9501
9949
|
const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath, fetchImpl, source: "existing_directory" });
|
|
9502
9950
|
const identity2 = configureOptimaWorktreeGitIdentity({ worktreePath: registered.worktree, identity: gitIdentity, runGitFn });
|
|
9503
9951
|
log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "existing_directory", visibility: registered.openChamber.visibility });
|
|
@@ -9506,7 +9954,7 @@ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId,
|
|
|
9506
9954
|
let parentBootstrap = null;
|
|
9507
9955
|
if (isSubtask) {
|
|
9508
9956
|
const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
|
|
9509
|
-
if (
|
|
9957
|
+
if (fs8.existsSync(parentWorktree)) {
|
|
9510
9958
|
const registeredParent = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, fetchImpl, source: "parent_existing_directory" });
|
|
9511
9959
|
const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: registeredParent.worktree, identity: gitIdentity, runGitFn });
|
|
9512
9960
|
parentBootstrap = { branch: parentBranch, worktree: registeredParent.worktree, reused: true, provider: "openchamber", visibility: registeredParent.openChamber.visibility };
|
|
@@ -9549,33 +9997,33 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
9549
9997
|
const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
|
|
9550
9998
|
if (existing) return { ...existing, branch, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", gitIdentity: configureOptimaWorktreeGitIdentity({ worktreePath: existing.worktree, identity: gitIdentity, runGitFn }) };
|
|
9551
9999
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9552
|
-
if (
|
|
9553
|
-
const resolvedWorktree2 =
|
|
10000
|
+
if (fs8.existsSync(worktreePath)) {
|
|
10001
|
+
const resolvedWorktree2 = path8.resolve(worktreePath);
|
|
9554
10002
|
return { branch, worktree: resolvedWorktree2, reused: true, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", gitIdentity: configureOptimaWorktreeGitIdentity({ worktreePath: resolvedWorktree2, identity: gitIdentity, runGitFn }) };
|
|
9555
10003
|
}
|
|
9556
10004
|
if (allowNonGitFallback) {
|
|
9557
|
-
|
|
9558
|
-
return { branch, worktree:
|
|
10005
|
+
fs8.mkdirSync(worktreePath, { recursive: true });
|
|
10006
|
+
return { branch, worktree: path8.resolve(worktreePath), reused: false, fallback: "non_git", parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", startPoint: parentBranch || "dev" };
|
|
9559
10007
|
}
|
|
9560
10008
|
let parentBootstrap = null;
|
|
9561
10009
|
if (isSubtask) {
|
|
9562
10010
|
const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
|
|
9563
|
-
if (!
|
|
10011
|
+
if (!fs8.existsSync(parentWorktree)) {
|
|
9564
10012
|
const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9565
10013
|
const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
|
|
9566
10014
|
addClickUpWorktreeForBranch({ baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, runGitFn });
|
|
9567
|
-
parentBootstrap = { branch: parentBranch, worktree:
|
|
10015
|
+
parentBootstrap = { branch: parentBranch, worktree: path8.resolve(parentWorktree), startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists };
|
|
9568
10016
|
const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: parentBootstrap.worktree, identity: gitIdentity, runGitFn });
|
|
9569
10017
|
if (parentIdentity.configured) parentBootstrap.gitIdentity = parentIdentity;
|
|
9570
10018
|
} else {
|
|
9571
|
-
parentBootstrap = { branch: parentBranch, worktree:
|
|
10019
|
+
parentBootstrap = { branch: parentBranch, worktree: path8.resolve(parentWorktree), reused: true };
|
|
9572
10020
|
const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: parentBootstrap.worktree, identity: gitIdentity, runGitFn });
|
|
9573
10021
|
if (parentIdentity.configured) parentBootstrap.gitIdentity = parentIdentity;
|
|
9574
10022
|
}
|
|
9575
10023
|
}
|
|
9576
10024
|
const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
|
|
9577
10025
|
addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn });
|
|
9578
|
-
const resolvedWorktree =
|
|
10026
|
+
const resolvedWorktree = path8.resolve(worktreePath);
|
|
9579
10027
|
return { branch, worktree: resolvedWorktree, reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", parentBootstrap: parentBootstrap || void 0, gitIdentity: configureOptimaWorktreeGitIdentity({ worktreePath: resolvedWorktree, identity: gitIdentity, runGitFn }) };
|
|
9580
10028
|
}
|
|
9581
10029
|
function normalizeClickUpDefinitionDocParent(parent = {}) {
|
|
@@ -9658,7 +10106,17 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9658
10106
|
wouldCreate: true
|
|
9659
10107
|
},
|
|
9660
10108
|
clickup: {
|
|
9661
|
-
comment:
|
|
10109
|
+
comment: formatClickUpStatusComment({
|
|
10110
|
+
title: "Task Workspace Ready",
|
|
10111
|
+
summary: "Optima prepared the task workspace and validation target.",
|
|
10112
|
+
context: [
|
|
10113
|
+
`Branch: ${branch}`,
|
|
10114
|
+
`Start from: ${startFrom}`,
|
|
10115
|
+
`Worktree: ${worktree}`,
|
|
10116
|
+
`Validation PR target: ${requiredPullRequest.targetBranch}`,
|
|
10117
|
+
`Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}`
|
|
10118
|
+
]
|
|
10119
|
+
}),
|
|
9662
10120
|
description,
|
|
9663
10121
|
fields: {
|
|
9664
10122
|
Definition: definitionContent,
|
|
@@ -9744,7 +10202,18 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
9744
10202
|
remove: removalTargets,
|
|
9745
10203
|
objective: assignsFinalApprovers ? "zero_product_manager_assigned_tasks" : "preserve_existing_owner_policy"
|
|
9746
10204
|
},
|
|
9747
|
-
comment:
|
|
10205
|
+
comment: formatClickUpStatusComment({
|
|
10206
|
+
title: "Workflow Status Update",
|
|
10207
|
+
summary: rule.comment,
|
|
10208
|
+
context: [
|
|
10209
|
+
`From: ${from}`,
|
|
10210
|
+
`To: ${rule.status}`,
|
|
10211
|
+
requiredPullRequest?.prUrl ? `PR: ${requiredPullRequest.prUrl}` : null,
|
|
10212
|
+
requiredPullRequest?.prNumber ? `PR number: ${requiredPullRequest.prNumber}` : null,
|
|
10213
|
+
requiredPullRequest?.sourceBranch ? `Source branch: ${requiredPullRequest.sourceBranch}` : null,
|
|
10214
|
+
requiredPullRequest?.targetBranch ? `Target branch: ${requiredPullRequest.targetBranch}` : null
|
|
10215
|
+
].filter(Boolean)
|
|
10216
|
+
}),
|
|
9748
10217
|
description,
|
|
9749
10218
|
fields,
|
|
9750
10219
|
definition_doc: definitionContent ? {
|
|
@@ -9757,8 +10226,8 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
9757
10226
|
}
|
|
9758
10227
|
function ensureOptimaGitignoreRules(worktree) {
|
|
9759
10228
|
if (!isGitRepository(worktree)) return { touched: false, added: [] };
|
|
9760
|
-
const gitignorePath =
|
|
9761
|
-
const existing =
|
|
10229
|
+
const gitignorePath = path8.join(worktree, ".gitignore");
|
|
10230
|
+
const existing = fs8.existsSync(gitignorePath) ? fs8.readFileSync(gitignorePath, "utf8") : "";
|
|
9762
10231
|
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
9763
10232
|
const missingRules = OPTIMA_GITIGNORE_RULES.filter((rule) => !existingRules.has(rule));
|
|
9764
10233
|
if (missingRules.length === 0) return { touched: false, added: [] };
|
|
@@ -9768,45 +10237,45 @@ function ensureOptimaGitignoreRules(worktree) {
|
|
|
9768
10237
|
"# Optima local/private state",
|
|
9769
10238
|
...missingRules
|
|
9770
10239
|
].join("\n");
|
|
9771
|
-
|
|
10240
|
+
fs8.writeFileSync(gitignorePath, `${existing}${prefix}${separator}${block}
|
|
9772
10241
|
`, "utf8");
|
|
9773
10242
|
return { touched: true, added: missingRules };
|
|
9774
10243
|
}
|
|
9775
10244
|
function optimaDir(worktree) {
|
|
9776
|
-
return
|
|
10245
|
+
return path8.join(worktree, OPTIMA_DIRNAME);
|
|
9777
10246
|
}
|
|
9778
10247
|
function optimaLocalConfigDir(worktree) {
|
|
9779
|
-
return
|
|
10248
|
+
return path8.join(optimaDir(worktree), ".config");
|
|
9780
10249
|
}
|
|
9781
10250
|
function optimaConfigDir(worktree) {
|
|
9782
10251
|
return optimaLocalConfigDir(worktree);
|
|
9783
10252
|
}
|
|
9784
10253
|
function optimaCodemapPath(worktree) {
|
|
9785
|
-
return
|
|
10254
|
+
return path8.join(optimaDir(worktree), "codemap.yml");
|
|
9786
10255
|
}
|
|
9787
10256
|
function optimaTasksDir(worktree) {
|
|
9788
|
-
return
|
|
10257
|
+
return path8.join(optimaDir(worktree), "tasks");
|
|
9789
10258
|
}
|
|
9790
10259
|
function optimaEvidencesDir(worktree) {
|
|
9791
|
-
return
|
|
10260
|
+
return path8.join(optimaDir(worktree), "evidences");
|
|
9792
10261
|
}
|
|
9793
10262
|
function optimaScrsDir(worktree) {
|
|
9794
|
-
return
|
|
10263
|
+
return path8.join(optimaDir(worktree), "docs", "scrs");
|
|
9795
10264
|
}
|
|
9796
10265
|
function legacyNomadworkDir(worktree) {
|
|
9797
|
-
return
|
|
10266
|
+
return path8.join(worktree, LEGACY_NOMADWORK_DIRNAME);
|
|
9798
10267
|
}
|
|
9799
10268
|
function legacyNomadworksDir(worktree) {
|
|
9800
|
-
return
|
|
10269
|
+
return path8.join(worktree, LEGACY_NOMADWORKS_DIRNAME);
|
|
9801
10270
|
}
|
|
9802
10271
|
function legacyOrbitaDir(worktree) {
|
|
9803
|
-
return
|
|
10272
|
+
return path8.join(worktree, LEGACY_ORBITA_DIRNAME);
|
|
9804
10273
|
}
|
|
9805
10274
|
function legacyStaticEngDir(worktree) {
|
|
9806
|
-
return
|
|
10275
|
+
return path8.join(worktree, LEGACY_STATICENG_DIRNAME);
|
|
9807
10276
|
}
|
|
9808
10277
|
function repoConfigPath(worktree) {
|
|
9809
|
-
return
|
|
10278
|
+
return path8.join(optimaConfigDir(worktree), "optima.yaml");
|
|
9810
10279
|
}
|
|
9811
10280
|
function normalizeLegacyDiscussionEntry(entry) {
|
|
9812
10281
|
if (!entry || typeof entry !== "object") return entry;
|
|
@@ -9819,25 +10288,25 @@ function normalizeLegacyDiscussionEntry(entry) {
|
|
|
9819
10288
|
return next;
|
|
9820
10289
|
}
|
|
9821
10290
|
function repoPoliciesDir(worktree) {
|
|
9822
|
-
return
|
|
10291
|
+
return path8.join(optimaDir(worktree), "policies");
|
|
9823
10292
|
}
|
|
9824
10293
|
function generatedPoliciesDir(worktree) {
|
|
9825
|
-
return
|
|
10294
|
+
return path8.join(optimaLocalConfigDir(worktree), "generated", "policies");
|
|
9826
10295
|
}
|
|
9827
10296
|
function generatedAgentsDir(worktree) {
|
|
9828
|
-
return
|
|
10297
|
+
return path8.join(optimaLocalConfigDir(worktree), "generated", "agents");
|
|
9829
10298
|
}
|
|
9830
10299
|
function repoAgentsDir(worktree) {
|
|
9831
|
-
return
|
|
10300
|
+
return path8.join(optimaDir(worktree), "agents");
|
|
9832
10301
|
}
|
|
9833
10302
|
function repoAgentAdditionsDir(worktree) {
|
|
9834
|
-
return
|
|
10303
|
+
return path8.join(optimaDir(worktree), "agent-additions");
|
|
9835
10304
|
}
|
|
9836
10305
|
function legacyRepoAgentsDir(worktree) {
|
|
9837
|
-
return
|
|
10306
|
+
return path8.join(legacyNomadworksDir(worktree), "agents");
|
|
9838
10307
|
}
|
|
9839
10308
|
function runtimeDiscussionRegistryPath(worktree) {
|
|
9840
|
-
return
|
|
10309
|
+
return path8.join(optimaLocalConfigDir(worktree), "runtime", "discussions.json");
|
|
9841
10310
|
}
|
|
9842
10311
|
function resolveConfigPath(worktree) {
|
|
9843
10312
|
return repoConfigPath(worktree);
|
|
@@ -9880,32 +10349,32 @@ function mergeClickUpAgentMetadata(existing, update = {}) {
|
|
|
9880
10349
|
return JSON.stringify(sortJsonValue(merged), null, 2);
|
|
9881
10350
|
}
|
|
9882
10351
|
function optimaRuntimeDir(worktree) {
|
|
9883
|
-
return
|
|
10352
|
+
return path8.join(optimaLocalConfigDir(worktree), "runtime");
|
|
9884
10353
|
}
|
|
9885
10354
|
function clickUpWebhookStatePath(worktree) {
|
|
9886
|
-
return
|
|
10355
|
+
return path8.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
|
|
9887
10356
|
}
|
|
9888
10357
|
function clickUpCommentLedgerPath(worktree) {
|
|
9889
|
-
return
|
|
10358
|
+
return path8.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
|
|
9890
10359
|
}
|
|
9891
10360
|
function clickUpWebhookLogPath(worktree) {
|
|
9892
|
-
return
|
|
10361
|
+
return path8.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
9893
10362
|
}
|
|
9894
10363
|
function normalizeClickUpWebhookLogLevel(value) {
|
|
9895
10364
|
const level = String(value || "info").trim().toLowerCase();
|
|
9896
10365
|
return CLICKUP_WEBHOOK_LOG_LEVELS.has(level) ? level : "info";
|
|
9897
10366
|
}
|
|
9898
10367
|
function clickUpWebhookAuditLogDir() {
|
|
9899
|
-
const dataHome = process.env.XDG_DATA_HOME &&
|
|
9900
|
-
return
|
|
10368
|
+
const dataHome = process.env.XDG_DATA_HOME && path8.isAbsolute(process.env.XDG_DATA_HOME) ? process.env.XDG_DATA_HOME : path8.join(os2.homedir(), ".local", "share", "opencode");
|
|
10369
|
+
return path8.join(dataHome, "opencode-optima");
|
|
9901
10370
|
}
|
|
9902
10371
|
function clickUpWebhookAuditLogPath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
9903
|
-
return
|
|
10372
|
+
return path8.join(logDir, `clickup-webhook-${at.toISOString().slice(0, 10)}.jsonl`);
|
|
9904
10373
|
}
|
|
9905
10374
|
function clickUpWebhookRequestFilePath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
9906
10375
|
const stamp = at.toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
9907
|
-
const shortId =
|
|
9908
|
-
return
|
|
10376
|
+
const shortId = crypto2.randomBytes(4).toString("hex");
|
|
10377
|
+
return path8.join(logDir, "requests", `clickup-webhook-${stamp}-${shortId}.json`);
|
|
9909
10378
|
}
|
|
9910
10379
|
function findOptimaPluginTupleOptions(pluginEntries = []) {
|
|
9911
10380
|
if (!Array.isArray(pluginEntries)) return null;
|
|
@@ -10065,7 +10534,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
10065
10534
|
};
|
|
10066
10535
|
const errors = [];
|
|
10067
10536
|
if (!config.enabled) errors.push("clickup.enabled must be true");
|
|
10068
|
-
if (!config.basePath || !
|
|
10537
|
+
if (!config.basePath || !path8.isAbsolute(config.basePath)) errors.push("clickup.base_path must be an absolute path");
|
|
10069
10538
|
if (!config.teamId) errors.push("clickup.team_id is required");
|
|
10070
10539
|
if (!config.apiToken) errors.push("clickup.api_token is required");
|
|
10071
10540
|
if (!config.webhook.publicUrl) errors.push("clickup.webhook.public_url is required");
|
|
@@ -10128,21 +10597,21 @@ function sanitizeClickUpWebhookState(state = {}, config = null) {
|
|
|
10128
10597
|
}
|
|
10129
10598
|
function readClickUpWebhookState(worktree, config = null) {
|
|
10130
10599
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
10131
|
-
if (!
|
|
10600
|
+
if (!fs8.existsSync(statePath)) return sanitizeClickUpWebhookState({}, config);
|
|
10132
10601
|
try {
|
|
10133
|
-
return sanitizeClickUpWebhookState(JSON.parse(
|
|
10602
|
+
return sanitizeClickUpWebhookState(JSON.parse(fs8.readFileSync(statePath, "utf8")), config);
|
|
10134
10603
|
} catch {
|
|
10135
10604
|
return sanitizeClickUpWebhookState({}, config);
|
|
10136
10605
|
}
|
|
10137
10606
|
}
|
|
10138
10607
|
function writeClickUpWebhookState(worktree, state, config = null) {
|
|
10139
10608
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
10140
|
-
|
|
10609
|
+
fs8.mkdirSync(path8.dirname(statePath), { recursive: true, mode: 448 });
|
|
10141
10610
|
const next = sanitizeClickUpWebhookState(state, config);
|
|
10142
|
-
|
|
10611
|
+
fs8.writeFileSync(statePath, `${JSON.stringify(next, null, 2)}
|
|
10143
10612
|
`, { encoding: "utf8", mode: 384 });
|
|
10144
10613
|
try {
|
|
10145
|
-
|
|
10614
|
+
fs8.chmodSync(statePath, 384);
|
|
10146
10615
|
} catch {
|
|
10147
10616
|
}
|
|
10148
10617
|
return next;
|
|
@@ -10155,26 +10624,6 @@ function isClickUpWebhookStateActive(state, config) {
|
|
|
10155
10624
|
state?.active && state?.webhookId && state?.secret && state?.publicUrl === config?.webhook?.publicUrl && hasEvents
|
|
10156
10625
|
);
|
|
10157
10626
|
}
|
|
10158
|
-
function expandHomePath(value = "") {
|
|
10159
|
-
const input = String(value || "").trim();
|
|
10160
|
-
if (input === "~") return os.homedir();
|
|
10161
|
-
if (input.startsWith("~/")) return path6.join(os.homedir(), input.slice(2));
|
|
10162
|
-
return input;
|
|
10163
|
-
}
|
|
10164
|
-
function resolveSecretReference(value = "") {
|
|
10165
|
-
const raw = String(value || "").trim();
|
|
10166
|
-
const envMatch = raw.match(/^\{env:([A-Za-z_][A-Za-z0-9_]*)\}$/);
|
|
10167
|
-
if (envMatch) return process.env[envMatch[1]] || "";
|
|
10168
|
-
const fileMatch = raw.match(/^\{file:(.+)\}$/);
|
|
10169
|
-
if (fileMatch) {
|
|
10170
|
-
try {
|
|
10171
|
-
return fs5.readFileSync(expandHomePath(fileMatch[1]), "utf8").trim();
|
|
10172
|
-
} catch {
|
|
10173
|
-
return "";
|
|
10174
|
-
}
|
|
10175
|
-
}
|
|
10176
|
-
return raw;
|
|
10177
|
-
}
|
|
10178
10627
|
function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
10179
10628
|
const token = resolveSecretReference(config?.apiToken);
|
|
10180
10629
|
const request = async (url, options = {}) => {
|
|
@@ -10239,212 +10688,6 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
10239
10688
|
}
|
|
10240
10689
|
};
|
|
10241
10690
|
}
|
|
10242
|
-
function base64UrlJson(value) {
|
|
10243
|
-
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
10244
|
-
}
|
|
10245
|
-
function resolveGitHubAppPrivateKey(app = {}) {
|
|
10246
|
-
const direct = resolveSecretReference(app.privateKey || "");
|
|
10247
|
-
if (direct) return direct.replace(/\\n/g, "\n");
|
|
10248
|
-
const file = expandHomePath(app.privateKeyFile || "");
|
|
10249
|
-
if (!file) return "";
|
|
10250
|
-
try {
|
|
10251
|
-
return fs5.readFileSync(file, "utf8").trim().replace(/\\n/g, "\n");
|
|
10252
|
-
} catch {
|
|
10253
|
-
return "";
|
|
10254
|
-
}
|
|
10255
|
-
}
|
|
10256
|
-
function createGitHubAppJwt({ appId, privateKey, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
10257
|
-
const key = String(privateKey || "").trim();
|
|
10258
|
-
const issuer = String(appId || "").trim();
|
|
10259
|
-
if (!issuer || !key) throw new Error("GitHub App app_id and private key are required");
|
|
10260
|
-
const nowSeconds = Math.floor(now().getTime() / 1e3);
|
|
10261
|
-
const header = base64UrlJson({ alg: "RS256", typ: "JWT" });
|
|
10262
|
-
const payload = base64UrlJson({ iat: nowSeconds - 60, exp: nowSeconds + 540, iss: issuer });
|
|
10263
|
-
const unsigned = `${header}.${payload}`;
|
|
10264
|
-
const signature = crypto.createSign("RSA-SHA256").update(unsigned).end().sign(key, "base64url");
|
|
10265
|
-
return `${unsigned}.${signature}`;
|
|
10266
|
-
}
|
|
10267
|
-
function encodeGitHubPathSegment(value) {
|
|
10268
|
-
return encodeURIComponent(String(value || ""));
|
|
10269
|
-
}
|
|
10270
|
-
function encodeGitHubBranchRef(branch = "") {
|
|
10271
|
-
return String(branch || "").split("/").map(encodeGitHubPathSegment).join("/");
|
|
10272
|
-
}
|
|
10273
|
-
function parseNullSeparatedGitOutput(output = "") {
|
|
10274
|
-
return String(output || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
10275
|
-
}
|
|
10276
|
-
function listWorktreeChangedPaths(worktree, runGitFn = runGit) {
|
|
10277
|
-
const tracked = parseNullSeparatedGitOutput(runGitFn(worktree, ["diff", "--name-only", "-z", "HEAD", "--"]));
|
|
10278
|
-
const untracked = parseNullSeparatedGitOutput(runGitFn(worktree, ["ls-files", "--others", "--exclude-standard", "-z", "--"]));
|
|
10279
|
-
return [.../* @__PURE__ */ new Set([...tracked, ...untracked])].filter((item) => item && !item.startsWith("../") && !path6.isAbsolute(item));
|
|
10280
|
-
}
|
|
10281
|
-
function currentGitBranch(worktree, runGitFn = runGit) {
|
|
10282
|
-
return String(runGitFn(worktree, ["rev-parse", "--abbrev-ref", "HEAD"]) || "").trim();
|
|
10283
|
-
}
|
|
10284
|
-
function treeEntryForWorktreePath(worktree, gitPath) {
|
|
10285
|
-
const absolutePath = path6.join(worktree, ...String(gitPath || "").split("/"));
|
|
10286
|
-
if (!fs5.existsSync(absolutePath)) return { path: gitPath, mode: "100644", type: "blob", sha: null, deleted: true };
|
|
10287
|
-
const stat = fs5.lstatSync(absolutePath);
|
|
10288
|
-
if (stat.isDirectory()) return null;
|
|
10289
|
-
if (stat.isSymbolicLink()) {
|
|
10290
|
-
return {
|
|
10291
|
-
path: gitPath,
|
|
10292
|
-
mode: "120000",
|
|
10293
|
-
type: "blob",
|
|
10294
|
-
content: fs5.readlinkSync(absolutePath),
|
|
10295
|
-
encoding: "utf-8"
|
|
10296
|
-
};
|
|
10297
|
-
}
|
|
10298
|
-
return {
|
|
10299
|
-
path: gitPath,
|
|
10300
|
-
mode: stat.mode & 73 ? "100755" : "100644",
|
|
10301
|
-
type: "blob",
|
|
10302
|
-
content: fs5.readFileSync(absolutePath).toString("base64"),
|
|
10303
|
-
encoding: "base64"
|
|
10304
|
-
};
|
|
10305
|
-
}
|
|
10306
|
-
function createGitHubApiClient(config = {}, fetchImpl = globalThis.fetch) {
|
|
10307
|
-
const staticToken = resolveSecretReference(config?.apiToken);
|
|
10308
|
-
const appConfig = isPlainObject(config?.app) ? config.app : {};
|
|
10309
|
-
const appEnabled = appConfig.enabled === true || Boolean(appConfig.appId && appConfig.installationId && (appConfig.privateKey || appConfig.privateKeyFile));
|
|
10310
|
-
const privateKey = resolveGitHubAppPrivateKey(appConfig);
|
|
10311
|
-
const owner = String(config?.owner || "").trim();
|
|
10312
|
-
const repo = String(config?.repo || "").trim();
|
|
10313
|
-
let installationToken = null;
|
|
10314
|
-
let installationTokenExpiresAt = 0;
|
|
10315
|
-
const requestJson = async (url, { method = "GET", headers = {}, body = void 0 } = {}) => {
|
|
10316
|
-
if (typeof fetchImpl !== "function") throw new Error("fetch is unavailable; inject a GitHub client for live PR lookup");
|
|
10317
|
-
const response = await fetchImpl(url, {
|
|
10318
|
-
method,
|
|
10319
|
-
headers: {
|
|
10320
|
-
Accept: "application/vnd.github+json",
|
|
10321
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
10322
|
-
...body === void 0 ? {} : { "Content-Type": "application/json" },
|
|
10323
|
-
...headers
|
|
10324
|
-
},
|
|
10325
|
-
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
10326
|
-
});
|
|
10327
|
-
if (!response.ok) throw new Error(`GitHub API request failed: ${response.status}`);
|
|
10328
|
-
return response.status === 204 ? null : response.json();
|
|
10329
|
-
};
|
|
10330
|
-
const getAuthToken = async () => {
|
|
10331
|
-
if (!appEnabled) return staticToken || "";
|
|
10332
|
-
const nowMs = Date.now();
|
|
10333
|
-
if (installationToken && installationTokenExpiresAt - 6e4 > nowMs) return installationToken;
|
|
10334
|
-
const jwt = createGitHubAppJwt({ appId: appConfig.appId, privateKey });
|
|
10335
|
-
const installation = await requestJson(`https://api.github.com/app/installations/${encodeURIComponent(appConfig.installationId)}/access_tokens`, {
|
|
10336
|
-
method: "POST",
|
|
10337
|
-
headers: { Authorization: `Bearer ${jwt}` }
|
|
10338
|
-
});
|
|
10339
|
-
installationToken = String(installation?.token || "").trim();
|
|
10340
|
-
installationTokenExpiresAt = Date.parse(installation?.expires_at || "") || nowMs + 3e6;
|
|
10341
|
-
if (!installationToken) throw new Error("GitHub App installation token response did not include a token");
|
|
10342
|
-
return installationToken;
|
|
10343
|
-
};
|
|
10344
|
-
const request = async (pathname, { method = "GET", body = void 0 } = {}) => {
|
|
10345
|
-
if (!owner || !repo) throw new Error("GitHub repository owner/repo is not configured");
|
|
10346
|
-
const token = await getAuthToken();
|
|
10347
|
-
return requestJson(`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}${pathname}`, {
|
|
10348
|
-
method,
|
|
10349
|
-
body,
|
|
10350
|
-
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
10351
|
-
});
|
|
10352
|
-
};
|
|
10353
|
-
const getRef = async (branch) => request(`/git/ref/heads/${encodeGitHubBranchRef(branch)}`);
|
|
10354
|
-
const getCommitObject = async (sha) => request(`/git/commits/${encodeURIComponent(sha)}`);
|
|
10355
|
-
const createBlob = async ({ content, encoding }) => request("/git/blobs", { method: "POST", body: { content, encoding } });
|
|
10356
|
-
const createTree = async ({ baseTree, tree }) => request("/git/trees", { method: "POST", body: { base_tree: baseTree, tree } });
|
|
10357
|
-
const createCommit = async ({ message, treeSha, parents }) => request("/git/commits", { method: "POST", body: { message, tree: treeSha, parents } });
|
|
10358
|
-
const updateRef = async ({ branch, sha, force = false }) => request(`/git/refs/heads/${encodeGitHubBranchRef(branch)}`, { method: "PATCH", body: { sha, force } });
|
|
10359
|
-
return {
|
|
10360
|
-
async getPullRequest(number) {
|
|
10361
|
-
return request(`/pulls/${encodeURIComponent(number)}`);
|
|
10362
|
-
},
|
|
10363
|
-
async createPullRequest({ title, head, base, body = "", draft = false, maintainerCanModify = true }) {
|
|
10364
|
-
return request("/pulls", {
|
|
10365
|
-
method: "POST",
|
|
10366
|
-
body: {
|
|
10367
|
-
title: String(title || ""),
|
|
10368
|
-
head: String(head || ""),
|
|
10369
|
-
base: String(base || ""),
|
|
10370
|
-
body: String(body || ""),
|
|
10371
|
-
draft: draft === true,
|
|
10372
|
-
maintainer_can_modify: maintainerCanModify !== false
|
|
10373
|
-
}
|
|
10374
|
-
});
|
|
10375
|
-
},
|
|
10376
|
-
async createIssueComment({ issueNumber, body }) {
|
|
10377
|
-
return request(`/issues/${encodeURIComponent(issueNumber)}/comments`, { method: "POST", body: { body: String(body || "") } });
|
|
10378
|
-
},
|
|
10379
|
-
async replyToReviewComment({ commentId, body }) {
|
|
10380
|
-
return request(`/pulls/comments/${encodeURIComponent(commentId)}/replies`, { method: "POST", body: { body: String(body || "") } });
|
|
10381
|
-
},
|
|
10382
|
-
async createPullRequestReview({ pullNumber, body, event = "COMMENT" }) {
|
|
10383
|
-
return request(`/pulls/${encodeURIComponent(pullNumber)}/reviews`, { method: "POST", body: { body: String(body || ""), event: String(event || "COMMENT").toUpperCase() } });
|
|
10384
|
-
},
|
|
10385
|
-
async mergePullRequest({ pullNumber, commitTitle = "", commitMessage = "", mergeMethod = "squash" }) {
|
|
10386
|
-
const body = {
|
|
10387
|
-
merge_method: String(mergeMethod || "squash")
|
|
10388
|
-
};
|
|
10389
|
-
if (commitTitle) body.commit_title = String(commitTitle);
|
|
10390
|
-
if (commitMessage) body.commit_message = String(commitMessage);
|
|
10391
|
-
return request(`/pulls/${encodeURIComponent(pullNumber)}/merge`, { method: "PUT", body });
|
|
10392
|
-
},
|
|
10393
|
-
async commitWorktree({ worktree, branch = "", message = "", runGitFn = runGit, syncLocal = false } = {}) {
|
|
10394
|
-
const directory = path6.resolve(String(worktree || ""));
|
|
10395
|
-
if (!directory || !fs5.existsSync(directory)) throw new Error("worktree does not exist");
|
|
10396
|
-
const targetBranch = String(branch || currentGitBranch(directory, runGitFn)).trim();
|
|
10397
|
-
if (!targetBranch || targetBranch === "HEAD") throw new Error("target branch is required for GitHub API commit");
|
|
10398
|
-
const commitMessage = String(message || "").trim();
|
|
10399
|
-
if (!commitMessage) throw new Error("commit message is required");
|
|
10400
|
-
const changedPaths = listWorktreeChangedPaths(directory, runGitFn);
|
|
10401
|
-
if (changedPaths.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
|
|
10402
|
-
const ref = await getRef(targetBranch);
|
|
10403
|
-
const headSha = ref?.object?.sha;
|
|
10404
|
-
if (!headSha) throw new Error(`GitHub ref for ${targetBranch} did not include a head sha`);
|
|
10405
|
-
const headCommit = await getCommitObject(headSha);
|
|
10406
|
-
const baseTree = headCommit?.tree?.sha;
|
|
10407
|
-
if (!baseTree) throw new Error(`GitHub commit ${headSha} did not include a tree sha`);
|
|
10408
|
-
const tree = [];
|
|
10409
|
-
for (const gitPath of changedPaths) {
|
|
10410
|
-
const entry = treeEntryForWorktreePath(directory, gitPath);
|
|
10411
|
-
if (!entry) continue;
|
|
10412
|
-
if (entry.deleted) {
|
|
10413
|
-
tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: null });
|
|
10414
|
-
continue;
|
|
10415
|
-
}
|
|
10416
|
-
const blob = await createBlob({ content: entry.content, encoding: entry.encoding });
|
|
10417
|
-
if (!blob?.sha) throw new Error(`GitHub blob creation failed for ${gitPath}`);
|
|
10418
|
-
tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: blob.sha });
|
|
10419
|
-
}
|
|
10420
|
-
if (tree.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
|
|
10421
|
-
const nextTree = await createTree({ baseTree, tree });
|
|
10422
|
-
const nextCommit = await createCommit({ message: commitMessage, treeSha: nextTree.sha, parents: [headSha] });
|
|
10423
|
-
if (!nextCommit?.sha) throw new Error("GitHub commit creation did not return a sha");
|
|
10424
|
-
const updatedRef = await updateRef({ branch: targetBranch, sha: nextCommit.sha, force: false });
|
|
10425
|
-
if (syncLocal) {
|
|
10426
|
-
runGitFn(directory, ["fetch", "origin", targetBranch]);
|
|
10427
|
-
runGitFn(directory, ["reset", "--hard", "FETCH_HEAD"]);
|
|
10428
|
-
}
|
|
10429
|
-
return {
|
|
10430
|
-
ok: true,
|
|
10431
|
-
action: "committed",
|
|
10432
|
-
branch: targetBranch,
|
|
10433
|
-
before: headSha,
|
|
10434
|
-
after: nextCommit.sha,
|
|
10435
|
-
changedPaths,
|
|
10436
|
-
treeEntries: tree.length,
|
|
10437
|
-
verification: nextCommit.verification || null,
|
|
10438
|
-
ref: updatedRef
|
|
10439
|
-
};
|
|
10440
|
-
},
|
|
10441
|
-
async authMode() {
|
|
10442
|
-
if (appEnabled) return { mode: "github_app", appId: String(appConfig.appId || ""), installationId: String(appConfig.installationId || "") };
|
|
10443
|
-
if (staticToken) return { mode: "token" };
|
|
10444
|
-
return { mode: "anonymous" };
|
|
10445
|
-
}
|
|
10446
|
-
};
|
|
10447
|
-
}
|
|
10448
10691
|
function createTestClickUpApiClient(config) {
|
|
10449
10692
|
const metadata = /* @__PURE__ */ new Map();
|
|
10450
10693
|
return {
|
|
@@ -10670,10 +10913,10 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
|
|
|
10670
10913
|
function verifyClickUpSignature(rawBody, signatureHeader, secret) {
|
|
10671
10914
|
const signature = String(signatureHeader || "").replace(/^sha256=/i, "").trim();
|
|
10672
10915
|
if (!signature || !secret) return false;
|
|
10673
|
-
const expected =
|
|
10916
|
+
const expected = crypto2.createHmac("sha256", secret).update(Buffer.isBuffer(rawBody) ? rawBody : String(rawBody || ""), "utf8").digest("hex");
|
|
10674
10917
|
const given = Buffer.from(signature, "hex");
|
|
10675
10918
|
const wanted = Buffer.from(expected, "hex");
|
|
10676
|
-
return given.length === wanted.length &&
|
|
10919
|
+
return given.length === wanted.length && crypto2.timingSafeEqual(given, wanted);
|
|
10677
10920
|
}
|
|
10678
10921
|
function verifyGitHubSignature(rawBody, signatureHeader, secret) {
|
|
10679
10922
|
return verifyClickUpSignature(rawBody, signatureHeader, secret);
|
|
@@ -10727,9 +10970,9 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
|
|
|
10727
10970
|
return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
|
|
10728
10971
|
}
|
|
10729
10972
|
function readClickUpCommentLedgerEntries(ledgerPath) {
|
|
10730
|
-
if (!ledgerPath || !
|
|
10973
|
+
if (!ledgerPath || !fs8.existsSync(ledgerPath)) return [];
|
|
10731
10974
|
try {
|
|
10732
|
-
const raw =
|
|
10975
|
+
const raw = fs8.readFileSync(ledgerPath, "utf8");
|
|
10733
10976
|
const entries = [];
|
|
10734
10977
|
for (const [index, line] of raw.split(/\r?\n/).entries()) {
|
|
10735
10978
|
if (!line.trim()) continue;
|
|
@@ -10753,11 +10996,11 @@ function readClickUpCommentLedger(ledgerPath) {
|
|
|
10753
10996
|
}
|
|
10754
10997
|
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
10755
10998
|
if (!ledgerPath || !entry.key) return;
|
|
10756
|
-
|
|
10757
|
-
|
|
10999
|
+
fs8.mkdirSync(path8.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
11000
|
+
fs8.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
10758
11001
|
`, { encoding: "utf8", mode: 384 });
|
|
10759
11002
|
try {
|
|
10760
|
-
|
|
11003
|
+
fs8.chmodSync(ledgerPath, 384);
|
|
10761
11004
|
} catch {
|
|
10762
11005
|
}
|
|
10763
11006
|
}
|
|
@@ -10776,7 +11019,7 @@ function stableClickUpCommentVersionMarker(value) {
|
|
|
10776
11019
|
const direct = value.version ?? value.date_updated ?? value.updated_at ?? value.revision ?? value.modified_at;
|
|
10777
11020
|
const directMarker = stableClickUpCommentVersionMarker(direct);
|
|
10778
11021
|
if (directMarker) return directMarker;
|
|
10779
|
-
return `json:${
|
|
11022
|
+
return `json:${crypto2.createHash("sha256").update(JSON.stringify(value, Object.keys(value).sort())).digest("hex").slice(0, 16)}`;
|
|
10780
11023
|
}
|
|
10781
11024
|
function clickUpCommentLedgerKey({ taskId, eventType, payload }) {
|
|
10782
11025
|
const history = Array.isArray(payload?.history_items) ? payload.history_items[0] : payload?.history_item;
|
|
@@ -10786,7 +11029,7 @@ function clickUpCommentLedgerKey({ taskId, eventType, payload }) {
|
|
|
10786
11029
|
const explicitVersion = stableClickUpCommentVersionMarker(
|
|
10787
11030
|
comment?.date_updated || comment?.dateUpdated || comment?.updated_at || comment?.updatedAt || comment?._version_vector || comment?.version_vector || comment?.versionVector || comment?.version || comment?.revision || comment?.modified_at || comment?.modifiedAt || ""
|
|
10788
11031
|
);
|
|
10789
|
-
const contentVersion =
|
|
11032
|
+
const contentVersion = crypto2.createHash("sha256").update(JSON.stringify({ text: clickUpCommentText(comment), parts: Array.isArray(comment.comment) ? comment.comment : null })).digest("hex").slice(0, 16);
|
|
10790
11033
|
return [String(taskId || "").trim(), "comment", commentId, explicitVersion || `sha256-${contentVersion}`].filter(Boolean).join(":");
|
|
10791
11034
|
}
|
|
10792
11035
|
function isClickUpCommentVersionProcessed({ ledgerPath, key, ledger = null, worktree = process.cwd() } = {}) {
|
|
@@ -11316,7 +11559,7 @@ function clampOpenCodeSessionText(text = "", maxLength = 12e3) {
|
|
|
11316
11559
|
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
11317
11560
|
}
|
|
11318
11561
|
function hashOpenCodeSessionText(text = "") {
|
|
11319
|
-
return
|
|
11562
|
+
return crypto2.createHash("sha256").update(String(text ?? "")).digest("hex");
|
|
11320
11563
|
}
|
|
11321
11564
|
async function readOpenCodeSessionMessages(client, { sessionId, directory, limit = 20 } = {}) {
|
|
11322
11565
|
if (typeof client?.session?.messages !== "function") return null;
|
|
@@ -11554,7 +11797,7 @@ async function readOpenCodeSessionControl(client, { sessionId, directory, limit
|
|
|
11554
11797
|
};
|
|
11555
11798
|
}
|
|
11556
11799
|
async function probeOpenCodeSessionControl(client, { directory, agent, omitAgentOnPrompt = false, text = "" } = {}) {
|
|
11557
|
-
const requestedMarker = text || `optima-session-probe-${
|
|
11800
|
+
const requestedMarker = text || `optima-session-probe-${crypto2.randomUUID()}`;
|
|
11558
11801
|
const marker = clampOpenCodeSessionText(requestedMarker);
|
|
11559
11802
|
const create = await createOpenCodeSessionControl(client, {
|
|
11560
11803
|
directory,
|
|
@@ -11782,9 +12025,9 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
11782
12025
|
}
|
|
11783
12026
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
11784
12027
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
11785
|
-
|
|
12028
|
+
fs8.mkdirSync(path8.dirname(logPath), { recursive: true });
|
|
11786
12029
|
const safeEntry = { ...entry, at: entry.at || (/* @__PURE__ */ new Date()).toISOString() };
|
|
11787
|
-
|
|
12030
|
+
fs8.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
11788
12031
|
`, "utf8");
|
|
11789
12032
|
}
|
|
11790
12033
|
function clickUpWebhookLifecycleLog(worktree, entry) {
|
|
@@ -11813,7 +12056,7 @@ function closeClickUpWebhookServer(server) {
|
|
|
11813
12056
|
});
|
|
11814
12057
|
}
|
|
11815
12058
|
function managedClickUpWebhookKey({ worktree, state, config } = {}) {
|
|
11816
|
-
return [
|
|
12059
|
+
return [path8.resolve(worktree || process.cwd()), state?.webhookId || "", config?.webhook?.publicUrl || ""].join("|");
|
|
11817
12060
|
}
|
|
11818
12061
|
async function deleteClickUpWebhookBestEffort({ webhookId, clickupClient, worktree, reason = "cleanup" } = {}) {
|
|
11819
12062
|
const id = String(webhookId || "").trim();
|
|
@@ -11913,7 +12156,7 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
11913
12156
|
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
11914
12157
|
if (level === "error" && !failed) return;
|
|
11915
12158
|
const logDir = clickUpWebhookAuditLogDir();
|
|
11916
|
-
|
|
12159
|
+
fs8.mkdirSync(logDir, { recursive: true });
|
|
11917
12160
|
const secretValues = [
|
|
11918
12161
|
resolveSecretReference(config?.apiToken),
|
|
11919
12162
|
resolveSecretReference(config?.github?.apiToken),
|
|
@@ -11924,8 +12167,8 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
11924
12167
|
let requestFile;
|
|
11925
12168
|
if (level === "verbose") {
|
|
11926
12169
|
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
11927
|
-
|
|
11928
|
-
requestFile =
|
|
12170
|
+
fs8.mkdirSync(path8.dirname(absoluteRequestFile), { recursive: true });
|
|
12171
|
+
requestFile = path8.relative(logDir, absoluteRequestFile).split(path8.sep).join("/");
|
|
11929
12172
|
const parsedBody = payload || (() => {
|
|
11930
12173
|
try {
|
|
11931
12174
|
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
@@ -11941,11 +12184,11 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
11941
12184
|
headers,
|
|
11942
12185
|
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
11943
12186
|
}, secretValues);
|
|
11944
|
-
|
|
12187
|
+
fs8.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
11945
12188
|
`, "utf8");
|
|
11946
12189
|
}
|
|
11947
12190
|
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
11948
|
-
|
|
12191
|
+
fs8.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
11949
12192
|
`, "utf8");
|
|
11950
12193
|
} catch {
|
|
11951
12194
|
}
|
|
@@ -11963,7 +12206,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
|
|
|
11963
12206
|
if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
|
|
11964
12207
|
}
|
|
11965
12208
|
}
|
|
11966
|
-
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktreeForWebhook, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host =
|
|
12209
|
+
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktreeForWebhook, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os2.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
11967
12210
|
const eventType = clickUpEventType(payload);
|
|
11968
12211
|
const eventKey = clickUpWebhookEventKey(payload);
|
|
11969
12212
|
const isStartupAssignmentReconciliation = payload?.startup_reconciliation === true && eventType === "taskAssigneeUpdated";
|
|
@@ -12732,17 +12975,17 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, g
|
|
|
12732
12975
|
return result;
|
|
12733
12976
|
}
|
|
12734
12977
|
function legacyVariantPath(destinationPath) {
|
|
12735
|
-
const parsed =
|
|
12978
|
+
const parsed = path8.parse(destinationPath);
|
|
12736
12979
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
12737
|
-
if (parsed.ext) return
|
|
12738
|
-
return
|
|
12980
|
+
if (parsed.ext) return path8.join(parsed.dir, `${parsed.name}.legacy-${stamp}${parsed.ext}`);
|
|
12981
|
+
return path8.join(parsed.dir, `${parsed.base}.legacy-${stamp}`);
|
|
12739
12982
|
}
|
|
12740
12983
|
function normalizeWorkflowTaskPath(taskPath) {
|
|
12741
12984
|
if (typeof taskPath !== "string") return { ok: false, message: "Error: task_path is required." };
|
|
12742
12985
|
const trimmed = taskPath.trim();
|
|
12743
12986
|
if (!trimmed) return { ok: false, message: "Error: task_path is required." };
|
|
12744
12987
|
const normalized = trimmed.replace(/\\/g, "/");
|
|
12745
|
-
if (
|
|
12988
|
+
if (path8.isAbsolute(trimmed)) return { ok: true, taskPath: trimmed };
|
|
12746
12989
|
if (normalized === "tasks" || normalized.startsWith("tasks/")) {
|
|
12747
12990
|
return {
|
|
12748
12991
|
ok: false,
|
|
@@ -12760,78 +13003,78 @@ function normalizeWorkflowTaskPath(taskPath) {
|
|
|
12760
13003
|
function mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState = null) {
|
|
12761
13004
|
const sourceWasTracked = isGitTracked(gitState, sourcePath);
|
|
12762
13005
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
12763
|
-
if (!
|
|
12764
|
-
|
|
12765
|
-
|
|
13006
|
+
if (!fs8.existsSync(destinationPath)) {
|
|
13007
|
+
fs8.mkdirSync(path8.dirname(destinationPath), { recursive: true });
|
|
13008
|
+
fs8.renameSync(sourcePath, destinationPath);
|
|
12766
13009
|
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
12767
13010
|
return;
|
|
12768
13011
|
}
|
|
12769
|
-
const ext =
|
|
13012
|
+
const ext = path8.extname(destinationPath).toLowerCase();
|
|
12770
13013
|
if ([".yaml", ".yml", ".json"].includes(ext)) {
|
|
12771
|
-
const sourceRaw =
|
|
12772
|
-
const destRaw =
|
|
13014
|
+
const sourceRaw = fs8.readFileSync(sourcePath, "utf8");
|
|
13015
|
+
const destRaw = fs8.readFileSync(destinationPath, "utf8");
|
|
12773
13016
|
const parser = ext === ".json" ? JSON : import_yaml3.default;
|
|
12774
13017
|
const sourceValue = parser.parse(sourceRaw) || {};
|
|
12775
13018
|
const destValue = parser.parse(destRaw) || {};
|
|
12776
13019
|
const merged = mergeStructuredValues(sourceValue, destValue);
|
|
12777
13020
|
const serialized = ext === ".json" ? `${JSON.stringify(merged, null, 2)}
|
|
12778
13021
|
` : import_yaml3.default.stringify(merged);
|
|
12779
|
-
|
|
12780
|
-
|
|
13022
|
+
fs8.writeFileSync(destinationPath, serialized, "utf8");
|
|
13023
|
+
fs8.unlinkSync(sourcePath);
|
|
12781
13024
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
12782
13025
|
return;
|
|
12783
13026
|
}
|
|
12784
13027
|
if (ext === ".md") {
|
|
12785
|
-
const sourceRaw =
|
|
12786
|
-
const destRaw =
|
|
13028
|
+
const sourceRaw = fs8.readFileSync(sourcePath, "utf8").trimEnd();
|
|
13029
|
+
const destRaw = fs8.readFileSync(destinationPath, "utf8").trimEnd();
|
|
12787
13030
|
if (sourceRaw && !destRaw.includes(sourceRaw)) {
|
|
12788
13031
|
const marker = `## Legacy Content From ${relativeSource}`;
|
|
12789
|
-
|
|
13032
|
+
fs8.writeFileSync(destinationPath, `${destRaw}
|
|
12790
13033
|
|
|
12791
13034
|
${marker}
|
|
12792
13035
|
|
|
12793
13036
|
${sourceRaw}
|
|
12794
13037
|
`, "utf8");
|
|
12795
13038
|
}
|
|
12796
|
-
|
|
13039
|
+
fs8.unlinkSync(sourcePath);
|
|
12797
13040
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
12798
13041
|
return;
|
|
12799
13042
|
}
|
|
12800
13043
|
const preservedPath = legacyVariantPath(destinationPath);
|
|
12801
|
-
|
|
13044
|
+
fs8.renameSync(sourcePath, preservedPath);
|
|
12802
13045
|
stageGitAwareMerge(sourcePath, preservedPath, gitState);
|
|
12803
13046
|
}
|
|
12804
|
-
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
12805
|
-
if (!
|
|
12806
|
-
const stat =
|
|
13047
|
+
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource = path8.basename(sourcePath), gitState = null) {
|
|
13048
|
+
if (!fs8.existsSync(sourcePath)) return;
|
|
13049
|
+
const stat = fs8.statSync(sourcePath);
|
|
12807
13050
|
if (stat.isDirectory()) {
|
|
12808
13051
|
const sourceWasTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
12809
13052
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
12810
|
-
if (!
|
|
12811
|
-
|
|
12812
|
-
|
|
13053
|
+
if (!fs8.existsSync(destinationPath)) {
|
|
13054
|
+
fs8.mkdirSync(path8.dirname(destinationPath), { recursive: true });
|
|
13055
|
+
fs8.renameSync(sourcePath, destinationPath);
|
|
12813
13056
|
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
12814
13057
|
return;
|
|
12815
13058
|
}
|
|
12816
|
-
|
|
12817
|
-
for (const entry of
|
|
13059
|
+
fs8.mkdirSync(destinationPath, { recursive: true });
|
|
13060
|
+
for (const entry of fs8.readdirSync(sourcePath)) {
|
|
12818
13061
|
mergePathIntoDestination(
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
13062
|
+
path8.join(sourcePath, entry),
|
|
13063
|
+
path8.join(destinationPath, entry),
|
|
13064
|
+
path8.join(relativeSource, entry),
|
|
12822
13065
|
gitState
|
|
12823
13066
|
);
|
|
12824
13067
|
}
|
|
12825
|
-
|
|
13068
|
+
fs8.rmSync(sourcePath, { recursive: true, force: true });
|
|
12826
13069
|
return;
|
|
12827
13070
|
}
|
|
12828
13071
|
mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState);
|
|
12829
13072
|
}
|
|
12830
13073
|
function isOptimaPluginPackageWorktree(worktree) {
|
|
12831
|
-
const packageJsonPath =
|
|
12832
|
-
if (!
|
|
13074
|
+
const packageJsonPath = path8.join(worktree, "package.json");
|
|
13075
|
+
if (!fs8.existsSync(packageJsonPath)) return false;
|
|
12833
13076
|
try {
|
|
12834
|
-
const pkg = JSON.parse(
|
|
13077
|
+
const pkg = JSON.parse(fs8.readFileSync(packageJsonPath, "utf8"));
|
|
12835
13078
|
return pkg?.name === "@defend-tech/opencode-optima";
|
|
12836
13079
|
} catch {
|
|
12837
13080
|
return false;
|
|
@@ -12840,59 +13083,59 @@ function isOptimaPluginPackageWorktree(worktree) {
|
|
|
12840
13083
|
function migrateLegacyOptimaLayout(worktree) {
|
|
12841
13084
|
const gitState = gitMigrationState(worktree);
|
|
12842
13085
|
const migrations = [
|
|
12843
|
-
[
|
|
12844
|
-
[
|
|
12845
|
-
[
|
|
12846
|
-
[
|
|
12847
|
-
[
|
|
12848
|
-
[
|
|
13086
|
+
[path8.join(legacyOrbitaDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path8.join(".orbita", "orbita.yaml")],
|
|
13087
|
+
[path8.join(legacyOrbitaDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path8.join(".orbita", "staticeng.yaml")],
|
|
13088
|
+
[path8.join(legacyOrbitaDir(worktree), ".config"), optimaLocalConfigDir(worktree), path8.join(".orbita", ".config")],
|
|
13089
|
+
[path8.join(legacyOrbitaDir(worktree), "config"), optimaLocalConfigDir(worktree), path8.join(".orbita", "config")],
|
|
13090
|
+
[path8.join(legacyOrbitaDir(worktree), "runtime"), path8.join(optimaLocalConfigDir(worktree), "runtime"), path8.join(".orbita", "runtime")],
|
|
13091
|
+
[path8.join(legacyOrbitaDir(worktree), "generated"), path8.join(optimaLocalConfigDir(worktree), "generated"), path8.join(".orbita", "generated")],
|
|
12849
13092
|
[legacyOrbitaDir(worktree), optimaDir(worktree), ".orbita"],
|
|
12850
|
-
[
|
|
12851
|
-
[
|
|
12852
|
-
[
|
|
12853
|
-
[
|
|
12854
|
-
[
|
|
12855
|
-
[
|
|
13093
|
+
[path8.join(legacyStaticEngDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path8.join(".staticeng", "staticeng.yaml")],
|
|
13094
|
+
[path8.join(legacyStaticEngDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path8.join(".staticeng", "orbita.yaml")],
|
|
13095
|
+
[path8.join(legacyStaticEngDir(worktree), ".config"), optimaLocalConfigDir(worktree), path8.join(".staticeng", ".config")],
|
|
13096
|
+
[path8.join(legacyStaticEngDir(worktree), "config"), optimaLocalConfigDir(worktree), path8.join(".staticeng", "config")],
|
|
13097
|
+
[path8.join(legacyStaticEngDir(worktree), "runtime"), path8.join(optimaLocalConfigDir(worktree), "runtime"), path8.join(".staticeng", "runtime")],
|
|
13098
|
+
[path8.join(legacyStaticEngDir(worktree), "generated"), path8.join(optimaLocalConfigDir(worktree), "generated"), path8.join(".staticeng", "generated")],
|
|
12856
13099
|
[legacyStaticEngDir(worktree), optimaDir(worktree), ".staticeng"],
|
|
12857
|
-
[
|
|
12858
|
-
[
|
|
12859
|
-
[
|
|
12860
|
-
[
|
|
12861
|
-
[
|
|
12862
|
-
[
|
|
12863
|
-
[
|
|
12864
|
-
[
|
|
12865
|
-
[
|
|
12866
|
-
[
|
|
12867
|
-
[
|
|
12868
|
-
[
|
|
13100
|
+
[path8.join(legacyNomadworkDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path8.join(".nomadwork", "nomadworks.yaml")],
|
|
13101
|
+
[path8.join(legacyNomadworksDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path8.join(".nomadworks", "nomadworks.yaml")],
|
|
13102
|
+
[path8.join(legacyNomadworkDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path8.join(".nomadwork", "staticeng.yaml")],
|
|
13103
|
+
[path8.join(legacyNomadworksDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path8.join(".nomadworks", "staticeng.yaml")],
|
|
13104
|
+
[path8.join(legacyNomadworkDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path8.join(".nomadwork", "orbita.yaml")],
|
|
13105
|
+
[path8.join(legacyNomadworksDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path8.join(".nomadworks", "orbita.yaml")],
|
|
13106
|
+
[path8.join(legacyNomadworkDir(worktree), "runtime"), path8.join(optimaLocalConfigDir(worktree), "runtime"), path8.join(".nomadwork", "runtime")],
|
|
13107
|
+
[path8.join(legacyNomadworksDir(worktree), "runtime"), path8.join(optimaLocalConfigDir(worktree), "runtime"), path8.join(".nomadworks", "runtime")],
|
|
13108
|
+
[path8.join(legacyNomadworkDir(worktree), "generated"), path8.join(optimaLocalConfigDir(worktree), "generated"), path8.join(".nomadwork", "generated")],
|
|
13109
|
+
[path8.join(legacyNomadworksDir(worktree), "generated"), path8.join(optimaLocalConfigDir(worktree), "generated"), path8.join(".nomadworks", "generated")],
|
|
13110
|
+
[path8.join(legacyNomadworkDir(worktree), "config"), optimaLocalConfigDir(worktree), path8.join(".nomadwork", "config")],
|
|
13111
|
+
[path8.join(legacyNomadworksDir(worktree), "config"), optimaLocalConfigDir(worktree), path8.join(".nomadworks", "config")],
|
|
12869
13112
|
[legacyNomadworkDir(worktree), optimaDir(worktree), ".nomadwork"],
|
|
12870
13113
|
[legacyNomadworksDir(worktree), optimaDir(worktree), ".nomadworks"],
|
|
12871
|
-
[
|
|
12872
|
-
[
|
|
12873
|
-
[
|
|
12874
|
-
[
|
|
12875
|
-
[
|
|
13114
|
+
[path8.join(worktree, "tasks"), optimaTasksDir(worktree), "tasks"],
|
|
13115
|
+
[path8.join(worktree, "evidences"), optimaEvidencesDir(worktree), "evidences"],
|
|
13116
|
+
[path8.join(worktree, "docs", "scrs"), optimaScrsDir(worktree), path8.join("docs", "scrs")],
|
|
13117
|
+
[path8.join(worktree, "codemap.yml"), optimaCodemapPath(worktree), "codemap.yml"],
|
|
13118
|
+
[path8.join(worktree, "codemap.yaml"), optimaCodemapPath(worktree), "codemap.yaml"]
|
|
12876
13119
|
];
|
|
12877
13120
|
if (!isOptimaPluginPackageWorktree(worktree)) {
|
|
12878
|
-
migrations.push([
|
|
13121
|
+
migrations.push([path8.join(worktree, "policies"), repoPoliciesDir(worktree), "policies"]);
|
|
12879
13122
|
}
|
|
12880
13123
|
for (const [source, destination, relativeSource] of migrations) {
|
|
12881
|
-
if (
|
|
13124
|
+
if (fs8.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
12882
13125
|
}
|
|
12883
13126
|
for (const [source, destination, relativeSource] of [
|
|
12884
|
-
[
|
|
12885
|
-
[
|
|
12886
|
-
[
|
|
12887
|
-
[
|
|
13127
|
+
[path8.join(optimaDir(worktree), "optima.yaml"), repoConfigPath(worktree), path8.join(".optima", "optima.yaml")],
|
|
13128
|
+
[path8.join(optimaDir(worktree), "config"), optimaLocalConfigDir(worktree), path8.join(".optima", "config")],
|
|
13129
|
+
[path8.join(optimaDir(worktree), "runtime"), path8.join(optimaLocalConfigDir(worktree), "runtime"), path8.join(".optima", "runtime")],
|
|
13130
|
+
[path8.join(optimaDir(worktree), "generated"), path8.join(optimaLocalConfigDir(worktree), "generated"), path8.join(".optima", "generated")]
|
|
12888
13131
|
]) {
|
|
12889
|
-
if (
|
|
13132
|
+
if (fs8.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
12890
13133
|
}
|
|
12891
13134
|
}
|
|
12892
13135
|
function listMarkdownFiles(dirPath) {
|
|
12893
|
-
if (!
|
|
13136
|
+
if (!fs8.existsSync(dirPath)) return [];
|
|
12894
13137
|
try {
|
|
12895
|
-
return
|
|
13138
|
+
return fs8.readdirSync(dirPath).filter((file) => file.endsWith(".md") && file.toLowerCase() !== "readme.md");
|
|
12896
13139
|
} catch (e) {
|
|
12897
13140
|
console.error(`[Optima] Failed to read markdown files from ${dirPath}:`, e);
|
|
12898
13141
|
return [];
|
|
@@ -12933,10 +13176,10 @@ function toModelString(provider, model) {
|
|
|
12933
13176
|
}
|
|
12934
13177
|
function readTaskMetadata(taskPath, worktree) {
|
|
12935
13178
|
if (!taskPath) return {};
|
|
12936
|
-
const absoluteTaskPath =
|
|
12937
|
-
if (!
|
|
13179
|
+
const absoluteTaskPath = path8.isAbsolute(taskPath) ? taskPath : path8.join(worktree, taskPath);
|
|
13180
|
+
if (!fs8.existsSync(absoluteTaskPath)) return {};
|
|
12938
13181
|
try {
|
|
12939
|
-
const raw =
|
|
13182
|
+
const raw = fs8.readFileSync(absoluteTaskPath, "utf8");
|
|
12940
13183
|
const { data } = parseFrontmatter(raw);
|
|
12941
13184
|
return {
|
|
12942
13185
|
complexity: typeof data.complexity === "string" ? data.complexity.trim().toLowerCase() : void 0,
|
|
@@ -12961,11 +13204,11 @@ function slugifyTitle(input) {
|
|
|
12961
13204
|
}
|
|
12962
13205
|
function loadDiscussionRegistry(worktree) {
|
|
12963
13206
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
12964
|
-
if (!
|
|
13207
|
+
if (!fs8.existsSync(registryPath)) {
|
|
12965
13208
|
return { version: 1, active: {} };
|
|
12966
13209
|
}
|
|
12967
13210
|
try {
|
|
12968
|
-
const parsed = JSON.parse(
|
|
13211
|
+
const parsed = JSON.parse(fs8.readFileSync(registryPath, "utf8"));
|
|
12969
13212
|
const registry = {
|
|
12970
13213
|
version: 1,
|
|
12971
13214
|
active: Object.fromEntries(Object.entries(parsed.active || {}).map(([key, value]) => [key, normalizeLegacyDiscussionEntry(value)]))
|
|
@@ -12978,34 +13221,34 @@ function loadDiscussionRegistry(worktree) {
|
|
|
12978
13221
|
}
|
|
12979
13222
|
function saveDiscussionRegistry(worktree, registry) {
|
|
12980
13223
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
12981
|
-
const runtimeDir =
|
|
12982
|
-
if (!
|
|
12983
|
-
|
|
13224
|
+
const runtimeDir = path8.dirname(registryPath);
|
|
13225
|
+
if (!fs8.existsSync(runtimeDir)) fs8.mkdirSync(runtimeDir, { recursive: true });
|
|
13226
|
+
fs8.writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf8");
|
|
12984
13227
|
}
|
|
12985
13228
|
function runtimeDiscussionsDir(worktree) {
|
|
12986
|
-
return
|
|
13229
|
+
return path8.join(optimaLocalConfigDir(worktree), "runtime", "discussions");
|
|
12987
13230
|
}
|
|
12988
13231
|
function archivedRuntimeDiscussionsDir(worktree) {
|
|
12989
|
-
return
|
|
13232
|
+
return path8.join(runtimeDiscussionsDir(worktree), "archive");
|
|
12990
13233
|
}
|
|
12991
13234
|
function finalDiscussionsDir(worktree) {
|
|
12992
|
-
return
|
|
13235
|
+
return path8.join(optimaTasksDir(worktree), "discussions");
|
|
12993
13236
|
}
|
|
12994
13237
|
function nextDiscussionIdentity(worktree, title) {
|
|
12995
13238
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
12996
13239
|
const runtimeDir = runtimeDiscussionsDir(worktree);
|
|
12997
|
-
if (!
|
|
12998
|
-
if (!
|
|
13240
|
+
if (!fs8.existsSync(discussionsDir)) fs8.mkdirSync(discussionsDir, { recursive: true });
|
|
13241
|
+
if (!fs8.existsSync(runtimeDir)) fs8.mkdirSync(runtimeDir, { recursive: true });
|
|
12999
13242
|
let sequence = 1;
|
|
13000
13243
|
while (true) {
|
|
13001
13244
|
const id = `DISCUSSION-${String(sequence).padStart(3, "0")}`;
|
|
13002
13245
|
const filename = `${id}-${slugifyTitle(title)}.md`;
|
|
13003
|
-
const summaryRelativePath =
|
|
13004
|
-
const summaryAbsolutePath =
|
|
13246
|
+
const summaryRelativePath = path8.join(".optima", "tasks", "discussions", filename);
|
|
13247
|
+
const summaryAbsolutePath = path8.join(worktree, summaryRelativePath);
|
|
13005
13248
|
const transcriptFilename = `${id}-transcript.md`;
|
|
13006
|
-
const transcriptRelativePath =
|
|
13007
|
-
const transcriptAbsolutePath =
|
|
13008
|
-
if (!
|
|
13249
|
+
const transcriptRelativePath = path8.join(".optima", ".config", "runtime", "discussions", transcriptFilename);
|
|
13250
|
+
const transcriptAbsolutePath = path8.join(worktree, transcriptRelativePath);
|
|
13251
|
+
if (!fs8.existsSync(summaryAbsolutePath) && !fs8.existsSync(transcriptAbsolutePath)) {
|
|
13009
13252
|
return {
|
|
13010
13253
|
id,
|
|
13011
13254
|
filename,
|
|
@@ -13021,23 +13264,23 @@ function nextDiscussionIdentity(worktree, title) {
|
|
|
13021
13264
|
}
|
|
13022
13265
|
function findDiscussionById(worktree, discussionID) {
|
|
13023
13266
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
13024
|
-
if (!
|
|
13025
|
-
const entries =
|
|
13267
|
+
if (!fs8.existsSync(discussionsDir)) return null;
|
|
13268
|
+
const entries = fs8.readdirSync(discussionsDir).filter((name) => name.startsWith(`${discussionID}-`) && name.endsWith(".md"));
|
|
13026
13269
|
if (entries.length === 0) return null;
|
|
13027
13270
|
const filename = entries.sort()[0];
|
|
13028
13271
|
const transcriptFilename = `${discussionID}-transcript.md`;
|
|
13029
13272
|
return {
|
|
13030
13273
|
id: discussionID,
|
|
13031
13274
|
filename,
|
|
13032
|
-
summaryRelativePath:
|
|
13033
|
-
summaryAbsolutePath:
|
|
13275
|
+
summaryRelativePath: path8.join(".optima", "tasks", "discussions", filename),
|
|
13276
|
+
summaryAbsolutePath: path8.join(discussionsDir, filename),
|
|
13034
13277
|
transcriptFilename,
|
|
13035
|
-
transcriptRelativePath:
|
|
13036
|
-
transcriptAbsolutePath:
|
|
13278
|
+
transcriptRelativePath: path8.join(".optima", ".config", "runtime", "discussions", transcriptFilename),
|
|
13279
|
+
transcriptAbsolutePath: path8.join(runtimeDiscussionsDir(worktree), transcriptFilename)
|
|
13037
13280
|
};
|
|
13038
13281
|
}
|
|
13039
13282
|
function parseDiscussionFile(filePath) {
|
|
13040
|
-
const raw =
|
|
13283
|
+
const raw = fs8.readFileSync(filePath, "utf8");
|
|
13041
13284
|
const { data, body } = parseFrontmatter(raw);
|
|
13042
13285
|
return { data, body: body.trimStart() };
|
|
13043
13286
|
}
|
|
@@ -13048,10 +13291,10 @@ ${import_yaml3.default.stringify(frontmatter).trim()}
|
|
|
13048
13291
|
|
|
13049
13292
|
${body.trimEnd()}
|
|
13050
13293
|
`;
|
|
13051
|
-
|
|
13294
|
+
fs8.writeFileSync(filePath, serialized, "utf8");
|
|
13052
13295
|
}
|
|
13053
13296
|
function setDiscussionStatus(filePath, status) {
|
|
13054
|
-
if (!
|
|
13297
|
+
if (!fs8.existsSync(filePath)) return;
|
|
13055
13298
|
const { data, body } = parseDiscussionFile(filePath);
|
|
13056
13299
|
writeDiscussionFile(filePath, { ...data, status }, body);
|
|
13057
13300
|
}
|
|
@@ -13116,18 +13359,18 @@ async function appendMessageIfNeeded(client, worktree, registry, sessionID, mess
|
|
|
13116
13359
|
});
|
|
13117
13360
|
const text = extractTextParts(response.data.parts || []);
|
|
13118
13361
|
if (!text) return;
|
|
13119
|
-
appendDiscussionMessage(
|
|
13362
|
+
appendDiscussionMessage(path8.join(worktree, discussion.transcriptPath), speaker, text, messageID);
|
|
13120
13363
|
discussion.appendedMessageIDs ??= [];
|
|
13121
13364
|
discussion.appendedMessageIDs.push(messageID);
|
|
13122
13365
|
saveDiscussionRegistry(worktree, registry);
|
|
13123
13366
|
}
|
|
13124
13367
|
async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
13125
|
-
const transcriptPath =
|
|
13126
|
-
const summaryPath =
|
|
13127
|
-
const summaryDir =
|
|
13128
|
-
if (!
|
|
13129
|
-
const hasExistingSummary =
|
|
13130
|
-
const priorMtimeMs = hasExistingSummary ?
|
|
13368
|
+
const transcriptPath = path8.join(worktree, discussion.transcriptPath);
|
|
13369
|
+
const summaryPath = path8.join(worktree, discussion.summaryPath);
|
|
13370
|
+
const summaryDir = path8.dirname(summaryPath);
|
|
13371
|
+
if (!fs8.existsSync(summaryDir)) fs8.mkdirSync(summaryDir, { recursive: true });
|
|
13372
|
+
const hasExistingSummary = fs8.existsSync(summaryPath);
|
|
13373
|
+
const priorMtimeMs = hasExistingSummary ? fs8.statSync(summaryPath).mtimeMs : null;
|
|
13131
13374
|
const summarizerSession = await client.session.create({
|
|
13132
13375
|
body: { title: `Discussion Summary: ${discussion.id}` }
|
|
13133
13376
|
});
|
|
@@ -13227,30 +13470,30 @@ async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
|
13227
13470
|
return { confirmation, summaryPath, transcriptPath, hasExistingSummary, priorMtimeMs };
|
|
13228
13471
|
}
|
|
13229
13472
|
function archiveDiscussionTranscript(worktree, transcriptRelativePath) {
|
|
13230
|
-
const sourcePath =
|
|
13231
|
-
if (!
|
|
13473
|
+
const sourcePath = path8.join(worktree, transcriptRelativePath);
|
|
13474
|
+
if (!fs8.existsSync(sourcePath)) return null;
|
|
13232
13475
|
const archiveDir = archivedRuntimeDiscussionsDir(worktree);
|
|
13233
|
-
if (!
|
|
13234
|
-
const targetPath =
|
|
13235
|
-
|
|
13476
|
+
if (!fs8.existsSync(archiveDir)) fs8.mkdirSync(archiveDir, { recursive: true });
|
|
13477
|
+
const targetPath = path8.join(archiveDir, path8.basename(sourcePath));
|
|
13478
|
+
fs8.renameSync(sourcePath, targetPath);
|
|
13236
13479
|
return targetPath;
|
|
13237
13480
|
}
|
|
13238
13481
|
async function finalizeClosingDiscussion(client, worktree, registry, sessionID, discussion) {
|
|
13239
13482
|
const { confirmation, summaryPath, hasExistingSummary, priorMtimeMs } = await summarizeDiscussionWithBA(client, worktree, discussion);
|
|
13240
|
-
if (!
|
|
13483
|
+
if (!fs8.existsSync(summaryPath)) {
|
|
13241
13484
|
throw new Error(`Discussion summary was not written to ${discussion.summaryPath}`);
|
|
13242
13485
|
}
|
|
13243
13486
|
if (hasExistingSummary) {
|
|
13244
|
-
const currentMtimeMs =
|
|
13487
|
+
const currentMtimeMs = fs8.statSync(summaryPath).mtimeMs;
|
|
13245
13488
|
if (currentMtimeMs <= priorMtimeMs) {
|
|
13246
13489
|
throw new Error(`Discussion summary file was not updated at ${discussion.summaryPath}`);
|
|
13247
13490
|
}
|
|
13248
13491
|
}
|
|
13249
|
-
const summaryContent =
|
|
13492
|
+
const summaryContent = fs8.readFileSync(summaryPath, "utf8").trim();
|
|
13250
13493
|
if (!summaryContent) {
|
|
13251
13494
|
throw new Error(`Discussion summary file is empty at ${discussion.summaryPath}`);
|
|
13252
13495
|
}
|
|
13253
|
-
const transcriptPath =
|
|
13496
|
+
const transcriptPath = path8.join(worktree, discussion.transcriptPath);
|
|
13254
13497
|
setDiscussionStatus(transcriptPath, "closed");
|
|
13255
13498
|
const archivedTranscriptPath = archiveDiscussionTranscript(worktree, discussion.transcriptPath);
|
|
13256
13499
|
delete registry.active[sessionID];
|
|
@@ -13258,7 +13501,7 @@ async function finalizeClosingDiscussion(client, worktree, registry, sessionID,
|
|
|
13258
13501
|
return {
|
|
13259
13502
|
confirmation,
|
|
13260
13503
|
summaryPath: discussion.summaryPath,
|
|
13261
|
-
archivedTranscriptPath: archivedTranscriptPath ?
|
|
13504
|
+
archivedTranscriptPath: archivedTranscriptPath ? path8.relative(worktree, archivedTranscriptPath) : path8.join(".optima", ".config", "runtime", "discussions", "archive", path8.basename(discussion.transcriptPath))
|
|
13262
13505
|
};
|
|
13263
13506
|
}
|
|
13264
13507
|
function normalizeTeamMode(value) {
|
|
@@ -13297,13 +13540,13 @@ function getOperatingTeamMode(repoCfg) {
|
|
|
13297
13540
|
}
|
|
13298
13541
|
function readResolvedFile(relativePath, worktree, options = {}) {
|
|
13299
13542
|
const filePath = resolveIncludeFile(`plugin:${relativePath}`, worktree, PKG_ROOT, options);
|
|
13300
|
-
if (!filePath || !
|
|
13301
|
-
return resolveIncludes(
|
|
13543
|
+
if (!filePath || !fs8.existsSync(filePath)) return "";
|
|
13544
|
+
return resolveIncludes(fs8.readFileSync(filePath, "utf8"), worktree, PKG_ROOT, options).trim();
|
|
13302
13545
|
}
|
|
13303
13546
|
function loadMarkdownFragment(filePath, worktree) {
|
|
13304
|
-
if (!
|
|
13547
|
+
if (!fs8.existsSync(filePath)) return "";
|
|
13305
13548
|
try {
|
|
13306
|
-
const raw =
|
|
13549
|
+
const raw = fs8.readFileSync(filePath, "utf8");
|
|
13307
13550
|
const { body } = parseFrontmatter(raw);
|
|
13308
13551
|
return resolveIncludes(body.trim(), worktree, PKG_ROOT);
|
|
13309
13552
|
} catch (e) {
|
|
@@ -13312,9 +13555,9 @@ function loadMarkdownFragment(filePath, worktree) {
|
|
|
13312
13555
|
}
|
|
13313
13556
|
}
|
|
13314
13557
|
function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
13315
|
-
if (!
|
|
13558
|
+
if (!fs8.existsSync(filePath)) return null;
|
|
13316
13559
|
try {
|
|
13317
|
-
const rawContent =
|
|
13560
|
+
const rawContent = fs8.readFileSync(filePath, "utf8");
|
|
13318
13561
|
const { data, body } = parseFrontmatter(rawContent);
|
|
13319
13562
|
const prompt = resolveIncludes(body.trim(), worktree, PKG_ROOT, options);
|
|
13320
13563
|
return { data, prompt };
|
|
@@ -13325,13 +13568,13 @@ function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
|
13325
13568
|
}
|
|
13326
13569
|
function syncGeneratedPolicies(worktree, repoCfg) {
|
|
13327
13570
|
if (repoCfg.policies?.extract_defaults !== "all") return;
|
|
13328
|
-
if (!
|
|
13571
|
+
if (!fs8.existsSync(BUNDLE_POLICIES_DIR)) return;
|
|
13329
13572
|
const generatedDir = generatedPoliciesDir(worktree);
|
|
13330
|
-
if (!
|
|
13331
|
-
const policyFiles =
|
|
13573
|
+
if (!fs8.existsSync(generatedDir)) fs8.mkdirSync(generatedDir, { recursive: true });
|
|
13574
|
+
const policyFiles = fs8.readdirSync(BUNDLE_POLICIES_DIR).filter((file) => file.endsWith(".md") && file !== "README.md");
|
|
13332
13575
|
for (const file of policyFiles) {
|
|
13333
|
-
const sourcePath =
|
|
13334
|
-
const source =
|
|
13576
|
+
const sourcePath = path8.join(BUNDLE_POLICIES_DIR, file);
|
|
13577
|
+
const source = fs8.readFileSync(sourcePath, "utf8").trimEnd();
|
|
13335
13578
|
const generated = [
|
|
13336
13579
|
"<!--",
|
|
13337
13580
|
"Generated from Optima plugin defaults.",
|
|
@@ -13342,19 +13585,19 @@ function syncGeneratedPolicies(worktree, repoCfg) {
|
|
|
13342
13585
|
source,
|
|
13343
13586
|
""
|
|
13344
13587
|
].join("\n");
|
|
13345
|
-
|
|
13588
|
+
fs8.writeFileSync(path8.join(generatedDir, file), generated, "utf8");
|
|
13346
13589
|
}
|
|
13347
13590
|
}
|
|
13348
13591
|
function ensureReadmeFile(dirPath, content) {
|
|
13349
|
-
if (!
|
|
13350
|
-
const readmePath =
|
|
13351
|
-
if (!
|
|
13352
|
-
|
|
13592
|
+
if (!fs8.existsSync(dirPath)) fs8.mkdirSync(dirPath, { recursive: true });
|
|
13593
|
+
const readmePath = path8.join(dirPath, "README.md");
|
|
13594
|
+
if (!fs8.existsSync(readmePath)) {
|
|
13595
|
+
fs8.writeFileSync(readmePath, content, "utf8");
|
|
13353
13596
|
}
|
|
13354
13597
|
}
|
|
13355
13598
|
function ensureFileIfMissing(filePath, content) {
|
|
13356
|
-
if (!
|
|
13357
|
-
if (!
|
|
13599
|
+
if (!fs8.existsSync(path8.dirname(filePath))) fs8.mkdirSync(path8.dirname(filePath), { recursive: true });
|
|
13600
|
+
if (!fs8.existsSync(filePath)) fs8.writeFileSync(filePath, content, "utf8");
|
|
13358
13601
|
}
|
|
13359
13602
|
function currentTasksRegistryContent() {
|
|
13360
13603
|
return "# Current Tasks (Backlog)\n\n## \u{1F4AC} Active Discussions\n- (None)\n\n## \u{1F680} Active\n- (None)\n\n## \u{1F4CB} Todo\n- (None)\n\n## \u{1F6D1} Blocked\n- (None)\n";
|
|
@@ -13440,16 +13683,16 @@ Never place implementation evidence under root \`evidences/\`.
|
|
|
13440
13683
|
}
|
|
13441
13684
|
function ensureOptimaTaskTemplates(worktree) {
|
|
13442
13685
|
const tasksDir = optimaTasksDir(worktree);
|
|
13443
|
-
ensureFileIfMissing(
|
|
13444
|
-
ensureFileIfMissing(
|
|
13686
|
+
ensureFileIfMissing(path8.join(tasksDir, "task-template.md"), taskTemplateContent());
|
|
13687
|
+
ensureFileIfMissing(path8.join(tasksDir, "subtask-template.md"), subtaskTemplateContent());
|
|
13445
13688
|
}
|
|
13446
13689
|
function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
13447
13690
|
const configPath = repoConfigPath(worktree);
|
|
13448
|
-
if (
|
|
13449
|
-
const templatePath =
|
|
13450
|
-
if (!
|
|
13451
|
-
const agentIds =
|
|
13452
|
-
let optimaConfig =
|
|
13691
|
+
if (fs8.existsSync(configPath)) return false;
|
|
13692
|
+
const templatePath = path8.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
13693
|
+
if (!fs8.existsSync(templatePath)) return false;
|
|
13694
|
+
const agentIds = fs8.existsSync(BUNDLE_AGENTS_DIR) ? fs8.readdirSync(BUNDLE_AGENTS_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")) : [];
|
|
13695
|
+
let optimaConfig = fs8.readFileSync(templatePath, "utf8");
|
|
13453
13696
|
optimaConfig = optimaConfig.replace("{{teamMode}}", normalizeTeamMode(teamMode));
|
|
13454
13697
|
let agentsSection = "";
|
|
13455
13698
|
for (const id of agentIds) {
|
|
@@ -13460,26 +13703,26 @@ function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
|
13460
13703
|
}
|
|
13461
13704
|
optimaConfig = optimaConfig.replace(/^agents:/m, "agents:\n" + agentsSection);
|
|
13462
13705
|
ensureFileIfMissing(configPath, optimaConfig);
|
|
13463
|
-
return
|
|
13706
|
+
return fs8.existsSync(configPath);
|
|
13464
13707
|
}
|
|
13465
13708
|
function scaffoldOptimaRootCodemap(worktree) {
|
|
13466
13709
|
const rootCodemapPath = optimaCodemapPath(worktree);
|
|
13467
|
-
if (
|
|
13468
|
-
const templatePath =
|
|
13469
|
-
if (!
|
|
13470
|
-
const codemapConfig =
|
|
13710
|
+
if (fs8.existsSync(rootCodemapPath)) return false;
|
|
13711
|
+
const templatePath = path8.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
13712
|
+
if (!fs8.existsSync(templatePath)) return false;
|
|
13713
|
+
const codemapConfig = fs8.readFileSync(templatePath, "utf8").replace("{{projectName}}", path8.basename(worktree));
|
|
13471
13714
|
ensureFileIfMissing(rootCodemapPath, codemapConfig);
|
|
13472
|
-
return
|
|
13715
|
+
return fs8.existsSync(rootCodemapPath);
|
|
13473
13716
|
}
|
|
13474
13717
|
function ensureOptimaRegistries(worktree) {
|
|
13475
13718
|
const tasksDir = optimaTasksDir(worktree);
|
|
13476
13719
|
const scrsDir = optimaScrsDir(worktree);
|
|
13477
|
-
if (!
|
|
13478
|
-
if (!
|
|
13479
|
-
ensureFileIfMissing(
|
|
13480
|
-
ensureFileIfMissing(
|
|
13481
|
-
ensureFileIfMissing(
|
|
13482
|
-
ensureFileIfMissing(
|
|
13720
|
+
if (!fs8.existsSync(tasksDir)) fs8.mkdirSync(tasksDir, { recursive: true });
|
|
13721
|
+
if (!fs8.existsSync(scrsDir)) fs8.mkdirSync(scrsDir, { recursive: true });
|
|
13722
|
+
ensureFileIfMissing(path8.join(tasksDir, "current.md"), currentTasksRegistryContent());
|
|
13723
|
+
ensureFileIfMissing(path8.join(tasksDir, "done.md"), doneTasksRegistryContent());
|
|
13724
|
+
ensureFileIfMissing(path8.join(scrsDir, "current.md"), currentScrRegistryContent());
|
|
13725
|
+
ensureFileIfMissing(path8.join(scrsDir, "done.md"), doneScrRegistryContent());
|
|
13483
13726
|
}
|
|
13484
13727
|
function scaffoldOptimaReadmes(worktree) {
|
|
13485
13728
|
ensureReadmeFile(repoPoliciesDir(worktree), REPO_LOCAL_POLICIES_README);
|
|
@@ -13641,12 +13884,12 @@ function shouldRegisterWorkflowProductManager(options = {}, worktree = process.c
|
|
|
13641
13884
|
}
|
|
13642
13885
|
function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
13643
13886
|
if (typeof candidate !== "string" || typeof basePath !== "string" || !candidate.trim() || !basePath.trim()) return false;
|
|
13644
|
-
const resolvedCandidate =
|
|
13645
|
-
const resolvedBase =
|
|
13646
|
-
const baseParent =
|
|
13647
|
-
if (
|
|
13648
|
-
const baseName =
|
|
13649
|
-
const candidateName =
|
|
13887
|
+
const resolvedCandidate = path8.resolve(candidate);
|
|
13888
|
+
const resolvedBase = path8.resolve(basePath);
|
|
13889
|
+
const baseParent = path8.dirname(resolvedBase);
|
|
13890
|
+
if (path8.dirname(resolvedCandidate) !== baseParent) return false;
|
|
13891
|
+
const baseName = path8.basename(resolvedBase);
|
|
13892
|
+
const candidateName = path8.basename(resolvedCandidate);
|
|
13650
13893
|
if (!candidateName.startsWith(`${baseName}-`)) return false;
|
|
13651
13894
|
const branchSlug = candidateName.slice(baseName.length + 1);
|
|
13652
13895
|
const parts = branchSlug.split("-").filter(Boolean);
|
|
@@ -13654,11 +13897,11 @@ function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
|
13654
13897
|
if (!(/* @__PURE__ */ new Set(["tarea", "bug", "doc", "poc", "idea"])).has(parts[0])) return false;
|
|
13655
13898
|
if (!parts.slice(1).every((part) => /^[a-z0-9][a-z0-9-]*$/.test(part))) return false;
|
|
13656
13899
|
try {
|
|
13657
|
-
const candidateStat =
|
|
13900
|
+
const candidateStat = fs8.lstatSync(resolvedCandidate);
|
|
13658
13901
|
if (!candidateStat.isDirectory() || candidateStat.isSymbolicLink()) return false;
|
|
13659
|
-
const realCandidate =
|
|
13660
|
-
const realBaseParent =
|
|
13661
|
-
return
|
|
13902
|
+
const realCandidate = fs8.realpathSync.native(resolvedCandidate);
|
|
13903
|
+
const realBaseParent = fs8.realpathSync.native(baseParent);
|
|
13904
|
+
return path8.dirname(realCandidate) === realBaseParent && path8.basename(realCandidate) === candidateName;
|
|
13662
13905
|
} catch {
|
|
13663
13906
|
return false;
|
|
13664
13907
|
}
|
|
@@ -13666,15 +13909,15 @@ function isClickUpDerivedWorktreeSibling(candidate, basePath) {
|
|
|
13666
13909
|
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
13667
13910
|
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
13668
13911
|
if (!safe.ok) return { ok: false, error: safe.message };
|
|
13669
|
-
const requested = String(requestedDirectory || "").trim() ?
|
|
13912
|
+
const requested = String(requestedDirectory || "").trim() ? path8.resolve(safe.worktree, String(requestedDirectory).trim()) : safe.worktree;
|
|
13670
13913
|
if (!isSafeWritableDirectory(requested)) return { ok: false, error: `Directory is not a safe writable directory: ${requested}` };
|
|
13671
13914
|
if (isSameOrNestedPath(requested, safe.worktree)) return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "context_worktree" };
|
|
13672
13915
|
const clickUpBasePath = clickUpWebhookValidation?.complete === true && clickUpWebhookValidation?.ok !== false ? clickUpWebhookValidation.config?.basePath : "";
|
|
13673
13916
|
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
13674
|
-
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath:
|
|
13917
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path8.resolve(clickUpBasePath) };
|
|
13675
13918
|
}
|
|
13676
13919
|
if (clickUpBasePath && isClickUpDerivedWorktreeSibling(requested, clickUpBasePath)) {
|
|
13677
|
-
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_derived_worktree", clickupBasePath:
|
|
13920
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_derived_worktree", clickupBasePath: path8.resolve(clickUpBasePath) };
|
|
13678
13921
|
}
|
|
13679
13922
|
return {
|
|
13680
13923
|
ok: false,
|
|
@@ -13708,8 +13951,8 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
13708
13951
|
if (!enabled) continue;
|
|
13709
13952
|
}
|
|
13710
13953
|
const promptOptions = { preferCompactPromptDocs: repoCfg.features?.compact_prompt_docs !== false };
|
|
13711
|
-
const bundledDefinition = loadAgentDefinition(
|
|
13712
|
-
const repoDefinition = loadAgentDefinition(
|
|
13954
|
+
const bundledDefinition = loadAgentDefinition(path8.join(BUNDLE_AGENTS_DIR, file), worktree, promptOptions);
|
|
13955
|
+
const repoDefinition = loadAgentDefinition(path8.join(repoAgentDefinitions, file), worktree, promptOptions) || loadAgentDefinition(path8.join(legacyAgentsDir, file), worktree, promptOptions);
|
|
13713
13956
|
const activeDefinition = repoDefinition || bundledDefinition;
|
|
13714
13957
|
if (!activeDefinition) continue;
|
|
13715
13958
|
const { data } = activeDefinition;
|
|
@@ -13718,7 +13961,7 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
13718
13961
|
if (modePromptFragment) finalPrompt = `${finalPrompt}
|
|
13719
13962
|
|
|
13720
13963
|
${modePromptFragment}`;
|
|
13721
|
-
const additionFragment = loadMarkdownFragment(
|
|
13964
|
+
const additionFragment = loadMarkdownFragment(path8.join(repoAgentAdditions, file), worktree);
|
|
13722
13965
|
if (additionFragment) {
|
|
13723
13966
|
finalPrompt = `${finalPrompt}
|
|
13724
13967
|
|
|
@@ -13771,14 +14014,14 @@ Use this Optima-provided fallback when the current task worktree lacks docs/core
|
|
|
13771
14014
|
}
|
|
13772
14015
|
ourAgents[id] = agentConfig;
|
|
13773
14016
|
if (repoCfg.features?.debug_dumps !== false) {
|
|
13774
|
-
const debugPath =
|
|
14017
|
+
const debugPath = path8.join(debugDir, `${id}.md`);
|
|
13775
14018
|
const { prompt, ...dumpConfig } = agentConfig;
|
|
13776
14019
|
const debugHeader = `---
|
|
13777
14020
|
${import_yaml3.default.stringify(dumpConfig).trim()}
|
|
13778
14021
|
---`;
|
|
13779
14022
|
try {
|
|
13780
|
-
if (!
|
|
13781
|
-
|
|
14023
|
+
if (!fs8.existsSync(debugDir)) fs8.mkdirSync(debugDir, { recursive: true });
|
|
14024
|
+
fs8.writeFileSync(debugPath, `${debugHeader}
|
|
13782
14025
|
|
|
13783
14026
|
${prompt}`, "utf8");
|
|
13784
14027
|
} catch (e) {
|
|
@@ -13802,9 +14045,9 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
13802
14045
|
const configPath = resolveConfigPath(worktree);
|
|
13803
14046
|
const discussionRegistry = loadDiscussionRegistry(worktree);
|
|
13804
14047
|
let repoCfg = { agents: {}, defaults: {}, features: {} };
|
|
13805
|
-
if (
|
|
14048
|
+
if (fs8.existsSync(configPath)) {
|
|
13806
14049
|
try {
|
|
13807
|
-
repoCfg = import_yaml3.default.parse(
|
|
14050
|
+
repoCfg = import_yaml3.default.parse(fs8.readFileSync(configPath, "utf8")) || repoCfg;
|
|
13808
14051
|
} catch (e) {
|
|
13809
14052
|
console.error(`[Optima] Failed to parse config at ${configPath}:`, e);
|
|
13810
14053
|
}
|
|
@@ -13932,10 +14175,10 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
13932
14175
|
}
|
|
13933
14176
|
migrateLegacyOptimaLayout(toolWorktree);
|
|
13934
14177
|
const cfgDir = optimaConfigDir(toolWorktree);
|
|
13935
|
-
if (!
|
|
13936
|
-
const optimaTmplPath =
|
|
13937
|
-
const codemapTmplPath =
|
|
13938
|
-
if (!
|
|
14178
|
+
if (!fs8.existsSync(cfgDir)) fs8.mkdirSync(cfgDir, { recursive: true });
|
|
14179
|
+
const optimaTmplPath = path8.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
14180
|
+
const codemapTmplPath = path8.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
14181
|
+
if (!fs8.existsSync(optimaTmplPath) || !fs8.existsSync(codemapTmplPath)) {
|
|
13939
14182
|
return "Error: Initialization templates not found in plugin.";
|
|
13940
14183
|
}
|
|
13941
14184
|
scaffoldOptimaConfig(toolWorktree, requestedTeamMode);
|
|
@@ -14056,14 +14299,14 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
14056
14299
|
async execute(args, context) {
|
|
14057
14300
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
14058
14301
|
if (!safe.ok) return safe.message;
|
|
14059
|
-
const summaryPath =
|
|
14060
|
-
if (!
|
|
14061
|
-
const taskPath = args.task_path ?
|
|
14302
|
+
const summaryPath = path8.resolve(safe.worktree, args.summary_path || "");
|
|
14303
|
+
if (!fs8.existsSync(summaryPath)) return `FAIL: summary_path not found: ${summaryPath}`;
|
|
14304
|
+
const taskPath = args.task_path ? path8.resolve(safe.worktree, args.task_path) : "";
|
|
14062
14305
|
const payload = buildClickUpSummaryPayload({
|
|
14063
|
-
summaryMarkdown:
|
|
14064
|
-
summaryPath:
|
|
14065
|
-
taskMarkdown: taskPath &&
|
|
14066
|
-
taskPath: taskPath ?
|
|
14306
|
+
summaryMarkdown: fs8.readFileSync(summaryPath, "utf8"),
|
|
14307
|
+
summaryPath: path8.relative(safe.worktree, summaryPath),
|
|
14308
|
+
taskMarkdown: taskPath && fs8.existsSync(taskPath) ? fs8.readFileSync(taskPath, "utf8") : "",
|
|
14309
|
+
taskPath: taskPath ? path8.relative(safe.worktree, taskPath) : "",
|
|
14067
14310
|
branch: args.branch,
|
|
14068
14311
|
worktree: args.worktree,
|
|
14069
14312
|
pr: args.pr
|
|
@@ -14142,12 +14385,12 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
14142
14385
|
async execute(args, context) {
|
|
14143
14386
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
14144
14387
|
if (!safe.ok) return safe.message;
|
|
14145
|
-
const markdownPath =
|
|
14146
|
-
if (!
|
|
14388
|
+
const markdownPath = path8.resolve(safe.worktree, args.markdown_path || "");
|
|
14389
|
+
if (!fs8.existsSync(markdownPath)) return `FAIL: markdown_path not found: ${markdownPath}`;
|
|
14147
14390
|
const payload = buildClickUpCreateSubtasksPayload({
|
|
14148
14391
|
parentTaskId: args.parent_task_id,
|
|
14149
|
-
markdown:
|
|
14150
|
-
sourcePath:
|
|
14392
|
+
markdown: fs8.readFileSync(markdownPath, "utf8"),
|
|
14393
|
+
sourcePath: path8.relative(safe.worktree, markdownPath),
|
|
14151
14394
|
parentBranch: args.parent_branch,
|
|
14152
14395
|
parentTaskType: args.parent_task_type || "Tarea",
|
|
14153
14396
|
apply: String(args.apply || "").toLowerCase() === "true"
|
|
@@ -14263,6 +14506,31 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
14263
14506
|
}
|
|
14264
14507
|
}
|
|
14265
14508
|
}),
|
|
14509
|
+
optima_github_verify_vercel_pr: tool({
|
|
14510
|
+
description: "Verify that a PR head commit has a successful Vercel preproduction deployment and a functional deployment URL before validation/handoff",
|
|
14511
|
+
args: {
|
|
14512
|
+
pr_number: tool.schema.number().describe("Pull request number"),
|
|
14513
|
+
context: tool.schema.string().describe("Required GitHub status context; defaults to 'Vercel \u2013 defend-preproduction'"),
|
|
14514
|
+
environment: tool.schema.string().describe("Required GitHub deployment environment; defaults to 'Preview \u2013 defend-preproduction'"),
|
|
14515
|
+
require_functional_url: tool.schema.string().describe("Set to 'false' to skip HTTP probing of the deployment URL")
|
|
14516
|
+
},
|
|
14517
|
+
async execute(args) {
|
|
14518
|
+
try {
|
|
14519
|
+
const auth = await requireGitHubAppClient();
|
|
14520
|
+
if (!auth.ok) return JSON.stringify(auth, null, 2);
|
|
14521
|
+
if (!runtimeGitHubClient?.verifyVercelPullRequestDeployment) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
|
|
14522
|
+
const result = await runtimeGitHubClient.verifyVercelPullRequestDeployment({
|
|
14523
|
+
pullNumber: args.pr_number,
|
|
14524
|
+
context: args.context || "Vercel \u2013 defend-preproduction",
|
|
14525
|
+
environment: args.environment || "Preview \u2013 defend-preproduction",
|
|
14526
|
+
requireFunctionalUrl: String(args.require_functional_url || "true").toLowerCase() !== "false"
|
|
14527
|
+
});
|
|
14528
|
+
return JSON.stringify(result, null, 2);
|
|
14529
|
+
} catch (error) {
|
|
14530
|
+
return JSON.stringify({ ok: false, error: error.message }, null, 2);
|
|
14531
|
+
}
|
|
14532
|
+
}
|
|
14533
|
+
}),
|
|
14266
14534
|
optima_github_merge_pr: tool({
|
|
14267
14535
|
description: "Merge a GitHub PR through the Optima GitHub App identity after required human approval gates pass",
|
|
14268
14536
|
args: {
|
|
@@ -14492,7 +14760,7 @@ Backfilled messages: ${backfilled}`;
|
|
|
14492
14760
|
if (!existing) {
|
|
14493
14761
|
return "FAIL: No active discussion exists for this session.";
|
|
14494
14762
|
}
|
|
14495
|
-
const discussionPath =
|
|
14763
|
+
const discussionPath = path8.join(toolWorktree, existing.transcriptPath);
|
|
14496
14764
|
setDiscussionStatus(discussionPath, "summarizing");
|
|
14497
14765
|
existing.status = "summarizing";
|
|
14498
14766
|
saveDiscussionRegistry(toolWorktree, toolRegistry);
|
|
@@ -14544,7 +14812,7 @@ Reason: ${err.message}`;
|
|
|
14544
14812
|
try {
|
|
14545
14813
|
const sessionResult = await client.session.create({
|
|
14546
14814
|
query: { directory: workflowDirectory },
|
|
14547
|
-
body: { title: `Workflow Run: ${
|
|
14815
|
+
body: { title: `Workflow Run: ${path8.basename(workflowTaskPath)}` }
|
|
14548
14816
|
});
|
|
14549
14817
|
const sessionId = sessionResult.data.id;
|
|
14550
14818
|
activeWorkflows.set(sessionId, { pmaSessionId, taskPath: workflowTaskPath, track: workflowTrack, directory: workflowDirectory });
|
|
@@ -14728,14 +14996,14 @@ function usage() {
|
|
|
14728
14996
|
].join("\n");
|
|
14729
14997
|
}
|
|
14730
14998
|
function expandHome(inputPath) {
|
|
14731
|
-
if (!inputPath || inputPath === "~") return
|
|
14732
|
-
if (inputPath.startsWith("~/")) return
|
|
14999
|
+
if (!inputPath || inputPath === "~") return os3.homedir();
|
|
15000
|
+
if (inputPath.startsWith("~/")) return path9.join(os3.homedir(), inputPath.slice(2));
|
|
14733
15001
|
return inputPath;
|
|
14734
15002
|
}
|
|
14735
15003
|
function resolveOpenCodeDbPath(inputPath = null) {
|
|
14736
15004
|
const expanded = expandHome(inputPath || process.env.OPTIMA_OPENCODE_DB_PATH || DEFAULT_DB_PATH);
|
|
14737
15005
|
try {
|
|
14738
|
-
if (
|
|
15006
|
+
if (fs9.existsSync(expanded) && fs9.statSync(expanded).isDirectory()) return path9.join(expanded, "opencode.db");
|
|
14739
15007
|
} catch {
|
|
14740
15008
|
return expanded;
|
|
14741
15009
|
}
|
|
@@ -14799,12 +15067,12 @@ function extractOpenedPathValue(value) {
|
|
|
14799
15067
|
}
|
|
14800
15068
|
function normalizePathCandidate(candidate) {
|
|
14801
15069
|
const expanded = expandHome(candidate);
|
|
14802
|
-
if (!
|
|
14803
|
-
return
|
|
15070
|
+
if (!path9.isAbsolute(expanded)) return null;
|
|
15071
|
+
return path9.resolve(expanded);
|
|
14804
15072
|
}
|
|
14805
15073
|
function discoverOpenCodePaths(dbPath) {
|
|
14806
15074
|
const resolvedDb = resolveOpenCodeDbPath(dbPath);
|
|
14807
|
-
if (!
|
|
15075
|
+
if (!fs9.existsSync(resolvedDb)) throw new Error(`OpenCode database not found: ${resolvedDb}`);
|
|
14808
15076
|
const tables = sqliteJson(resolvedDb, "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name");
|
|
14809
15077
|
const discovered = /* @__PURE__ */ new Set();
|
|
14810
15078
|
for (const table of tables) {
|
|
@@ -14829,27 +15097,27 @@ function discoverOpenCodePaths(dbPath) {
|
|
|
14829
15097
|
function collectMarkdownOverridesFrom(baseDir) {
|
|
14830
15098
|
const files = [];
|
|
14831
15099
|
for (const dirName of OVERRIDE_DIRS) {
|
|
14832
|
-
const dirPath =
|
|
14833
|
-
if (!
|
|
14834
|
-
for (const entry of
|
|
15100
|
+
const dirPath = path9.join(baseDir, dirName);
|
|
15101
|
+
if (!fs9.existsSync(dirPath)) continue;
|
|
15102
|
+
for (const entry of fs9.readdirSync(dirPath, { withFileTypes: true })) {
|
|
14835
15103
|
if (!entry.isFile()) continue;
|
|
14836
15104
|
if (!entry.name.endsWith(".md")) continue;
|
|
14837
15105
|
if (entry.name.toLowerCase() === "readme.md") continue;
|
|
14838
|
-
files.push(
|
|
15106
|
+
files.push(path9.join(dirPath, entry.name));
|
|
14839
15107
|
}
|
|
14840
15108
|
}
|
|
14841
15109
|
return files;
|
|
14842
15110
|
}
|
|
14843
15111
|
function collectOverrideFiles(worktree) {
|
|
14844
|
-
return collectMarkdownOverridesFrom(
|
|
15112
|
+
return collectMarkdownOverridesFrom(path9.join(worktree, ".optima")).sort();
|
|
14845
15113
|
}
|
|
14846
15114
|
function collectPlannedOverrideFiles(worktree) {
|
|
14847
15115
|
return [
|
|
14848
15116
|
...collectOverrideFiles(worktree),
|
|
14849
|
-
...collectMarkdownOverridesFrom(
|
|
14850
|
-
...collectMarkdownOverridesFrom(
|
|
14851
|
-
...collectMarkdownOverridesFrom(
|
|
14852
|
-
...collectMarkdownOverridesFrom(
|
|
15117
|
+
...collectMarkdownOverridesFrom(path9.join(worktree, ".staticeng")),
|
|
15118
|
+
...collectMarkdownOverridesFrom(path9.join(worktree, ".orbita")),
|
|
15119
|
+
...collectMarkdownOverridesFrom(path9.join(worktree, ".nomadwork")),
|
|
15120
|
+
...collectMarkdownOverridesFrom(path9.join(worktree, ".nomadworks"))
|
|
14853
15121
|
].sort();
|
|
14854
15122
|
}
|
|
14855
15123
|
var CRC_TABLE = (() => {
|
|
@@ -14882,14 +15150,14 @@ function writeUInt16LE(value) {
|
|
|
14882
15150
|
return buffer;
|
|
14883
15151
|
}
|
|
14884
15152
|
function createZipBackup(files, backupPath) {
|
|
14885
|
-
|
|
15153
|
+
fs9.mkdirSync(path9.dirname(backupPath), { recursive: true });
|
|
14886
15154
|
const localParts = [];
|
|
14887
15155
|
const centralParts = [];
|
|
14888
15156
|
let offset = 0;
|
|
14889
15157
|
const now = dosTimeDate();
|
|
14890
15158
|
for (const filePath of files) {
|
|
14891
|
-
const data =
|
|
14892
|
-
const name =
|
|
15159
|
+
const data = fs9.readFileSync(filePath);
|
|
15160
|
+
const name = path9.resolve(filePath).replace(/^\//, "").split(path9.sep).join("/");
|
|
14893
15161
|
const nameBuffer = Buffer.from(name, "utf8");
|
|
14894
15162
|
const crc = crc32(data);
|
|
14895
15163
|
const local = Buffer.concat([
|
|
@@ -14941,7 +15209,7 @@ function createZipBackup(files, backupPath) {
|
|
|
14941
15209
|
writeUInt32LE(offset),
|
|
14942
15210
|
writeUInt16LE(0)
|
|
14943
15211
|
]);
|
|
14944
|
-
|
|
15212
|
+
fs9.writeFileSync(backupPath, Buffer.concat([...localParts, ...centralParts, end]));
|
|
14945
15213
|
}
|
|
14946
15214
|
function timestamp() {
|
|
14947
15215
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
@@ -14951,17 +15219,17 @@ function validationStatus(result) {
|
|
|
14951
15219
|
return result.ok ? "passed" : "failed";
|
|
14952
15220
|
}
|
|
14953
15221
|
function hasLegacyMarker(worktree) {
|
|
14954
|
-
return
|
|
15222
|
+
return fs9.existsSync(path9.join(worktree, ".staticeng")) || fs9.existsSync(path9.join(worktree, ".orbita")) || fs9.existsSync(path9.join(worktree, ".nomadwork")) || fs9.existsSync(path9.join(worktree, ".nomadworks"));
|
|
14955
15223
|
}
|
|
14956
15224
|
function rootOptimaArtifacts(worktree) {
|
|
14957
15225
|
const artifacts = [
|
|
14958
|
-
["tasks/",
|
|
14959
|
-
["evidences/",
|
|
14960
|
-
["docs/scrs/",
|
|
14961
|
-
["codemap.yml",
|
|
14962
|
-
["codemap.yaml",
|
|
15226
|
+
["tasks/", path9.join(worktree, "tasks")],
|
|
15227
|
+
["evidences/", path9.join(worktree, "evidences")],
|
|
15228
|
+
["docs/scrs/", path9.join(worktree, "docs", "scrs")],
|
|
15229
|
+
["codemap.yml", path9.join(worktree, "codemap.yml")],
|
|
15230
|
+
["codemap.yaml", path9.join(worktree, "codemap.yaml")]
|
|
14963
15231
|
];
|
|
14964
|
-
return artifacts.map(([display, artifactPath]) => ({ display, path: artifactPath })).filter((item) =>
|
|
15232
|
+
return artifacts.map(([display, artifactPath]) => ({ display, path: artifactPath })).filter((item) => fs9.existsSync(item.path));
|
|
14965
15233
|
}
|
|
14966
15234
|
function hasSanitizableOverrides(worktree) {
|
|
14967
15235
|
return collectPlannedOverrideFiles(worktree).length > 0;
|
|
@@ -14987,7 +15255,7 @@ function planOptimaMigration(worktree, dryRun) {
|
|
|
14987
15255
|
...overrides.map((file) => ({
|
|
14988
15256
|
category: "override",
|
|
14989
15257
|
action: dryRun ? "would_remove_after_backup" : "removed_after_backup",
|
|
14990
|
-
path:
|
|
15258
|
+
path: path9.relative(worktree, file).split(path9.sep).join("/"),
|
|
14991
15259
|
detail: "Remove implicit agent/policy override after host-level backup."
|
|
14992
15260
|
}))
|
|
14993
15261
|
],
|
|
@@ -15008,12 +15276,12 @@ async function sanitizeHost(options = {}) {
|
|
|
15008
15276
|
const repoStates = [];
|
|
15009
15277
|
for (const candidate of paths) {
|
|
15010
15278
|
try {
|
|
15011
|
-
if (!
|
|
15279
|
+
if (!fs9.existsSync(candidate)) {
|
|
15012
15280
|
report.totals.skipped += 1;
|
|
15013
15281
|
report.repos.push({ path: candidate, status: "skipped", reason: "missing" });
|
|
15014
15282
|
continue;
|
|
15015
15283
|
}
|
|
15016
|
-
if (!
|
|
15284
|
+
if (!fs9.statSync(candidate).isDirectory()) {
|
|
15017
15285
|
report.totals.skipped += 1;
|
|
15018
15286
|
report.repos.push({ path: candidate, status: "skipped", reason: "not_directory" });
|
|
15019
15287
|
continue;
|
|
@@ -15035,7 +15303,7 @@ async function sanitizeHost(options = {}) {
|
|
|
15035
15303
|
const overrides = repoStates.flatMap((state) => state.overrideFiles.map((file) => ({ repo: state.repo, file })));
|
|
15036
15304
|
report.totals.overrideFiles = overrides.length;
|
|
15037
15305
|
if (!dryRun && overrides.length > 0) {
|
|
15038
|
-
const backupPath =
|
|
15306
|
+
const backupPath = path9.join(os3.homedir(), BACKUP_DIRNAME, `${timestamp()}.zip`);
|
|
15039
15307
|
try {
|
|
15040
15308
|
createZipBackup(overrides.map((item) => item.file), backupPath);
|
|
15041
15309
|
report.backupPath = backupPath;
|
|
@@ -15051,7 +15319,7 @@ async function sanitizeHost(options = {}) {
|
|
|
15051
15319
|
for (const state of repoStates) {
|
|
15052
15320
|
try {
|
|
15053
15321
|
if (!dryRun) {
|
|
15054
|
-
for (const file of state.overrideFiles)
|
|
15322
|
+
for (const file of state.overrideFiles) fs9.rmSync(file, { force: true });
|
|
15055
15323
|
}
|
|
15056
15324
|
const validation = !dryRun && hasLegacyMarker(state.repo) ? await optima_validate_logic(state.repo) : null;
|
|
15057
15325
|
const status = (!validation || validation.ok) && state.plan.unresolved.length === 0 ? "repaired" : "failed";
|
|
@@ -15064,7 +15332,7 @@ async function sanitizeHost(options = {}) {
|
|
|
15064
15332
|
repairActions: state.plan.actions.length,
|
|
15065
15333
|
repairSkipped: state.plan.skippedRepair === true,
|
|
15066
15334
|
overrideFiles: state.overrideFiles.length,
|
|
15067
|
-
overridePaths: state.overrideFiles.map((file) =>
|
|
15335
|
+
overridePaths: state.overrideFiles.map((file) => path9.relative(state.repo, file).split(path9.sep).join("/")),
|
|
15068
15336
|
validation: validation ? validationStatus(validation) : "skipped",
|
|
15069
15337
|
unresolved: state.plan.unresolved
|
|
15070
15338
|
});
|