@defend-tech/opencode-optima 0.1.54 → 0.1.56
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 -0
- package/Agents_Common.prompt.md +2 -0
- package/README.md +10 -0
- package/assets/agents/business_analyst.md +10 -5
- package/assets/agents/developer.md +9 -2
- package/assets/agents/ops_product_manager.md +7 -7
- package/assets/agents/product_manager.md +12 -4
- package/assets/agents/qa_engineer.md +8 -0
- package/assets/agents/tech_lead.md +8 -0
- package/assets/agents/technical_architect.md +8 -1
- package/assets/agents/ui_ux_designer.md +8 -0
- package/assets/agents/workflow_product_manager.md +20 -20
- package/assets/agents/workflow_runner.md +9 -1
- package/dist/index.js +1440 -621
- package/dist/sanitize_cli.js +1473 -654
- package/docs/guides/AGENTS.md +3 -3
- package/docs/guides/TOOLS.md +143 -1
- package/docs/setup/CONFIGURATION.md +4 -0
- package/package.json +2 -2
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, path7) {
|
|
113
|
+
const ctrl = callVisitor(key, node, visitor, path7);
|
|
114
114
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
115
|
-
replaceNode(key,
|
|
116
|
-
return visit_(key, ctrl, visitor,
|
|
115
|
+
replaceNode(key, path7, ctrl);
|
|
116
|
+
return visit_(key, ctrl, visitor, path7);
|
|
117
117
|
}
|
|
118
118
|
if (typeof ctrl !== "symbol") {
|
|
119
119
|
if (identity.isCollection(node)) {
|
|
120
|
-
|
|
120
|
+
path7 = Object.freeze(path7.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, path7);
|
|
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
|
+
path7 = Object.freeze(path7.concat(node));
|
|
134
|
+
const ck = visit_("key", node.key, visitor, path7);
|
|
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, path7);
|
|
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, path7) {
|
|
161
|
+
const ctrl = await callVisitor(key, node, visitor, path7);
|
|
162
162
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
163
|
-
replaceNode(key,
|
|
164
|
-
return visitAsync_(key, ctrl, visitor,
|
|
163
|
+
replaceNode(key, path7, ctrl);
|
|
164
|
+
return visitAsync_(key, ctrl, visitor, path7);
|
|
165
165
|
}
|
|
166
166
|
if (typeof ctrl !== "symbol") {
|
|
167
167
|
if (identity.isCollection(node)) {
|
|
168
|
-
|
|
168
|
+
path7 = Object.freeze(path7.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, path7);
|
|
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
|
+
path7 = Object.freeze(path7.concat(node));
|
|
182
|
+
const ck = await visitAsync_("key", node.key, visitor, path7);
|
|
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, path7);
|
|
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, path7) {
|
|
215
215
|
if (typeof visitor === "function")
|
|
216
|
-
return visitor(key, node,
|
|
216
|
+
return visitor(key, node, path7);
|
|
217
217
|
if (identity.isMap(node))
|
|
218
|
-
return visitor.Map?.(key, node,
|
|
218
|
+
return visitor.Map?.(key, node, path7);
|
|
219
219
|
if (identity.isSeq(node))
|
|
220
|
-
return visitor.Seq?.(key, node,
|
|
220
|
+
return visitor.Seq?.(key, node, path7);
|
|
221
221
|
if (identity.isPair(node))
|
|
222
|
-
return visitor.Pair?.(key, node,
|
|
222
|
+
return visitor.Pair?.(key, node, path7);
|
|
223
223
|
if (identity.isScalar(node))
|
|
224
|
-
return visitor.Scalar?.(key, node,
|
|
224
|
+
return visitor.Scalar?.(key, node, path7);
|
|
225
225
|
if (identity.isAlias(node))
|
|
226
|
-
return visitor.Alias?.(key, node,
|
|
226
|
+
return visitor.Alias?.(key, node, path7);
|
|
227
227
|
return void 0;
|
|
228
228
|
}
|
|
229
|
-
function replaceNode(key,
|
|
230
|
-
const parent =
|
|
229
|
+
function replaceNode(key, path7, node) {
|
|
230
|
+
const parent = path7[path7.length - 1];
|
|
231
231
|
if (identity.isCollection(parent)) {
|
|
232
232
|
parent.items[key] = node;
|
|
233
233
|
} else if (identity.isPair(parent)) {
|
|
@@ -633,6 +633,8 @@ var require_Alias = __commonJS({
|
|
|
633
633
|
* instance of the `source` anchor before this node.
|
|
634
634
|
*/
|
|
635
635
|
resolve(doc, ctx) {
|
|
636
|
+
if (ctx?.maxAliasCount === 0)
|
|
637
|
+
throw new ReferenceError("Alias resolution is disabled");
|
|
636
638
|
let nodes;
|
|
637
639
|
if (ctx?.aliasResolveCache) {
|
|
638
640
|
nodes = ctx.aliasResolveCache;
|
|
@@ -835,10 +837,10 @@ var require_Collection = __commonJS({
|
|
|
835
837
|
var createNode = require_createNode();
|
|
836
838
|
var identity = require_identity();
|
|
837
839
|
var Node = require_Node();
|
|
838
|
-
function collectionFromPath(schema,
|
|
840
|
+
function collectionFromPath(schema, path7, value) {
|
|
839
841
|
let v = value;
|
|
840
|
-
for (let i =
|
|
841
|
-
const k =
|
|
842
|
+
for (let i = path7.length - 1; i >= 0; --i) {
|
|
843
|
+
const k = path7[i];
|
|
842
844
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
843
845
|
const a = [];
|
|
844
846
|
a[k] = v;
|
|
@@ -857,7 +859,7 @@ var require_Collection = __commonJS({
|
|
|
857
859
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
858
860
|
});
|
|
859
861
|
}
|
|
860
|
-
var isEmptyPath = (
|
|
862
|
+
var isEmptyPath = (path7) => path7 == null || typeof path7 === "object" && !!path7[Symbol.iterator]().next().done;
|
|
861
863
|
var Collection = class extends Node.NodeBase {
|
|
862
864
|
constructor(type, schema) {
|
|
863
865
|
super(type);
|
|
@@ -887,11 +889,11 @@ var require_Collection = __commonJS({
|
|
|
887
889
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
888
890
|
* that already exists in the map.
|
|
889
891
|
*/
|
|
890
|
-
addIn(
|
|
891
|
-
if (isEmptyPath(
|
|
892
|
+
addIn(path7, value) {
|
|
893
|
+
if (isEmptyPath(path7))
|
|
892
894
|
this.add(value);
|
|
893
895
|
else {
|
|
894
|
-
const [key, ...rest] =
|
|
896
|
+
const [key, ...rest] = path7;
|
|
895
897
|
const node = this.get(key, true);
|
|
896
898
|
if (identity.isCollection(node))
|
|
897
899
|
node.addIn(rest, value);
|
|
@@ -905,8 +907,8 @@ var require_Collection = __commonJS({
|
|
|
905
907
|
* Removes a value from the collection.
|
|
906
908
|
* @returns `true` if the item was found and removed.
|
|
907
909
|
*/
|
|
908
|
-
deleteIn(
|
|
909
|
-
const [key, ...rest] =
|
|
910
|
+
deleteIn(path7) {
|
|
911
|
+
const [key, ...rest] = path7;
|
|
910
912
|
if (rest.length === 0)
|
|
911
913
|
return this.delete(key);
|
|
912
914
|
const node = this.get(key, true);
|
|
@@ -920,8 +922,8 @@ var require_Collection = __commonJS({
|
|
|
920
922
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
921
923
|
* `true` (collections are always returned intact).
|
|
922
924
|
*/
|
|
923
|
-
getIn(
|
|
924
|
-
const [key, ...rest] =
|
|
925
|
+
getIn(path7, keepScalar) {
|
|
926
|
+
const [key, ...rest] = path7;
|
|
925
927
|
const node = this.get(key, true);
|
|
926
928
|
if (rest.length === 0)
|
|
927
929
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -939,8 +941,8 @@ var require_Collection = __commonJS({
|
|
|
939
941
|
/**
|
|
940
942
|
* Checks if the collection includes a value with the key `key`.
|
|
941
943
|
*/
|
|
942
|
-
hasIn(
|
|
943
|
-
const [key, ...rest] =
|
|
944
|
+
hasIn(path7) {
|
|
945
|
+
const [key, ...rest] = path7;
|
|
944
946
|
if (rest.length === 0)
|
|
945
947
|
return this.has(key);
|
|
946
948
|
const node = this.get(key, true);
|
|
@@ -950,8 +952,8 @@ var require_Collection = __commonJS({
|
|
|
950
952
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
951
953
|
* boolean to add/remove the item from the set.
|
|
952
954
|
*/
|
|
953
|
-
setIn(
|
|
954
|
-
const [key, ...rest] =
|
|
955
|
+
setIn(path7, value) {
|
|
956
|
+
const [key, ...rest] = path7;
|
|
955
957
|
if (rest.length === 0) {
|
|
956
958
|
this.set(key, value);
|
|
957
959
|
} else {
|
|
@@ -1432,6 +1434,7 @@ var require_stringify = __commonJS({
|
|
|
1432
1434
|
nullStr: "null",
|
|
1433
1435
|
simpleKeys: false,
|
|
1434
1436
|
singleQuote: null,
|
|
1437
|
+
trailingComma: false,
|
|
1435
1438
|
trueStr: "true",
|
|
1436
1439
|
verifyAliasOrder: true
|
|
1437
1440
|
}, doc.schema.toStringOptions, options);
|
|
@@ -1704,18 +1707,18 @@ var require_merge = __commonJS({
|
|
|
1704
1707
|
};
|
|
1705
1708
|
var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default);
|
|
1706
1709
|
function addMergeToJSMap(ctx, map, value) {
|
|
1707
|
-
|
|
1708
|
-
if (identity.isSeq(
|
|
1709
|
-
for (const it of
|
|
1710
|
+
const source = resolveAliasValue(ctx, value);
|
|
1711
|
+
if (identity.isSeq(source))
|
|
1712
|
+
for (const it of source.items)
|
|
1710
1713
|
mergeValue(ctx, map, it);
|
|
1711
|
-
else if (Array.isArray(
|
|
1712
|
-
for (const it of
|
|
1714
|
+
else if (Array.isArray(source))
|
|
1715
|
+
for (const it of source)
|
|
1713
1716
|
mergeValue(ctx, map, it);
|
|
1714
1717
|
else
|
|
1715
|
-
mergeValue(ctx, map,
|
|
1718
|
+
mergeValue(ctx, map, source);
|
|
1716
1719
|
}
|
|
1717
1720
|
function mergeValue(ctx, map, value) {
|
|
1718
|
-
const source = ctx
|
|
1721
|
+
const source = resolveAliasValue(ctx, value);
|
|
1719
1722
|
if (!identity.isMap(source))
|
|
1720
1723
|
throw new Error("Merge sources must be maps or map aliases");
|
|
1721
1724
|
const srcMap = source.toJSON(null, ctx, Map);
|
|
@@ -1736,6 +1739,9 @@ var require_merge = __commonJS({
|
|
|
1736
1739
|
}
|
|
1737
1740
|
return map;
|
|
1738
1741
|
}
|
|
1742
|
+
function resolveAliasValue(ctx, value) {
|
|
1743
|
+
return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
|
|
1744
|
+
}
|
|
1739
1745
|
exports.addMergeToJSMap = addMergeToJSMap;
|
|
1740
1746
|
exports.isMergeKey = isMergeKey;
|
|
1741
1747
|
exports.merge = merge;
|
|
@@ -1949,12 +1955,19 @@ ${indent}${line}` : "\n";
|
|
|
1949
1955
|
if (comment)
|
|
1950
1956
|
reqNewline = true;
|
|
1951
1957
|
let str = stringify.stringify(item, itemCtx, () => comment = null);
|
|
1952
|
-
|
|
1958
|
+
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
1959
|
+
if (i < items.length - 1) {
|
|
1953
1960
|
str += ",";
|
|
1961
|
+
} else if (ctx.options.trailingComma) {
|
|
1962
|
+
if (ctx.options.lineWidth > 0) {
|
|
1963
|
+
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
1964
|
+
}
|
|
1965
|
+
if (reqNewline) {
|
|
1966
|
+
str += ",";
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1954
1969
|
if (comment)
|
|
1955
1970
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
1956
|
-
if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
|
|
1957
|
-
reqNewline = true;
|
|
1958
1971
|
lines.push(str);
|
|
1959
1972
|
linesAtValue = lines.length;
|
|
1960
1973
|
}
|
|
@@ -2366,7 +2379,7 @@ var require_stringifyNumber = __commonJS({
|
|
|
2366
2379
|
if (!isFinite(num))
|
|
2367
2380
|
return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
|
|
2368
2381
|
let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
2369
|
-
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") &&
|
|
2382
|
+
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
|
|
2370
2383
|
let i = n.indexOf(".");
|
|
2371
2384
|
if (i < 0) {
|
|
2372
2385
|
i = n.length;
|
|
@@ -3455,9 +3468,9 @@ var require_Document = __commonJS({
|
|
|
3455
3468
|
this.contents.add(value);
|
|
3456
3469
|
}
|
|
3457
3470
|
/** Adds a value to the document. */
|
|
3458
|
-
addIn(
|
|
3471
|
+
addIn(path7, value) {
|
|
3459
3472
|
if (assertCollection(this.contents))
|
|
3460
|
-
this.contents.addIn(
|
|
3473
|
+
this.contents.addIn(path7, value);
|
|
3461
3474
|
}
|
|
3462
3475
|
/**
|
|
3463
3476
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -3532,14 +3545,14 @@ var require_Document = __commonJS({
|
|
|
3532
3545
|
* Removes a value from the document.
|
|
3533
3546
|
* @returns `true` if the item was found and removed.
|
|
3534
3547
|
*/
|
|
3535
|
-
deleteIn(
|
|
3536
|
-
if (Collection.isEmptyPath(
|
|
3548
|
+
deleteIn(path7) {
|
|
3549
|
+
if (Collection.isEmptyPath(path7)) {
|
|
3537
3550
|
if (this.contents == null)
|
|
3538
3551
|
return false;
|
|
3539
3552
|
this.contents = null;
|
|
3540
3553
|
return true;
|
|
3541
3554
|
}
|
|
3542
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
3555
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path7) : false;
|
|
3543
3556
|
}
|
|
3544
3557
|
/**
|
|
3545
3558
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -3554,10 +3567,10 @@ var require_Document = __commonJS({
|
|
|
3554
3567
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
3555
3568
|
* `true` (collections are always returned intact).
|
|
3556
3569
|
*/
|
|
3557
|
-
getIn(
|
|
3558
|
-
if (Collection.isEmptyPath(
|
|
3570
|
+
getIn(path7, keepScalar) {
|
|
3571
|
+
if (Collection.isEmptyPath(path7))
|
|
3559
3572
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
3560
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
3573
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path7, keepScalar) : void 0;
|
|
3561
3574
|
}
|
|
3562
3575
|
/**
|
|
3563
3576
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -3568,10 +3581,10 @@ var require_Document = __commonJS({
|
|
|
3568
3581
|
/**
|
|
3569
3582
|
* Checks if the document includes a value at `path`.
|
|
3570
3583
|
*/
|
|
3571
|
-
hasIn(
|
|
3572
|
-
if (Collection.isEmptyPath(
|
|
3584
|
+
hasIn(path7) {
|
|
3585
|
+
if (Collection.isEmptyPath(path7))
|
|
3573
3586
|
return this.contents !== void 0;
|
|
3574
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
3587
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path7) : false;
|
|
3575
3588
|
}
|
|
3576
3589
|
/**
|
|
3577
3590
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -3588,13 +3601,13 @@ var require_Document = __commonJS({
|
|
|
3588
3601
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
3589
3602
|
* boolean to add/remove the item from the set.
|
|
3590
3603
|
*/
|
|
3591
|
-
setIn(
|
|
3592
|
-
if (Collection.isEmptyPath(
|
|
3604
|
+
setIn(path7, value) {
|
|
3605
|
+
if (Collection.isEmptyPath(path7)) {
|
|
3593
3606
|
this.contents = value;
|
|
3594
3607
|
} else if (this.contents == null) {
|
|
3595
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
3608
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path7), value);
|
|
3596
3609
|
} else if (assertCollection(this.contents)) {
|
|
3597
|
-
this.contents.setIn(
|
|
3610
|
+
this.contents.setIn(path7, value);
|
|
3598
3611
|
}
|
|
3599
3612
|
}
|
|
3600
3613
|
/**
|
|
@@ -4738,7 +4751,7 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4738
4751
|
while (next === " " || next === " ")
|
|
4739
4752
|
next = source[++i + 1];
|
|
4740
4753
|
} else if (next === "x" || next === "u" || next === "U") {
|
|
4741
|
-
const length =
|
|
4754
|
+
const length = next === "x" ? 2 : next === "u" ? 4 : 8;
|
|
4742
4755
|
res += parseCharCode(source, i + 1, length, onError);
|
|
4743
4756
|
i += length;
|
|
4744
4757
|
} else {
|
|
@@ -4813,12 +4826,13 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4813
4826
|
const cc = source.substr(offset, length);
|
|
4814
4827
|
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
|
|
4815
4828
|
const code = ok ? parseInt(cc, 16) : NaN;
|
|
4816
|
-
|
|
4829
|
+
try {
|
|
4830
|
+
return String.fromCodePoint(code);
|
|
4831
|
+
} catch {
|
|
4817
4832
|
const raw = source.substr(offset - 2, length + 2);
|
|
4818
4833
|
onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
|
|
4819
4834
|
return raw;
|
|
4820
4835
|
}
|
|
4821
|
-
return String.fromCodePoint(code);
|
|
4822
4836
|
}
|
|
4823
4837
|
exports.resolveFlowScalar = resolveFlowScalar;
|
|
4824
4838
|
}
|
|
@@ -4968,17 +4982,22 @@ var require_compose_node = __commonJS({
|
|
|
4968
4982
|
case "block-map":
|
|
4969
4983
|
case "block-seq":
|
|
4970
4984
|
case "flow-collection":
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4985
|
+
try {
|
|
4986
|
+
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
|
|
4987
|
+
if (anchor)
|
|
4988
|
+
node.anchor = anchor.source.substring(1);
|
|
4989
|
+
} catch (error) {
|
|
4990
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4991
|
+
onError(token, "RESOURCE_EXHAUSTION", message);
|
|
4992
|
+
}
|
|
4974
4993
|
break;
|
|
4975
4994
|
default: {
|
|
4976
4995
|
const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
|
|
4977
4996
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
4978
|
-
node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
|
|
4979
4997
|
isSrcToken = false;
|
|
4980
4998
|
}
|
|
4981
4999
|
}
|
|
5000
|
+
node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
|
|
4982
5001
|
if (anchor && node.anchor === "")
|
|
4983
5002
|
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
|
|
4984
5003
|
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
|
|
@@ -5163,8 +5182,10 @@ ${cb}` : comment;
|
|
|
5163
5182
|
}
|
|
5164
5183
|
}
|
|
5165
5184
|
if (afterDoc) {
|
|
5166
|
-
|
|
5167
|
-
|
|
5185
|
+
for (let i = 0; i < this.errors.length; ++i)
|
|
5186
|
+
doc.errors.push(this.errors[i]);
|
|
5187
|
+
for (let i = 0; i < this.warnings.length; ++i)
|
|
5188
|
+
doc.warnings.push(this.warnings[i]);
|
|
5168
5189
|
} else {
|
|
5169
5190
|
doc.errors = this.errors;
|
|
5170
5191
|
doc.warnings = this.warnings;
|
|
@@ -5546,9 +5567,9 @@ var require_cst_visit = __commonJS({
|
|
|
5546
5567
|
visit.BREAK = BREAK;
|
|
5547
5568
|
visit.SKIP = SKIP;
|
|
5548
5569
|
visit.REMOVE = REMOVE;
|
|
5549
|
-
visit.itemAtPath = (cst,
|
|
5570
|
+
visit.itemAtPath = (cst, path7) => {
|
|
5550
5571
|
let item = cst;
|
|
5551
|
-
for (const [field, index] of
|
|
5572
|
+
for (const [field, index] of path7) {
|
|
5552
5573
|
const tok = item?.[field];
|
|
5553
5574
|
if (tok && "items" in tok) {
|
|
5554
5575
|
item = tok.items[index];
|
|
@@ -5557,23 +5578,23 @@ var require_cst_visit = __commonJS({
|
|
|
5557
5578
|
}
|
|
5558
5579
|
return item;
|
|
5559
5580
|
};
|
|
5560
|
-
visit.parentCollection = (cst,
|
|
5561
|
-
const parent = visit.itemAtPath(cst,
|
|
5562
|
-
const field =
|
|
5581
|
+
visit.parentCollection = (cst, path7) => {
|
|
5582
|
+
const parent = visit.itemAtPath(cst, path7.slice(0, -1));
|
|
5583
|
+
const field = path7[path7.length - 1][0];
|
|
5563
5584
|
const coll = parent?.[field];
|
|
5564
5585
|
if (coll && "items" in coll)
|
|
5565
5586
|
return coll;
|
|
5566
5587
|
throw new Error("Parent collection not found");
|
|
5567
5588
|
};
|
|
5568
|
-
function _visit(
|
|
5569
|
-
let ctrl = visitor(item,
|
|
5589
|
+
function _visit(path7, item, visitor) {
|
|
5590
|
+
let ctrl = visitor(item, path7);
|
|
5570
5591
|
if (typeof ctrl === "symbol")
|
|
5571
5592
|
return ctrl;
|
|
5572
5593
|
for (const field of ["key", "value"]) {
|
|
5573
5594
|
const token = item[field];
|
|
5574
5595
|
if (token && "items" in token) {
|
|
5575
5596
|
for (let i = 0; i < token.items.length; ++i) {
|
|
5576
|
-
const ci = _visit(Object.freeze(
|
|
5597
|
+
const ci = _visit(Object.freeze(path7.concat([[field, i]])), token.items[i], visitor);
|
|
5577
5598
|
if (typeof ci === "number")
|
|
5578
5599
|
i = ci - 1;
|
|
5579
5600
|
else if (ci === BREAK)
|
|
@@ -5584,10 +5605,10 @@ var require_cst_visit = __commonJS({
|
|
|
5584
5605
|
}
|
|
5585
5606
|
}
|
|
5586
5607
|
if (typeof ctrl === "function" && field === "key")
|
|
5587
|
-
ctrl = ctrl(item,
|
|
5608
|
+
ctrl = ctrl(item, path7);
|
|
5588
5609
|
}
|
|
5589
5610
|
}
|
|
5590
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5611
|
+
return typeof ctrl === "function" ? ctrl(item, path7) : ctrl;
|
|
5591
5612
|
}
|
|
5592
5613
|
exports.visit = visit;
|
|
5593
5614
|
}
|
|
@@ -5897,7 +5918,7 @@ var require_lexer = __commonJS({
|
|
|
5897
5918
|
const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
|
|
5898
5919
|
this.indentNext = this.indentValue + 1;
|
|
5899
5920
|
this.indentValue += n;
|
|
5900
|
-
return
|
|
5921
|
+
return "block-start";
|
|
5901
5922
|
}
|
|
5902
5923
|
return "doc";
|
|
5903
5924
|
}
|
|
@@ -6196,28 +6217,38 @@ var require_lexer = __commonJS({
|
|
|
6196
6217
|
return 0;
|
|
6197
6218
|
}
|
|
6198
6219
|
*pushIndicators() {
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6220
|
+
let n = 0;
|
|
6221
|
+
loop: while (true) {
|
|
6222
|
+
switch (this.charAt(0)) {
|
|
6223
|
+
case "!":
|
|
6224
|
+
n += yield* this.pushTag();
|
|
6225
|
+
n += yield* this.pushSpaces(true);
|
|
6226
|
+
continue loop;
|
|
6227
|
+
case "&":
|
|
6228
|
+
n += yield* this.pushUntil(isNotAnchorChar);
|
|
6229
|
+
n += yield* this.pushSpaces(true);
|
|
6230
|
+
continue loop;
|
|
6231
|
+
case "-":
|
|
6232
|
+
// this is an error
|
|
6233
|
+
case "?":
|
|
6234
|
+
// this is an error outside flow collections
|
|
6235
|
+
case ":": {
|
|
6236
|
+
const inFlow = this.flowLevel > 0;
|
|
6237
|
+
const ch1 = this.charAt(1);
|
|
6238
|
+
if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
|
|
6239
|
+
if (!inFlow)
|
|
6240
|
+
this.indentNext = this.indentValue + 1;
|
|
6241
|
+
else if (this.flowKey)
|
|
6242
|
+
this.flowKey = false;
|
|
6243
|
+
n += yield* this.pushCount(1);
|
|
6244
|
+
n += yield* this.pushSpaces(true);
|
|
6245
|
+
continue loop;
|
|
6246
|
+
}
|
|
6217
6247
|
}
|
|
6218
6248
|
}
|
|
6249
|
+
break loop;
|
|
6219
6250
|
}
|
|
6220
|
-
return
|
|
6251
|
+
return n;
|
|
6221
6252
|
}
|
|
6222
6253
|
*pushTag() {
|
|
6223
6254
|
if (this.charAt(1) === "<") {
|
|
@@ -6376,6 +6407,13 @@ var require_parser = __commonJS({
|
|
|
6376
6407
|
}
|
|
6377
6408
|
return prev.splice(i, prev.length);
|
|
6378
6409
|
}
|
|
6410
|
+
function arrayPushArray(target, source) {
|
|
6411
|
+
if (source.length < 1e5)
|
|
6412
|
+
Array.prototype.push.apply(target, source);
|
|
6413
|
+
else
|
|
6414
|
+
for (let i = 0; i < source.length; ++i)
|
|
6415
|
+
target.push(source[i]);
|
|
6416
|
+
}
|
|
6379
6417
|
function fixFlowSeqItems(fc) {
|
|
6380
6418
|
if (fc.start.type === "flow-seq-start") {
|
|
6381
6419
|
for (const it of fc.items) {
|
|
@@ -6385,11 +6423,11 @@ var require_parser = __commonJS({
|
|
|
6385
6423
|
delete it.key;
|
|
6386
6424
|
if (isFlowToken(it.value)) {
|
|
6387
6425
|
if (it.value.end)
|
|
6388
|
-
|
|
6426
|
+
arrayPushArray(it.value.end, it.sep);
|
|
6389
6427
|
else
|
|
6390
6428
|
it.value.end = it.sep;
|
|
6391
6429
|
} else
|
|
6392
|
-
|
|
6430
|
+
arrayPushArray(it.start, it.sep);
|
|
6393
6431
|
delete it.sep;
|
|
6394
6432
|
}
|
|
6395
6433
|
}
|
|
@@ -6744,7 +6782,7 @@ var require_parser = __commonJS({
|
|
|
6744
6782
|
const prev = map.items[map.items.length - 2];
|
|
6745
6783
|
const end = prev?.value?.end;
|
|
6746
6784
|
if (Array.isArray(end)) {
|
|
6747
|
-
|
|
6785
|
+
arrayPushArray(end, it.start);
|
|
6748
6786
|
end.push(this.sourceToken);
|
|
6749
6787
|
map.items.pop();
|
|
6750
6788
|
return;
|
|
@@ -6872,14 +6910,14 @@ var require_parser = __commonJS({
|
|
|
6872
6910
|
case "scalar":
|
|
6873
6911
|
case "single-quoted-scalar":
|
|
6874
6912
|
case "double-quoted-scalar": {
|
|
6875
|
-
const
|
|
6913
|
+
const fs7 = this.flowScalar(this.type);
|
|
6876
6914
|
if (atNextItem || it.value) {
|
|
6877
|
-
map.items.push({ start, key:
|
|
6915
|
+
map.items.push({ start, key: fs7, sep: [] });
|
|
6878
6916
|
this.onKeyLine = true;
|
|
6879
6917
|
} else if (it.sep) {
|
|
6880
|
-
this.stack.push(
|
|
6918
|
+
this.stack.push(fs7);
|
|
6881
6919
|
} else {
|
|
6882
|
-
Object.assign(it, { key:
|
|
6920
|
+
Object.assign(it, { key: fs7, sep: [] });
|
|
6883
6921
|
this.onKeyLine = true;
|
|
6884
6922
|
}
|
|
6885
6923
|
return;
|
|
@@ -6932,7 +6970,7 @@ var require_parser = __commonJS({
|
|
|
6932
6970
|
const prev = seq.items[seq.items.length - 2];
|
|
6933
6971
|
const end = prev?.value?.end;
|
|
6934
6972
|
if (Array.isArray(end)) {
|
|
6935
|
-
|
|
6973
|
+
arrayPushArray(end, it.start);
|
|
6936
6974
|
end.push(this.sourceToken);
|
|
6937
6975
|
seq.items.pop();
|
|
6938
6976
|
return;
|
|
@@ -7007,13 +7045,13 @@ var require_parser = __commonJS({
|
|
|
7007
7045
|
case "scalar":
|
|
7008
7046
|
case "single-quoted-scalar":
|
|
7009
7047
|
case "double-quoted-scalar": {
|
|
7010
|
-
const
|
|
7048
|
+
const fs7 = this.flowScalar(this.type);
|
|
7011
7049
|
if (!it || it.value)
|
|
7012
|
-
fc.items.push({ start: [], key:
|
|
7050
|
+
fc.items.push({ start: [], key: fs7, sep: [] });
|
|
7013
7051
|
else if (it.sep)
|
|
7014
|
-
this.stack.push(
|
|
7052
|
+
this.stack.push(fs7);
|
|
7015
7053
|
else
|
|
7016
|
-
Object.assign(it, { key:
|
|
7054
|
+
Object.assign(it, { key: fs7, sep: [] });
|
|
7017
7055
|
return;
|
|
7018
7056
|
}
|
|
7019
7057
|
case "flow-map-end":
|
|
@@ -7551,17 +7589,17 @@ var require_ignore = __commonJS({
|
|
|
7551
7589
|
var throwError = (message, Ctor) => {
|
|
7552
7590
|
throw new Ctor(message);
|
|
7553
7591
|
};
|
|
7554
|
-
var checkPath = (
|
|
7555
|
-
if (!isString(
|
|
7592
|
+
var checkPath = (path7, originalPath, doThrow) => {
|
|
7593
|
+
if (!isString(path7)) {
|
|
7556
7594
|
return doThrow(
|
|
7557
7595
|
`path must be a string, but got \`${originalPath}\``,
|
|
7558
7596
|
TypeError
|
|
7559
7597
|
);
|
|
7560
7598
|
}
|
|
7561
|
-
if (!
|
|
7599
|
+
if (!path7) {
|
|
7562
7600
|
return doThrow(`path must not be empty`, TypeError);
|
|
7563
7601
|
}
|
|
7564
|
-
if (checkPath.isNotRelative(
|
|
7602
|
+
if (checkPath.isNotRelative(path7)) {
|
|
7565
7603
|
const r = "`path.relative()`d";
|
|
7566
7604
|
return doThrow(
|
|
7567
7605
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -7570,7 +7608,7 @@ var require_ignore = __commonJS({
|
|
|
7570
7608
|
}
|
|
7571
7609
|
return true;
|
|
7572
7610
|
};
|
|
7573
|
-
var isNotRelative = (
|
|
7611
|
+
var isNotRelative = (path7) => REGEX_TEST_INVALID_PATH.test(path7);
|
|
7574
7612
|
checkPath.isNotRelative = isNotRelative;
|
|
7575
7613
|
checkPath.convert = (p) => p;
|
|
7576
7614
|
var Ignore = class {
|
|
@@ -7629,7 +7667,7 @@ var require_ignore = __commonJS({
|
|
|
7629
7667
|
// setting `checkUnignored` to `false` could reduce additional
|
|
7630
7668
|
// path matching.
|
|
7631
7669
|
// @returns {TestResult} true if a file is ignored
|
|
7632
|
-
_testOne(
|
|
7670
|
+
_testOne(path7, checkUnignored) {
|
|
7633
7671
|
let ignored = false;
|
|
7634
7672
|
let unignored = false;
|
|
7635
7673
|
this._rules.forEach((rule) => {
|
|
@@ -7637,7 +7675,7 @@ var require_ignore = __commonJS({
|
|
|
7637
7675
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
7638
7676
|
return;
|
|
7639
7677
|
}
|
|
7640
|
-
const matched = rule.regex.test(
|
|
7678
|
+
const matched = rule.regex.test(path7);
|
|
7641
7679
|
if (matched) {
|
|
7642
7680
|
ignored = !negative;
|
|
7643
7681
|
unignored = negative;
|
|
@@ -7650,24 +7688,24 @@ var require_ignore = __commonJS({
|
|
|
7650
7688
|
}
|
|
7651
7689
|
// @returns {TestResult}
|
|
7652
7690
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
7653
|
-
const
|
|
7691
|
+
const path7 = originalPath && checkPath.convert(originalPath);
|
|
7654
7692
|
checkPath(
|
|
7655
|
-
|
|
7693
|
+
path7,
|
|
7656
7694
|
originalPath,
|
|
7657
7695
|
this._allowRelativePaths ? RETURN_FALSE : throwError
|
|
7658
7696
|
);
|
|
7659
|
-
return this._t(
|
|
7697
|
+
return this._t(path7, cache, checkUnignored, slices);
|
|
7660
7698
|
}
|
|
7661
|
-
_t(
|
|
7662
|
-
if (
|
|
7663
|
-
return cache[
|
|
7699
|
+
_t(path7, cache, checkUnignored, slices) {
|
|
7700
|
+
if (path7 in cache) {
|
|
7701
|
+
return cache[path7];
|
|
7664
7702
|
}
|
|
7665
7703
|
if (!slices) {
|
|
7666
|
-
slices =
|
|
7704
|
+
slices = path7.split(SLASH);
|
|
7667
7705
|
}
|
|
7668
7706
|
slices.pop();
|
|
7669
7707
|
if (!slices.length) {
|
|
7670
|
-
return cache[
|
|
7708
|
+
return cache[path7] = this._testOne(path7, checkUnignored);
|
|
7671
7709
|
}
|
|
7672
7710
|
const parent = this._t(
|
|
7673
7711
|
slices.join(SLASH) + SLASH,
|
|
@@ -7675,24 +7713,24 @@ var require_ignore = __commonJS({
|
|
|
7675
7713
|
checkUnignored,
|
|
7676
7714
|
slices
|
|
7677
7715
|
);
|
|
7678
|
-
return cache[
|
|
7716
|
+
return cache[path7] = parent.ignored ? parent : this._testOne(path7, checkUnignored);
|
|
7679
7717
|
}
|
|
7680
|
-
ignores(
|
|
7681
|
-
return this._test(
|
|
7718
|
+
ignores(path7) {
|
|
7719
|
+
return this._test(path7, this._ignoreCache, false).ignored;
|
|
7682
7720
|
}
|
|
7683
7721
|
createFilter() {
|
|
7684
|
-
return (
|
|
7722
|
+
return (path7) => !this.ignores(path7);
|
|
7685
7723
|
}
|
|
7686
7724
|
filter(paths) {
|
|
7687
7725
|
return makeArray(paths).filter(this.createFilter());
|
|
7688
7726
|
}
|
|
7689
7727
|
// @returns {TestResult}
|
|
7690
|
-
test(
|
|
7691
|
-
return this._test(
|
|
7728
|
+
test(path7) {
|
|
7729
|
+
return this._test(path7, this._testCache, true);
|
|
7692
7730
|
}
|
|
7693
7731
|
};
|
|
7694
7732
|
var factory = (options) => new Ignore(options);
|
|
7695
|
-
var isPathValid = (
|
|
7733
|
+
var isPathValid = (path7) => checkPath(path7 && checkPath.convert(path7), path7, RETURN_FALSE);
|
|
7696
7734
|
factory.isPathValid = isPathValid;
|
|
7697
7735
|
factory.default = factory;
|
|
7698
7736
|
module.exports = factory;
|
|
@@ -7703,44 +7741,632 @@ var require_ignore = __commonJS({
|
|
|
7703
7741
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
7704
7742
|
checkPath.convert = makePosix;
|
|
7705
7743
|
const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
7706
|
-
checkPath.isNotRelative = (
|
|
7744
|
+
checkPath.isNotRelative = (path7) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path7) || isNotRelative(path7);
|
|
7707
7745
|
}
|
|
7708
7746
|
}
|
|
7709
7747
|
});
|
|
7710
7748
|
|
|
7711
7749
|
// src/sanitize_cli.js
|
|
7712
|
-
import
|
|
7750
|
+
import fs6 from "node:fs";
|
|
7713
7751
|
import os2 from "node:os";
|
|
7714
|
-
import
|
|
7752
|
+
import path6 from "node:path";
|
|
7715
7753
|
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
7716
7754
|
|
|
7717
7755
|
// src/index.js
|
|
7718
|
-
var
|
|
7719
|
-
var
|
|
7756
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
7757
|
+
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
7720
7758
|
import crypto from "node:crypto";
|
|
7721
|
-
import
|
|
7759
|
+
import fs5 from "node:fs";
|
|
7722
7760
|
import http from "node:http";
|
|
7723
7761
|
import os from "node:os";
|
|
7724
|
-
import
|
|
7725
|
-
import { execFileSync } from "node:child_process";
|
|
7762
|
+
import path5 from "node:path";
|
|
7726
7763
|
import { fileURLToPath } from "node:url";
|
|
7727
7764
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
7728
7765
|
|
|
7729
|
-
// src/
|
|
7730
|
-
var import_yaml = __toESM(require_dist(), 1);
|
|
7731
|
-
var import_ignore = __toESM(require_ignore(), 1);
|
|
7766
|
+
// src/git_utils.js
|
|
7732
7767
|
import fs from "node:fs";
|
|
7733
7768
|
import path from "node:path";
|
|
7769
|
+
import { execFileSync } from "node:child_process";
|
|
7770
|
+
function toGitPath(worktree, targetPath) {
|
|
7771
|
+
const relativePath = path.relative(worktree, targetPath);
|
|
7772
|
+
if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath)) return null;
|
|
7773
|
+
return relativePath.split(path.sep).join("/");
|
|
7774
|
+
}
|
|
7775
|
+
function runGit(worktree, args) {
|
|
7776
|
+
return execFileSync("git", args, {
|
|
7777
|
+
cwd: worktree,
|
|
7778
|
+
encoding: "utf8",
|
|
7779
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7780
|
+
});
|
|
7781
|
+
}
|
|
7782
|
+
function sameFilesystemPath(left, right) {
|
|
7783
|
+
try {
|
|
7784
|
+
return fs.realpathSync(left) === fs.realpathSync(right);
|
|
7785
|
+
} catch {
|
|
7786
|
+
return path.resolve(left) === path.resolve(right);
|
|
7787
|
+
}
|
|
7788
|
+
}
|
|
7789
|
+
function gitMigrationState(worktree) {
|
|
7790
|
+
try {
|
|
7791
|
+
const root = runGit(worktree, ["rev-parse", "--show-toplevel"]).trim();
|
|
7792
|
+
if (!sameFilesystemPath(root, worktree)) return null;
|
|
7793
|
+
return { worktree };
|
|
7794
|
+
} catch {
|
|
7795
|
+
return null;
|
|
7796
|
+
}
|
|
7797
|
+
}
|
|
7798
|
+
function isGitTracked(gitState, targetPath) {
|
|
7799
|
+
if (!gitState) return false;
|
|
7800
|
+
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
7801
|
+
if (!gitPath) return false;
|
|
7802
|
+
try {
|
|
7803
|
+
runGit(gitState.worktree, ["ls-files", "--error-unmatch", "--", gitPath]);
|
|
7804
|
+
return true;
|
|
7805
|
+
} catch {
|
|
7806
|
+
return false;
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
function hasGitTrackedChildren(gitState, targetPath) {
|
|
7810
|
+
if (!gitState) return false;
|
|
7811
|
+
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
7812
|
+
if (!gitPath) return false;
|
|
7813
|
+
try {
|
|
7814
|
+
return runGit(gitState.worktree, ["ls-files", "--", `${gitPath}/`]).trim().length > 0;
|
|
7815
|
+
} catch {
|
|
7816
|
+
return false;
|
|
7817
|
+
}
|
|
7818
|
+
}
|
|
7819
|
+
function gitAwareMoveIfTracked(sourcePath, destinationPath, gitState) {
|
|
7820
|
+
const sourceIsTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
7821
|
+
if (!gitState || !sourceIsTracked || fs.existsSync(destinationPath)) return false;
|
|
7822
|
+
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
7823
|
+
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
7824
|
+
if (!sourceGitPath || !destinationGitPath) return false;
|
|
7825
|
+
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
7826
|
+
try {
|
|
7827
|
+
runGit(gitState.worktree, ["mv", "--", sourceGitPath, destinationGitPath]);
|
|
7828
|
+
return true;
|
|
7829
|
+
} catch {
|
|
7830
|
+
return false;
|
|
7831
|
+
}
|
|
7832
|
+
}
|
|
7833
|
+
function stageGitAwareMerge(sourcePath, destinationPath, gitState) {
|
|
7834
|
+
if (!gitState) return;
|
|
7835
|
+
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
7836
|
+
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
7837
|
+
if (!sourceGitPath || !destinationGitPath) return;
|
|
7838
|
+
try {
|
|
7839
|
+
runGit(gitState.worktree, ["add", "-A", "--", sourceGitPath, destinationGitPath]);
|
|
7840
|
+
} catch {
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
|
|
7844
|
+
// src/include_resolver.js
|
|
7845
|
+
import fs2 from "node:fs";
|
|
7846
|
+
import path2 from "node:path";
|
|
7847
|
+
function compactPromptPath(filePath) {
|
|
7848
|
+
if (!filePath.endsWith(".md") || filePath.endsWith(".prompt.md")) return null;
|
|
7849
|
+
return filePath.replace(/\.md$/, ".prompt.md");
|
|
7850
|
+
}
|
|
7851
|
+
function isSameOrNestedPath(candidate, root) {
|
|
7852
|
+
if (typeof candidate !== "string" || typeof root !== "string" || !candidate.trim() || !root.trim()) return false;
|
|
7853
|
+
const resolvedCandidate = path2.resolve(candidate);
|
|
7854
|
+
const resolvedRoot = path2.resolve(root);
|
|
7855
|
+
const relative = path2.relative(resolvedRoot, resolvedCandidate);
|
|
7856
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path2.isAbsolute(relative);
|
|
7857
|
+
}
|
|
7858
|
+
function resolveWithin(baseDir, relativePath) {
|
|
7859
|
+
if (!relativePath) return null;
|
|
7860
|
+
if (path2.isAbsolute(relativePath)) return null;
|
|
7861
|
+
const resolved = path2.resolve(baseDir, relativePath);
|
|
7862
|
+
return isSameOrNestedPath(resolved, baseDir) ? resolved : null;
|
|
7863
|
+
}
|
|
7864
|
+
function withCompactPreference(paths, options = {}) {
|
|
7865
|
+
if (!options.preferCompactPromptDocs) return paths;
|
|
7866
|
+
const compactPaths = paths.map((filePath) => filePath ? compactPromptPath(filePath) : null).filter(Boolean);
|
|
7867
|
+
return [...compactPaths, ...paths];
|
|
7868
|
+
}
|
|
7869
|
+
function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
7870
|
+
const trimmed = String(includeRef || "").trim();
|
|
7871
|
+
const scopedMatch = trimmed.match(/^([a-z]+):(.*)$/i);
|
|
7872
|
+
const scope = scopedMatch?.[1]?.toLowerCase();
|
|
7873
|
+
const target = scopedMatch ? scopedMatch[2].trim() : trimmed;
|
|
7874
|
+
const optimaRoot = options.optimaRoot || path2.join(repoRoot, ".optima");
|
|
7875
|
+
const repoPolicyRoot = options.repoPolicyRoot || path2.join(optimaRoot, "policies");
|
|
7876
|
+
const bundlePolicyRoot = options.bundlePolicyRoot || path2.join(bundleRoot, "assets", "policies");
|
|
7877
|
+
if (scope === "plugin") {
|
|
7878
|
+
for (const filePath of withCompactPreference([resolveWithin(bundleRoot, target)], options)) {
|
|
7879
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7880
|
+
}
|
|
7881
|
+
return null;
|
|
7882
|
+
}
|
|
7883
|
+
if (scope === "repo") {
|
|
7884
|
+
for (const filePath of withCompactPreference([resolveWithin(optimaRoot, target)], options)) {
|
|
7885
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7886
|
+
}
|
|
7887
|
+
return null;
|
|
7888
|
+
}
|
|
7889
|
+
if (scope === "policy") {
|
|
7890
|
+
const candidates2 = withCompactPreference([
|
|
7891
|
+
resolveWithin(repoPolicyRoot, target),
|
|
7892
|
+
resolveWithin(bundlePolicyRoot, target)
|
|
7893
|
+
], options);
|
|
7894
|
+
for (const filePath of candidates2) {
|
|
7895
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7896
|
+
}
|
|
7897
|
+
return null;
|
|
7898
|
+
}
|
|
7899
|
+
const candidates = withCompactPreference([
|
|
7900
|
+
resolveWithin(repoRoot, target),
|
|
7901
|
+
resolveWithin(bundleRoot, target)
|
|
7902
|
+
], options);
|
|
7903
|
+
for (const filePath of candidates) {
|
|
7904
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7905
|
+
}
|
|
7906
|
+
return null;
|
|
7907
|
+
}
|
|
7908
|
+
function resolveIncludes(text, repoRoot, bundleRoot, options = {}) {
|
|
7909
|
+
const includeDepth = Number(options.includeDepth || 0);
|
|
7910
|
+
const maxIncludeDepth = Number(options.maxIncludeDepth || 20);
|
|
7911
|
+
if (includeDepth > maxIncludeDepth) {
|
|
7912
|
+
return "\n\n# ERROR: Include recursion limit exceeded.\n\n";
|
|
7913
|
+
}
|
|
7914
|
+
const includeRegex = /<include:(.*?)>/g;
|
|
7915
|
+
return String(text || "").replace(includeRegex, (match, includeRef) => {
|
|
7916
|
+
const filePath = resolveIncludeFile(includeRef, repoRoot, bundleRoot, options);
|
|
7917
|
+
if (!filePath) {
|
|
7918
|
+
console.warn(`[Optima] Include file not found: ${includeRef}`);
|
|
7919
|
+
return `
|
|
7920
|
+
|
|
7921
|
+
# ERROR: Include file not found: ${includeRef}
|
|
7922
|
+
|
|
7923
|
+
`;
|
|
7924
|
+
}
|
|
7925
|
+
const content = fs2.readFileSync(filePath, "utf8");
|
|
7926
|
+
return resolveIncludes(content, repoRoot, bundleRoot, { ...options, includeDepth: includeDepth + 1, maxIncludeDepth });
|
|
7927
|
+
});
|
|
7928
|
+
}
|
|
7929
|
+
|
|
7930
|
+
// src/repair.js
|
|
7931
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
7932
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
7933
|
+
import fs3 from "node:fs";
|
|
7934
|
+
import path3 from "node:path";
|
|
7935
|
+
var CODEMAP_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7936
|
+
".js",
|
|
7937
|
+
".ts",
|
|
7938
|
+
".tsx",
|
|
7939
|
+
".jsx",
|
|
7940
|
+
".dart",
|
|
7941
|
+
".py",
|
|
7942
|
+
".go",
|
|
7943
|
+
".rs",
|
|
7944
|
+
".java",
|
|
7945
|
+
".c",
|
|
7946
|
+
".cpp",
|
|
7947
|
+
".cs",
|
|
7948
|
+
".php",
|
|
7949
|
+
".rb",
|
|
7950
|
+
".swift",
|
|
7951
|
+
".kt",
|
|
7952
|
+
".m",
|
|
7953
|
+
".sh",
|
|
7954
|
+
".sql",
|
|
7955
|
+
".yaml",
|
|
7956
|
+
".yml",
|
|
7957
|
+
".json",
|
|
7958
|
+
".md"
|
|
7959
|
+
]);
|
|
7960
|
+
var LEGACY_OPERATIONAL_ROOTS = /* @__PURE__ */ new Set([
|
|
7961
|
+
"tasks",
|
|
7962
|
+
"evidences",
|
|
7963
|
+
".orbita",
|
|
7964
|
+
".staticeng",
|
|
7965
|
+
".nomadworks",
|
|
7966
|
+
".nomadwork",
|
|
7967
|
+
"nomadworks",
|
|
7968
|
+
".codenomad"
|
|
7969
|
+
]);
|
|
7970
|
+
var OPTIMA_OPERATIONAL_FOLDERS = [".optima", "templates", "dist"];
|
|
7971
|
+
function relPath(worktree, targetPath) {
|
|
7972
|
+
return path3.relative(worktree, targetPath).split(path3.sep).join("/") || ".";
|
|
7973
|
+
}
|
|
7974
|
+
function normalizeCodemapRelPath(relPathValue) {
|
|
7975
|
+
return path3.normalize(relPathValue).replace(/\\/g, "/").replace(/\/$/, "");
|
|
7976
|
+
}
|
|
7977
|
+
function firstPathSegment(relPathValue) {
|
|
7978
|
+
return normalizeCodemapRelPath(relPathValue).split("/").filter(Boolean)[0] || "";
|
|
7979
|
+
}
|
|
7980
|
+
function isOperationalRelPath(relPathValue) {
|
|
7981
|
+
if (!relPathValue) return false;
|
|
7982
|
+
const normalized = normalizeCodemapRelPath(relPathValue);
|
|
7983
|
+
const firstSegment = firstPathSegment(normalized);
|
|
7984
|
+
if (LEGACY_OPERATIONAL_ROOTS.has(firstSegment)) return true;
|
|
7985
|
+
return OPTIMA_OPERATIONAL_FOLDERS.some((folder) => normalized === folder || normalized.startsWith(`${folder}/`));
|
|
7986
|
+
}
|
|
7987
|
+
function isHiddenTree(relPathValue) {
|
|
7988
|
+
if (!relPathValue) return false;
|
|
7989
|
+
return normalizeCodemapRelPath(relPathValue).split("/").some((part) => part.startsWith("."));
|
|
7990
|
+
}
|
|
7991
|
+
function loadGitIgnoreMatcher(worktree) {
|
|
7992
|
+
const ig = (0, import_ignore.default)();
|
|
7993
|
+
ig.add(".git");
|
|
7994
|
+
const gitignorePath = path3.join(worktree, ".gitignore");
|
|
7995
|
+
if (fs3.existsSync(gitignorePath)) ig.add(fs3.readFileSync(gitignorePath, "utf8"));
|
|
7996
|
+
return ig;
|
|
7997
|
+
}
|
|
7998
|
+
function codemapSectionEntries(value) {
|
|
7999
|
+
if (Array.isArray(value)) return value;
|
|
8000
|
+
if (value && typeof value === "object") return Object.values(value);
|
|
8001
|
+
return [];
|
|
8002
|
+
}
|
|
8003
|
+
function codemapIndexedPaths(map) {
|
|
8004
|
+
const indexed = /* @__PURE__ */ new Set();
|
|
8005
|
+
for (const section of ["modules", "entrypoints", "sources_of_truth", "links", "internals"]) {
|
|
8006
|
+
for (const item of codemapSectionEntries(map?.[section])) {
|
|
8007
|
+
if (item?.path) indexed.add(normalizeCodemapRelPath(item.path));
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
return indexed;
|
|
8011
|
+
}
|
|
8012
|
+
function readCodemap(filePath, unresolved, worktree) {
|
|
8013
|
+
try {
|
|
8014
|
+
const parsed = import_yaml.default.parse(fs3.readFileSync(filePath, "utf8"));
|
|
8015
|
+
if (!parsed || typeof parsed !== "object") {
|
|
8016
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, filePath), message: "CodeMap is empty or invalid; manual repair required." });
|
|
8017
|
+
return null;
|
|
8018
|
+
}
|
|
8019
|
+
return parsed;
|
|
8020
|
+
} catch {
|
|
8021
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, filePath), message: "CodeMap has invalid YAML; manual repair required." });
|
|
8022
|
+
return null;
|
|
8023
|
+
}
|
|
8024
|
+
}
|
|
8025
|
+
function writeCodemap(filePath, map) {
|
|
8026
|
+
fs3.writeFileSync(filePath, import_yaml.default.stringify(map), "utf8");
|
|
8027
|
+
}
|
|
8028
|
+
function isIgnoredPath(ig, relativePath) {
|
|
8029
|
+
const normalized = normalizeCodemapRelPath(relativePath);
|
|
8030
|
+
return Boolean(normalized) && ig.ignores(normalized);
|
|
8031
|
+
}
|
|
8032
|
+
function hasImmediateSourceFile(dirPath, ig, worktree) {
|
|
8033
|
+
if (!fs3.existsSync(dirPath)) return false;
|
|
8034
|
+
for (const item of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8035
|
+
const childPath = path3.join(dirPath, item.name);
|
|
8036
|
+
const relative = path3.relative(worktree, childPath);
|
|
8037
|
+
if (isIgnoredPath(ig, relative) || isOperationalRelPath(relative)) continue;
|
|
8038
|
+
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) return true;
|
|
8039
|
+
}
|
|
8040
|
+
return false;
|
|
8041
|
+
}
|
|
8042
|
+
function containsSource(dirPath, ig, worktree) {
|
|
8043
|
+
if (!fs3.existsSync(dirPath)) return false;
|
|
8044
|
+
const dirRelPath = path3.relative(worktree, dirPath);
|
|
8045
|
+
if (isOperationalRelPath(dirRelPath) || isHiddenTree(dirRelPath)) return false;
|
|
8046
|
+
for (const item of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8047
|
+
const childPath = path3.join(dirPath, item.name);
|
|
8048
|
+
const relative = path3.relative(worktree, childPath);
|
|
8049
|
+
if (isIgnoredPath(ig, relative) || isOperationalRelPath(relative) || isHiddenTree(relative)) continue;
|
|
8050
|
+
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) return true;
|
|
8051
|
+
if (item.isDirectory() && containsSource(childPath, ig, worktree)) return true;
|
|
8052
|
+
}
|
|
8053
|
+
return false;
|
|
8054
|
+
}
|
|
8055
|
+
function createModuleCodemap(dirPath, worktree, deps) {
|
|
8056
|
+
const entries = fs3.readdirSync(dirPath, { withFileTypes: true });
|
|
8057
|
+
const entrypoints = [];
|
|
8058
|
+
const internals = [];
|
|
8059
|
+
const relToRoot = path3.relative(dirPath, deps.optimaCodemapPath(worktree)).split(path3.sep).join("/");
|
|
8060
|
+
for (const item of entries) {
|
|
8061
|
+
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) continue;
|
|
8062
|
+
const bucket = /^index\.(js|ts|tsx|jsx)$|^main\.(js|ts|tsx|jsx|py|go|rs|java)$/.test(item.name) ? entrypoints : internals;
|
|
8063
|
+
bucket.push({ path: item.name });
|
|
8064
|
+
}
|
|
8065
|
+
const map = { scope: "module", parent: relToRoot };
|
|
8066
|
+
if (entrypoints.length > 0) map.entrypoints = entrypoints;
|
|
8067
|
+
map.internals = internals;
|
|
8068
|
+
return map;
|
|
8069
|
+
}
|
|
8070
|
+
function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unresolved, deps) {
|
|
8071
|
+
const map = readCodemap(codemapPath, unresolved, worktree);
|
|
8072
|
+
if (!map) return;
|
|
8073
|
+
const isRootMap = codemapPath === deps.optimaCodemapPath(worktree);
|
|
8074
|
+
const mapDir = path3.dirname(codemapPath);
|
|
8075
|
+
const pathBase = isRootMap ? worktree : mapDir;
|
|
8076
|
+
let changed = false;
|
|
8077
|
+
for (const section of ["modules", "entrypoints", "sources_of_truth", "links", "internals"]) {
|
|
8078
|
+
const originalEntries = codemapSectionEntries(map[section]);
|
|
8079
|
+
if (!Array.isArray(map[section]) && originalEntries.length > 0) {
|
|
8080
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, codemapPath), message: `Section '${section}' is object-shaped; repair leaves it unchanged to avoid changing user structure.` });
|
|
8081
|
+
continue;
|
|
8082
|
+
}
|
|
8083
|
+
const nextEntries = [];
|
|
8084
|
+
for (const entry of originalEntries) {
|
|
8085
|
+
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://") || path3.isAbsolute(entry.path)) {
|
|
8086
|
+
nextEntries.push(entry);
|
|
8087
|
+
continue;
|
|
8088
|
+
}
|
|
8089
|
+
const normalizedPath = normalizeCodemapRelPath(entry.path);
|
|
8090
|
+
const absPath = path3.join(pathBase, normalizedPath);
|
|
8091
|
+
if (!fs3.existsSync(absPath)) {
|
|
8092
|
+
registerAction({ category: "codemap", action: apply ? "removed" : "would_remove", path: relPath(worktree, codemapPath), detail: `Remove broken ${section.slice(0, -1)} reference '${entry.path}'.` });
|
|
8093
|
+
changed = true;
|
|
8094
|
+
continue;
|
|
8095
|
+
}
|
|
8096
|
+
const parts = normalizedPath.split("/").filter((part) => part && part !== ".");
|
|
8097
|
+
const maxParts = isRootMap ? 2 : 1;
|
|
8098
|
+
if (parts.length > maxParts) {
|
|
8099
|
+
const localPath = isRootMap ? parts.slice(0, 2).join("/") : parts[0];
|
|
8100
|
+
const localAbs = path3.join(pathBase, localPath);
|
|
8101
|
+
if (fs3.existsSync(localAbs)) {
|
|
8102
|
+
nextEntries.push({ ...entry, path: localPath });
|
|
8103
|
+
changed = true;
|
|
8104
|
+
registerAction({ category: "codemap", action: apply ? "updated" : "would_update", path: relPath(worktree, codemapPath), detail: `Shorten '${entry.path}' to local path '${localPath}'.` });
|
|
8105
|
+
continue;
|
|
8106
|
+
}
|
|
8107
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, codemapPath), message: `Path '${entry.path}' violates Rule of Local Knowledge and no safe local replacement exists.` });
|
|
8108
|
+
nextEntries.push(entry);
|
|
8109
|
+
continue;
|
|
8110
|
+
}
|
|
8111
|
+
nextEntries.push(entry);
|
|
8112
|
+
}
|
|
8113
|
+
if (Array.isArray(map[section])) map[section] = nextEntries;
|
|
8114
|
+
}
|
|
8115
|
+
if (map.scope === "module" && !isOperationalRelPath(path3.relative(worktree, mapDir))) {
|
|
8116
|
+
const indexed = codemapIndexedPaths(map);
|
|
8117
|
+
const internals = Array.isArray(map.internals) ? map.internals : [];
|
|
8118
|
+
for (const item of fs3.readdirSync(mapDir, { withFileTypes: true })) {
|
|
8119
|
+
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) continue;
|
|
8120
|
+
if (indexed.has(item.name)) continue;
|
|
8121
|
+
internals.push({ path: item.name });
|
|
8122
|
+
indexed.add(item.name);
|
|
8123
|
+
changed = true;
|
|
8124
|
+
registerAction({ category: "codemap", action: apply ? "updated" : "would_update", path: relPath(worktree, codemapPath), detail: `Categorize immediate source file '${item.name}' as internal.` });
|
|
8125
|
+
}
|
|
8126
|
+
if (internals.length > 0) map.internals = internals;
|
|
8127
|
+
}
|
|
8128
|
+
if (changed && apply) writeCodemap(codemapPath, map);
|
|
8129
|
+
}
|
|
8130
|
+
function repairCodemaps(worktree, apply, actions, unresolved, deps) {
|
|
8131
|
+
const ig = loadGitIgnoreMatcher(worktree);
|
|
8132
|
+
const rootCodemapPath = deps.optimaCodemapPath(worktree);
|
|
8133
|
+
const rootCodemapMissing = !fs3.existsSync(rootCodemapPath);
|
|
8134
|
+
if (rootCodemapMissing) {
|
|
8135
|
+
actions.push({ category: "codemap", action: apply ? "created" : "would_create", path: ".optima/codemap.yml", detail: "Create missing root CodeMap from template." });
|
|
8136
|
+
if (apply) deps.scaffoldOptimaRootCodemap(worktree);
|
|
8137
|
+
}
|
|
8138
|
+
if (fs3.existsSync(rootCodemapPath)) repairSingleCodemap(rootCodemapPath, worktree, apply, (action) => actions.push(action), unresolved, deps);
|
|
8139
|
+
const rootMap = fs3.existsSync(rootCodemapPath) ? readCodemap(rootCodemapPath, unresolved, worktree) : null;
|
|
8140
|
+
let rootChanged = false;
|
|
8141
|
+
const rootModulesRepairable = rootMap && (rootMap.modules === void 0 || Array.isArray(rootMap.modules));
|
|
8142
|
+
if (rootMap) {
|
|
8143
|
+
rootMap.scope ||= "repo";
|
|
8144
|
+
if (rootModulesRepairable) {
|
|
8145
|
+
rootMap.modules = Array.isArray(rootMap.modules) ? rootMap.modules : [];
|
|
8146
|
+
} else {
|
|
8147
|
+
unresolved.push({ category: "codemap", path: ".optima/codemap.yml", message: "Root modules section is object-shaped; repair will not add module entries to avoid changing user structure." });
|
|
8148
|
+
}
|
|
8149
|
+
}
|
|
8150
|
+
function walk(dirPath) {
|
|
8151
|
+
const relative = path3.relative(worktree, dirPath);
|
|
8152
|
+
if (relative && (isIgnoredPath(ig, relative) || isOperationalRelPath(relative) || isHiddenTree(relative))) return;
|
|
8153
|
+
const codemapPath = path3.join(dirPath, "codemap.yml");
|
|
8154
|
+
const hasCodemap = fs3.existsSync(codemapPath);
|
|
8155
|
+
const sourceDir = relative && containsSource(dirPath, ig, worktree);
|
|
8156
|
+
if (sourceDir && !hasCodemap && dirPath !== worktree) {
|
|
8157
|
+
if (hasImmediateSourceFile(dirPath, ig, worktree)) {
|
|
8158
|
+
actions.push({ category: "codemap", action: apply ? "created" : "would_create", path: relPath(worktree, codemapPath), detail: "Create missing module CodeMap for source directory." });
|
|
8159
|
+
if (apply) writeCodemap(codemapPath, createModuleCodemap(dirPath, worktree, deps));
|
|
8160
|
+
} else {
|
|
8161
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, dirPath), message: "Directory contains source only in nested directories; create a local CodeMap after deciding module boundaries." });
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
if (rootMap && rootModulesRepairable && relative && sourceDir && normalizeCodemapRelPath(relative).split("/").length === 1) {
|
|
8165
|
+
const modules = codemapSectionEntries(rootMap.modules);
|
|
8166
|
+
if (!modules.some((entry) => normalizeCodemapRelPath(entry.path) === normalizeCodemapRelPath(relative))) {
|
|
8167
|
+
rootMap.modules = modules;
|
|
8168
|
+
rootMap.modules.push({ path: normalizeCodemapRelPath(relative), summary: "Maintained source module." });
|
|
8169
|
+
rootChanged = true;
|
|
8170
|
+
actions.push({ category: "codemap", action: apply ? "updated" : "would_update", path: ".optima/codemap.yml", detail: `Register top-level source module '${normalizeCodemapRelPath(relative)}'.` });
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
8173
|
+
if (fs3.existsSync(codemapPath) && codemapPath !== rootCodemapPath) repairSingleCodemap(codemapPath, worktree, apply, (action) => actions.push(action), unresolved, deps);
|
|
8174
|
+
for (const item of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8175
|
+
if (item.isDirectory()) walk(path3.join(dirPath, item.name));
|
|
8176
|
+
}
|
|
8177
|
+
}
|
|
8178
|
+
if (fs3.existsSync(worktree)) walk(worktree);
|
|
8179
|
+
if (rootMap && rootChanged && apply) writeCodemap(rootCodemapPath, rootMap);
|
|
8180
|
+
}
|
|
8181
|
+
function walkMarkdownFiles(dirPath) {
|
|
8182
|
+
if (!fs3.existsSync(dirPath)) return [];
|
|
8183
|
+
const files = [];
|
|
8184
|
+
for (const entry of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8185
|
+
const entryPath = path3.join(dirPath, entry.name);
|
|
8186
|
+
if (entry.isDirectory()) {
|
|
8187
|
+
files.push(...walkMarkdownFiles(entryPath));
|
|
8188
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
8189
|
+
files.push(entryPath);
|
|
8190
|
+
}
|
|
8191
|
+
}
|
|
8192
|
+
return files;
|
|
8193
|
+
}
|
|
8194
|
+
function isMarkdownReferenceNormalizationExempt(line) {
|
|
8195
|
+
const normalized = line.toLowerCase().replace(/[`*_~]/g, "");
|
|
8196
|
+
const pathToken = "(?:tasks/|evidences/|docs/scrs/|codemap\\.yml|logs/|screenshots/)";
|
|
8197
|
+
if (new RegExp(`\\b(root|legacy)\\s+${pathToken}`).test(normalized)) return true;
|
|
8198
|
+
if (new RegExp(`\\b(do not|don't|never|must not|should not|avoid|forbid|prohibit)\\b[^\\n]*${pathToken}`).test(normalized)) return true;
|
|
8199
|
+
if (/\b(historical|problem|prohibition)\b[^\n]*(tasks\/|evidences\/|docs\/scrs\/|codemap\.yml|logs\/|screenshots\/)/.test(normalized)) return true;
|
|
8200
|
+
return false;
|
|
8201
|
+
}
|
|
8202
|
+
function rewriteReferenceToken(text, fromPrefix, toPrefix) {
|
|
8203
|
+
const escapedPrefix = fromPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8204
|
+
const pattern = new RegExp("(^|[\\s([{\"'<`])" + escapedPrefix + `(?=[^\\s)\\]}>"']+)`, "g");
|
|
8205
|
+
return text.replace(pattern, `$1${toPrefix}`);
|
|
8206
|
+
}
|
|
8207
|
+
function rewriteOptimaMarkdownReferences(content, evidenceBasePath = null) {
|
|
8208
|
+
return content.split(/(\r?\n)/).map((part) => {
|
|
8209
|
+
if (part === "\n" || part === "\r\n" || isMarkdownReferenceNormalizationExempt(part)) return part;
|
|
8210
|
+
let next = part;
|
|
8211
|
+
next = rewriteReferenceToken(next, "tasks/", ".optima/tasks/");
|
|
8212
|
+
next = rewriteReferenceToken(next, "evidences/", ".optima/evidences/");
|
|
8213
|
+
next = rewriteReferenceToken(next, "docs/scrs/", ".optima/docs/scrs/");
|
|
8214
|
+
next = next.replace(/(^|[\s([{"'<`])codemap\.yml\b/g, "$1.optima/codemap.yml");
|
|
8215
|
+
if (evidenceBasePath) {
|
|
8216
|
+
next = rewriteReferenceToken(next, "logs/", `${evidenceBasePath}/logs/`);
|
|
8217
|
+
next = rewriteReferenceToken(next, "screenshots/", `${evidenceBasePath}/screenshots/`);
|
|
8218
|
+
}
|
|
8219
|
+
return next;
|
|
8220
|
+
}).join("");
|
|
8221
|
+
}
|
|
8222
|
+
function optimaEvidenceBaseForFile(worktree, filePath, deps) {
|
|
8223
|
+
const evidenceRoot = deps.optimaEvidencesDir(worktree);
|
|
8224
|
+
const relative = path3.relative(evidenceRoot, filePath).split(path3.sep).join("/");
|
|
8225
|
+
const [taskId] = relative.split("/");
|
|
8226
|
+
if (!taskId || taskId === ".." || relative.startsWith("../")) return null;
|
|
8227
|
+
return `.optima/evidences/${taskId}`;
|
|
8228
|
+
}
|
|
8229
|
+
function normalizeOptimaMarkdownReferences(worktree, deps) {
|
|
8230
|
+
const scanRoots = [
|
|
8231
|
+
deps.optimaTasksDir(worktree),
|
|
8232
|
+
deps.optimaEvidencesDir(worktree),
|
|
8233
|
+
deps.optimaScrsDir(worktree)
|
|
8234
|
+
];
|
|
8235
|
+
const changed = [];
|
|
8236
|
+
for (const filePath of scanRoots.flatMap(walkMarkdownFiles)) {
|
|
8237
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
8238
|
+
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) + path3.sep) ? optimaEvidenceBaseForFile(worktree, filePath, deps) : null;
|
|
8239
|
+
const rewritten = rewriteOptimaMarkdownReferences(raw, evidenceBasePath);
|
|
8240
|
+
if (rewritten !== raw) {
|
|
8241
|
+
fs3.writeFileSync(filePath, rewritten, "utf8");
|
|
8242
|
+
changed.push(relPath(worktree, filePath));
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
return changed;
|
|
8246
|
+
}
|
|
8247
|
+
function missingOptimaGitignoreRules(worktree, deps) {
|
|
8248
|
+
if (!deps.isGitRepository(worktree)) return [];
|
|
8249
|
+
const gitignorePath = path3.join(worktree, ".gitignore");
|
|
8250
|
+
const existing = fs3.existsSync(gitignorePath) ? fs3.readFileSync(gitignorePath, "utf8") : "";
|
|
8251
|
+
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
8252
|
+
return deps.optimaGitignoreRules.filter((rule) => !existingRules.has(rule));
|
|
8253
|
+
}
|
|
8254
|
+
function repairMode(args = {}) {
|
|
8255
|
+
const value = args.apply ?? args.mode;
|
|
8256
|
+
if (value === true || value === "apply") return "apply";
|
|
8257
|
+
return "dry-run";
|
|
8258
|
+
}
|
|
8259
|
+
function pushRepairAction(actions, category, action, pathValue, detail) {
|
|
8260
|
+
actions.push({ category, action, path: pathValue, detail });
|
|
8261
|
+
}
|
|
8262
|
+
function planOptimaRepair(worktree, args = {}, deps) {
|
|
8263
|
+
const mode = repairMode(args);
|
|
8264
|
+
const apply = mode === "apply";
|
|
8265
|
+
const actions = [];
|
|
8266
|
+
const unresolved = [];
|
|
8267
|
+
const hadOptima = fs3.existsSync(deps.optimaDir(worktree));
|
|
8268
|
+
const missingGitignoreRules = missingOptimaGitignoreRules(worktree, deps);
|
|
8269
|
+
const markdownBefore = apply ? [] : walkMarkdownFiles(deps.optimaDir(worktree)).filter((filePath) => {
|
|
8270
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
8271
|
+
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) + path3.sep) ? optimaEvidenceBaseForFile(worktree, filePath, deps) : null;
|
|
8272
|
+
return rewriteOptimaMarkdownReferences(raw, evidenceBasePath) !== raw;
|
|
8273
|
+
}).map((filePath) => relPath(worktree, filePath));
|
|
8274
|
+
if (!hadOptima) pushRepairAction(actions, "operational", apply ? "created" : "would_create", ".optima/", "Create Optima operational root.");
|
|
8275
|
+
const legacySources = [
|
|
8276
|
+
[".orbita/", deps.legacyOrbitaDir(worktree)],
|
|
8277
|
+
[".staticeng/", deps.legacyStaticEngDir(worktree)],
|
|
8278
|
+
[".nomadwork/", deps.legacyNomadworkDir(worktree)],
|
|
8279
|
+
[".nomadworks/", deps.legacyNomadworksDir(worktree)],
|
|
8280
|
+
["tasks/", path3.join(worktree, "tasks")],
|
|
8281
|
+
["evidences/", path3.join(worktree, "evidences")],
|
|
8282
|
+
["docs/scrs/", path3.join(worktree, "docs", "scrs")],
|
|
8283
|
+
["codemap.yml", path3.join(worktree, "codemap.yml")],
|
|
8284
|
+
["codemap.yaml", path3.join(worktree, "codemap.yaml")]
|
|
8285
|
+
];
|
|
8286
|
+
if (!deps.isOptimaPluginPackageWorktree(worktree)) legacySources.push(["policies/", path3.join(worktree, "policies")]);
|
|
8287
|
+
for (const [display, sourcePath] of legacySources) {
|
|
8288
|
+
if (fs3.existsSync(sourcePath)) pushRepairAction(actions, "legacy", apply ? "migrated" : "would_migrate", display, "Move or merge legacy/root Optima artifact into .optima using preservation safeguards.");
|
|
8289
|
+
}
|
|
8290
|
+
if (apply) deps.migrateLegacyOptimaLayout(worktree);
|
|
8291
|
+
const configMissing = !fs3.existsSync(deps.repoConfigPath(worktree));
|
|
8292
|
+
if (configMissing) pushRepairAction(actions, "config", apply ? "created" : "would_create", ".optima/.config/optima.yaml", "Create missing local Optima config from template.");
|
|
8293
|
+
if (apply) deps.scaffoldOptimaConfig(worktree, args.team_mode || "full");
|
|
8294
|
+
const requiredFiles = [
|
|
8295
|
+
[path3.join(deps.optimaTasksDir(worktree), "current.md"), "registry", ".optima/tasks/current.md", deps.currentTasksRegistryContent()],
|
|
8296
|
+
[path3.join(deps.optimaTasksDir(worktree), "done.md"), "registry", ".optima/tasks/done.md", deps.doneTasksRegistryContent()],
|
|
8297
|
+
[path3.join(deps.optimaScrsDir(worktree), "current.md"), "registry", ".optima/docs/scrs/current.md", deps.currentScrRegistryContent()],
|
|
8298
|
+
[path3.join(deps.optimaScrsDir(worktree), "done.md"), "registry", ".optima/docs/scrs/done.md", deps.doneScrRegistryContent()],
|
|
8299
|
+
[path3.join(deps.optimaTasksDir(worktree), "task-template.md"), "template", ".optima/tasks/task-template.md", deps.taskTemplateContent()],
|
|
8300
|
+
[path3.join(deps.optimaTasksDir(worktree), "subtask-template.md"), "template", ".optima/tasks/subtask-template.md", deps.subtaskTemplateContent()],
|
|
8301
|
+
[path3.join(deps.repoPoliciesDir(worktree), "README.md"), "policy", ".optima/policies/README.md", deps.repoLocalPoliciesReadme]
|
|
8302
|
+
];
|
|
8303
|
+
for (const [filePath, category, displayPath, content] of requiredFiles) {
|
|
8304
|
+
if (!fs3.existsSync(filePath)) {
|
|
8305
|
+
pushRepairAction(actions, category, apply ? "created" : "would_create", displayPath, "Create missing Optima scaffold file without overwriting existing content.");
|
|
8306
|
+
if (apply) deps.ensureFileIfMissing(filePath, content);
|
|
8307
|
+
}
|
|
8308
|
+
}
|
|
8309
|
+
if (apply) {
|
|
8310
|
+
deps.scaffoldOptimaReadmes(worktree);
|
|
8311
|
+
deps.ensureOptimaRegistries(worktree);
|
|
8312
|
+
normalizeOptimaMarkdownReferences(worktree, deps).forEach((filePath) => {
|
|
8313
|
+
pushRepairAction(actions, "markdown", "normalized", filePath, "Normalize Optima artifact paths in Markdown.");
|
|
8314
|
+
});
|
|
8315
|
+
} else {
|
|
8316
|
+
markdownBefore.forEach((filePath) => pushRepairAction(actions, "markdown", "would_normalize", filePath, "Normalize Optima artifact paths in Markdown."));
|
|
8317
|
+
}
|
|
8318
|
+
if (missingGitignoreRules.length > 0) {
|
|
8319
|
+
pushRepairAction(actions, "gitignore", apply ? "updated" : "would_update", ".gitignore", `Add ${missingGitignoreRules.length} Optima local/private rule(s).`);
|
|
8320
|
+
if (apply) deps.ensureOptimaGitignoreRules(worktree);
|
|
8321
|
+
}
|
|
8322
|
+
repairCodemaps(worktree, apply, actions, unresolved, deps);
|
|
8323
|
+
return { mode, actions, unresolved };
|
|
8324
|
+
}
|
|
8325
|
+
function formatRepairResult(plan, validationResult = null, formatValidationResult2 = (res) => JSON.stringify(res, null, 2)) {
|
|
8326
|
+
const lines = [
|
|
8327
|
+
`Optima repair ${plan.mode === "apply" ? "applied" : "dry-run"}.`,
|
|
8328
|
+
"Scope inspected: config, registries, templates, policy scaffolding, legacy/root artifact leakage, Markdown path normalization, gitignore rules, and CodeMap integrity.",
|
|
8329
|
+
"",
|
|
8330
|
+
"Planned/Applied fixes:"
|
|
8331
|
+
];
|
|
8332
|
+
if (plan.actions.length === 0) {
|
|
8333
|
+
lines.push("- None");
|
|
8334
|
+
} else {
|
|
8335
|
+
for (const action of plan.actions) {
|
|
8336
|
+
lines.push(`- [${action.category}] ${action.action} ${action.path}: ${action.detail}`);
|
|
8337
|
+
}
|
|
8338
|
+
}
|
|
8339
|
+
lines.push("", "Unresolved issues:");
|
|
8340
|
+
if (plan.unresolved.length === 0) {
|
|
8341
|
+
lines.push("- None");
|
|
8342
|
+
} else {
|
|
8343
|
+
for (const issue of plan.unresolved) {
|
|
8344
|
+
lines.push(`- [${issue.category}] ${issue.path}: ${issue.message}`);
|
|
8345
|
+
}
|
|
8346
|
+
}
|
|
8347
|
+
if (validationResult) {
|
|
8348
|
+
lines.push("", "Validation after apply:", formatValidationResult2(validationResult));
|
|
8349
|
+
} else {
|
|
8350
|
+
lines.push("", "Validation after apply: not run in dry-run mode.");
|
|
8351
|
+
}
|
|
8352
|
+
return lines.join("\n");
|
|
8353
|
+
}
|
|
8354
|
+
|
|
8355
|
+
// src/validate_logic.js
|
|
8356
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
8357
|
+
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
8358
|
+
import fs4 from "node:fs";
|
|
8359
|
+
import path4 from "node:path";
|
|
7734
8360
|
async function optima_validate_logic(worktree) {
|
|
7735
|
-
const rootCodemapPath =
|
|
7736
|
-
if (!
|
|
8361
|
+
const rootCodemapPath = path4.join(worktree, ".optima", "codemap.yml");
|
|
8362
|
+
if (!fs4.existsSync(rootCodemapPath)) return { ok: false, errors: [".optima/codemap.yml not found."], warnings: [] };
|
|
7737
8363
|
const errors = [];
|
|
7738
8364
|
const warnings = [];
|
|
7739
|
-
const ig = (0,
|
|
8365
|
+
const ig = (0, import_ignore2.default)();
|
|
7740
8366
|
ig.add(".git");
|
|
7741
|
-
const gitignorePath =
|
|
7742
|
-
if (
|
|
7743
|
-
ig.add(
|
|
8367
|
+
const gitignorePath = path4.join(worktree, ".gitignore");
|
|
8368
|
+
if (fs4.existsSync(gitignorePath)) {
|
|
8369
|
+
ig.add(fs4.readFileSync(gitignorePath, "utf8"));
|
|
7744
8370
|
}
|
|
7745
8371
|
const sourceExtensions = [
|
|
7746
8372
|
".js",
|
|
@@ -7778,32 +8404,32 @@ async function optima_validate_logic(worktree) {
|
|
|
7778
8404
|
".codenomad"
|
|
7779
8405
|
]);
|
|
7780
8406
|
const operationalFolders = [".optima", "templates", "dist"];
|
|
7781
|
-
const
|
|
7782
|
-
if (!
|
|
7783
|
-
return
|
|
8407
|
+
const isHiddenTree2 = (relPath2) => {
|
|
8408
|
+
if (!relPath2) return false;
|
|
8409
|
+
return relPath2.split(path4.sep).some((part) => part.startsWith("."));
|
|
7784
8410
|
};
|
|
7785
|
-
const
|
|
7786
|
-
const
|
|
7787
|
-
if (!
|
|
7788
|
-
const firstSegment =
|
|
8411
|
+
const firstPathSegment2 = (relPath2) => relPath2.split(path4.sep).filter(Boolean)[0] || "";
|
|
8412
|
+
const isOperationalRelPath2 = (relPath2) => {
|
|
8413
|
+
if (!relPath2) return false;
|
|
8414
|
+
const firstSegment = firstPathSegment2(relPath2);
|
|
7789
8415
|
if (legacyOperationalRoots.has(firstSegment)) return true;
|
|
7790
|
-
return operationalFolders.some((f) =>
|
|
8416
|
+
return operationalFolders.some((f) => relPath2 === f || relPath2.startsWith(f + path4.sep));
|
|
7791
8417
|
};
|
|
7792
8418
|
const getSectionEntries = (value) => {
|
|
7793
8419
|
if (Array.isArray(value)) return value;
|
|
7794
8420
|
if (value && typeof value === "object") return Object.values(value);
|
|
7795
8421
|
return [];
|
|
7796
8422
|
};
|
|
7797
|
-
const normalizeRelativePath = (
|
|
8423
|
+
const normalizeRelativePath = (relPath2) => path4.normalize(relPath2).replace(/\\/g, "/").replace(/\/$/, "");
|
|
7798
8424
|
const isSourceDir = (dirPath) => {
|
|
7799
|
-
const dirRelPath =
|
|
7800
|
-
if (
|
|
7801
|
-
const items =
|
|
8425
|
+
const dirRelPath = path4.relative(worktree, dirPath);
|
|
8426
|
+
if (isOperationalRelPath2(dirRelPath)) return false;
|
|
8427
|
+
const items = fs4.readdirSync(dirPath, { withFileTypes: true });
|
|
7802
8428
|
for (const item of items) {
|
|
7803
|
-
const childPath =
|
|
7804
|
-
const
|
|
7805
|
-
if (ig.ignores(
|
|
7806
|
-
if (item.isFile() && sourceExtensions.includes(
|
|
8429
|
+
const childPath = path4.join(dirPath, item.name);
|
|
8430
|
+
const relPath2 = path4.relative(worktree, childPath);
|
|
8431
|
+
if (ig.ignores(relPath2) || isOperationalRelPath2(relPath2)) continue;
|
|
8432
|
+
if (item.isFile() && sourceExtensions.includes(path4.extname(item.name))) return true;
|
|
7807
8433
|
if (item.isDirectory()) {
|
|
7808
8434
|
if (isSourceDir(childPath)) return true;
|
|
7809
8435
|
}
|
|
@@ -7811,10 +8437,10 @@ async function optima_validate_logic(worktree) {
|
|
|
7811
8437
|
return false;
|
|
7812
8438
|
};
|
|
7813
8439
|
function validateMap(filePath) {
|
|
7814
|
-
const content =
|
|
8440
|
+
const content = fs4.readFileSync(filePath, "utf8");
|
|
7815
8441
|
let map;
|
|
7816
8442
|
try {
|
|
7817
|
-
map =
|
|
8443
|
+
map = import_yaml2.default.parse(content);
|
|
7818
8444
|
if (!map || typeof map !== "object") {
|
|
7819
8445
|
errors.push(`${filePath}: CodeMap is empty or invalid.`);
|
|
7820
8446
|
return;
|
|
@@ -7823,32 +8449,32 @@ async function optima_validate_logic(worktree) {
|
|
|
7823
8449
|
errors.push(`${filePath}: Invalid YAML.`);
|
|
7824
8450
|
return;
|
|
7825
8451
|
}
|
|
7826
|
-
const dir =
|
|
8452
|
+
const dir = path4.dirname(filePath);
|
|
7827
8453
|
const pathBase = filePath === rootCodemapPath ? worktree : dir;
|
|
7828
8454
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
7829
8455
|
const sectionsToVerify = ["modules", "entrypoints", "sources_of_truth", "links", "internals"];
|
|
7830
8456
|
for (const section of sectionsToVerify) {
|
|
7831
8457
|
for (const item of getSectionEntries(map[section])) {
|
|
7832
8458
|
if (item?.path) {
|
|
7833
|
-
indexedPaths.add(
|
|
8459
|
+
indexedPaths.add(path4.normalize(item.path));
|
|
7834
8460
|
if (section === "links" && (item.path.startsWith("http://") || item.path.startsWith("https://"))) {
|
|
7835
8461
|
continue;
|
|
7836
8462
|
}
|
|
7837
|
-
const absPath =
|
|
7838
|
-
if (!
|
|
8463
|
+
const absPath = path4.isAbsolute(item.path) ? item.path : path4.join(pathBase, item.path);
|
|
8464
|
+
if (!fs4.existsSync(absPath)) {
|
|
7839
8465
|
errors.push(`${filePath}: ${section.slice(0, -1)} path does not exist: ${item.path}`);
|
|
7840
8466
|
}
|
|
7841
8467
|
}
|
|
7842
8468
|
}
|
|
7843
8469
|
}
|
|
7844
|
-
const relDir =
|
|
7845
|
-
const isOperational =
|
|
8470
|
+
const relDir = path4.relative(worktree, dir);
|
|
8471
|
+
const isOperational = isOperationalRelPath2(relDir);
|
|
7846
8472
|
if (map.scope === "module" && !isOperational) {
|
|
7847
|
-
const items =
|
|
8473
|
+
const items = fs4.readdirSync(dir, { withFileTypes: true });
|
|
7848
8474
|
for (const item of items) {
|
|
7849
|
-
if (item.isFile() && sourceExtensions.includes(
|
|
8475
|
+
if (item.isFile() && sourceExtensions.includes(path4.extname(item.name))) {
|
|
7850
8476
|
if (item.name === "codemap.yml") continue;
|
|
7851
|
-
if (!indexedPaths.has(
|
|
8477
|
+
if (!indexedPaths.has(path4.normalize(item.name))) {
|
|
7852
8478
|
errors.push(`${filePath}: Unindexed source file found: '${item.name}'. Every source file must be categorized in a section (e.g., 'internals').`);
|
|
7853
8479
|
}
|
|
7854
8480
|
}
|
|
@@ -7858,7 +8484,7 @@ async function optima_validate_logic(worktree) {
|
|
|
7858
8484
|
for (const key of pathKeys) {
|
|
7859
8485
|
for (const entry of getSectionEntries(map[key])) {
|
|
7860
8486
|
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://")) continue;
|
|
7861
|
-
if (
|
|
8487
|
+
if (path4.isAbsolute(entry.path)) continue;
|
|
7862
8488
|
const normalizedPath = normalizeRelativePath(entry.path);
|
|
7863
8489
|
if (!normalizedPath || normalizedPath === ".") continue;
|
|
7864
8490
|
const parts = normalizedPath.split("/").filter((p) => p && p !== ".");
|
|
@@ -7870,30 +8496,30 @@ async function optima_validate_logic(worktree) {
|
|
|
7870
8496
|
}
|
|
7871
8497
|
}
|
|
7872
8498
|
const walk = (dir) => {
|
|
7873
|
-
const relDir =
|
|
8499
|
+
const relDir = path4.relative(worktree, dir);
|
|
7874
8500
|
if (relDir && ig.ignores(relDir)) return;
|
|
7875
|
-
if (
|
|
7876
|
-
if (
|
|
7877
|
-
const hasCodemap =
|
|
7878
|
-
const items =
|
|
7879
|
-
if (!relDir.startsWith(
|
|
8501
|
+
if (isOperationalRelPath2(relDir)) return;
|
|
8502
|
+
if (isHiddenTree2(relDir)) return;
|
|
8503
|
+
const hasCodemap = fs4.existsSync(path4.join(dir, "codemap.yml"));
|
|
8504
|
+
const items = fs4.readdirSync(dir, { withFileTypes: true });
|
|
8505
|
+
if (!relDir.startsWith(path4.join(".optima", "tasks", "done"))) {
|
|
7880
8506
|
for (const item of items) {
|
|
7881
8507
|
if (item.isFile() && item.name.endsWith(".md")) {
|
|
7882
|
-
const content =
|
|
8508
|
+
const content = fs4.readFileSync(path4.join(dir, item.name), "utf8");
|
|
7883
8509
|
if (content.includes("[To be defined]") || content.includes("[Insert ")) {
|
|
7884
|
-
const relFilePath =
|
|
8510
|
+
const relFilePath = path4.join(relDir, item.name);
|
|
7885
8511
|
errors.push(`Documentation Placeholder found: '${relFilePath}' still contains [To be defined] or [Insert ...] placeholders.`);
|
|
7886
8512
|
}
|
|
7887
8513
|
}
|
|
7888
8514
|
}
|
|
7889
8515
|
}
|
|
7890
|
-
const isOperational =
|
|
8516
|
+
const isOperational = isOperationalRelPath2(relDir);
|
|
7891
8517
|
if (relDir !== "" && !hasCodemap && isSourceDir(dir) && !isOperational) {
|
|
7892
8518
|
errors.push(`Missing CodeMap: Directory '${relDir}' contains source but has no codemap.yml.`);
|
|
7893
8519
|
}
|
|
7894
|
-
if (hasCodemap) validateMap(
|
|
8520
|
+
if (hasCodemap) validateMap(path4.join(dir, "codemap.yml"));
|
|
7895
8521
|
for (const item of items) {
|
|
7896
|
-
if (item.isDirectory()) walk(
|
|
8522
|
+
if (item.isDirectory()) walk(path4.join(dir, item.name));
|
|
7897
8523
|
}
|
|
7898
8524
|
};
|
|
7899
8525
|
validateMap(rootCodemapPath);
|
|
@@ -7906,12 +8532,12 @@ async function optima_validate_logic(worktree) {
|
|
|
7906
8532
|
}
|
|
7907
8533
|
|
|
7908
8534
|
// src/index.js
|
|
7909
|
-
var PKG_ROOT =
|
|
7910
|
-
var BUNDLE_ASSETS_DIR =
|
|
7911
|
-
var BUNDLE_AGENTS_DIR =
|
|
7912
|
-
var BUNDLE_POLICIES_DIR =
|
|
7913
|
-
var TEMPLATES_DIR =
|
|
7914
|
-
var HUMANS_REGISTRY_PATH =
|
|
8535
|
+
var PKG_ROOT = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8536
|
+
var BUNDLE_ASSETS_DIR = path5.join(PKG_ROOT, "assets");
|
|
8537
|
+
var BUNDLE_AGENTS_DIR = path5.join(BUNDLE_ASSETS_DIR, "agents");
|
|
8538
|
+
var BUNDLE_POLICIES_DIR = path5.join(BUNDLE_ASSETS_DIR, "policies");
|
|
8539
|
+
var TEMPLATES_DIR = path5.join(PKG_ROOT, "templates");
|
|
8540
|
+
var HUMANS_REGISTRY_PATH = path5.join(PKG_ROOT, "docs", "core", "humans.md");
|
|
7915
8541
|
var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
7916
8542
|
var MINI_MODE_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
7917
8543
|
var CLICKUP_IGNORED_TASK_TYPES = ["Idea", "Backlog", "Hito", "Nota de reuni\xF3n", "Respuesta del formulario"];
|
|
@@ -8047,18 +8673,18 @@ function objectIdentity(value) {
|
|
|
8047
8673
|
return objectIdentityMap.get(value);
|
|
8048
8674
|
}
|
|
8049
8675
|
function isRootDirectory(candidate) {
|
|
8050
|
-
const resolved =
|
|
8051
|
-
return resolved ===
|
|
8676
|
+
const resolved = path5.resolve(candidate);
|
|
8677
|
+
return resolved === path5.parse(resolved).root;
|
|
8052
8678
|
}
|
|
8053
8679
|
function isSafeWritableDirectory(candidate) {
|
|
8054
8680
|
if (typeof candidate !== "string" || !candidate.trim()) return false;
|
|
8055
|
-
if (!
|
|
8056
|
-
const resolved =
|
|
8681
|
+
if (!path5.isAbsolute(candidate)) return false;
|
|
8682
|
+
const resolved = path5.resolve(candidate);
|
|
8057
8683
|
if (isRootDirectory(resolved)) return false;
|
|
8058
8684
|
try {
|
|
8059
|
-
const stat =
|
|
8685
|
+
const stat = fs5.statSync(resolved);
|
|
8060
8686
|
if (!stat.isDirectory()) return false;
|
|
8061
|
-
|
|
8687
|
+
fs5.accessSync(resolved, fs5.constants.W_OK);
|
|
8062
8688
|
return true;
|
|
8063
8689
|
} catch {
|
|
8064
8690
|
return false;
|
|
@@ -8074,7 +8700,7 @@ function resolveSafeWorktree(context = {}, pluginWorktree = null) {
|
|
|
8074
8700
|
];
|
|
8075
8701
|
for (const candidate of candidates) {
|
|
8076
8702
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8077
|
-
const resolved =
|
|
8703
|
+
const resolved = path5.resolve(candidate);
|
|
8078
8704
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8079
8705
|
}
|
|
8080
8706
|
throw new Error(SAFE_WORKTREE_FAILURE);
|
|
@@ -8089,13 +8715,13 @@ function safeWorktreeOrFailure(context, pluginWorktree) {
|
|
|
8089
8715
|
function explicitSafeInputWorktree(input = {}) {
|
|
8090
8716
|
for (const candidate of [input?.worktree, input?.directory]) {
|
|
8091
8717
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8092
|
-
const resolved =
|
|
8718
|
+
const resolved = path5.resolve(candidate);
|
|
8093
8719
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8094
8720
|
}
|
|
8095
8721
|
return null;
|
|
8096
8722
|
}
|
|
8097
8723
|
function isGitRepository(worktree) {
|
|
8098
|
-
return
|
|
8724
|
+
return fs5.existsSync(path5.join(worktree, ".git"));
|
|
8099
8725
|
}
|
|
8100
8726
|
function normalizeLooseToken(value) {
|
|
8101
8727
|
return String(value ?? "").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -8129,8 +8755,8 @@ function parseHumansRegistry(markdown = "") {
|
|
|
8129
8755
|
return roles;
|
|
8130
8756
|
}
|
|
8131
8757
|
function loadHumansRegistry(registryPath = HUMANS_REGISTRY_PATH) {
|
|
8132
|
-
if (!
|
|
8133
|
-
return parseHumansRegistry(
|
|
8758
|
+
if (!fs5.existsSync(registryPath)) return {};
|
|
8759
|
+
return parseHumansRegistry(fs5.readFileSync(registryPath, "utf8"));
|
|
8134
8760
|
}
|
|
8135
8761
|
function resolveHumanRoles(roles = CLICKUP_FINAL_APPROVER_ROLES, registry = loadHumansRegistry()) {
|
|
8136
8762
|
return [...new Set(roles.map((role) => registry[String(role ?? "").trim()] || "").filter(Boolean))];
|
|
@@ -8297,7 +8923,7 @@ function parseMarkdownArtifact(markdown = "", { requiredSections = [] } = {}) {
|
|
|
8297
8923
|
};
|
|
8298
8924
|
}
|
|
8299
8925
|
function readMarkdownArtifact(filePath, options = {}) {
|
|
8300
|
-
const markdown =
|
|
8926
|
+
const markdown = fs5.readFileSync(filePath, "utf8");
|
|
8301
8927
|
return parseMarkdownArtifact(markdown, options);
|
|
8302
8928
|
}
|
|
8303
8929
|
function stripRawLogSections(sections = {}) {
|
|
@@ -8583,7 +9209,7 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
|
|
|
8583
9209
|
function deriveClickUpWorktree({ baseWorktree = "", taskId, taskType, parentTaskId, subtaskId } = {}) {
|
|
8584
9210
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId, subtaskId, taskId });
|
|
8585
9211
|
const root = baseWorktree || process.cwd();
|
|
8586
|
-
return
|
|
9212
|
+
return path5.join(path5.dirname(root), `${path5.basename(root)}-${branch.replace(/\//g, "-")}`);
|
|
8587
9213
|
}
|
|
8588
9214
|
function clickUpCustomFieldValue(task = {}, names = []) {
|
|
8589
9215
|
const fields = Array.isArray(task.custom_fields) ? task.custom_fields : [];
|
|
@@ -8607,12 +9233,12 @@ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
|
|
|
8607
9233
|
const taskMetadata = metadataTaskRouting(metadata);
|
|
8608
9234
|
const existingWorktree = typeof taskMetadata.worktree === "string" ? taskMetadata.worktree.trim() : "";
|
|
8609
9235
|
const existingBranch = typeof taskMetadata.branch === "string" ? taskMetadata.branch.trim() : "";
|
|
8610
|
-
if (!existingWorktree || !
|
|
9236
|
+
if (!existingWorktree || !path5.isAbsolute(existingWorktree) || !fs5.existsSync(existingWorktree)) return null;
|
|
8611
9237
|
try {
|
|
8612
|
-
const stat =
|
|
9238
|
+
const stat = fs5.statSync(existingWorktree);
|
|
8613
9239
|
if (!stat.isDirectory()) return null;
|
|
8614
9240
|
if (branch && existingBranch && existingBranch !== branch) return null;
|
|
8615
|
-
return { branch: existingBranch || branch, worktree:
|
|
9241
|
+
return { branch: existingBranch || branch, worktree: path5.resolve(existingWorktree), reused: true };
|
|
8616
9242
|
} catch {
|
|
8617
9243
|
return null;
|
|
8618
9244
|
}
|
|
@@ -8623,10 +9249,10 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
8623
9249
|
const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
|
|
8624
9250
|
if (existing) return { ...existing, branch };
|
|
8625
9251
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
8626
|
-
if (
|
|
9252
|
+
if (fs5.existsSync(worktreePath)) return { branch, worktree: path5.resolve(worktreePath), reused: true };
|
|
8627
9253
|
if (allowNonGitFallback) {
|
|
8628
|
-
|
|
8629
|
-
return { branch, worktree:
|
|
9254
|
+
fs5.mkdirSync(worktreePath, { recursive: true });
|
|
9255
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, fallback: "non_git" };
|
|
8630
9256
|
}
|
|
8631
9257
|
const startPoint = (() => {
|
|
8632
9258
|
try {
|
|
@@ -8637,7 +9263,7 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
8637
9263
|
}
|
|
8638
9264
|
})();
|
|
8639
9265
|
runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
|
|
8640
|
-
return { branch, worktree:
|
|
9266
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, startPoint };
|
|
8641
9267
|
}
|
|
8642
9268
|
function normalizeClickUpDefinitionDocParent(parent = {}) {
|
|
8643
9269
|
const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
|
|
@@ -8772,76 +9398,10 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
8772
9398
|
}
|
|
8773
9399
|
};
|
|
8774
9400
|
}
|
|
8775
|
-
function toGitPath(worktree, targetPath) {
|
|
8776
|
-
const relativePath = path2.relative(worktree, targetPath);
|
|
8777
|
-
if (!relativePath || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) return null;
|
|
8778
|
-
return relativePath.split(path2.sep).join("/");
|
|
8779
|
-
}
|
|
8780
|
-
function runGit(worktree, args) {
|
|
8781
|
-
return execFileSync("git", args, {
|
|
8782
|
-
cwd: worktree,
|
|
8783
|
-
encoding: "utf8",
|
|
8784
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
8785
|
-
});
|
|
8786
|
-
}
|
|
8787
|
-
function gitMigrationState(worktree) {
|
|
8788
|
-
try {
|
|
8789
|
-
const root = runGit(worktree, ["rev-parse", "--show-toplevel"]).trim();
|
|
8790
|
-
if (path2.resolve(root) !== path2.resolve(worktree)) return null;
|
|
8791
|
-
return { worktree };
|
|
8792
|
-
} catch {
|
|
8793
|
-
return null;
|
|
8794
|
-
}
|
|
8795
|
-
}
|
|
8796
|
-
function isGitTracked(gitState, targetPath) {
|
|
8797
|
-
if (!gitState) return false;
|
|
8798
|
-
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
8799
|
-
if (!gitPath) return false;
|
|
8800
|
-
try {
|
|
8801
|
-
runGit(gitState.worktree, ["ls-files", "--error-unmatch", "--", gitPath]);
|
|
8802
|
-
return true;
|
|
8803
|
-
} catch {
|
|
8804
|
-
return false;
|
|
8805
|
-
}
|
|
8806
|
-
}
|
|
8807
|
-
function hasGitTrackedChildren(gitState, targetPath) {
|
|
8808
|
-
if (!gitState) return false;
|
|
8809
|
-
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
8810
|
-
if (!gitPath) return false;
|
|
8811
|
-
try {
|
|
8812
|
-
return runGit(gitState.worktree, ["ls-files", "--", `${gitPath}/`]).trim().length > 0;
|
|
8813
|
-
} catch {
|
|
8814
|
-
return false;
|
|
8815
|
-
}
|
|
8816
|
-
}
|
|
8817
|
-
function gitAwareMoveIfTracked(sourcePath, destinationPath, gitState) {
|
|
8818
|
-
const sourceIsTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
8819
|
-
if (!gitState || !sourceIsTracked || fs2.existsSync(destinationPath)) return false;
|
|
8820
|
-
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
8821
|
-
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
8822
|
-
if (!sourceGitPath || !destinationGitPath) return false;
|
|
8823
|
-
fs2.mkdirSync(path2.dirname(destinationPath), { recursive: true });
|
|
8824
|
-
try {
|
|
8825
|
-
runGit(gitState.worktree, ["mv", "--", sourceGitPath, destinationGitPath]);
|
|
8826
|
-
return true;
|
|
8827
|
-
} catch {
|
|
8828
|
-
return false;
|
|
8829
|
-
}
|
|
8830
|
-
}
|
|
8831
|
-
function stageGitAwareMerge(sourcePath, destinationPath, gitState) {
|
|
8832
|
-
if (!gitState || !isGitTracked(gitState, sourcePath)) return;
|
|
8833
|
-
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
8834
|
-
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
8835
|
-
if (!sourceGitPath || !destinationGitPath) return;
|
|
8836
|
-
try {
|
|
8837
|
-
runGit(gitState.worktree, ["add", "-A", "--", sourceGitPath, destinationGitPath]);
|
|
8838
|
-
} catch {
|
|
8839
|
-
}
|
|
8840
|
-
}
|
|
8841
9401
|
function ensureOptimaGitignoreRules(worktree) {
|
|
8842
9402
|
if (!isGitRepository(worktree)) return { touched: false, added: [] };
|
|
8843
|
-
const gitignorePath =
|
|
8844
|
-
const existing =
|
|
9403
|
+
const gitignorePath = path5.join(worktree, ".gitignore");
|
|
9404
|
+
const existing = fs5.existsSync(gitignorePath) ? fs5.readFileSync(gitignorePath, "utf8") : "";
|
|
8845
9405
|
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
8846
9406
|
const missingRules = OPTIMA_GITIGNORE_RULES.filter((rule) => !existingRules.has(rule));
|
|
8847
9407
|
if (missingRules.length === 0) return { touched: false, added: [] };
|
|
@@ -8851,49 +9411,45 @@ function ensureOptimaGitignoreRules(worktree) {
|
|
|
8851
9411
|
"# Optima local/private state",
|
|
8852
9412
|
...missingRules
|
|
8853
9413
|
].join("\n");
|
|
8854
|
-
|
|
9414
|
+
fs5.writeFileSync(gitignorePath, `${existing}${prefix}${separator}${block}
|
|
8855
9415
|
`, "utf8");
|
|
8856
9416
|
return { touched: true, added: missingRules };
|
|
8857
9417
|
}
|
|
8858
|
-
function compactPromptPath(filePath) {
|
|
8859
|
-
if (!filePath.endsWith(".md") || filePath.endsWith(".prompt.md")) return null;
|
|
8860
|
-
return filePath.replace(/\.md$/, ".prompt.md");
|
|
8861
|
-
}
|
|
8862
9418
|
function optimaDir(worktree) {
|
|
8863
|
-
return
|
|
9419
|
+
return path5.join(worktree, OPTIMA_DIRNAME);
|
|
8864
9420
|
}
|
|
8865
9421
|
function optimaLocalConfigDir(worktree) {
|
|
8866
|
-
return
|
|
9422
|
+
return path5.join(optimaDir(worktree), ".config");
|
|
8867
9423
|
}
|
|
8868
9424
|
function optimaConfigDir(worktree) {
|
|
8869
9425
|
return optimaLocalConfigDir(worktree);
|
|
8870
9426
|
}
|
|
8871
9427
|
function optimaCodemapPath(worktree) {
|
|
8872
|
-
return
|
|
9428
|
+
return path5.join(optimaDir(worktree), "codemap.yml");
|
|
8873
9429
|
}
|
|
8874
9430
|
function optimaTasksDir(worktree) {
|
|
8875
|
-
return
|
|
9431
|
+
return path5.join(optimaDir(worktree), "tasks");
|
|
8876
9432
|
}
|
|
8877
9433
|
function optimaEvidencesDir(worktree) {
|
|
8878
|
-
return
|
|
9434
|
+
return path5.join(optimaDir(worktree), "evidences");
|
|
8879
9435
|
}
|
|
8880
9436
|
function optimaScrsDir(worktree) {
|
|
8881
|
-
return
|
|
9437
|
+
return path5.join(optimaDir(worktree), "docs", "scrs");
|
|
8882
9438
|
}
|
|
8883
9439
|
function legacyNomadworkDir(worktree) {
|
|
8884
|
-
return
|
|
9440
|
+
return path5.join(worktree, LEGACY_NOMADWORK_DIRNAME);
|
|
8885
9441
|
}
|
|
8886
9442
|
function legacyNomadworksDir(worktree) {
|
|
8887
|
-
return
|
|
9443
|
+
return path5.join(worktree, LEGACY_NOMADWORKS_DIRNAME);
|
|
8888
9444
|
}
|
|
8889
9445
|
function legacyOrbitaDir(worktree) {
|
|
8890
|
-
return
|
|
9446
|
+
return path5.join(worktree, LEGACY_ORBITA_DIRNAME);
|
|
8891
9447
|
}
|
|
8892
9448
|
function legacyStaticEngDir(worktree) {
|
|
8893
|
-
return
|
|
9449
|
+
return path5.join(worktree, LEGACY_STATICENG_DIRNAME);
|
|
8894
9450
|
}
|
|
8895
9451
|
function repoConfigPath(worktree) {
|
|
8896
|
-
return
|
|
9452
|
+
return path5.join(optimaConfigDir(worktree), "optima.yaml");
|
|
8897
9453
|
}
|
|
8898
9454
|
function normalizeLegacyDiscussionEntry(entry) {
|
|
8899
9455
|
if (!entry || typeof entry !== "object") return entry;
|
|
@@ -8906,25 +9462,25 @@ function normalizeLegacyDiscussionEntry(entry) {
|
|
|
8906
9462
|
return next;
|
|
8907
9463
|
}
|
|
8908
9464
|
function repoPoliciesDir(worktree) {
|
|
8909
|
-
return
|
|
9465
|
+
return path5.join(optimaDir(worktree), "policies");
|
|
8910
9466
|
}
|
|
8911
9467
|
function generatedPoliciesDir(worktree) {
|
|
8912
|
-
return
|
|
9468
|
+
return path5.join(optimaLocalConfigDir(worktree), "generated", "policies");
|
|
8913
9469
|
}
|
|
8914
9470
|
function generatedAgentsDir(worktree) {
|
|
8915
|
-
return
|
|
9471
|
+
return path5.join(optimaLocalConfigDir(worktree), "generated", "agents");
|
|
8916
9472
|
}
|
|
8917
9473
|
function repoAgentsDir(worktree) {
|
|
8918
|
-
return
|
|
9474
|
+
return path5.join(optimaDir(worktree), "agents");
|
|
8919
9475
|
}
|
|
8920
9476
|
function repoAgentAdditionsDir(worktree) {
|
|
8921
|
-
return
|
|
9477
|
+
return path5.join(optimaDir(worktree), "agent-additions");
|
|
8922
9478
|
}
|
|
8923
9479
|
function legacyRepoAgentsDir(worktree) {
|
|
8924
|
-
return
|
|
9480
|
+
return path5.join(legacyNomadworksDir(worktree), "agents");
|
|
8925
9481
|
}
|
|
8926
9482
|
function runtimeDiscussionRegistryPath(worktree) {
|
|
8927
|
-
return
|
|
9483
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime", "discussions.json");
|
|
8928
9484
|
}
|
|
8929
9485
|
function resolveConfigPath(worktree) {
|
|
8930
9486
|
return repoConfigPath(worktree);
|
|
@@ -8967,32 +9523,32 @@ function mergeClickUpAgentMetadata(existing, update = {}) {
|
|
|
8967
9523
|
return JSON.stringify(sortJsonValue(merged), null, 2);
|
|
8968
9524
|
}
|
|
8969
9525
|
function optimaRuntimeDir(worktree) {
|
|
8970
|
-
return
|
|
9526
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime");
|
|
8971
9527
|
}
|
|
8972
9528
|
function clickUpWebhookStatePath(worktree) {
|
|
8973
|
-
return
|
|
9529
|
+
return path5.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
|
|
8974
9530
|
}
|
|
8975
9531
|
function clickUpCommentLedgerPath(worktree) {
|
|
8976
|
-
return
|
|
9532
|
+
return path5.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
|
|
8977
9533
|
}
|
|
8978
9534
|
function clickUpWebhookLogPath(worktree) {
|
|
8979
|
-
return
|
|
9535
|
+
return path5.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
8980
9536
|
}
|
|
8981
9537
|
function normalizeClickUpWebhookLogLevel(value) {
|
|
8982
9538
|
const level = String(value || "info").trim().toLowerCase();
|
|
8983
9539
|
return CLICKUP_WEBHOOK_LOG_LEVELS.has(level) ? level : "info";
|
|
8984
9540
|
}
|
|
8985
9541
|
function clickUpWebhookAuditLogDir() {
|
|
8986
|
-
const dataHome = process.env.XDG_DATA_HOME &&
|
|
8987
|
-
return
|
|
9542
|
+
const dataHome = process.env.XDG_DATA_HOME && path5.isAbsolute(process.env.XDG_DATA_HOME) ? process.env.XDG_DATA_HOME : path5.join(os.homedir(), ".local", "share", "opencode");
|
|
9543
|
+
return path5.join(dataHome, "opencode-optima");
|
|
8988
9544
|
}
|
|
8989
9545
|
function clickUpWebhookAuditLogPath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8990
|
-
return
|
|
9546
|
+
return path5.join(logDir, `clickup-webhook-${at.toISOString().slice(0, 10)}.jsonl`);
|
|
8991
9547
|
}
|
|
8992
9548
|
function clickUpWebhookRequestFilePath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8993
9549
|
const stamp = at.toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
8994
9550
|
const shortId = crypto.randomBytes(4).toString("hex");
|
|
8995
|
-
return
|
|
9551
|
+
return path5.join(logDir, "requests", `clickup-webhook-${stamp}-${shortId}.json`);
|
|
8996
9552
|
}
|
|
8997
9553
|
function findOptimaPluginTupleOptions(pluginEntries = []) {
|
|
8998
9554
|
if (!Array.isArray(pluginEntries)) return null;
|
|
@@ -9074,7 +9630,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9074
9630
|
};
|
|
9075
9631
|
const errors = [];
|
|
9076
9632
|
if (!config.enabled) errors.push("clickup.enabled must be true");
|
|
9077
|
-
if (!config.basePath || !
|
|
9633
|
+
if (!config.basePath || !path5.isAbsolute(config.basePath)) errors.push("clickup.base_path must be an absolute path");
|
|
9078
9634
|
if (!config.teamId) errors.push("clickup.team_id is required");
|
|
9079
9635
|
if (!config.apiToken) errors.push("clickup.api_token is required");
|
|
9080
9636
|
if (!config.webhook.publicUrl) errors.push("clickup.webhook.public_url is required");
|
|
@@ -9116,21 +9672,21 @@ function sanitizeClickUpWebhookState(state = {}, config = null) {
|
|
|
9116
9672
|
}
|
|
9117
9673
|
function readClickUpWebhookState(worktree, config = null) {
|
|
9118
9674
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9119
|
-
if (!
|
|
9675
|
+
if (!fs5.existsSync(statePath)) return sanitizeClickUpWebhookState({}, config);
|
|
9120
9676
|
try {
|
|
9121
|
-
return sanitizeClickUpWebhookState(JSON.parse(
|
|
9677
|
+
return sanitizeClickUpWebhookState(JSON.parse(fs5.readFileSync(statePath, "utf8")), config);
|
|
9122
9678
|
} catch {
|
|
9123
9679
|
return sanitizeClickUpWebhookState({}, config);
|
|
9124
9680
|
}
|
|
9125
9681
|
}
|
|
9126
9682
|
function writeClickUpWebhookState(worktree, state, config = null) {
|
|
9127
9683
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9128
|
-
|
|
9684
|
+
fs5.mkdirSync(path5.dirname(statePath), { recursive: true, mode: 448 });
|
|
9129
9685
|
const next = sanitizeClickUpWebhookState(state, config);
|
|
9130
|
-
|
|
9686
|
+
fs5.writeFileSync(statePath, `${JSON.stringify(next, null, 2)}
|
|
9131
9687
|
`, { encoding: "utf8", mode: 384 });
|
|
9132
9688
|
try {
|
|
9133
|
-
|
|
9689
|
+
fs5.chmodSync(statePath, 384);
|
|
9134
9690
|
} catch {
|
|
9135
9691
|
}
|
|
9136
9692
|
return next;
|
|
@@ -9146,7 +9702,7 @@ function isClickUpWebhookStateActive(state, config) {
|
|
|
9146
9702
|
function expandHomePath(value = "") {
|
|
9147
9703
|
const input = String(value || "").trim();
|
|
9148
9704
|
if (input === "~") return os.homedir();
|
|
9149
|
-
if (input.startsWith("~/")) return
|
|
9705
|
+
if (input.startsWith("~/")) return path5.join(os.homedir(), input.slice(2));
|
|
9150
9706
|
return input;
|
|
9151
9707
|
}
|
|
9152
9708
|
function resolveSecretReference(value = "") {
|
|
@@ -9156,7 +9712,7 @@ function resolveSecretReference(value = "") {
|
|
|
9156
9712
|
const fileMatch = raw.match(/^\{file:(.+)\}$/);
|
|
9157
9713
|
if (fileMatch) {
|
|
9158
9714
|
try {
|
|
9159
|
-
return
|
|
9715
|
+
return fs5.readFileSync(expandHomePath(fileMatch[1]), "utf8").trim();
|
|
9160
9716
|
} catch {
|
|
9161
9717
|
return "";
|
|
9162
9718
|
}
|
|
@@ -9412,9 +9968,9 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
|
|
|
9412
9968
|
}
|
|
9413
9969
|
function readClickUpCommentLedger(ledgerPath) {
|
|
9414
9970
|
const processed = /* @__PURE__ */ new Set();
|
|
9415
|
-
if (!ledgerPath || !
|
|
9971
|
+
if (!ledgerPath || !fs5.existsSync(ledgerPath)) return processed;
|
|
9416
9972
|
try {
|
|
9417
|
-
const raw =
|
|
9973
|
+
const raw = fs5.readFileSync(ledgerPath, "utf8");
|
|
9418
9974
|
for (const [index, line] of raw.split(/\r?\n/).entries()) {
|
|
9419
9975
|
if (!line.trim()) continue;
|
|
9420
9976
|
try {
|
|
@@ -9431,11 +9987,11 @@ function readClickUpCommentLedger(ledgerPath) {
|
|
|
9431
9987
|
}
|
|
9432
9988
|
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
9433
9989
|
if (!ledgerPath || !entry.key) return;
|
|
9434
|
-
|
|
9435
|
-
|
|
9990
|
+
fs5.mkdirSync(path5.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
9991
|
+
fs5.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
9436
9992
|
`, { encoding: "utf8", mode: 384 });
|
|
9437
9993
|
try {
|
|
9438
|
-
|
|
9994
|
+
fs5.chmodSync(ledgerPath, 384);
|
|
9439
9995
|
} catch {
|
|
9440
9996
|
}
|
|
9441
9997
|
}
|
|
@@ -9837,12 +10393,170 @@ function normalizeOpenCodeSessionMessages(result) {
|
|
|
9837
10393
|
if (Array.isArray(data?.items)) return [...data.items];
|
|
9838
10394
|
return [];
|
|
9839
10395
|
}
|
|
10396
|
+
function clampOpenCodeMessageLimit(limit) {
|
|
10397
|
+
const value = Number(limit);
|
|
10398
|
+
if (!Number.isFinite(value)) return 20;
|
|
10399
|
+
return Math.min(50, Math.max(1, Math.floor(value)));
|
|
10400
|
+
}
|
|
10401
|
+
function clampOpenCodeSessionText(text = "", maxLength = 12e3) {
|
|
10402
|
+
const value = String(text ?? "");
|
|
10403
|
+
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
10404
|
+
}
|
|
10405
|
+
function hashOpenCodeSessionText(text = "") {
|
|
10406
|
+
return crypto.createHash("sha256").update(String(text ?? "")).digest("hex");
|
|
10407
|
+
}
|
|
9840
10408
|
async function readOpenCodeSessionMessages(client, { sessionId, directory, limit = 20 } = {}) {
|
|
9841
10409
|
if (typeof client?.session?.messages !== "function") return null;
|
|
9842
|
-
const query = { limit };
|
|
10410
|
+
const query = { limit: clampOpenCodeMessageLimit(limit) };
|
|
9843
10411
|
if (directory) query.directory = directory;
|
|
9844
10412
|
return normalizeOpenCodeSessionMessages(await client.session.messages({ path: { id: sessionId }, query }));
|
|
9845
10413
|
}
|
|
10414
|
+
function openCodeResultSummary(result) {
|
|
10415
|
+
const data = result?.data ?? result;
|
|
10416
|
+
return {
|
|
10417
|
+
type: Array.isArray(data) ? "array" : typeof data,
|
|
10418
|
+
id: data?.id || data?.sessionID || data?.sessionId || result?.id || result?.sessionID || result?.sessionId || null,
|
|
10419
|
+
keys: data && typeof data === "object" && !Array.isArray(data) ? Object.keys(data).slice(0, 20) : [],
|
|
10420
|
+
dataKeys: result?.data && typeof result.data === "object" && !Array.isArray(result.data) ? Object.keys(result.data).slice(0, 20) : [],
|
|
10421
|
+
status: result?.status || result?.response?.status || null
|
|
10422
|
+
};
|
|
10423
|
+
}
|
|
10424
|
+
function normalizeOpenCodeMessageRole(message = {}) {
|
|
10425
|
+
return message.role || message.info?.role || message.author?.role || message.type || "";
|
|
10426
|
+
}
|
|
10427
|
+
function normalizeOpenCodeMessageAgent(message = {}) {
|
|
10428
|
+
return message.agent || message.info?.agent || message.mode?.agent || message.author?.agent || "";
|
|
10429
|
+
}
|
|
10430
|
+
function normalizeOpenCodeMessageMode(message = {}) {
|
|
10431
|
+
return message.mode || message.info?.mode || message.metadata?.mode || "";
|
|
10432
|
+
}
|
|
10433
|
+
function normalizeOpenCodeMessageId(message = {}) {
|
|
10434
|
+
return message.id || message.messageID || message.messageId || message.info?.id || message.data?.id || null;
|
|
10435
|
+
}
|
|
10436
|
+
function summarizeOpenCodeMessages(messages = [], { snippetLength = 160, maxMessages = 50 } = {}) {
|
|
10437
|
+
const list = Array.isArray(messages) ? messages.slice(0, maxMessages) : [];
|
|
10438
|
+
const safeSnippetLength = Math.min(240, Math.max(20, Number(snippetLength) || 160));
|
|
10439
|
+
return list.map((message, index) => {
|
|
10440
|
+
const text = openCodeMessageText(message).replace(/\s+/g, " ").trim();
|
|
10441
|
+
return {
|
|
10442
|
+
index,
|
|
10443
|
+
id: normalizeOpenCodeMessageId(message),
|
|
10444
|
+
role: normalizeOpenCodeMessageRole(message),
|
|
10445
|
+
agent: normalizeOpenCodeMessageAgent(message),
|
|
10446
|
+
mode: normalizeOpenCodeMessageMode(message),
|
|
10447
|
+
text_length: text.length,
|
|
10448
|
+
text_hash: hashOpenCodeSessionText(text),
|
|
10449
|
+
snippet: text.length > safeSnippetLength ? `${text.slice(0, safeSnippetLength)}...` : text
|
|
10450
|
+
};
|
|
10451
|
+
});
|
|
10452
|
+
}
|
|
10453
|
+
function promptResultAssistantReturned(result) {
|
|
10454
|
+
const parts = normalizePromptResponseParts(result);
|
|
10455
|
+
const text = extractTextParts(parts);
|
|
10456
|
+
const data = result?.data ?? result;
|
|
10457
|
+
return Boolean(text || data?.role === "assistant" || data?.info?.role === "assistant");
|
|
10458
|
+
}
|
|
10459
|
+
async function createOpenCodeSessionControl(client, { directory, title, agent } = {}) {
|
|
10460
|
+
if (typeof client?.session?.create !== "function") throw new Error("OpenCode client does not expose session.create.");
|
|
10461
|
+
const body = { title };
|
|
10462
|
+
if (agent) body.agent = agent;
|
|
10463
|
+
const result = await client.session.create({ query: { directory }, body });
|
|
10464
|
+
const sessionId = extractOpenCodeSessionId(result);
|
|
10465
|
+
if (!sessionId) throw new Error("OpenCode session create response did not include a session id.");
|
|
10466
|
+
return {
|
|
10467
|
+
ok: true,
|
|
10468
|
+
session_id: sessionId,
|
|
10469
|
+
title,
|
|
10470
|
+
directory,
|
|
10471
|
+
agent: agent || null,
|
|
10472
|
+
result_summary: openCodeResultSummary(result)
|
|
10473
|
+
};
|
|
10474
|
+
}
|
|
10475
|
+
async function promptOpenCodeSessionControl(client, { sessionId, directory, text, agent, omitAgent = false, requireReadable = true } = {}) {
|
|
10476
|
+
if (typeof client?.session?.prompt !== "function") throw new Error("OpenCode client does not expose session.prompt.");
|
|
10477
|
+
if (requireReadable) await assertOpenCodeSessionReadableForDirectory(client, { sessionId, directory });
|
|
10478
|
+
const safeText = clampOpenCodeSessionText(text);
|
|
10479
|
+
const body = { parts: [{ type: "text", text: safeText }] };
|
|
10480
|
+
if (!omitAgent && agent) body.agent = agent;
|
|
10481
|
+
const result = await client.session.prompt({ path: { id: sessionId }, query: { directory }, body });
|
|
10482
|
+
return {
|
|
10483
|
+
ok: true,
|
|
10484
|
+
session_id: sessionId,
|
|
10485
|
+
directory,
|
|
10486
|
+
agent: omitAgent ? null : agent || null,
|
|
10487
|
+
omit_agent: Boolean(omitAgent),
|
|
10488
|
+
text_length: safeText.length,
|
|
10489
|
+
text_truncated: String(text ?? "").length > safeText.length,
|
|
10490
|
+
text_hash: hashOpenCodeSessionText(safeText),
|
|
10491
|
+
assistant_returned: promptResultAssistantReturned(result),
|
|
10492
|
+
returned_parts_count: normalizePromptResponseParts(result).length,
|
|
10493
|
+
result_summary: openCodeResultSummary(result)
|
|
10494
|
+
};
|
|
10495
|
+
}
|
|
10496
|
+
async function assertOpenCodeSessionReadableForDirectory(client, { sessionId, directory } = {}) {
|
|
10497
|
+
try {
|
|
10498
|
+
await readOpenCodeSessionMessages(client, { sessionId, directory, limit: 1 });
|
|
10499
|
+
return true;
|
|
10500
|
+
} catch (error) {
|
|
10501
|
+
throw new Error(`OpenCode session '${sessionId}' is not readable for the requested safe directory.`);
|
|
10502
|
+
}
|
|
10503
|
+
}
|
|
10504
|
+
async function readOpenCodeSessionControl(client, { sessionId, directory, limit = 20 } = {}) {
|
|
10505
|
+
const boundedLimit = clampOpenCodeMessageLimit(limit);
|
|
10506
|
+
const messages = await readOpenCodeSessionMessages(client, { sessionId, directory, limit: boundedLimit });
|
|
10507
|
+
if (!messages) throw new Error("OpenCode client does not expose session.messages.");
|
|
10508
|
+
return {
|
|
10509
|
+
ok: true,
|
|
10510
|
+
session_id: sessionId,
|
|
10511
|
+
directory: directory || null,
|
|
10512
|
+
limit: boundedLimit,
|
|
10513
|
+
count: messages.length,
|
|
10514
|
+
messages: summarizeOpenCodeMessages(messages)
|
|
10515
|
+
};
|
|
10516
|
+
}
|
|
10517
|
+
async function probeOpenCodeSessionControl(client, { directory, agent, omitAgentOnPrompt = false, text = "" } = {}) {
|
|
10518
|
+
const requestedMarker = text || `optima-session-probe-${crypto.randomUUID()}`;
|
|
10519
|
+
const marker = clampOpenCodeSessionText(requestedMarker);
|
|
10520
|
+
const create = await createOpenCodeSessionControl(client, {
|
|
10521
|
+
directory,
|
|
10522
|
+
title: `Optima session probe ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
10523
|
+
agent
|
|
10524
|
+
});
|
|
10525
|
+
const prompt = await promptOpenCodeSessionControl(client, {
|
|
10526
|
+
sessionId: create.session_id,
|
|
10527
|
+
directory,
|
|
10528
|
+
text: marker,
|
|
10529
|
+
agent,
|
|
10530
|
+
omitAgent: omitAgentOnPrompt,
|
|
10531
|
+
requireReadable: false
|
|
10532
|
+
});
|
|
10533
|
+
const rawMessages = await readOpenCodeSessionMessages(client, { sessionId: create.session_id, directory, limit: 50 });
|
|
10534
|
+
if (!rawMessages) throw new Error("OpenCode client does not expose session.messages.");
|
|
10535
|
+
const fullMessageText = rawMessages.map(openCodeMessageText).join("\n");
|
|
10536
|
+
const messages = {
|
|
10537
|
+
ok: true,
|
|
10538
|
+
session_id: create.session_id,
|
|
10539
|
+
directory,
|
|
10540
|
+
limit: 50,
|
|
10541
|
+
count: rawMessages.length,
|
|
10542
|
+
messages: summarizeOpenCodeMessages(rawMessages)
|
|
10543
|
+
};
|
|
10544
|
+
return {
|
|
10545
|
+
ok: true,
|
|
10546
|
+
session_id: create.session_id,
|
|
10547
|
+
directory,
|
|
10548
|
+
agent: agent || null,
|
|
10549
|
+
omit_agent_on_prompt: Boolean(omitAgentOnPrompt),
|
|
10550
|
+
marker_length: marker.length,
|
|
10551
|
+
marker_truncated: requestedMarker.length > marker.length,
|
|
10552
|
+
marker_hash: hashOpenCodeSessionText(marker),
|
|
10553
|
+
marker_visible: fullMessageText.includes(marker),
|
|
10554
|
+
assistant_visible: rawMessages.some((message) => normalizeLooseToken(normalizeOpenCodeMessageRole(message)) === "assistant" || Boolean(normalizeOpenCodeMessageAgent(message) && openCodeMessageText(message))),
|
|
10555
|
+
create,
|
|
10556
|
+
prompt,
|
|
10557
|
+
messages
|
|
10558
|
+
};
|
|
10559
|
+
}
|
|
9846
10560
|
function openCodeMessageText(message) {
|
|
9847
10561
|
const parts = [message?.text, message?.content, message?.message, message?.body?.text, message?.data?.text];
|
|
9848
10562
|
const partList = Array.isArray(message?.parts) ? message.parts : Array.isArray(message?.body?.parts) ? message.body.parts : [];
|
|
@@ -9993,9 +10707,9 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
9993
10707
|
}
|
|
9994
10708
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
9995
10709
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
9996
|
-
|
|
10710
|
+
fs5.mkdirSync(path5.dirname(logPath), { recursive: true });
|
|
9997
10711
|
const safeEntry = { ...entry, at: entry.at || (/* @__PURE__ */ new Date()).toISOString() };
|
|
9998
|
-
|
|
10712
|
+
fs5.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
9999
10713
|
`, "utf8");
|
|
10000
10714
|
}
|
|
10001
10715
|
function clickUpWebhookLifecycleLog(worktree, entry) {
|
|
@@ -10024,7 +10738,7 @@ function closeClickUpWebhookServer(server) {
|
|
|
10024
10738
|
});
|
|
10025
10739
|
}
|
|
10026
10740
|
function managedClickUpWebhookKey({ worktree, state, config } = {}) {
|
|
10027
|
-
return [
|
|
10741
|
+
return [path5.resolve(worktree || process.cwd()), state?.webhookId || "", config?.webhook?.publicUrl || ""].join("|");
|
|
10028
10742
|
}
|
|
10029
10743
|
async function deleteClickUpWebhookBestEffort({ webhookId, clickupClient, worktree, reason = "cleanup" } = {}) {
|
|
10030
10744
|
const id = String(webhookId || "").trim();
|
|
@@ -10123,13 +10837,13 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
10123
10837
|
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
10124
10838
|
if (level === "error" && !failed) return;
|
|
10125
10839
|
const logDir = clickUpWebhookAuditLogDir();
|
|
10126
|
-
|
|
10840
|
+
fs5.mkdirSync(logDir, { recursive: true });
|
|
10127
10841
|
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
10128
10842
|
let requestFile;
|
|
10129
10843
|
if (level === "verbose") {
|
|
10130
10844
|
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
10131
|
-
|
|
10132
|
-
requestFile =
|
|
10845
|
+
fs5.mkdirSync(path5.dirname(absoluteRequestFile), { recursive: true });
|
|
10846
|
+
requestFile = path5.relative(logDir, absoluteRequestFile).split(path5.sep).join("/");
|
|
10133
10847
|
const parsedBody = payload || (() => {
|
|
10134
10848
|
try {
|
|
10135
10849
|
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
@@ -10145,11 +10859,11 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
10145
10859
|
headers,
|
|
10146
10860
|
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
10147
10861
|
}, secretValues);
|
|
10148
|
-
|
|
10862
|
+
fs5.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
10149
10863
|
`, "utf8");
|
|
10150
10864
|
}
|
|
10151
10865
|
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
10152
|
-
|
|
10866
|
+
fs5.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
10153
10867
|
`, "utf8");
|
|
10154
10868
|
} catch {
|
|
10155
10869
|
}
|
|
@@ -10505,7 +11219,7 @@ function clickUpListenerFingerprint({ config, state, worktree, clickupClient, op
|
|
|
10505
11219
|
return JSON.stringify({
|
|
10506
11220
|
publicUrl: config?.webhook?.publicUrl || "",
|
|
10507
11221
|
path: clickUpWebhookExpectedPath(config),
|
|
10508
|
-
worktree:
|
|
11222
|
+
worktree: path5.resolve(worktree || process.cwd()),
|
|
10509
11223
|
webhookId: state?.webhookId || "",
|
|
10510
11224
|
secret: state?.secret || "",
|
|
10511
11225
|
events: config?.webhook?.events || [],
|
|
@@ -10602,17 +11316,17 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, o
|
|
|
10602
11316
|
return result;
|
|
10603
11317
|
}
|
|
10604
11318
|
function legacyVariantPath(destinationPath) {
|
|
10605
|
-
const parsed =
|
|
11319
|
+
const parsed = path5.parse(destinationPath);
|
|
10606
11320
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
10607
|
-
if (parsed.ext) return
|
|
10608
|
-
return
|
|
11321
|
+
if (parsed.ext) return path5.join(parsed.dir, `${parsed.name}.legacy-${stamp}${parsed.ext}`);
|
|
11322
|
+
return path5.join(parsed.dir, `${parsed.base}.legacy-${stamp}`);
|
|
10609
11323
|
}
|
|
10610
11324
|
function normalizeWorkflowTaskPath(taskPath) {
|
|
10611
11325
|
if (typeof taskPath !== "string") return { ok: false, message: "Error: task_path is required." };
|
|
10612
11326
|
const trimmed = taskPath.trim();
|
|
10613
11327
|
if (!trimmed) return { ok: false, message: "Error: task_path is required." };
|
|
10614
11328
|
const normalized = trimmed.replace(/\\/g, "/");
|
|
10615
|
-
if (
|
|
11329
|
+
if (path5.isAbsolute(trimmed)) return { ok: true, taskPath: trimmed };
|
|
10616
11330
|
if (normalized === "tasks" || normalized.startsWith("tasks/")) {
|
|
10617
11331
|
return {
|
|
10618
11332
|
ok: false,
|
|
@@ -10628,76 +11342,80 @@ function normalizeWorkflowTaskPath(taskPath) {
|
|
|
10628
11342
|
return { ok: true, taskPath: trimmed };
|
|
10629
11343
|
}
|
|
10630
11344
|
function mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState = null) {
|
|
11345
|
+
const sourceWasTracked = isGitTracked(gitState, sourcePath);
|
|
10631
11346
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
10632
|
-
if (!
|
|
10633
|
-
|
|
10634
|
-
|
|
11347
|
+
if (!fs5.existsSync(destinationPath)) {
|
|
11348
|
+
fs5.mkdirSync(path5.dirname(destinationPath), { recursive: true });
|
|
11349
|
+
fs5.renameSync(sourcePath, destinationPath);
|
|
11350
|
+
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10635
11351
|
return;
|
|
10636
11352
|
}
|
|
10637
|
-
const ext =
|
|
11353
|
+
const ext = path5.extname(destinationPath).toLowerCase();
|
|
10638
11354
|
if ([".yaml", ".yml", ".json"].includes(ext)) {
|
|
10639
|
-
const sourceRaw =
|
|
10640
|
-
const destRaw =
|
|
10641
|
-
const parser = ext === ".json" ? JSON :
|
|
11355
|
+
const sourceRaw = fs5.readFileSync(sourcePath, "utf8");
|
|
11356
|
+
const destRaw = fs5.readFileSync(destinationPath, "utf8");
|
|
11357
|
+
const parser = ext === ".json" ? JSON : import_yaml3.default;
|
|
10642
11358
|
const sourceValue = parser.parse(sourceRaw) || {};
|
|
10643
11359
|
const destValue = parser.parse(destRaw) || {};
|
|
10644
11360
|
const merged = mergeStructuredValues(sourceValue, destValue);
|
|
10645
11361
|
const serialized = ext === ".json" ? `${JSON.stringify(merged, null, 2)}
|
|
10646
|
-
` :
|
|
10647
|
-
|
|
10648
|
-
|
|
11362
|
+
` : import_yaml3.default.stringify(merged);
|
|
11363
|
+
fs5.writeFileSync(destinationPath, serialized, "utf8");
|
|
11364
|
+
fs5.unlinkSync(sourcePath);
|
|
10649
11365
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10650
11366
|
return;
|
|
10651
11367
|
}
|
|
10652
11368
|
if (ext === ".md") {
|
|
10653
|
-
const sourceRaw =
|
|
10654
|
-
const destRaw =
|
|
11369
|
+
const sourceRaw = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
11370
|
+
const destRaw = fs5.readFileSync(destinationPath, "utf8").trimEnd();
|
|
10655
11371
|
if (sourceRaw && !destRaw.includes(sourceRaw)) {
|
|
10656
11372
|
const marker = `## Legacy Content From ${relativeSource}`;
|
|
10657
|
-
|
|
11373
|
+
fs5.writeFileSync(destinationPath, `${destRaw}
|
|
10658
11374
|
|
|
10659
11375
|
${marker}
|
|
10660
11376
|
|
|
10661
11377
|
${sourceRaw}
|
|
10662
11378
|
`, "utf8");
|
|
10663
11379
|
}
|
|
10664
|
-
|
|
11380
|
+
fs5.unlinkSync(sourcePath);
|
|
10665
11381
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10666
11382
|
return;
|
|
10667
11383
|
}
|
|
10668
11384
|
const preservedPath = legacyVariantPath(destinationPath);
|
|
10669
|
-
|
|
11385
|
+
fs5.renameSync(sourcePath, preservedPath);
|
|
10670
11386
|
stageGitAwareMerge(sourcePath, preservedPath, gitState);
|
|
10671
11387
|
}
|
|
10672
|
-
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
10673
|
-
if (!
|
|
10674
|
-
const stat =
|
|
11388
|
+
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource = path5.basename(sourcePath), gitState = null) {
|
|
11389
|
+
if (!fs5.existsSync(sourcePath)) return;
|
|
11390
|
+
const stat = fs5.statSync(sourcePath);
|
|
10675
11391
|
if (stat.isDirectory()) {
|
|
11392
|
+
const sourceWasTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
10676
11393
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
10677
|
-
if (!
|
|
10678
|
-
|
|
10679
|
-
|
|
11394
|
+
if (!fs5.existsSync(destinationPath)) {
|
|
11395
|
+
fs5.mkdirSync(path5.dirname(destinationPath), { recursive: true });
|
|
11396
|
+
fs5.renameSync(sourcePath, destinationPath);
|
|
11397
|
+
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10680
11398
|
return;
|
|
10681
11399
|
}
|
|
10682
|
-
|
|
10683
|
-
for (const entry of
|
|
11400
|
+
fs5.mkdirSync(destinationPath, { recursive: true });
|
|
11401
|
+
for (const entry of fs5.readdirSync(sourcePath)) {
|
|
10684
11402
|
mergePathIntoDestination(
|
|
10685
|
-
|
|
10686
|
-
|
|
10687
|
-
|
|
11403
|
+
path5.join(sourcePath, entry),
|
|
11404
|
+
path5.join(destinationPath, entry),
|
|
11405
|
+
path5.join(relativeSource, entry),
|
|
10688
11406
|
gitState
|
|
10689
11407
|
);
|
|
10690
11408
|
}
|
|
10691
|
-
|
|
11409
|
+
fs5.rmSync(sourcePath, { recursive: true, force: true });
|
|
10692
11410
|
return;
|
|
10693
11411
|
}
|
|
10694
11412
|
mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState);
|
|
10695
11413
|
}
|
|
10696
11414
|
function isOptimaPluginPackageWorktree(worktree) {
|
|
10697
|
-
const packageJsonPath =
|
|
10698
|
-
if (!
|
|
11415
|
+
const packageJsonPath = path5.join(worktree, "package.json");
|
|
11416
|
+
if (!fs5.existsSync(packageJsonPath)) return false;
|
|
10699
11417
|
try {
|
|
10700
|
-
const pkg = JSON.parse(
|
|
11418
|
+
const pkg = JSON.parse(fs5.readFileSync(packageJsonPath, "utf8"));
|
|
10701
11419
|
return pkg?.name === "@defend-tech/opencode-optima";
|
|
10702
11420
|
} catch {
|
|
10703
11421
|
return false;
|
|
@@ -10706,59 +11424,59 @@ function isOptimaPluginPackageWorktree(worktree) {
|
|
|
10706
11424
|
function migrateLegacyOptimaLayout(worktree) {
|
|
10707
11425
|
const gitState = gitMigrationState(worktree);
|
|
10708
11426
|
const migrations = [
|
|
10709
|
-
[
|
|
10710
|
-
[
|
|
10711
|
-
[
|
|
10712
|
-
[
|
|
10713
|
-
[
|
|
10714
|
-
[
|
|
11427
|
+
[path5.join(legacyOrbitaDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".orbita", "orbita.yaml")],
|
|
11428
|
+
[path5.join(legacyOrbitaDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".orbita", "staticeng.yaml")],
|
|
11429
|
+
[path5.join(legacyOrbitaDir(worktree), ".config"), optimaLocalConfigDir(worktree), path5.join(".orbita", ".config")],
|
|
11430
|
+
[path5.join(legacyOrbitaDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".orbita", "config")],
|
|
11431
|
+
[path5.join(legacyOrbitaDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".orbita", "runtime")],
|
|
11432
|
+
[path5.join(legacyOrbitaDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".orbita", "generated")],
|
|
10715
11433
|
[legacyOrbitaDir(worktree), optimaDir(worktree), ".orbita"],
|
|
10716
|
-
[
|
|
10717
|
-
[
|
|
10718
|
-
[
|
|
10719
|
-
[
|
|
10720
|
-
[
|
|
10721
|
-
[
|
|
11434
|
+
[path5.join(legacyStaticEngDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".staticeng", "staticeng.yaml")],
|
|
11435
|
+
[path5.join(legacyStaticEngDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".staticeng", "orbita.yaml")],
|
|
11436
|
+
[path5.join(legacyStaticEngDir(worktree), ".config"), optimaLocalConfigDir(worktree), path5.join(".staticeng", ".config")],
|
|
11437
|
+
[path5.join(legacyStaticEngDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".staticeng", "config")],
|
|
11438
|
+
[path5.join(legacyStaticEngDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".staticeng", "runtime")],
|
|
11439
|
+
[path5.join(legacyStaticEngDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".staticeng", "generated")],
|
|
10722
11440
|
[legacyStaticEngDir(worktree), optimaDir(worktree), ".staticeng"],
|
|
10723
|
-
[
|
|
10724
|
-
[
|
|
10725
|
-
[
|
|
10726
|
-
[
|
|
10727
|
-
[
|
|
10728
|
-
[
|
|
10729
|
-
[
|
|
10730
|
-
[
|
|
10731
|
-
[
|
|
10732
|
-
[
|
|
10733
|
-
[
|
|
10734
|
-
[
|
|
11441
|
+
[path5.join(legacyNomadworkDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "nomadworks.yaml")],
|
|
11442
|
+
[path5.join(legacyNomadworksDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "nomadworks.yaml")],
|
|
11443
|
+
[path5.join(legacyNomadworkDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "staticeng.yaml")],
|
|
11444
|
+
[path5.join(legacyNomadworksDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "staticeng.yaml")],
|
|
11445
|
+
[path5.join(legacyNomadworkDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "orbita.yaml")],
|
|
11446
|
+
[path5.join(legacyNomadworksDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "orbita.yaml")],
|
|
11447
|
+
[path5.join(legacyNomadworkDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".nomadwork", "runtime")],
|
|
11448
|
+
[path5.join(legacyNomadworksDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".nomadworks", "runtime")],
|
|
11449
|
+
[path5.join(legacyNomadworkDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".nomadwork", "generated")],
|
|
11450
|
+
[path5.join(legacyNomadworksDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".nomadworks", "generated")],
|
|
11451
|
+
[path5.join(legacyNomadworkDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".nomadwork", "config")],
|
|
11452
|
+
[path5.join(legacyNomadworksDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".nomadworks", "config")],
|
|
10735
11453
|
[legacyNomadworkDir(worktree), optimaDir(worktree), ".nomadwork"],
|
|
10736
11454
|
[legacyNomadworksDir(worktree), optimaDir(worktree), ".nomadworks"],
|
|
10737
|
-
[
|
|
10738
|
-
[
|
|
10739
|
-
[
|
|
10740
|
-
[
|
|
10741
|
-
[
|
|
11455
|
+
[path5.join(worktree, "tasks"), optimaTasksDir(worktree), "tasks"],
|
|
11456
|
+
[path5.join(worktree, "evidences"), optimaEvidencesDir(worktree), "evidences"],
|
|
11457
|
+
[path5.join(worktree, "docs", "scrs"), optimaScrsDir(worktree), path5.join("docs", "scrs")],
|
|
11458
|
+
[path5.join(worktree, "codemap.yml"), optimaCodemapPath(worktree), "codemap.yml"],
|
|
11459
|
+
[path5.join(worktree, "codemap.yaml"), optimaCodemapPath(worktree), "codemap.yaml"]
|
|
10742
11460
|
];
|
|
10743
11461
|
if (!isOptimaPluginPackageWorktree(worktree)) {
|
|
10744
|
-
migrations.push([
|
|
11462
|
+
migrations.push([path5.join(worktree, "policies"), repoPoliciesDir(worktree), "policies"]);
|
|
10745
11463
|
}
|
|
10746
11464
|
for (const [source, destination, relativeSource] of migrations) {
|
|
10747
|
-
if (
|
|
11465
|
+
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
10748
11466
|
}
|
|
10749
11467
|
for (const [source, destination, relativeSource] of [
|
|
10750
|
-
[
|
|
10751
|
-
[
|
|
10752
|
-
[
|
|
10753
|
-
[
|
|
11468
|
+
[path5.join(optimaDir(worktree), "optima.yaml"), repoConfigPath(worktree), path5.join(".optima", "optima.yaml")],
|
|
11469
|
+
[path5.join(optimaDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".optima", "config")],
|
|
11470
|
+
[path5.join(optimaDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".optima", "runtime")],
|
|
11471
|
+
[path5.join(optimaDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".optima", "generated")]
|
|
10754
11472
|
]) {
|
|
10755
|
-
if (
|
|
11473
|
+
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
10756
11474
|
}
|
|
10757
11475
|
}
|
|
10758
11476
|
function listMarkdownFiles(dirPath) {
|
|
10759
|
-
if (!
|
|
11477
|
+
if (!fs5.existsSync(dirPath)) return [];
|
|
10760
11478
|
try {
|
|
10761
|
-
return
|
|
11479
|
+
return fs5.readdirSync(dirPath).filter((file) => file.endsWith(".md") && file.toLowerCase() !== "readme.md");
|
|
10762
11480
|
} catch (e) {
|
|
10763
11481
|
console.error(`[Optima] Failed to read markdown files from ${dirPath}:`, e);
|
|
10764
11482
|
return [];
|
|
@@ -10768,67 +11486,6 @@ function normalizePolicyExtraction(value) {
|
|
|
10768
11486
|
if (typeof value !== "string") return "none";
|
|
10769
11487
|
return value.trim().toLowerCase() === "all" ? "all" : "none";
|
|
10770
11488
|
}
|
|
10771
|
-
function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
10772
|
-
const trimmed = includeRef.trim();
|
|
10773
|
-
const scopedMatch = trimmed.match(/^([a-z]+):(.*)$/i);
|
|
10774
|
-
const scope = scopedMatch?.[1]?.toLowerCase();
|
|
10775
|
-
const target = scopedMatch ? scopedMatch[2].trim() : trimmed;
|
|
10776
|
-
const resolveRelative = (baseDir, relativePath) => {
|
|
10777
|
-
if (!relativePath) return null;
|
|
10778
|
-
return path2.isAbsolute(relativePath) ? relativePath : path2.join(baseDir, relativePath);
|
|
10779
|
-
};
|
|
10780
|
-
const withCompactPreference = (paths) => {
|
|
10781
|
-
if (!options.preferCompactPromptDocs) return paths;
|
|
10782
|
-
const compactPaths = paths.map((filePath) => filePath ? compactPromptPath(filePath) : null).filter(Boolean);
|
|
10783
|
-
return [...compactPaths, ...paths];
|
|
10784
|
-
};
|
|
10785
|
-
if (scope === "plugin") {
|
|
10786
|
-
for (const filePath of withCompactPreference([resolveRelative(bundleRoot, target)])) {
|
|
10787
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10788
|
-
}
|
|
10789
|
-
return null;
|
|
10790
|
-
}
|
|
10791
|
-
if (scope === "repo") {
|
|
10792
|
-
for (const filePath of withCompactPreference([resolveRelative(optimaDir(repoRoot), target)])) {
|
|
10793
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10794
|
-
}
|
|
10795
|
-
return null;
|
|
10796
|
-
}
|
|
10797
|
-
if (scope === "policy") {
|
|
10798
|
-
const candidates2 = withCompactPreference([
|
|
10799
|
-
resolveRelative(repoPoliciesDir(repoRoot), target),
|
|
10800
|
-
resolveRelative(BUNDLE_POLICIES_DIR, target)
|
|
10801
|
-
]);
|
|
10802
|
-
for (const filePath of candidates2) {
|
|
10803
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10804
|
-
}
|
|
10805
|
-
return null;
|
|
10806
|
-
}
|
|
10807
|
-
const candidates = withCompactPreference([
|
|
10808
|
-
resolveRelative(repoRoot, target),
|
|
10809
|
-
resolveRelative(bundleRoot, target)
|
|
10810
|
-
]);
|
|
10811
|
-
for (const filePath of candidates) {
|
|
10812
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10813
|
-
}
|
|
10814
|
-
return null;
|
|
10815
|
-
}
|
|
10816
|
-
function resolveIncludes(text, repoRoot, bundleRoot, options = {}) {
|
|
10817
|
-
const includeRegex = /<include:(.*?)>/g;
|
|
10818
|
-
return text.replace(includeRegex, (match, includeRef) => {
|
|
10819
|
-
const filePath = resolveIncludeFile(includeRef, repoRoot, bundleRoot, options);
|
|
10820
|
-
if (!filePath) {
|
|
10821
|
-
console.warn(`[Optima] Include file not found: ${includeRef}`);
|
|
10822
|
-
return `
|
|
10823
|
-
|
|
10824
|
-
# ERROR: Include file not found: ${includeRef}
|
|
10825
|
-
|
|
10826
|
-
`;
|
|
10827
|
-
}
|
|
10828
|
-
const content = fs2.readFileSync(filePath, "utf8");
|
|
10829
|
-
return resolveIncludes(content, repoRoot, bundleRoot, options);
|
|
10830
|
-
});
|
|
10831
|
-
}
|
|
10832
11489
|
function parseFrontmatter(mdText) {
|
|
10833
11490
|
const lines = mdText.split(/\r?\n/);
|
|
10834
11491
|
if (lines[0]?.trim() !== "---") return { data: {}, body: mdText };
|
|
@@ -10843,7 +11500,7 @@ function parseFrontmatter(mdText) {
|
|
|
10843
11500
|
const fmText = lines.slice(1, end).join("\n");
|
|
10844
11501
|
const body = lines.slice(end + 1).join("\n");
|
|
10845
11502
|
try {
|
|
10846
|
-
return { data:
|
|
11503
|
+
return { data: import_yaml3.default.parse(fmText) || {}, body };
|
|
10847
11504
|
} catch {
|
|
10848
11505
|
return { data: {}, body };
|
|
10849
11506
|
}
|
|
@@ -10860,10 +11517,10 @@ function toModelString(provider, model) {
|
|
|
10860
11517
|
}
|
|
10861
11518
|
function readTaskMetadata(taskPath, worktree) {
|
|
10862
11519
|
if (!taskPath) return {};
|
|
10863
|
-
const absoluteTaskPath =
|
|
10864
|
-
if (!
|
|
11520
|
+
const absoluteTaskPath = path5.isAbsolute(taskPath) ? taskPath : path5.join(worktree, taskPath);
|
|
11521
|
+
if (!fs5.existsSync(absoluteTaskPath)) return {};
|
|
10865
11522
|
try {
|
|
10866
|
-
const raw =
|
|
11523
|
+
const raw = fs5.readFileSync(absoluteTaskPath, "utf8");
|
|
10867
11524
|
const { data } = parseFrontmatter(raw);
|
|
10868
11525
|
return {
|
|
10869
11526
|
complexity: typeof data.complexity === "string" ? data.complexity.trim().toLowerCase() : void 0,
|
|
@@ -10888,11 +11545,11 @@ function slugifyTitle(input) {
|
|
|
10888
11545
|
}
|
|
10889
11546
|
function loadDiscussionRegistry(worktree) {
|
|
10890
11547
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
10891
|
-
if (!
|
|
11548
|
+
if (!fs5.existsSync(registryPath)) {
|
|
10892
11549
|
return { version: 1, active: {} };
|
|
10893
11550
|
}
|
|
10894
11551
|
try {
|
|
10895
|
-
const parsed = JSON.parse(
|
|
11552
|
+
const parsed = JSON.parse(fs5.readFileSync(registryPath, "utf8"));
|
|
10896
11553
|
const registry = {
|
|
10897
11554
|
version: 1,
|
|
10898
11555
|
active: Object.fromEntries(Object.entries(parsed.active || {}).map(([key, value]) => [key, normalizeLegacyDiscussionEntry(value)]))
|
|
@@ -10905,34 +11562,34 @@ function loadDiscussionRegistry(worktree) {
|
|
|
10905
11562
|
}
|
|
10906
11563
|
function saveDiscussionRegistry(worktree, registry) {
|
|
10907
11564
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
10908
|
-
const runtimeDir =
|
|
10909
|
-
if (!
|
|
10910
|
-
|
|
11565
|
+
const runtimeDir = path5.dirname(registryPath);
|
|
11566
|
+
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
11567
|
+
fs5.writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf8");
|
|
10911
11568
|
}
|
|
10912
11569
|
function runtimeDiscussionsDir(worktree) {
|
|
10913
|
-
return
|
|
11570
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime", "discussions");
|
|
10914
11571
|
}
|
|
10915
11572
|
function archivedRuntimeDiscussionsDir(worktree) {
|
|
10916
|
-
return
|
|
11573
|
+
return path5.join(runtimeDiscussionsDir(worktree), "archive");
|
|
10917
11574
|
}
|
|
10918
11575
|
function finalDiscussionsDir(worktree) {
|
|
10919
|
-
return
|
|
11576
|
+
return path5.join(optimaTasksDir(worktree), "discussions");
|
|
10920
11577
|
}
|
|
10921
11578
|
function nextDiscussionIdentity(worktree, title) {
|
|
10922
11579
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
10923
11580
|
const runtimeDir = runtimeDiscussionsDir(worktree);
|
|
10924
|
-
if (!
|
|
10925
|
-
if (!
|
|
11581
|
+
if (!fs5.existsSync(discussionsDir)) fs5.mkdirSync(discussionsDir, { recursive: true });
|
|
11582
|
+
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
10926
11583
|
let sequence = 1;
|
|
10927
11584
|
while (true) {
|
|
10928
11585
|
const id = `DISCUSSION-${String(sequence).padStart(3, "0")}`;
|
|
10929
11586
|
const filename = `${id}-${slugifyTitle(title)}.md`;
|
|
10930
|
-
const summaryRelativePath =
|
|
10931
|
-
const summaryAbsolutePath =
|
|
11587
|
+
const summaryRelativePath = path5.join(".optima", "tasks", "discussions", filename);
|
|
11588
|
+
const summaryAbsolutePath = path5.join(worktree, summaryRelativePath);
|
|
10932
11589
|
const transcriptFilename = `${id}-transcript.md`;
|
|
10933
|
-
const transcriptRelativePath =
|
|
10934
|
-
const transcriptAbsolutePath =
|
|
10935
|
-
if (!
|
|
11590
|
+
const transcriptRelativePath = path5.join(".optima", ".config", "runtime", "discussions", transcriptFilename);
|
|
11591
|
+
const transcriptAbsolutePath = path5.join(worktree, transcriptRelativePath);
|
|
11592
|
+
if (!fs5.existsSync(summaryAbsolutePath) && !fs5.existsSync(transcriptAbsolutePath)) {
|
|
10936
11593
|
return {
|
|
10937
11594
|
id,
|
|
10938
11595
|
filename,
|
|
@@ -10948,37 +11605,37 @@ function nextDiscussionIdentity(worktree, title) {
|
|
|
10948
11605
|
}
|
|
10949
11606
|
function findDiscussionById(worktree, discussionID) {
|
|
10950
11607
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
10951
|
-
if (!
|
|
10952
|
-
const entries =
|
|
11608
|
+
if (!fs5.existsSync(discussionsDir)) return null;
|
|
11609
|
+
const entries = fs5.readdirSync(discussionsDir).filter((name) => name.startsWith(`${discussionID}-`) && name.endsWith(".md"));
|
|
10953
11610
|
if (entries.length === 0) return null;
|
|
10954
11611
|
const filename = entries.sort()[0];
|
|
10955
11612
|
const transcriptFilename = `${discussionID}-transcript.md`;
|
|
10956
11613
|
return {
|
|
10957
11614
|
id: discussionID,
|
|
10958
11615
|
filename,
|
|
10959
|
-
summaryRelativePath:
|
|
10960
|
-
summaryAbsolutePath:
|
|
11616
|
+
summaryRelativePath: path5.join(".optima", "tasks", "discussions", filename),
|
|
11617
|
+
summaryAbsolutePath: path5.join(discussionsDir, filename),
|
|
10961
11618
|
transcriptFilename,
|
|
10962
|
-
transcriptRelativePath:
|
|
10963
|
-
transcriptAbsolutePath:
|
|
11619
|
+
transcriptRelativePath: path5.join(".optima", ".config", "runtime", "discussions", transcriptFilename),
|
|
11620
|
+
transcriptAbsolutePath: path5.join(runtimeDiscussionsDir(worktree), transcriptFilename)
|
|
10964
11621
|
};
|
|
10965
11622
|
}
|
|
10966
11623
|
function parseDiscussionFile(filePath) {
|
|
10967
|
-
const raw =
|
|
11624
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
10968
11625
|
const { data, body } = parseFrontmatter(raw);
|
|
10969
11626
|
return { data, body: body.trimStart() };
|
|
10970
11627
|
}
|
|
10971
11628
|
function writeDiscussionFile(filePath, frontmatter, body) {
|
|
10972
11629
|
const serialized = `---
|
|
10973
|
-
${
|
|
11630
|
+
${import_yaml3.default.stringify(frontmatter).trim()}
|
|
10974
11631
|
---
|
|
10975
11632
|
|
|
10976
11633
|
${body.trimEnd()}
|
|
10977
11634
|
`;
|
|
10978
|
-
|
|
11635
|
+
fs5.writeFileSync(filePath, serialized, "utf8");
|
|
10979
11636
|
}
|
|
10980
11637
|
function setDiscussionStatus(filePath, status) {
|
|
10981
|
-
if (!
|
|
11638
|
+
if (!fs5.existsSync(filePath)) return;
|
|
10982
11639
|
const { data, body } = parseDiscussionFile(filePath);
|
|
10983
11640
|
writeDiscussionFile(filePath, { ...data, status }, body);
|
|
10984
11641
|
}
|
|
@@ -11043,18 +11700,18 @@ async function appendMessageIfNeeded(client, worktree, registry, sessionID, mess
|
|
|
11043
11700
|
});
|
|
11044
11701
|
const text = extractTextParts(response.data.parts || []);
|
|
11045
11702
|
if (!text) return;
|
|
11046
|
-
appendDiscussionMessage(
|
|
11703
|
+
appendDiscussionMessage(path5.join(worktree, discussion.transcriptPath), speaker, text, messageID);
|
|
11047
11704
|
discussion.appendedMessageIDs ??= [];
|
|
11048
11705
|
discussion.appendedMessageIDs.push(messageID);
|
|
11049
11706
|
saveDiscussionRegistry(worktree, registry);
|
|
11050
11707
|
}
|
|
11051
11708
|
async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
11052
|
-
const transcriptPath =
|
|
11053
|
-
const summaryPath =
|
|
11054
|
-
const summaryDir =
|
|
11055
|
-
if (!
|
|
11056
|
-
const hasExistingSummary =
|
|
11057
|
-
const priorMtimeMs = hasExistingSummary ?
|
|
11709
|
+
const transcriptPath = path5.join(worktree, discussion.transcriptPath);
|
|
11710
|
+
const summaryPath = path5.join(worktree, discussion.summaryPath);
|
|
11711
|
+
const summaryDir = path5.dirname(summaryPath);
|
|
11712
|
+
if (!fs5.existsSync(summaryDir)) fs5.mkdirSync(summaryDir, { recursive: true });
|
|
11713
|
+
const hasExistingSummary = fs5.existsSync(summaryPath);
|
|
11714
|
+
const priorMtimeMs = hasExistingSummary ? fs5.statSync(summaryPath).mtimeMs : null;
|
|
11058
11715
|
const summarizerSession = await client.session.create({
|
|
11059
11716
|
body: { title: `Discussion Summary: ${discussion.id}` }
|
|
11060
11717
|
});
|
|
@@ -11154,30 +11811,30 @@ async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
|
11154
11811
|
return { confirmation, summaryPath, transcriptPath, hasExistingSummary, priorMtimeMs };
|
|
11155
11812
|
}
|
|
11156
11813
|
function archiveDiscussionTranscript(worktree, transcriptRelativePath) {
|
|
11157
|
-
const sourcePath =
|
|
11158
|
-
if (!
|
|
11814
|
+
const sourcePath = path5.join(worktree, transcriptRelativePath);
|
|
11815
|
+
if (!fs5.existsSync(sourcePath)) return null;
|
|
11159
11816
|
const archiveDir = archivedRuntimeDiscussionsDir(worktree);
|
|
11160
|
-
if (!
|
|
11161
|
-
const targetPath =
|
|
11162
|
-
|
|
11817
|
+
if (!fs5.existsSync(archiveDir)) fs5.mkdirSync(archiveDir, { recursive: true });
|
|
11818
|
+
const targetPath = path5.join(archiveDir, path5.basename(sourcePath));
|
|
11819
|
+
fs5.renameSync(sourcePath, targetPath);
|
|
11163
11820
|
return targetPath;
|
|
11164
11821
|
}
|
|
11165
11822
|
async function finalizeClosingDiscussion(client, worktree, registry, sessionID, discussion) {
|
|
11166
11823
|
const { confirmation, summaryPath, hasExistingSummary, priorMtimeMs } = await summarizeDiscussionWithBA(client, worktree, discussion);
|
|
11167
|
-
if (!
|
|
11824
|
+
if (!fs5.existsSync(summaryPath)) {
|
|
11168
11825
|
throw new Error(`Discussion summary was not written to ${discussion.summaryPath}`);
|
|
11169
11826
|
}
|
|
11170
11827
|
if (hasExistingSummary) {
|
|
11171
|
-
const currentMtimeMs =
|
|
11828
|
+
const currentMtimeMs = fs5.statSync(summaryPath).mtimeMs;
|
|
11172
11829
|
if (currentMtimeMs <= priorMtimeMs) {
|
|
11173
11830
|
throw new Error(`Discussion summary file was not updated at ${discussion.summaryPath}`);
|
|
11174
11831
|
}
|
|
11175
11832
|
}
|
|
11176
|
-
const summaryContent =
|
|
11833
|
+
const summaryContent = fs5.readFileSync(summaryPath, "utf8").trim();
|
|
11177
11834
|
if (!summaryContent) {
|
|
11178
11835
|
throw new Error(`Discussion summary file is empty at ${discussion.summaryPath}`);
|
|
11179
11836
|
}
|
|
11180
|
-
const transcriptPath =
|
|
11837
|
+
const transcriptPath = path5.join(worktree, discussion.transcriptPath);
|
|
11181
11838
|
setDiscussionStatus(transcriptPath, "closed");
|
|
11182
11839
|
const archivedTranscriptPath = archiveDiscussionTranscript(worktree, discussion.transcriptPath);
|
|
11183
11840
|
delete registry.active[sessionID];
|
|
@@ -11185,7 +11842,7 @@ async function finalizeClosingDiscussion(client, worktree, registry, sessionID,
|
|
|
11185
11842
|
return {
|
|
11186
11843
|
confirmation,
|
|
11187
11844
|
summaryPath: discussion.summaryPath,
|
|
11188
|
-
archivedTranscriptPath: archivedTranscriptPath ?
|
|
11845
|
+
archivedTranscriptPath: archivedTranscriptPath ? path5.relative(worktree, archivedTranscriptPath) : path5.join(".optima", ".config", "runtime", "discussions", "archive", path5.basename(discussion.transcriptPath))
|
|
11189
11846
|
};
|
|
11190
11847
|
}
|
|
11191
11848
|
function normalizeTeamMode(value) {
|
|
@@ -11224,13 +11881,13 @@ function getOperatingTeamMode(repoCfg) {
|
|
|
11224
11881
|
}
|
|
11225
11882
|
function readResolvedFile(relativePath, worktree, options = {}) {
|
|
11226
11883
|
const filePath = resolveIncludeFile(`plugin:${relativePath}`, worktree, PKG_ROOT, options);
|
|
11227
|
-
if (!filePath || !
|
|
11228
|
-
return resolveIncludes(
|
|
11884
|
+
if (!filePath || !fs5.existsSync(filePath)) return "";
|
|
11885
|
+
return resolveIncludes(fs5.readFileSync(filePath, "utf8"), worktree, PKG_ROOT, options).trim();
|
|
11229
11886
|
}
|
|
11230
11887
|
function loadMarkdownFragment(filePath, worktree) {
|
|
11231
|
-
if (!
|
|
11888
|
+
if (!fs5.existsSync(filePath)) return "";
|
|
11232
11889
|
try {
|
|
11233
|
-
const raw =
|
|
11890
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
11234
11891
|
const { body } = parseFrontmatter(raw);
|
|
11235
11892
|
return resolveIncludes(body.trim(), worktree, PKG_ROOT);
|
|
11236
11893
|
} catch (e) {
|
|
@@ -11239,9 +11896,9 @@ function loadMarkdownFragment(filePath, worktree) {
|
|
|
11239
11896
|
}
|
|
11240
11897
|
}
|
|
11241
11898
|
function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
11242
|
-
if (!
|
|
11899
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
11243
11900
|
try {
|
|
11244
|
-
const rawContent =
|
|
11901
|
+
const rawContent = fs5.readFileSync(filePath, "utf8");
|
|
11245
11902
|
const { data, body } = parseFrontmatter(rawContent);
|
|
11246
11903
|
const prompt = resolveIncludes(body.trim(), worktree, PKG_ROOT, options);
|
|
11247
11904
|
return { data, prompt };
|
|
@@ -11252,13 +11909,13 @@ function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
|
11252
11909
|
}
|
|
11253
11910
|
function syncGeneratedPolicies(worktree, repoCfg) {
|
|
11254
11911
|
if (repoCfg.policies?.extract_defaults !== "all") return;
|
|
11255
|
-
if (!
|
|
11912
|
+
if (!fs5.existsSync(BUNDLE_POLICIES_DIR)) return;
|
|
11256
11913
|
const generatedDir = generatedPoliciesDir(worktree);
|
|
11257
|
-
if (!
|
|
11258
|
-
const policyFiles =
|
|
11914
|
+
if (!fs5.existsSync(generatedDir)) fs5.mkdirSync(generatedDir, { recursive: true });
|
|
11915
|
+
const policyFiles = fs5.readdirSync(BUNDLE_POLICIES_DIR).filter((file) => file.endsWith(".md") && file !== "README.md");
|
|
11259
11916
|
for (const file of policyFiles) {
|
|
11260
|
-
const sourcePath =
|
|
11261
|
-
const source =
|
|
11917
|
+
const sourcePath = path5.join(BUNDLE_POLICIES_DIR, file);
|
|
11918
|
+
const source = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
11262
11919
|
const generated = [
|
|
11263
11920
|
"<!--",
|
|
11264
11921
|
"Generated from Optima plugin defaults.",
|
|
@@ -11269,19 +11926,31 @@ function syncGeneratedPolicies(worktree, repoCfg) {
|
|
|
11269
11926
|
source,
|
|
11270
11927
|
""
|
|
11271
11928
|
].join("\n");
|
|
11272
|
-
|
|
11929
|
+
fs5.writeFileSync(path5.join(generatedDir, file), generated, "utf8");
|
|
11273
11930
|
}
|
|
11274
11931
|
}
|
|
11275
11932
|
function ensureReadmeFile(dirPath, content) {
|
|
11276
|
-
if (!
|
|
11277
|
-
const readmePath =
|
|
11278
|
-
if (!
|
|
11279
|
-
|
|
11933
|
+
if (!fs5.existsSync(dirPath)) fs5.mkdirSync(dirPath, { recursive: true });
|
|
11934
|
+
const readmePath = path5.join(dirPath, "README.md");
|
|
11935
|
+
if (!fs5.existsSync(readmePath)) {
|
|
11936
|
+
fs5.writeFileSync(readmePath, content, "utf8");
|
|
11280
11937
|
}
|
|
11281
11938
|
}
|
|
11282
11939
|
function ensureFileIfMissing(filePath, content) {
|
|
11283
|
-
if (!
|
|
11284
|
-
if (!
|
|
11940
|
+
if (!fs5.existsSync(path5.dirname(filePath))) fs5.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
11941
|
+
if (!fs5.existsSync(filePath)) fs5.writeFileSync(filePath, content, "utf8");
|
|
11942
|
+
}
|
|
11943
|
+
function currentTasksRegistryContent() {
|
|
11944
|
+
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";
|
|
11945
|
+
}
|
|
11946
|
+
function doneTasksRegistryContent() {
|
|
11947
|
+
return "# Completed Tasks (Registry)\n\n| Date | Task ID | SCR ID | Commit | Summary |\n| :--- | :--- | :--- | :--- | :--- |\n";
|
|
11948
|
+
}
|
|
11949
|
+
function currentScrRegistryContent() {
|
|
11950
|
+
return "# Current Spec Change Requests (Backlog)\n\n## \u{1F680} Active/Review\n- (None)\n\n## \u{1F4CB} Approved (Ready for Implementation)\n- (None)\n\n## \u{1F4A1} Proposed\n- (None)\n";
|
|
11951
|
+
}
|
|
11952
|
+
function doneScrRegistryContent() {
|
|
11953
|
+
return "# Implemented Spec Change Requests\n\n| Date | SCR ID | Title | Related Feature | Task ID |\n| :--- | :--- | :--- | :--- | :--- |\n";
|
|
11285
11954
|
}
|
|
11286
11955
|
function taskTemplateContent() {
|
|
11287
11956
|
return `---
|
|
@@ -11355,19 +12024,58 @@ Never place implementation evidence under root \`evidences/\`.
|
|
|
11355
12024
|
}
|
|
11356
12025
|
function ensureOptimaTaskTemplates(worktree) {
|
|
11357
12026
|
const tasksDir = optimaTasksDir(worktree);
|
|
11358
|
-
ensureFileIfMissing(
|
|
11359
|
-
ensureFileIfMissing(
|
|
12027
|
+
ensureFileIfMissing(path5.join(tasksDir, "task-template.md"), taskTemplateContent());
|
|
12028
|
+
ensureFileIfMissing(path5.join(tasksDir, "subtask-template.md"), subtaskTemplateContent());
|
|
12029
|
+
}
|
|
12030
|
+
function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
12031
|
+
const configPath = repoConfigPath(worktree);
|
|
12032
|
+
if (fs5.existsSync(configPath)) return false;
|
|
12033
|
+
const templatePath = path5.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12034
|
+
if (!fs5.existsSync(templatePath)) return false;
|
|
12035
|
+
const agentIds = fs5.existsSync(BUNDLE_AGENTS_DIR) ? fs5.readdirSync(BUNDLE_AGENTS_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")) : [];
|
|
12036
|
+
let optimaConfig = fs5.readFileSync(templatePath, "utf8");
|
|
12037
|
+
optimaConfig = optimaConfig.replace("{{teamMode}}", normalizeTeamMode(teamMode));
|
|
12038
|
+
let agentsSection = "";
|
|
12039
|
+
for (const id of agentIds) {
|
|
12040
|
+
const enabled = isAgentEnabledForTeamMode(id, normalizeTeamMode(teamMode)) ? "true" : "false";
|
|
12041
|
+
agentsSection += ` ${id}:
|
|
12042
|
+
enabled: ${enabled}
|
|
12043
|
+
`;
|
|
12044
|
+
}
|
|
12045
|
+
optimaConfig = optimaConfig.replace(/^agents:/m, "agents:\n" + agentsSection);
|
|
12046
|
+
ensureFileIfMissing(configPath, optimaConfig);
|
|
12047
|
+
return fs5.existsSync(configPath);
|
|
12048
|
+
}
|
|
12049
|
+
function scaffoldOptimaRootCodemap(worktree) {
|
|
12050
|
+
const rootCodemapPath = optimaCodemapPath(worktree);
|
|
12051
|
+
if (fs5.existsSync(rootCodemapPath)) return false;
|
|
12052
|
+
const templatePath = path5.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12053
|
+
if (!fs5.existsSync(templatePath)) return false;
|
|
12054
|
+
const codemapConfig = fs5.readFileSync(templatePath, "utf8").replace("{{projectName}}", path5.basename(worktree));
|
|
12055
|
+
ensureFileIfMissing(rootCodemapPath, codemapConfig);
|
|
12056
|
+
return fs5.existsSync(rootCodemapPath);
|
|
12057
|
+
}
|
|
12058
|
+
function ensureOptimaRegistries(worktree) {
|
|
12059
|
+
const tasksDir = optimaTasksDir(worktree);
|
|
12060
|
+
const scrsDir = optimaScrsDir(worktree);
|
|
12061
|
+
if (!fs5.existsSync(tasksDir)) fs5.mkdirSync(tasksDir, { recursive: true });
|
|
12062
|
+
if (!fs5.existsSync(scrsDir)) fs5.mkdirSync(scrsDir, { recursive: true });
|
|
12063
|
+
ensureFileIfMissing(path5.join(tasksDir, "current.md"), currentTasksRegistryContent());
|
|
12064
|
+
ensureFileIfMissing(path5.join(tasksDir, "done.md"), doneTasksRegistryContent());
|
|
12065
|
+
ensureFileIfMissing(path5.join(scrsDir, "current.md"), currentScrRegistryContent());
|
|
12066
|
+
ensureFileIfMissing(path5.join(scrsDir, "done.md"), doneScrRegistryContent());
|
|
11360
12067
|
}
|
|
11361
12068
|
function scaffoldOptimaReadmes(worktree) {
|
|
11362
12069
|
ensureReadmeFile(repoPoliciesDir(worktree), REPO_LOCAL_POLICIES_README);
|
|
11363
12070
|
ensureReadmeFile(repoAgentsDir(worktree), [
|
|
11364
12071
|
"# Repository Agents",
|
|
11365
12072
|
"",
|
|
11366
|
-
"Place full repository-local agent definitions here.",
|
|
12073
|
+
"Place full repository-local agent definitions here only when explicitly requested by the user.",
|
|
11367
12074
|
"",
|
|
11368
|
-
"- Use `.optima/agents/<agent>.md` to override a bundled agent's full base definition.",
|
|
11369
|
-
"- Use `.optima/agents/<agent>.md` to define a brand new custom repository agent.",
|
|
11370
|
-
"- Files in this folder are treated as full agent definitions.",
|
|
12075
|
+
"- Use `.optima/agents/<agent>.md` only to intentionally override a bundled agent's full base definition.",
|
|
12076
|
+
"- Use `.optima/agents/<agent>.md` only to define a brand new custom repository agent.",
|
|
12077
|
+
"- Files in this folder are treated as full agent definitions and block bundled prompt updates for that agent.",
|
|
12078
|
+
"- Prefer `.optima/agent-additions/<agent>.md` for normal repository-specific guidance.",
|
|
11371
12079
|
"- `README.md` is ignored by agent discovery.",
|
|
11372
12080
|
"",
|
|
11373
12081
|
"## Include Types Available In Custom Agents",
|
|
@@ -11461,6 +12169,38 @@ function scaffoldOptimaReadmes(worktree) {
|
|
|
11461
12169
|
].join("\n"));
|
|
11462
12170
|
ensureOptimaTaskTemplates(worktree);
|
|
11463
12171
|
}
|
|
12172
|
+
function optimaRepairDependencies() {
|
|
12173
|
+
return {
|
|
12174
|
+
currentScrRegistryContent,
|
|
12175
|
+
currentTasksRegistryContent,
|
|
12176
|
+
doneScrRegistryContent,
|
|
12177
|
+
doneTasksRegistryContent,
|
|
12178
|
+
ensureFileIfMissing,
|
|
12179
|
+
ensureOptimaGitignoreRules,
|
|
12180
|
+
ensureOptimaRegistries,
|
|
12181
|
+
isGitRepository,
|
|
12182
|
+
isOptimaPluginPackageWorktree,
|
|
12183
|
+
legacyNomadworkDir,
|
|
12184
|
+
legacyNomadworksDir,
|
|
12185
|
+
legacyOrbitaDir,
|
|
12186
|
+
legacyStaticEngDir,
|
|
12187
|
+
migrateLegacyOptimaLayout,
|
|
12188
|
+
optimaCodemapPath,
|
|
12189
|
+
optimaDir,
|
|
12190
|
+
optimaEvidencesDir,
|
|
12191
|
+
optimaGitignoreRules: OPTIMA_GITIGNORE_RULES,
|
|
12192
|
+
optimaScrsDir,
|
|
12193
|
+
optimaTasksDir,
|
|
12194
|
+
repoConfigPath,
|
|
12195
|
+
repoLocalPoliciesReadme: REPO_LOCAL_POLICIES_README,
|
|
12196
|
+
repoPoliciesDir,
|
|
12197
|
+
scaffoldOptimaConfig,
|
|
12198
|
+
scaffoldOptimaReadmes,
|
|
12199
|
+
scaffoldOptimaRootCodemap,
|
|
12200
|
+
subtaskTemplateContent,
|
|
12201
|
+
taskTemplateContent
|
|
12202
|
+
};
|
|
12203
|
+
}
|
|
11464
12204
|
function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
11465
12205
|
const fragmentMap = {
|
|
11466
12206
|
product_manager: {
|
|
@@ -11476,18 +12216,26 @@ function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
|
11476
12216
|
if (!fragmentPath) return "";
|
|
11477
12217
|
return readResolvedFile(fragmentPath, worktree, { preferCompactPromptDocs: true });
|
|
11478
12218
|
}
|
|
11479
|
-
function isSameOrNestedPath(candidate, root) {
|
|
11480
|
-
if (typeof candidate !== "string" || typeof root !== "string" || !candidate.trim() || !root.trim()) return false;
|
|
11481
|
-
const resolvedCandidate = path2.resolve(candidate);
|
|
11482
|
-
const resolvedRoot = path2.resolve(root);
|
|
11483
|
-
const relative = path2.relative(resolvedRoot, resolvedCandidate);
|
|
11484
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !path2.isAbsolute(relative);
|
|
11485
|
-
}
|
|
11486
12219
|
function shouldRegisterWorkflowProductManager(options = {}, worktree = process.cwd()) {
|
|
11487
12220
|
if (options.clickUpWebhookActive === true) return true;
|
|
11488
12221
|
const validation = options.clickUpWebhookValidation;
|
|
11489
12222
|
return validation?.complete === true && validation?.ok !== false && isSameOrNestedPath(worktree, validation.config?.basePath);
|
|
11490
12223
|
}
|
|
12224
|
+
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
12225
|
+
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
12226
|
+
if (!safe.ok) return { ok: false, error: safe.message };
|
|
12227
|
+
const requested = String(requestedDirectory || "").trim() ? path5.resolve(safe.worktree, String(requestedDirectory).trim()) : safe.worktree;
|
|
12228
|
+
if (!isSafeWritableDirectory(requested)) return { ok: false, error: `Directory is not a safe writable directory: ${requested}` };
|
|
12229
|
+
if (isSameOrNestedPath(requested, safe.worktree)) return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "context_worktree" };
|
|
12230
|
+
const clickUpBasePath = clickUpWebhookValidation?.complete === true && clickUpWebhookValidation?.ok !== false ? clickUpWebhookValidation.config?.basePath : "";
|
|
12231
|
+
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
12232
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
12233
|
+
}
|
|
12234
|
+
return {
|
|
12235
|
+
ok: false,
|
|
12236
|
+
error: `Directory '${requested}' is outside the safe worktree '${safe.worktree}' and configured ClickUp base path.`
|
|
12237
|
+
};
|
|
12238
|
+
}
|
|
11491
12239
|
function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, options = {}) {
|
|
11492
12240
|
const optimaActive = repoCfg && repoCfg.enabled === true;
|
|
11493
12241
|
const clickUpWebhookActive = shouldRegisterWorkflowProductManager(options, worktree);
|
|
@@ -11515,8 +12263,8 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
11515
12263
|
if (!enabled) continue;
|
|
11516
12264
|
}
|
|
11517
12265
|
const promptOptions = { preferCompactPromptDocs: repoCfg.features?.compact_prompt_docs !== false };
|
|
11518
|
-
const bundledDefinition = loadAgentDefinition(
|
|
11519
|
-
const repoDefinition = loadAgentDefinition(
|
|
12266
|
+
const bundledDefinition = loadAgentDefinition(path5.join(BUNDLE_AGENTS_DIR, file), worktree, promptOptions);
|
|
12267
|
+
const repoDefinition = loadAgentDefinition(path5.join(repoAgentDefinitions, file), worktree, promptOptions) || loadAgentDefinition(path5.join(legacyAgentsDir, file), worktree, promptOptions);
|
|
11520
12268
|
const activeDefinition = repoDefinition || bundledDefinition;
|
|
11521
12269
|
if (!activeDefinition) continue;
|
|
11522
12270
|
const { data } = activeDefinition;
|
|
@@ -11525,7 +12273,7 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
11525
12273
|
if (modePromptFragment) finalPrompt = `${finalPrompt}
|
|
11526
12274
|
|
|
11527
12275
|
${modePromptFragment}`;
|
|
11528
|
-
const additionFragment = loadMarkdownFragment(
|
|
12276
|
+
const additionFragment = loadMarkdownFragment(path5.join(repoAgentAdditions, file), worktree);
|
|
11529
12277
|
if (additionFragment) {
|
|
11530
12278
|
finalPrompt = `${finalPrompt}
|
|
11531
12279
|
|
|
@@ -11578,14 +12326,14 @@ Use this Optima-provided fallback when the current task worktree lacks docs/core
|
|
|
11578
12326
|
}
|
|
11579
12327
|
ourAgents[id] = agentConfig;
|
|
11580
12328
|
if (repoCfg.features?.debug_dumps !== false) {
|
|
11581
|
-
const debugPath =
|
|
12329
|
+
const debugPath = path5.join(debugDir, `${id}.md`);
|
|
11582
12330
|
const { prompt, ...dumpConfig } = agentConfig;
|
|
11583
12331
|
const debugHeader = `---
|
|
11584
|
-
${
|
|
12332
|
+
${import_yaml3.default.stringify(dumpConfig).trim()}
|
|
11585
12333
|
---`;
|
|
11586
12334
|
try {
|
|
11587
|
-
if (!
|
|
11588
|
-
|
|
12335
|
+
if (!fs5.existsSync(debugDir)) fs5.mkdirSync(debugDir, { recursive: true });
|
|
12336
|
+
fs5.writeFileSync(debugPath, `${debugHeader}
|
|
11589
12337
|
|
|
11590
12338
|
${prompt}`, "utf8");
|
|
11591
12339
|
} catch (e) {
|
|
@@ -11609,9 +12357,9 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11609
12357
|
const configPath = resolveConfigPath(worktree);
|
|
11610
12358
|
const discussionRegistry = loadDiscussionRegistry(worktree);
|
|
11611
12359
|
let repoCfg = { agents: {}, defaults: {}, features: {} };
|
|
11612
|
-
if (
|
|
12360
|
+
if (fs5.existsSync(configPath)) {
|
|
11613
12361
|
try {
|
|
11614
|
-
repoCfg =
|
|
12362
|
+
repoCfg = import_yaml3.default.parse(fs5.readFileSync(configPath, "utf8")) || repoCfg;
|
|
11615
12363
|
} catch (e) {
|
|
11616
12364
|
console.error(`[Optima] Failed to parse config at ${configPath}:`, e);
|
|
11617
12365
|
}
|
|
@@ -11718,54 +12466,16 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11718
12466
|
}
|
|
11719
12467
|
migrateLegacyOptimaLayout(toolWorktree);
|
|
11720
12468
|
const cfgDir = optimaConfigDir(toolWorktree);
|
|
11721
|
-
if (!
|
|
11722
|
-
const
|
|
11723
|
-
const
|
|
11724
|
-
|
|
11725
|
-
if (!fs2.existsSync(optimaTmplPath) || !fs2.existsSync(codemapTmplPath)) {
|
|
12469
|
+
if (!fs5.existsSync(cfgDir)) fs5.mkdirSync(cfgDir, { recursive: true });
|
|
12470
|
+
const optimaTmplPath = path5.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12471
|
+
const codemapTmplPath = path5.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12472
|
+
if (!fs5.existsSync(optimaTmplPath) || !fs5.existsSync(codemapTmplPath)) {
|
|
11726
12473
|
return "Error: Initialization templates not found in plugin.";
|
|
11727
12474
|
}
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
let agentsSection = "";
|
|
11731
|
-
for (const id of agentIds) {
|
|
11732
|
-
const enabled = isAgentEnabledForTeamMode(id, requestedTeamMode) ? "true" : "false";
|
|
11733
|
-
agentsSection += ` ${id}:
|
|
11734
|
-
enabled: ${enabled}
|
|
11735
|
-
`;
|
|
11736
|
-
}
|
|
11737
|
-
optimaConfig = optimaConfig.replace(/^agents:/m, "agents:\n" + agentsSection);
|
|
11738
|
-
let codemapConfig = fs2.readFileSync(codemapTmplPath, "utf8");
|
|
11739
|
-
codemapConfig = codemapConfig.replace("{{projectName}}", path2.basename(toolWorktree));
|
|
11740
|
-
const cfgFilePath = repoConfigPath(toolWorktree);
|
|
11741
|
-
const rootCodemapPath = optimaCodemapPath(toolWorktree);
|
|
11742
|
-
if (!fs2.existsSync(cfgFilePath)) {
|
|
11743
|
-
fs2.writeFileSync(cfgFilePath, optimaConfig, "utf8");
|
|
11744
|
-
}
|
|
11745
|
-
if (!fs2.existsSync(rootCodemapPath)) {
|
|
11746
|
-
fs2.writeFileSync(rootCodemapPath, codemapConfig, "utf8");
|
|
11747
|
-
}
|
|
12475
|
+
scaffoldOptimaConfig(toolWorktree, requestedTeamMode);
|
|
12476
|
+
scaffoldOptimaRootCodemap(toolWorktree);
|
|
11748
12477
|
scaffoldOptimaReadmes(toolWorktree);
|
|
11749
|
-
|
|
11750
|
-
const scrsDir = optimaScrsDir(toolWorktree);
|
|
11751
|
-
if (!fs2.existsSync(tasksDir)) fs2.mkdirSync(tasksDir, { recursive: true });
|
|
11752
|
-
if (!fs2.existsSync(scrsDir)) fs2.mkdirSync(scrsDir, { recursive: true });
|
|
11753
|
-
const currentPath = path2.join(tasksDir, "current.md");
|
|
11754
|
-
const donePath = path2.join(tasksDir, "done.md");
|
|
11755
|
-
const scrsCurrentPath = path2.join(scrsDir, "current.md");
|
|
11756
|
-
const scrsDonePath = path2.join(scrsDir, "done.md");
|
|
11757
|
-
if (!fs2.existsSync(currentPath)) {
|
|
11758
|
-
fs2.writeFileSync(currentPath, "# 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", "utf8");
|
|
11759
|
-
}
|
|
11760
|
-
if (!fs2.existsSync(donePath)) {
|
|
11761
|
-
fs2.writeFileSync(donePath, "# Completed Tasks (Registry)\n\n| Date | Task ID | SCR ID | Commit | Summary |\n| :--- | :--- | :--- | :--- | :--- |\n", "utf8");
|
|
11762
|
-
}
|
|
11763
|
-
if (!fs2.existsSync(scrsCurrentPath)) {
|
|
11764
|
-
fs2.writeFileSync(scrsCurrentPath, "# Current Spec Change Requests (Backlog)\n\n## \u{1F680} Active/Review\n- (None)\n\n## \u{1F4CB} Approved (Ready for Implementation)\n- (None)\n\n## \u{1F4A1} Proposed\n- (None)\n", "utf8");
|
|
11765
|
-
}
|
|
11766
|
-
if (!fs2.existsSync(scrsDonePath)) {
|
|
11767
|
-
fs2.writeFileSync(scrsDonePath, "# Implemented Spec Change Requests\n\n| Date | SCR ID | Title | Related Feature | Task ID |\n| :--- | :--- | :--- | :--- | :--- |\n", "utf8");
|
|
11768
|
-
}
|
|
12478
|
+
ensureOptimaRegistries(toolWorktree);
|
|
11769
12479
|
const gitignoreResult = ensureOptimaGitignoreRules(toolWorktree);
|
|
11770
12480
|
const gitignoreNote = gitignoreResult.added.length > 0 ? ` Added ${gitignoreResult.added.length} Optima local/private .gitignore rule(s).` : "";
|
|
11771
12481
|
const initSummary = `Optima initialized in '${requestedTeamMode}' team mode: .optima/.config/optima.yaml, repo policy/agent folders, registries, and .optima/codemap.yml created.${gitignoreNote}`;
|
|
@@ -11774,6 +12484,100 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11774
12484
|
Restart or reload OpenCode manually if the newly scaffolded config or agents are not visible in this session. Optima did not dispose or reload the active OpenCode instance automatically to avoid aborting the current session.`;
|
|
11775
12485
|
}
|
|
11776
12486
|
}),
|
|
12487
|
+
optima_session_create: tool({
|
|
12488
|
+
description: "Create an OpenCode session through the installed structured SDK shape",
|
|
12489
|
+
args: {
|
|
12490
|
+
directory: tool.schema.string().describe("Project directory for the OpenCode session"),
|
|
12491
|
+
title: tool.schema.string().describe("Session title"),
|
|
12492
|
+
agent: tool.schema.string().describe("Optional agent id for session creation")
|
|
12493
|
+
},
|
|
12494
|
+
async execute(args, context) {
|
|
12495
|
+
try {
|
|
12496
|
+
const directory = resolveSessionToolDirectory({ requestedDirectory: args.directory, context, pluginWorktree: worktree, clickUpWebhookValidation });
|
|
12497
|
+
if (!directory.ok) return JSON.stringify({ ok: false, error: directory.error }, null, 2);
|
|
12498
|
+
const result = await createOpenCodeSessionControl(input.client, {
|
|
12499
|
+
directory: directory.directory,
|
|
12500
|
+
title: args.title,
|
|
12501
|
+
agent: args.agent
|
|
12502
|
+
});
|
|
12503
|
+
return JSON.stringify(result, null, 2);
|
|
12504
|
+
} catch (error) {
|
|
12505
|
+
return JSON.stringify({ ok: false, error: error.message }, null, 2);
|
|
12506
|
+
}
|
|
12507
|
+
}
|
|
12508
|
+
}),
|
|
12509
|
+
optima_session_prompt: tool({
|
|
12510
|
+
description: "Prompt an existing OpenCode session through the installed structured SDK shape",
|
|
12511
|
+
args: {
|
|
12512
|
+
session_id: tool.schema.string().describe("OpenCode session id"),
|
|
12513
|
+
directory: tool.schema.string().describe("Project directory for the OpenCode session"),
|
|
12514
|
+
text: tool.schema.string().describe("Text prompt to send"),
|
|
12515
|
+
agent: tool.schema.string().describe("Optional agent id for the prompt body"),
|
|
12516
|
+
omit_agent: tool.schema.string().describe("Set to 'true' to omit agent from the prompt body")
|
|
12517
|
+
},
|
|
12518
|
+
async execute(args, context) {
|
|
12519
|
+
try {
|
|
12520
|
+
const directory = resolveSessionToolDirectory({ requestedDirectory: args.directory, context, pluginWorktree: worktree, clickUpWebhookValidation });
|
|
12521
|
+
if (!directory.ok) return JSON.stringify({ ok: false, error: directory.error }, null, 2);
|
|
12522
|
+
const result = await promptOpenCodeSessionControl(input.client, {
|
|
12523
|
+
sessionId: args.session_id,
|
|
12524
|
+
directory: directory.directory,
|
|
12525
|
+
text: args.text,
|
|
12526
|
+
agent: args.agent,
|
|
12527
|
+
omitAgent: String(args.omit_agent || "").toLowerCase() === "true"
|
|
12528
|
+
});
|
|
12529
|
+
return JSON.stringify(result, null, 2);
|
|
12530
|
+
} catch (error) {
|
|
12531
|
+
return JSON.stringify({ ok: false, error: error.message }, null, 2);
|
|
12532
|
+
}
|
|
12533
|
+
}
|
|
12534
|
+
}),
|
|
12535
|
+
optima_session_messages: tool({
|
|
12536
|
+
description: "Read and summarize OpenCode session messages through the installed structured SDK shape",
|
|
12537
|
+
args: {
|
|
12538
|
+
session_id: tool.schema.string().describe("OpenCode session id"),
|
|
12539
|
+
directory: tool.schema.string().describe("Optional project directory for the OpenCode session"),
|
|
12540
|
+
limit: tool.schema.number().describe("Optional message limit")
|
|
12541
|
+
},
|
|
12542
|
+
async execute(args, context) {
|
|
12543
|
+
try {
|
|
12544
|
+
const directory = resolveSessionToolDirectory({ requestedDirectory: args.directory, context, pluginWorktree: worktree, clickUpWebhookValidation });
|
|
12545
|
+
if (!directory.ok) return JSON.stringify({ ok: false, error: directory.error }, null, 2);
|
|
12546
|
+
const result = await readOpenCodeSessionControl(input.client, {
|
|
12547
|
+
sessionId: args.session_id,
|
|
12548
|
+
directory: directory.directory,
|
|
12549
|
+
limit: args.limit
|
|
12550
|
+
});
|
|
12551
|
+
return JSON.stringify(result, null, 2);
|
|
12552
|
+
} catch (error) {
|
|
12553
|
+
return JSON.stringify({ ok: false, error: error.message }, null, 2);
|
|
12554
|
+
}
|
|
12555
|
+
}
|
|
12556
|
+
}),
|
|
12557
|
+
optima_session_probe: tool({
|
|
12558
|
+
description: "Create, prompt, and read an OpenCode session without touching ClickUp state",
|
|
12559
|
+
args: {
|
|
12560
|
+
directory: tool.schema.string().describe("Project directory for the OpenCode session"),
|
|
12561
|
+
agent: tool.schema.string().describe("Optional agent id to probe"),
|
|
12562
|
+
omit_agent_on_prompt: tool.schema.string().describe("Set to 'true' to omit agent from the prompt body"),
|
|
12563
|
+
text: tool.schema.string().describe("Optional marker text to send")
|
|
12564
|
+
},
|
|
12565
|
+
async execute(args, context) {
|
|
12566
|
+
try {
|
|
12567
|
+
const directory = resolveSessionToolDirectory({ requestedDirectory: args.directory, context, pluginWorktree: worktree, clickUpWebhookValidation });
|
|
12568
|
+
if (!directory.ok) return JSON.stringify({ ok: false, error: directory.error }, null, 2);
|
|
12569
|
+
const result = await probeOpenCodeSessionControl(input.client, {
|
|
12570
|
+
directory: directory.directory,
|
|
12571
|
+
agent: args.agent,
|
|
12572
|
+
omitAgentOnPrompt: String(args.omit_agent_on_prompt || "").toLowerCase() === "true",
|
|
12573
|
+
text: args.text
|
|
12574
|
+
});
|
|
12575
|
+
return JSON.stringify(result, null, 2);
|
|
12576
|
+
} catch (error) {
|
|
12577
|
+
return JSON.stringify({ ok: false, error: error.message }, null, 2);
|
|
12578
|
+
}
|
|
12579
|
+
}
|
|
12580
|
+
}),
|
|
11777
12581
|
optima_clickup_sync_summary: tool({
|
|
11778
12582
|
description: "Generate a dry-run ClickUp summary/comment payload from Optima Markdown task and evidence artifacts",
|
|
11779
12583
|
args: {
|
|
@@ -11786,14 +12590,14 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
11786
12590
|
async execute(args, context) {
|
|
11787
12591
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
11788
12592
|
if (!safe.ok) return safe.message;
|
|
11789
|
-
const summaryPath =
|
|
11790
|
-
if (!
|
|
11791
|
-
const taskPath = args.task_path ?
|
|
12593
|
+
const summaryPath = path5.resolve(safe.worktree, args.summary_path || "");
|
|
12594
|
+
if (!fs5.existsSync(summaryPath)) return `FAIL: summary_path not found: ${summaryPath}`;
|
|
12595
|
+
const taskPath = args.task_path ? path5.resolve(safe.worktree, args.task_path) : "";
|
|
11792
12596
|
const payload = buildClickUpSummaryPayload({
|
|
11793
|
-
summaryMarkdown:
|
|
11794
|
-
summaryPath:
|
|
11795
|
-
taskMarkdown: taskPath &&
|
|
11796
|
-
taskPath: taskPath ?
|
|
12597
|
+
summaryMarkdown: fs5.readFileSync(summaryPath, "utf8"),
|
|
12598
|
+
summaryPath: path5.relative(safe.worktree, summaryPath),
|
|
12599
|
+
taskMarkdown: taskPath && fs5.existsSync(taskPath) ? fs5.readFileSync(taskPath, "utf8") : "",
|
|
12600
|
+
taskPath: taskPath ? path5.relative(safe.worktree, taskPath) : "",
|
|
11797
12601
|
branch: args.branch,
|
|
11798
12602
|
worktree: args.worktree,
|
|
11799
12603
|
pr: args.pr
|
|
@@ -11872,12 +12676,12 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
11872
12676
|
async execute(args, context) {
|
|
11873
12677
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
11874
12678
|
if (!safe.ok) return safe.message;
|
|
11875
|
-
const markdownPath =
|
|
11876
|
-
if (!
|
|
12679
|
+
const markdownPath = path5.resolve(safe.worktree, args.markdown_path || "");
|
|
12680
|
+
if (!fs5.existsSync(markdownPath)) return `FAIL: markdown_path not found: ${markdownPath}`;
|
|
11877
12681
|
const payload = buildClickUpCreateSubtasksPayload({
|
|
11878
12682
|
parentTaskId: args.parent_task_id,
|
|
11879
|
-
markdown:
|
|
11880
|
-
sourcePath:
|
|
12683
|
+
markdown: fs5.readFileSync(markdownPath, "utf8"),
|
|
12684
|
+
sourcePath: path5.relative(safe.worktree, markdownPath),
|
|
11881
12685
|
parentBranch: args.parent_branch,
|
|
11882
12686
|
parentTaskType: args.parent_task_type || "Tarea",
|
|
11883
12687
|
apply: String(args.apply || "").toLowerCase() === "true"
|
|
@@ -11909,6 +12713,21 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
11909
12713
|
return formatValidationResult(res);
|
|
11910
12714
|
}
|
|
11911
12715
|
}),
|
|
12716
|
+
optima_repair: tool({
|
|
12717
|
+
description: "Inspect and repair Optima operational errors across config, registries, templates, policies, legacy artifacts, Markdown paths, gitignore rules, and CodeMaps. Dry-run by default; set apply true to mutate safely.",
|
|
12718
|
+
args: {
|
|
12719
|
+
apply: tool.schema.boolean().optional().describe("Set true to apply deterministic Optima metadata repairs. Defaults to false dry-run."),
|
|
12720
|
+
mode: tool.schema.string().optional().describe("Optional mode: 'dry-run' or 'apply'. Dry-run is the default."),
|
|
12721
|
+
team_mode: tool.schema.string().optional().describe("Team mode for a newly created config when missing: mini or full. Defaults to full.")
|
|
12722
|
+
},
|
|
12723
|
+
async execute(args, context) {
|
|
12724
|
+
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12725
|
+
if (!safe.ok) return safe.message;
|
|
12726
|
+
const plan = planOptimaRepair(safe.worktree, args, optimaRepairDependencies());
|
|
12727
|
+
const validationResult = plan.mode === "apply" ? await optima_validate_logic(safe.worktree) : null;
|
|
12728
|
+
return formatRepairResult(plan, validationResult, formatValidationResult);
|
|
12729
|
+
}
|
|
12730
|
+
}),
|
|
11912
12731
|
optima_start_discussion: tool({
|
|
11913
12732
|
description: "Start an automatic discussion transcript for this session",
|
|
11914
12733
|
args: {
|
|
@@ -12061,7 +12880,7 @@ Backfilled messages: ${backfilled}`;
|
|
|
12061
12880
|
if (!existing) {
|
|
12062
12881
|
return "FAIL: No active discussion exists for this session.";
|
|
12063
12882
|
}
|
|
12064
|
-
const discussionPath =
|
|
12883
|
+
const discussionPath = path5.join(toolWorktree, existing.transcriptPath);
|
|
12065
12884
|
setDiscussionStatus(discussionPath, "summarizing");
|
|
12066
12885
|
existing.status = "summarizing";
|
|
12067
12886
|
saveDiscussionRegistry(toolWorktree, toolRegistry);
|
|
@@ -12113,7 +12932,7 @@ Reason: ${err.message}`;
|
|
|
12113
12932
|
try {
|
|
12114
12933
|
const sessionResult = await client.session.create({
|
|
12115
12934
|
query: { directory: workflowDirectory },
|
|
12116
|
-
body: { title: `Workflow Run: ${
|
|
12935
|
+
body: { title: `Workflow Run: ${path5.basename(workflowTaskPath)}` }
|
|
12117
12936
|
});
|
|
12118
12937
|
const sessionId = sessionResult.data.id;
|
|
12119
12938
|
activeWorkflows.set(sessionId, { pmaSessionId, taskPath: workflowTaskPath, track: workflowTrack, directory: workflowDirectory });
|
|
@@ -12242,7 +13061,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
12242
13061
|
}
|
|
12243
13062
|
};
|
|
12244
13063
|
}
|
|
12245
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, createOpenCodeSession, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
13064
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12246
13065
|
|
|
12247
13066
|
// src/sanitize_cli.js
|
|
12248
13067
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|
|
@@ -12298,13 +13117,13 @@ function usage() {
|
|
|
12298
13117
|
}
|
|
12299
13118
|
function expandHome(inputPath) {
|
|
12300
13119
|
if (!inputPath || inputPath === "~") return os2.homedir();
|
|
12301
|
-
if (inputPath.startsWith("~/")) return
|
|
13120
|
+
if (inputPath.startsWith("~/")) return path6.join(os2.homedir(), inputPath.slice(2));
|
|
12302
13121
|
return inputPath;
|
|
12303
13122
|
}
|
|
12304
13123
|
function resolveOpenCodeDbPath(inputPath = null) {
|
|
12305
13124
|
const expanded = expandHome(inputPath || process.env.OPTIMA_OPENCODE_DB_PATH || DEFAULT_DB_PATH);
|
|
12306
13125
|
try {
|
|
12307
|
-
if (
|
|
13126
|
+
if (fs6.existsSync(expanded) && fs6.statSync(expanded).isDirectory()) return path6.join(expanded, "opencode.db");
|
|
12308
13127
|
} catch {
|
|
12309
13128
|
return expanded;
|
|
12310
13129
|
}
|
|
@@ -12368,12 +13187,12 @@ function extractOpenedPathValue(value) {
|
|
|
12368
13187
|
}
|
|
12369
13188
|
function normalizePathCandidate(candidate) {
|
|
12370
13189
|
const expanded = expandHome(candidate);
|
|
12371
|
-
if (!
|
|
12372
|
-
return
|
|
13190
|
+
if (!path6.isAbsolute(expanded)) return null;
|
|
13191
|
+
return path6.resolve(expanded);
|
|
12373
13192
|
}
|
|
12374
13193
|
function discoverOpenCodePaths(dbPath) {
|
|
12375
13194
|
const resolvedDb = resolveOpenCodeDbPath(dbPath);
|
|
12376
|
-
if (!
|
|
13195
|
+
if (!fs6.existsSync(resolvedDb)) throw new Error(`OpenCode database not found: ${resolvedDb}`);
|
|
12377
13196
|
const tables = sqliteJson(resolvedDb, "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name");
|
|
12378
13197
|
const discovered = /* @__PURE__ */ new Set();
|
|
12379
13198
|
for (const table of tables) {
|
|
@@ -12398,27 +13217,27 @@ function discoverOpenCodePaths(dbPath) {
|
|
|
12398
13217
|
function collectMarkdownOverridesFrom(baseDir) {
|
|
12399
13218
|
const files = [];
|
|
12400
13219
|
for (const dirName of OVERRIDE_DIRS) {
|
|
12401
|
-
const dirPath =
|
|
12402
|
-
if (!
|
|
12403
|
-
for (const entry of
|
|
13220
|
+
const dirPath = path6.join(baseDir, dirName);
|
|
13221
|
+
if (!fs6.existsSync(dirPath)) continue;
|
|
13222
|
+
for (const entry of fs6.readdirSync(dirPath, { withFileTypes: true })) {
|
|
12404
13223
|
if (!entry.isFile()) continue;
|
|
12405
13224
|
if (!entry.name.endsWith(".md")) continue;
|
|
12406
13225
|
if (entry.name.toLowerCase() === "readme.md") continue;
|
|
12407
|
-
files.push(
|
|
13226
|
+
files.push(path6.join(dirPath, entry.name));
|
|
12408
13227
|
}
|
|
12409
13228
|
}
|
|
12410
13229
|
return files;
|
|
12411
13230
|
}
|
|
12412
13231
|
function collectOverrideFiles(worktree) {
|
|
12413
|
-
return collectMarkdownOverridesFrom(
|
|
13232
|
+
return collectMarkdownOverridesFrom(path6.join(worktree, ".optima")).sort();
|
|
12414
13233
|
}
|
|
12415
13234
|
function collectPlannedOverrideFiles(worktree) {
|
|
12416
13235
|
return [
|
|
12417
13236
|
...collectOverrideFiles(worktree),
|
|
12418
|
-
...collectMarkdownOverridesFrom(
|
|
12419
|
-
...collectMarkdownOverridesFrom(
|
|
12420
|
-
...collectMarkdownOverridesFrom(
|
|
12421
|
-
...collectMarkdownOverridesFrom(
|
|
13237
|
+
...collectMarkdownOverridesFrom(path6.join(worktree, ".staticeng")),
|
|
13238
|
+
...collectMarkdownOverridesFrom(path6.join(worktree, ".orbita")),
|
|
13239
|
+
...collectMarkdownOverridesFrom(path6.join(worktree, ".nomadwork")),
|
|
13240
|
+
...collectMarkdownOverridesFrom(path6.join(worktree, ".nomadworks"))
|
|
12422
13241
|
].sort();
|
|
12423
13242
|
}
|
|
12424
13243
|
var CRC_TABLE = (() => {
|
|
@@ -12451,14 +13270,14 @@ function writeUInt16LE(value) {
|
|
|
12451
13270
|
return buffer;
|
|
12452
13271
|
}
|
|
12453
13272
|
function createZipBackup(files, backupPath) {
|
|
12454
|
-
|
|
13273
|
+
fs6.mkdirSync(path6.dirname(backupPath), { recursive: true });
|
|
12455
13274
|
const localParts = [];
|
|
12456
13275
|
const centralParts = [];
|
|
12457
13276
|
let offset = 0;
|
|
12458
13277
|
const now = dosTimeDate();
|
|
12459
13278
|
for (const filePath of files) {
|
|
12460
|
-
const data =
|
|
12461
|
-
const name =
|
|
13279
|
+
const data = fs6.readFileSync(filePath);
|
|
13280
|
+
const name = path6.resolve(filePath).replace(/^\//, "").split(path6.sep).join("/");
|
|
12462
13281
|
const nameBuffer = Buffer.from(name, "utf8");
|
|
12463
13282
|
const crc = crc32(data);
|
|
12464
13283
|
const local = Buffer.concat([
|
|
@@ -12510,7 +13329,7 @@ function createZipBackup(files, backupPath) {
|
|
|
12510
13329
|
writeUInt32LE(offset),
|
|
12511
13330
|
writeUInt16LE(0)
|
|
12512
13331
|
]);
|
|
12513
|
-
|
|
13332
|
+
fs6.writeFileSync(backupPath, Buffer.concat([...localParts, ...centralParts, end]));
|
|
12514
13333
|
}
|
|
12515
13334
|
function timestamp() {
|
|
12516
13335
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
@@ -12520,17 +13339,17 @@ function validationStatus(result) {
|
|
|
12520
13339
|
return result.ok ? "passed" : "failed";
|
|
12521
13340
|
}
|
|
12522
13341
|
function hasLegacyMarker(worktree) {
|
|
12523
|
-
return
|
|
13342
|
+
return fs6.existsSync(path6.join(worktree, ".staticeng")) || fs6.existsSync(path6.join(worktree, ".orbita")) || fs6.existsSync(path6.join(worktree, ".nomadwork")) || fs6.existsSync(path6.join(worktree, ".nomadworks"));
|
|
12524
13343
|
}
|
|
12525
13344
|
function rootOptimaArtifacts(worktree) {
|
|
12526
13345
|
const artifacts = [
|
|
12527
|
-
["tasks/",
|
|
12528
|
-
["evidences/",
|
|
12529
|
-
["docs/scrs/",
|
|
12530
|
-
["codemap.yml",
|
|
12531
|
-
["codemap.yaml",
|
|
13346
|
+
["tasks/", path6.join(worktree, "tasks")],
|
|
13347
|
+
["evidences/", path6.join(worktree, "evidences")],
|
|
13348
|
+
["docs/scrs/", path6.join(worktree, "docs", "scrs")],
|
|
13349
|
+
["codemap.yml", path6.join(worktree, "codemap.yml")],
|
|
13350
|
+
["codemap.yaml", path6.join(worktree, "codemap.yaml")]
|
|
12532
13351
|
];
|
|
12533
|
-
return artifacts.map(([display, artifactPath]) => ({ display, path: artifactPath })).filter((item) =>
|
|
13352
|
+
return artifacts.map(([display, artifactPath]) => ({ display, path: artifactPath })).filter((item) => fs6.existsSync(item.path));
|
|
12534
13353
|
}
|
|
12535
13354
|
function hasSanitizableOverrides(worktree) {
|
|
12536
13355
|
return collectPlannedOverrideFiles(worktree).length > 0;
|
|
@@ -12556,7 +13375,7 @@ function planOptimaMigration(worktree, dryRun) {
|
|
|
12556
13375
|
...overrides.map((file) => ({
|
|
12557
13376
|
category: "override",
|
|
12558
13377
|
action: dryRun ? "would_remove_after_backup" : "removed_after_backup",
|
|
12559
|
-
path:
|
|
13378
|
+
path: path6.relative(worktree, file).split(path6.sep).join("/"),
|
|
12560
13379
|
detail: "Remove implicit agent/policy override after host-level backup."
|
|
12561
13380
|
}))
|
|
12562
13381
|
],
|
|
@@ -12577,12 +13396,12 @@ async function sanitizeHost(options = {}) {
|
|
|
12577
13396
|
const repoStates = [];
|
|
12578
13397
|
for (const candidate of paths) {
|
|
12579
13398
|
try {
|
|
12580
|
-
if (!
|
|
13399
|
+
if (!fs6.existsSync(candidate)) {
|
|
12581
13400
|
report.totals.skipped += 1;
|
|
12582
13401
|
report.repos.push({ path: candidate, status: "skipped", reason: "missing" });
|
|
12583
13402
|
continue;
|
|
12584
13403
|
}
|
|
12585
|
-
if (!
|
|
13404
|
+
if (!fs6.statSync(candidate).isDirectory()) {
|
|
12586
13405
|
report.totals.skipped += 1;
|
|
12587
13406
|
report.repos.push({ path: candidate, status: "skipped", reason: "not_directory" });
|
|
12588
13407
|
continue;
|
|
@@ -12604,7 +13423,7 @@ async function sanitizeHost(options = {}) {
|
|
|
12604
13423
|
const overrides = repoStates.flatMap((state) => state.overrideFiles.map((file) => ({ repo: state.repo, file })));
|
|
12605
13424
|
report.totals.overrideFiles = overrides.length;
|
|
12606
13425
|
if (!dryRun && overrides.length > 0) {
|
|
12607
|
-
const backupPath =
|
|
13426
|
+
const backupPath = path6.join(os2.homedir(), BACKUP_DIRNAME, `${timestamp()}.zip`);
|
|
12608
13427
|
try {
|
|
12609
13428
|
createZipBackup(overrides.map((item) => item.file), backupPath);
|
|
12610
13429
|
report.backupPath = backupPath;
|
|
@@ -12620,7 +13439,7 @@ async function sanitizeHost(options = {}) {
|
|
|
12620
13439
|
for (const state of repoStates) {
|
|
12621
13440
|
try {
|
|
12622
13441
|
if (!dryRun) {
|
|
12623
|
-
for (const file of state.overrideFiles)
|
|
13442
|
+
for (const file of state.overrideFiles) fs6.rmSync(file, { force: true });
|
|
12624
13443
|
}
|
|
12625
13444
|
const validation = !dryRun && hasLegacyMarker(state.repo) ? await optima_validate_logic(state.repo) : null;
|
|
12626
13445
|
const status = (!validation || validation.ok) && state.plan.unresolved.length === 0 ? "repaired" : "failed";
|
|
@@ -12633,7 +13452,7 @@ async function sanitizeHost(options = {}) {
|
|
|
12633
13452
|
repairActions: state.plan.actions.length,
|
|
12634
13453
|
repairSkipped: state.plan.skippedRepair === true,
|
|
12635
13454
|
overrideFiles: state.overrideFiles.length,
|
|
12636
|
-
overridePaths: state.overrideFiles.map((file) =>
|
|
13455
|
+
overridePaths: state.overrideFiles.map((file) => path6.relative(state.repo, file).split(path6.sep).join("/")),
|
|
12637
13456
|
validation: validation ? validationStatus(validation) : "skipped",
|
|
12638
13457
|
unresolved: state.plan.unresolved
|
|
12639
13458
|
});
|