@defend-tech/opencode-optima 0.1.55 → 0.1.57
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 +1213 -625
- package/dist/sanitize_cli.js +1246 -658
- 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/index.js
CHANGED
|
@@ -108,17 +108,17 @@ var require_visit = __commonJS({
|
|
|
108
108
|
visit.BREAK = BREAK;
|
|
109
109
|
visit.SKIP = SKIP;
|
|
110
110
|
visit.REMOVE = REMOVE;
|
|
111
|
-
function visit_(key, node, visitor,
|
|
112
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
111
|
+
function visit_(key, node, visitor, path6) {
|
|
112
|
+
const ctrl = callVisitor(key, node, visitor, path6);
|
|
113
113
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
114
|
-
replaceNode(key,
|
|
115
|
-
return visit_(key, ctrl, visitor,
|
|
114
|
+
replaceNode(key, path6, ctrl);
|
|
115
|
+
return visit_(key, ctrl, visitor, path6);
|
|
116
116
|
}
|
|
117
117
|
if (typeof ctrl !== "symbol") {
|
|
118
118
|
if (identity.isCollection(node)) {
|
|
119
|
-
|
|
119
|
+
path6 = Object.freeze(path6.concat(node));
|
|
120
120
|
for (let i = 0; i < node.items.length; ++i) {
|
|
121
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
121
|
+
const ci = visit_(i, node.items[i], visitor, path6);
|
|
122
122
|
if (typeof ci === "number")
|
|
123
123
|
i = ci - 1;
|
|
124
124
|
else if (ci === BREAK)
|
|
@@ -129,13 +129,13 @@ var require_visit = __commonJS({
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
} else if (identity.isPair(node)) {
|
|
132
|
-
|
|
133
|
-
const ck = visit_("key", node.key, visitor,
|
|
132
|
+
path6 = Object.freeze(path6.concat(node));
|
|
133
|
+
const ck = visit_("key", node.key, visitor, path6);
|
|
134
134
|
if (ck === BREAK)
|
|
135
135
|
return BREAK;
|
|
136
136
|
else if (ck === REMOVE)
|
|
137
137
|
node.key = null;
|
|
138
|
-
const cv = visit_("value", node.value, visitor,
|
|
138
|
+
const cv = visit_("value", node.value, visitor, path6);
|
|
139
139
|
if (cv === BREAK)
|
|
140
140
|
return BREAK;
|
|
141
141
|
else if (cv === REMOVE)
|
|
@@ -156,17 +156,17 @@ var require_visit = __commonJS({
|
|
|
156
156
|
visitAsync.BREAK = BREAK;
|
|
157
157
|
visitAsync.SKIP = SKIP;
|
|
158
158
|
visitAsync.REMOVE = REMOVE;
|
|
159
|
-
async function visitAsync_(key, node, visitor,
|
|
160
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
159
|
+
async function visitAsync_(key, node, visitor, path6) {
|
|
160
|
+
const ctrl = await callVisitor(key, node, visitor, path6);
|
|
161
161
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
162
|
-
replaceNode(key,
|
|
163
|
-
return visitAsync_(key, ctrl, visitor,
|
|
162
|
+
replaceNode(key, path6, ctrl);
|
|
163
|
+
return visitAsync_(key, ctrl, visitor, path6);
|
|
164
164
|
}
|
|
165
165
|
if (typeof ctrl !== "symbol") {
|
|
166
166
|
if (identity.isCollection(node)) {
|
|
167
|
-
|
|
167
|
+
path6 = Object.freeze(path6.concat(node));
|
|
168
168
|
for (let i = 0; i < node.items.length; ++i) {
|
|
169
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
169
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path6);
|
|
170
170
|
if (typeof ci === "number")
|
|
171
171
|
i = ci - 1;
|
|
172
172
|
else if (ci === BREAK)
|
|
@@ -177,13 +177,13 @@ var require_visit = __commonJS({
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
} else if (identity.isPair(node)) {
|
|
180
|
-
|
|
181
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
180
|
+
path6 = Object.freeze(path6.concat(node));
|
|
181
|
+
const ck = await visitAsync_("key", node.key, visitor, path6);
|
|
182
182
|
if (ck === BREAK)
|
|
183
183
|
return BREAK;
|
|
184
184
|
else if (ck === REMOVE)
|
|
185
185
|
node.key = null;
|
|
186
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
186
|
+
const cv = await visitAsync_("value", node.value, visitor, path6);
|
|
187
187
|
if (cv === BREAK)
|
|
188
188
|
return BREAK;
|
|
189
189
|
else if (cv === REMOVE)
|
|
@@ -210,23 +210,23 @@ var require_visit = __commonJS({
|
|
|
210
210
|
}
|
|
211
211
|
return visitor;
|
|
212
212
|
}
|
|
213
|
-
function callVisitor(key, node, visitor,
|
|
213
|
+
function callVisitor(key, node, visitor, path6) {
|
|
214
214
|
if (typeof visitor === "function")
|
|
215
|
-
return visitor(key, node,
|
|
215
|
+
return visitor(key, node, path6);
|
|
216
216
|
if (identity.isMap(node))
|
|
217
|
-
return visitor.Map?.(key, node,
|
|
217
|
+
return visitor.Map?.(key, node, path6);
|
|
218
218
|
if (identity.isSeq(node))
|
|
219
|
-
return visitor.Seq?.(key, node,
|
|
219
|
+
return visitor.Seq?.(key, node, path6);
|
|
220
220
|
if (identity.isPair(node))
|
|
221
|
-
return visitor.Pair?.(key, node,
|
|
221
|
+
return visitor.Pair?.(key, node, path6);
|
|
222
222
|
if (identity.isScalar(node))
|
|
223
|
-
return visitor.Scalar?.(key, node,
|
|
223
|
+
return visitor.Scalar?.(key, node, path6);
|
|
224
224
|
if (identity.isAlias(node))
|
|
225
|
-
return visitor.Alias?.(key, node,
|
|
225
|
+
return visitor.Alias?.(key, node, path6);
|
|
226
226
|
return void 0;
|
|
227
227
|
}
|
|
228
|
-
function replaceNode(key,
|
|
229
|
-
const parent =
|
|
228
|
+
function replaceNode(key, path6, node) {
|
|
229
|
+
const parent = path6[path6.length - 1];
|
|
230
230
|
if (identity.isCollection(parent)) {
|
|
231
231
|
parent.items[key] = node;
|
|
232
232
|
} else if (identity.isPair(parent)) {
|
|
@@ -632,6 +632,8 @@ var require_Alias = __commonJS({
|
|
|
632
632
|
* instance of the `source` anchor before this node.
|
|
633
633
|
*/
|
|
634
634
|
resolve(doc, ctx) {
|
|
635
|
+
if (ctx?.maxAliasCount === 0)
|
|
636
|
+
throw new ReferenceError("Alias resolution is disabled");
|
|
635
637
|
let nodes;
|
|
636
638
|
if (ctx?.aliasResolveCache) {
|
|
637
639
|
nodes = ctx.aliasResolveCache;
|
|
@@ -834,10 +836,10 @@ var require_Collection = __commonJS({
|
|
|
834
836
|
var createNode = require_createNode();
|
|
835
837
|
var identity = require_identity();
|
|
836
838
|
var Node = require_Node();
|
|
837
|
-
function collectionFromPath(schema,
|
|
839
|
+
function collectionFromPath(schema, path6, value) {
|
|
838
840
|
let v = value;
|
|
839
|
-
for (let i =
|
|
840
|
-
const k =
|
|
841
|
+
for (let i = path6.length - 1; i >= 0; --i) {
|
|
842
|
+
const k = path6[i];
|
|
841
843
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
842
844
|
const a = [];
|
|
843
845
|
a[k] = v;
|
|
@@ -856,7 +858,7 @@ var require_Collection = __commonJS({
|
|
|
856
858
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
857
859
|
});
|
|
858
860
|
}
|
|
859
|
-
var isEmptyPath = (
|
|
861
|
+
var isEmptyPath = (path6) => path6 == null || typeof path6 === "object" && !!path6[Symbol.iterator]().next().done;
|
|
860
862
|
var Collection = class extends Node.NodeBase {
|
|
861
863
|
constructor(type, schema) {
|
|
862
864
|
super(type);
|
|
@@ -886,11 +888,11 @@ var require_Collection = __commonJS({
|
|
|
886
888
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
887
889
|
* that already exists in the map.
|
|
888
890
|
*/
|
|
889
|
-
addIn(
|
|
890
|
-
if (isEmptyPath(
|
|
891
|
+
addIn(path6, value) {
|
|
892
|
+
if (isEmptyPath(path6))
|
|
891
893
|
this.add(value);
|
|
892
894
|
else {
|
|
893
|
-
const [key, ...rest] =
|
|
895
|
+
const [key, ...rest] = path6;
|
|
894
896
|
const node = this.get(key, true);
|
|
895
897
|
if (identity.isCollection(node))
|
|
896
898
|
node.addIn(rest, value);
|
|
@@ -904,8 +906,8 @@ var require_Collection = __commonJS({
|
|
|
904
906
|
* Removes a value from the collection.
|
|
905
907
|
* @returns `true` if the item was found and removed.
|
|
906
908
|
*/
|
|
907
|
-
deleteIn(
|
|
908
|
-
const [key, ...rest] =
|
|
909
|
+
deleteIn(path6) {
|
|
910
|
+
const [key, ...rest] = path6;
|
|
909
911
|
if (rest.length === 0)
|
|
910
912
|
return this.delete(key);
|
|
911
913
|
const node = this.get(key, true);
|
|
@@ -919,8 +921,8 @@ var require_Collection = __commonJS({
|
|
|
919
921
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
920
922
|
* `true` (collections are always returned intact).
|
|
921
923
|
*/
|
|
922
|
-
getIn(
|
|
923
|
-
const [key, ...rest] =
|
|
924
|
+
getIn(path6, keepScalar) {
|
|
925
|
+
const [key, ...rest] = path6;
|
|
924
926
|
const node = this.get(key, true);
|
|
925
927
|
if (rest.length === 0)
|
|
926
928
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -938,8 +940,8 @@ var require_Collection = __commonJS({
|
|
|
938
940
|
/**
|
|
939
941
|
* Checks if the collection includes a value with the key `key`.
|
|
940
942
|
*/
|
|
941
|
-
hasIn(
|
|
942
|
-
const [key, ...rest] =
|
|
943
|
+
hasIn(path6) {
|
|
944
|
+
const [key, ...rest] = path6;
|
|
943
945
|
if (rest.length === 0)
|
|
944
946
|
return this.has(key);
|
|
945
947
|
const node = this.get(key, true);
|
|
@@ -949,8 +951,8 @@ var require_Collection = __commonJS({
|
|
|
949
951
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
950
952
|
* boolean to add/remove the item from the set.
|
|
951
953
|
*/
|
|
952
|
-
setIn(
|
|
953
|
-
const [key, ...rest] =
|
|
954
|
+
setIn(path6, value) {
|
|
955
|
+
const [key, ...rest] = path6;
|
|
954
956
|
if (rest.length === 0) {
|
|
955
957
|
this.set(key, value);
|
|
956
958
|
} else {
|
|
@@ -1431,6 +1433,7 @@ var require_stringify = __commonJS({
|
|
|
1431
1433
|
nullStr: "null",
|
|
1432
1434
|
simpleKeys: false,
|
|
1433
1435
|
singleQuote: null,
|
|
1436
|
+
trailingComma: false,
|
|
1434
1437
|
trueStr: "true",
|
|
1435
1438
|
verifyAliasOrder: true
|
|
1436
1439
|
}, doc.schema.toStringOptions, options);
|
|
@@ -1703,18 +1706,18 @@ var require_merge = __commonJS({
|
|
|
1703
1706
|
};
|
|
1704
1707
|
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);
|
|
1705
1708
|
function addMergeToJSMap(ctx, map, value) {
|
|
1706
|
-
|
|
1707
|
-
if (identity.isSeq(
|
|
1708
|
-
for (const it of
|
|
1709
|
+
const source = resolveAliasValue(ctx, value);
|
|
1710
|
+
if (identity.isSeq(source))
|
|
1711
|
+
for (const it of source.items)
|
|
1709
1712
|
mergeValue(ctx, map, it);
|
|
1710
|
-
else if (Array.isArray(
|
|
1711
|
-
for (const it of
|
|
1713
|
+
else if (Array.isArray(source))
|
|
1714
|
+
for (const it of source)
|
|
1712
1715
|
mergeValue(ctx, map, it);
|
|
1713
1716
|
else
|
|
1714
|
-
mergeValue(ctx, map,
|
|
1717
|
+
mergeValue(ctx, map, source);
|
|
1715
1718
|
}
|
|
1716
1719
|
function mergeValue(ctx, map, value) {
|
|
1717
|
-
const source = ctx
|
|
1720
|
+
const source = resolveAliasValue(ctx, value);
|
|
1718
1721
|
if (!identity.isMap(source))
|
|
1719
1722
|
throw new Error("Merge sources must be maps or map aliases");
|
|
1720
1723
|
const srcMap = source.toJSON(null, ctx, Map);
|
|
@@ -1735,6 +1738,9 @@ var require_merge = __commonJS({
|
|
|
1735
1738
|
}
|
|
1736
1739
|
return map;
|
|
1737
1740
|
}
|
|
1741
|
+
function resolveAliasValue(ctx, value) {
|
|
1742
|
+
return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
|
|
1743
|
+
}
|
|
1738
1744
|
exports.addMergeToJSMap = addMergeToJSMap;
|
|
1739
1745
|
exports.isMergeKey = isMergeKey;
|
|
1740
1746
|
exports.merge = merge;
|
|
@@ -1948,12 +1954,19 @@ ${indent}${line}` : "\n";
|
|
|
1948
1954
|
if (comment)
|
|
1949
1955
|
reqNewline = true;
|
|
1950
1956
|
let str = stringify.stringify(item, itemCtx, () => comment = null);
|
|
1951
|
-
|
|
1957
|
+
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
1958
|
+
if (i < items.length - 1) {
|
|
1952
1959
|
str += ",";
|
|
1960
|
+
} else if (ctx.options.trailingComma) {
|
|
1961
|
+
if (ctx.options.lineWidth > 0) {
|
|
1962
|
+
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
1963
|
+
}
|
|
1964
|
+
if (reqNewline) {
|
|
1965
|
+
str += ",";
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1953
1968
|
if (comment)
|
|
1954
1969
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
1955
|
-
if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
|
|
1956
|
-
reqNewline = true;
|
|
1957
1970
|
lines.push(str);
|
|
1958
1971
|
linesAtValue = lines.length;
|
|
1959
1972
|
}
|
|
@@ -2365,7 +2378,7 @@ var require_stringifyNumber = __commonJS({
|
|
|
2365
2378
|
if (!isFinite(num))
|
|
2366
2379
|
return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
|
|
2367
2380
|
let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
2368
|
-
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") &&
|
|
2381
|
+
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
|
|
2369
2382
|
let i = n.indexOf(".");
|
|
2370
2383
|
if (i < 0) {
|
|
2371
2384
|
i = n.length;
|
|
@@ -3454,9 +3467,9 @@ var require_Document = __commonJS({
|
|
|
3454
3467
|
this.contents.add(value);
|
|
3455
3468
|
}
|
|
3456
3469
|
/** Adds a value to the document. */
|
|
3457
|
-
addIn(
|
|
3470
|
+
addIn(path6, value) {
|
|
3458
3471
|
if (assertCollection(this.contents))
|
|
3459
|
-
this.contents.addIn(
|
|
3472
|
+
this.contents.addIn(path6, value);
|
|
3460
3473
|
}
|
|
3461
3474
|
/**
|
|
3462
3475
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -3531,14 +3544,14 @@ var require_Document = __commonJS({
|
|
|
3531
3544
|
* Removes a value from the document.
|
|
3532
3545
|
* @returns `true` if the item was found and removed.
|
|
3533
3546
|
*/
|
|
3534
|
-
deleteIn(
|
|
3535
|
-
if (Collection.isEmptyPath(
|
|
3547
|
+
deleteIn(path6) {
|
|
3548
|
+
if (Collection.isEmptyPath(path6)) {
|
|
3536
3549
|
if (this.contents == null)
|
|
3537
3550
|
return false;
|
|
3538
3551
|
this.contents = null;
|
|
3539
3552
|
return true;
|
|
3540
3553
|
}
|
|
3541
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
3554
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path6) : false;
|
|
3542
3555
|
}
|
|
3543
3556
|
/**
|
|
3544
3557
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -3553,10 +3566,10 @@ var require_Document = __commonJS({
|
|
|
3553
3566
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
3554
3567
|
* `true` (collections are always returned intact).
|
|
3555
3568
|
*/
|
|
3556
|
-
getIn(
|
|
3557
|
-
if (Collection.isEmptyPath(
|
|
3569
|
+
getIn(path6, keepScalar) {
|
|
3570
|
+
if (Collection.isEmptyPath(path6))
|
|
3558
3571
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
3559
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
3572
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path6, keepScalar) : void 0;
|
|
3560
3573
|
}
|
|
3561
3574
|
/**
|
|
3562
3575
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -3567,10 +3580,10 @@ var require_Document = __commonJS({
|
|
|
3567
3580
|
/**
|
|
3568
3581
|
* Checks if the document includes a value at `path`.
|
|
3569
3582
|
*/
|
|
3570
|
-
hasIn(
|
|
3571
|
-
if (Collection.isEmptyPath(
|
|
3583
|
+
hasIn(path6) {
|
|
3584
|
+
if (Collection.isEmptyPath(path6))
|
|
3572
3585
|
return this.contents !== void 0;
|
|
3573
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
3586
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path6) : false;
|
|
3574
3587
|
}
|
|
3575
3588
|
/**
|
|
3576
3589
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -3587,13 +3600,13 @@ var require_Document = __commonJS({
|
|
|
3587
3600
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
3588
3601
|
* boolean to add/remove the item from the set.
|
|
3589
3602
|
*/
|
|
3590
|
-
setIn(
|
|
3591
|
-
if (Collection.isEmptyPath(
|
|
3603
|
+
setIn(path6, value) {
|
|
3604
|
+
if (Collection.isEmptyPath(path6)) {
|
|
3592
3605
|
this.contents = value;
|
|
3593
3606
|
} else if (this.contents == null) {
|
|
3594
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
3607
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path6), value);
|
|
3595
3608
|
} else if (assertCollection(this.contents)) {
|
|
3596
|
-
this.contents.setIn(
|
|
3609
|
+
this.contents.setIn(path6, value);
|
|
3597
3610
|
}
|
|
3598
3611
|
}
|
|
3599
3612
|
/**
|
|
@@ -4737,7 +4750,7 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4737
4750
|
while (next === " " || next === " ")
|
|
4738
4751
|
next = source[++i + 1];
|
|
4739
4752
|
} else if (next === "x" || next === "u" || next === "U") {
|
|
4740
|
-
const length =
|
|
4753
|
+
const length = next === "x" ? 2 : next === "u" ? 4 : 8;
|
|
4741
4754
|
res += parseCharCode(source, i + 1, length, onError);
|
|
4742
4755
|
i += length;
|
|
4743
4756
|
} else {
|
|
@@ -4812,12 +4825,13 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4812
4825
|
const cc = source.substr(offset, length);
|
|
4813
4826
|
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
|
|
4814
4827
|
const code = ok ? parseInt(cc, 16) : NaN;
|
|
4815
|
-
|
|
4828
|
+
try {
|
|
4829
|
+
return String.fromCodePoint(code);
|
|
4830
|
+
} catch {
|
|
4816
4831
|
const raw = source.substr(offset - 2, length + 2);
|
|
4817
4832
|
onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
|
|
4818
4833
|
return raw;
|
|
4819
4834
|
}
|
|
4820
|
-
return String.fromCodePoint(code);
|
|
4821
4835
|
}
|
|
4822
4836
|
exports.resolveFlowScalar = resolveFlowScalar;
|
|
4823
4837
|
}
|
|
@@ -4967,17 +4981,22 @@ var require_compose_node = __commonJS({
|
|
|
4967
4981
|
case "block-map":
|
|
4968
4982
|
case "block-seq":
|
|
4969
4983
|
case "flow-collection":
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4984
|
+
try {
|
|
4985
|
+
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
|
|
4986
|
+
if (anchor)
|
|
4987
|
+
node.anchor = anchor.source.substring(1);
|
|
4988
|
+
} catch (error) {
|
|
4989
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4990
|
+
onError(token, "RESOURCE_EXHAUSTION", message);
|
|
4991
|
+
}
|
|
4973
4992
|
break;
|
|
4974
4993
|
default: {
|
|
4975
4994
|
const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
|
|
4976
4995
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
4977
|
-
node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
|
|
4978
4996
|
isSrcToken = false;
|
|
4979
4997
|
}
|
|
4980
4998
|
}
|
|
4999
|
+
node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
|
|
4981
5000
|
if (anchor && node.anchor === "")
|
|
4982
5001
|
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
|
|
4983
5002
|
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
|
|
@@ -5162,8 +5181,10 @@ ${cb}` : comment;
|
|
|
5162
5181
|
}
|
|
5163
5182
|
}
|
|
5164
5183
|
if (afterDoc) {
|
|
5165
|
-
|
|
5166
|
-
|
|
5184
|
+
for (let i = 0; i < this.errors.length; ++i)
|
|
5185
|
+
doc.errors.push(this.errors[i]);
|
|
5186
|
+
for (let i = 0; i < this.warnings.length; ++i)
|
|
5187
|
+
doc.warnings.push(this.warnings[i]);
|
|
5167
5188
|
} else {
|
|
5168
5189
|
doc.errors = this.errors;
|
|
5169
5190
|
doc.warnings = this.warnings;
|
|
@@ -5545,9 +5566,9 @@ var require_cst_visit = __commonJS({
|
|
|
5545
5566
|
visit.BREAK = BREAK;
|
|
5546
5567
|
visit.SKIP = SKIP;
|
|
5547
5568
|
visit.REMOVE = REMOVE;
|
|
5548
|
-
visit.itemAtPath = (cst,
|
|
5569
|
+
visit.itemAtPath = (cst, path6) => {
|
|
5549
5570
|
let item = cst;
|
|
5550
|
-
for (const [field, index] of
|
|
5571
|
+
for (const [field, index] of path6) {
|
|
5551
5572
|
const tok = item?.[field];
|
|
5552
5573
|
if (tok && "items" in tok) {
|
|
5553
5574
|
item = tok.items[index];
|
|
@@ -5556,23 +5577,23 @@ var require_cst_visit = __commonJS({
|
|
|
5556
5577
|
}
|
|
5557
5578
|
return item;
|
|
5558
5579
|
};
|
|
5559
|
-
visit.parentCollection = (cst,
|
|
5560
|
-
const parent = visit.itemAtPath(cst,
|
|
5561
|
-
const field =
|
|
5580
|
+
visit.parentCollection = (cst, path6) => {
|
|
5581
|
+
const parent = visit.itemAtPath(cst, path6.slice(0, -1));
|
|
5582
|
+
const field = path6[path6.length - 1][0];
|
|
5562
5583
|
const coll = parent?.[field];
|
|
5563
5584
|
if (coll && "items" in coll)
|
|
5564
5585
|
return coll;
|
|
5565
5586
|
throw new Error("Parent collection not found");
|
|
5566
5587
|
};
|
|
5567
|
-
function _visit(
|
|
5568
|
-
let ctrl = visitor(item,
|
|
5588
|
+
function _visit(path6, item, visitor) {
|
|
5589
|
+
let ctrl = visitor(item, path6);
|
|
5569
5590
|
if (typeof ctrl === "symbol")
|
|
5570
5591
|
return ctrl;
|
|
5571
5592
|
for (const field of ["key", "value"]) {
|
|
5572
5593
|
const token = item[field];
|
|
5573
5594
|
if (token && "items" in token) {
|
|
5574
5595
|
for (let i = 0; i < token.items.length; ++i) {
|
|
5575
|
-
const ci = _visit(Object.freeze(
|
|
5596
|
+
const ci = _visit(Object.freeze(path6.concat([[field, i]])), token.items[i], visitor);
|
|
5576
5597
|
if (typeof ci === "number")
|
|
5577
5598
|
i = ci - 1;
|
|
5578
5599
|
else if (ci === BREAK)
|
|
@@ -5583,10 +5604,10 @@ var require_cst_visit = __commonJS({
|
|
|
5583
5604
|
}
|
|
5584
5605
|
}
|
|
5585
5606
|
if (typeof ctrl === "function" && field === "key")
|
|
5586
|
-
ctrl = ctrl(item,
|
|
5607
|
+
ctrl = ctrl(item, path6);
|
|
5587
5608
|
}
|
|
5588
5609
|
}
|
|
5589
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5610
|
+
return typeof ctrl === "function" ? ctrl(item, path6) : ctrl;
|
|
5590
5611
|
}
|
|
5591
5612
|
exports.visit = visit;
|
|
5592
5613
|
}
|
|
@@ -5896,7 +5917,7 @@ var require_lexer = __commonJS({
|
|
|
5896
5917
|
const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
|
|
5897
5918
|
this.indentNext = this.indentValue + 1;
|
|
5898
5919
|
this.indentValue += n;
|
|
5899
|
-
return
|
|
5920
|
+
return "block-start";
|
|
5900
5921
|
}
|
|
5901
5922
|
return "doc";
|
|
5902
5923
|
}
|
|
@@ -6195,28 +6216,38 @@ var require_lexer = __commonJS({
|
|
|
6195
6216
|
return 0;
|
|
6196
6217
|
}
|
|
6197
6218
|
*pushIndicators() {
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6219
|
+
let n = 0;
|
|
6220
|
+
loop: while (true) {
|
|
6221
|
+
switch (this.charAt(0)) {
|
|
6222
|
+
case "!":
|
|
6223
|
+
n += yield* this.pushTag();
|
|
6224
|
+
n += yield* this.pushSpaces(true);
|
|
6225
|
+
continue loop;
|
|
6226
|
+
case "&":
|
|
6227
|
+
n += yield* this.pushUntil(isNotAnchorChar);
|
|
6228
|
+
n += yield* this.pushSpaces(true);
|
|
6229
|
+
continue loop;
|
|
6230
|
+
case "-":
|
|
6231
|
+
// this is an error
|
|
6232
|
+
case "?":
|
|
6233
|
+
// this is an error outside flow collections
|
|
6234
|
+
case ":": {
|
|
6235
|
+
const inFlow = this.flowLevel > 0;
|
|
6236
|
+
const ch1 = this.charAt(1);
|
|
6237
|
+
if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
|
|
6238
|
+
if (!inFlow)
|
|
6239
|
+
this.indentNext = this.indentValue + 1;
|
|
6240
|
+
else if (this.flowKey)
|
|
6241
|
+
this.flowKey = false;
|
|
6242
|
+
n += yield* this.pushCount(1);
|
|
6243
|
+
n += yield* this.pushSpaces(true);
|
|
6244
|
+
continue loop;
|
|
6245
|
+
}
|
|
6216
6246
|
}
|
|
6217
6247
|
}
|
|
6248
|
+
break loop;
|
|
6218
6249
|
}
|
|
6219
|
-
return
|
|
6250
|
+
return n;
|
|
6220
6251
|
}
|
|
6221
6252
|
*pushTag() {
|
|
6222
6253
|
if (this.charAt(1) === "<") {
|
|
@@ -6375,6 +6406,13 @@ var require_parser = __commonJS({
|
|
|
6375
6406
|
}
|
|
6376
6407
|
return prev.splice(i, prev.length);
|
|
6377
6408
|
}
|
|
6409
|
+
function arrayPushArray(target, source) {
|
|
6410
|
+
if (source.length < 1e5)
|
|
6411
|
+
Array.prototype.push.apply(target, source);
|
|
6412
|
+
else
|
|
6413
|
+
for (let i = 0; i < source.length; ++i)
|
|
6414
|
+
target.push(source[i]);
|
|
6415
|
+
}
|
|
6378
6416
|
function fixFlowSeqItems(fc) {
|
|
6379
6417
|
if (fc.start.type === "flow-seq-start") {
|
|
6380
6418
|
for (const it of fc.items) {
|
|
@@ -6384,11 +6422,11 @@ var require_parser = __commonJS({
|
|
|
6384
6422
|
delete it.key;
|
|
6385
6423
|
if (isFlowToken(it.value)) {
|
|
6386
6424
|
if (it.value.end)
|
|
6387
|
-
|
|
6425
|
+
arrayPushArray(it.value.end, it.sep);
|
|
6388
6426
|
else
|
|
6389
6427
|
it.value.end = it.sep;
|
|
6390
6428
|
} else
|
|
6391
|
-
|
|
6429
|
+
arrayPushArray(it.start, it.sep);
|
|
6392
6430
|
delete it.sep;
|
|
6393
6431
|
}
|
|
6394
6432
|
}
|
|
@@ -6743,7 +6781,7 @@ var require_parser = __commonJS({
|
|
|
6743
6781
|
const prev = map.items[map.items.length - 2];
|
|
6744
6782
|
const end = prev?.value?.end;
|
|
6745
6783
|
if (Array.isArray(end)) {
|
|
6746
|
-
|
|
6784
|
+
arrayPushArray(end, it.start);
|
|
6747
6785
|
end.push(this.sourceToken);
|
|
6748
6786
|
map.items.pop();
|
|
6749
6787
|
return;
|
|
@@ -6871,14 +6909,14 @@ var require_parser = __commonJS({
|
|
|
6871
6909
|
case "scalar":
|
|
6872
6910
|
case "single-quoted-scalar":
|
|
6873
6911
|
case "double-quoted-scalar": {
|
|
6874
|
-
const
|
|
6912
|
+
const fs6 = this.flowScalar(this.type);
|
|
6875
6913
|
if (atNextItem || it.value) {
|
|
6876
|
-
map.items.push({ start, key:
|
|
6914
|
+
map.items.push({ start, key: fs6, sep: [] });
|
|
6877
6915
|
this.onKeyLine = true;
|
|
6878
6916
|
} else if (it.sep) {
|
|
6879
|
-
this.stack.push(
|
|
6917
|
+
this.stack.push(fs6);
|
|
6880
6918
|
} else {
|
|
6881
|
-
Object.assign(it, { key:
|
|
6919
|
+
Object.assign(it, { key: fs6, sep: [] });
|
|
6882
6920
|
this.onKeyLine = true;
|
|
6883
6921
|
}
|
|
6884
6922
|
return;
|
|
@@ -6931,7 +6969,7 @@ var require_parser = __commonJS({
|
|
|
6931
6969
|
const prev = seq.items[seq.items.length - 2];
|
|
6932
6970
|
const end = prev?.value?.end;
|
|
6933
6971
|
if (Array.isArray(end)) {
|
|
6934
|
-
|
|
6972
|
+
arrayPushArray(end, it.start);
|
|
6935
6973
|
end.push(this.sourceToken);
|
|
6936
6974
|
seq.items.pop();
|
|
6937
6975
|
return;
|
|
@@ -7006,13 +7044,13 @@ var require_parser = __commonJS({
|
|
|
7006
7044
|
case "scalar":
|
|
7007
7045
|
case "single-quoted-scalar":
|
|
7008
7046
|
case "double-quoted-scalar": {
|
|
7009
|
-
const
|
|
7047
|
+
const fs6 = this.flowScalar(this.type);
|
|
7010
7048
|
if (!it || it.value)
|
|
7011
|
-
fc.items.push({ start: [], key:
|
|
7049
|
+
fc.items.push({ start: [], key: fs6, sep: [] });
|
|
7012
7050
|
else if (it.sep)
|
|
7013
|
-
this.stack.push(
|
|
7051
|
+
this.stack.push(fs6);
|
|
7014
7052
|
else
|
|
7015
|
-
Object.assign(it, { key:
|
|
7053
|
+
Object.assign(it, { key: fs6, sep: [] });
|
|
7016
7054
|
return;
|
|
7017
7055
|
}
|
|
7018
7056
|
case "flow-map-end":
|
|
@@ -7550,17 +7588,17 @@ var require_ignore = __commonJS({
|
|
|
7550
7588
|
var throwError = (message, Ctor) => {
|
|
7551
7589
|
throw new Ctor(message);
|
|
7552
7590
|
};
|
|
7553
|
-
var checkPath = (
|
|
7554
|
-
if (!isString(
|
|
7591
|
+
var checkPath = (path6, originalPath, doThrow) => {
|
|
7592
|
+
if (!isString(path6)) {
|
|
7555
7593
|
return doThrow(
|
|
7556
7594
|
`path must be a string, but got \`${originalPath}\``,
|
|
7557
7595
|
TypeError
|
|
7558
7596
|
);
|
|
7559
7597
|
}
|
|
7560
|
-
if (!
|
|
7598
|
+
if (!path6) {
|
|
7561
7599
|
return doThrow(`path must not be empty`, TypeError);
|
|
7562
7600
|
}
|
|
7563
|
-
if (checkPath.isNotRelative(
|
|
7601
|
+
if (checkPath.isNotRelative(path6)) {
|
|
7564
7602
|
const r = "`path.relative()`d";
|
|
7565
7603
|
return doThrow(
|
|
7566
7604
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -7569,7 +7607,7 @@ var require_ignore = __commonJS({
|
|
|
7569
7607
|
}
|
|
7570
7608
|
return true;
|
|
7571
7609
|
};
|
|
7572
|
-
var isNotRelative = (
|
|
7610
|
+
var isNotRelative = (path6) => REGEX_TEST_INVALID_PATH.test(path6);
|
|
7573
7611
|
checkPath.isNotRelative = isNotRelative;
|
|
7574
7612
|
checkPath.convert = (p) => p;
|
|
7575
7613
|
var Ignore = class {
|
|
@@ -7628,7 +7666,7 @@ var require_ignore = __commonJS({
|
|
|
7628
7666
|
// setting `checkUnignored` to `false` could reduce additional
|
|
7629
7667
|
// path matching.
|
|
7630
7668
|
// @returns {TestResult} true if a file is ignored
|
|
7631
|
-
_testOne(
|
|
7669
|
+
_testOne(path6, checkUnignored) {
|
|
7632
7670
|
let ignored = false;
|
|
7633
7671
|
let unignored = false;
|
|
7634
7672
|
this._rules.forEach((rule) => {
|
|
@@ -7636,7 +7674,7 @@ var require_ignore = __commonJS({
|
|
|
7636
7674
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
7637
7675
|
return;
|
|
7638
7676
|
}
|
|
7639
|
-
const matched = rule.regex.test(
|
|
7677
|
+
const matched = rule.regex.test(path6);
|
|
7640
7678
|
if (matched) {
|
|
7641
7679
|
ignored = !negative;
|
|
7642
7680
|
unignored = negative;
|
|
@@ -7649,24 +7687,24 @@ var require_ignore = __commonJS({
|
|
|
7649
7687
|
}
|
|
7650
7688
|
// @returns {TestResult}
|
|
7651
7689
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
7652
|
-
const
|
|
7690
|
+
const path6 = originalPath && checkPath.convert(originalPath);
|
|
7653
7691
|
checkPath(
|
|
7654
|
-
|
|
7692
|
+
path6,
|
|
7655
7693
|
originalPath,
|
|
7656
7694
|
this._allowRelativePaths ? RETURN_FALSE : throwError
|
|
7657
7695
|
);
|
|
7658
|
-
return this._t(
|
|
7696
|
+
return this._t(path6, cache, checkUnignored, slices);
|
|
7659
7697
|
}
|
|
7660
|
-
_t(
|
|
7661
|
-
if (
|
|
7662
|
-
return cache[
|
|
7698
|
+
_t(path6, cache, checkUnignored, slices) {
|
|
7699
|
+
if (path6 in cache) {
|
|
7700
|
+
return cache[path6];
|
|
7663
7701
|
}
|
|
7664
7702
|
if (!slices) {
|
|
7665
|
-
slices =
|
|
7703
|
+
slices = path6.split(SLASH);
|
|
7666
7704
|
}
|
|
7667
7705
|
slices.pop();
|
|
7668
7706
|
if (!slices.length) {
|
|
7669
|
-
return cache[
|
|
7707
|
+
return cache[path6] = this._testOne(path6, checkUnignored);
|
|
7670
7708
|
}
|
|
7671
7709
|
const parent = this._t(
|
|
7672
7710
|
slices.join(SLASH) + SLASH,
|
|
@@ -7674,24 +7712,24 @@ var require_ignore = __commonJS({
|
|
|
7674
7712
|
checkUnignored,
|
|
7675
7713
|
slices
|
|
7676
7714
|
);
|
|
7677
|
-
return cache[
|
|
7715
|
+
return cache[path6] = parent.ignored ? parent : this._testOne(path6, checkUnignored);
|
|
7678
7716
|
}
|
|
7679
|
-
ignores(
|
|
7680
|
-
return this._test(
|
|
7717
|
+
ignores(path6) {
|
|
7718
|
+
return this._test(path6, this._ignoreCache, false).ignored;
|
|
7681
7719
|
}
|
|
7682
7720
|
createFilter() {
|
|
7683
|
-
return (
|
|
7721
|
+
return (path6) => !this.ignores(path6);
|
|
7684
7722
|
}
|
|
7685
7723
|
filter(paths) {
|
|
7686
7724
|
return makeArray(paths).filter(this.createFilter());
|
|
7687
7725
|
}
|
|
7688
7726
|
// @returns {TestResult}
|
|
7689
|
-
test(
|
|
7690
|
-
return this._test(
|
|
7727
|
+
test(path6) {
|
|
7728
|
+
return this._test(path6, this._testCache, true);
|
|
7691
7729
|
}
|
|
7692
7730
|
};
|
|
7693
7731
|
var factory = (options) => new Ignore(options);
|
|
7694
|
-
var isPathValid = (
|
|
7732
|
+
var isPathValid = (path6) => checkPath(path6 && checkPath.convert(path6), path6, RETURN_FALSE);
|
|
7695
7733
|
factory.isPathValid = isPathValid;
|
|
7696
7734
|
factory.default = factory;
|
|
7697
7735
|
module.exports = factory;
|
|
@@ -7702,38 +7740,626 @@ var require_ignore = __commonJS({
|
|
|
7702
7740
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
7703
7741
|
checkPath.convert = makePosix;
|
|
7704
7742
|
const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
7705
|
-
checkPath.isNotRelative = (
|
|
7743
|
+
checkPath.isNotRelative = (path6) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path6) || isNotRelative(path6);
|
|
7706
7744
|
}
|
|
7707
7745
|
}
|
|
7708
7746
|
});
|
|
7709
7747
|
|
|
7710
7748
|
// src/index.js
|
|
7711
|
-
var
|
|
7712
|
-
var
|
|
7749
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
7750
|
+
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
7713
7751
|
import crypto from "node:crypto";
|
|
7714
|
-
import
|
|
7752
|
+
import fs5 from "node:fs";
|
|
7715
7753
|
import http from "node:http";
|
|
7716
7754
|
import os from "node:os";
|
|
7717
|
-
import
|
|
7718
|
-
import { execFileSync } from "node:child_process";
|
|
7755
|
+
import path5 from "node:path";
|
|
7719
7756
|
import { fileURLToPath } from "node:url";
|
|
7720
7757
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
7721
7758
|
|
|
7722
|
-
// src/
|
|
7723
|
-
var import_yaml = __toESM(require_dist(), 1);
|
|
7724
|
-
var import_ignore = __toESM(require_ignore(), 1);
|
|
7759
|
+
// src/git_utils.js
|
|
7725
7760
|
import fs from "node:fs";
|
|
7726
7761
|
import path from "node:path";
|
|
7762
|
+
import { execFileSync } from "node:child_process";
|
|
7763
|
+
function toGitPath(worktree, targetPath) {
|
|
7764
|
+
const relativePath = path.relative(worktree, targetPath);
|
|
7765
|
+
if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath)) return null;
|
|
7766
|
+
return relativePath.split(path.sep).join("/");
|
|
7767
|
+
}
|
|
7768
|
+
function runGit(worktree, args) {
|
|
7769
|
+
return execFileSync("git", args, {
|
|
7770
|
+
cwd: worktree,
|
|
7771
|
+
encoding: "utf8",
|
|
7772
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7773
|
+
});
|
|
7774
|
+
}
|
|
7775
|
+
function sameFilesystemPath(left, right) {
|
|
7776
|
+
try {
|
|
7777
|
+
return fs.realpathSync(left) === fs.realpathSync(right);
|
|
7778
|
+
} catch {
|
|
7779
|
+
return path.resolve(left) === path.resolve(right);
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
function gitMigrationState(worktree) {
|
|
7783
|
+
try {
|
|
7784
|
+
const root = runGit(worktree, ["rev-parse", "--show-toplevel"]).trim();
|
|
7785
|
+
if (!sameFilesystemPath(root, worktree)) return null;
|
|
7786
|
+
return { worktree };
|
|
7787
|
+
} catch {
|
|
7788
|
+
return null;
|
|
7789
|
+
}
|
|
7790
|
+
}
|
|
7791
|
+
function isGitTracked(gitState, targetPath) {
|
|
7792
|
+
if (!gitState) return false;
|
|
7793
|
+
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
7794
|
+
if (!gitPath) return false;
|
|
7795
|
+
try {
|
|
7796
|
+
runGit(gitState.worktree, ["ls-files", "--error-unmatch", "--", gitPath]);
|
|
7797
|
+
return true;
|
|
7798
|
+
} catch {
|
|
7799
|
+
return false;
|
|
7800
|
+
}
|
|
7801
|
+
}
|
|
7802
|
+
function hasGitTrackedChildren(gitState, targetPath) {
|
|
7803
|
+
if (!gitState) return false;
|
|
7804
|
+
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
7805
|
+
if (!gitPath) return false;
|
|
7806
|
+
try {
|
|
7807
|
+
return runGit(gitState.worktree, ["ls-files", "--", `${gitPath}/`]).trim().length > 0;
|
|
7808
|
+
} catch {
|
|
7809
|
+
return false;
|
|
7810
|
+
}
|
|
7811
|
+
}
|
|
7812
|
+
function gitAwareMoveIfTracked(sourcePath, destinationPath, gitState) {
|
|
7813
|
+
const sourceIsTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
7814
|
+
if (!gitState || !sourceIsTracked || fs.existsSync(destinationPath)) return false;
|
|
7815
|
+
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
7816
|
+
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
7817
|
+
if (!sourceGitPath || !destinationGitPath) return false;
|
|
7818
|
+
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
7819
|
+
try {
|
|
7820
|
+
runGit(gitState.worktree, ["mv", "--", sourceGitPath, destinationGitPath]);
|
|
7821
|
+
return true;
|
|
7822
|
+
} catch {
|
|
7823
|
+
return false;
|
|
7824
|
+
}
|
|
7825
|
+
}
|
|
7826
|
+
function stageGitAwareMerge(sourcePath, destinationPath, gitState) {
|
|
7827
|
+
if (!gitState) return;
|
|
7828
|
+
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
7829
|
+
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
7830
|
+
if (!sourceGitPath || !destinationGitPath) return;
|
|
7831
|
+
try {
|
|
7832
|
+
runGit(gitState.worktree, ["add", "-A", "--", sourceGitPath, destinationGitPath]);
|
|
7833
|
+
} catch {
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7837
|
+
// src/include_resolver.js
|
|
7838
|
+
import fs2 from "node:fs";
|
|
7839
|
+
import path2 from "node:path";
|
|
7840
|
+
function compactPromptPath(filePath) {
|
|
7841
|
+
if (!filePath.endsWith(".md") || filePath.endsWith(".prompt.md")) return null;
|
|
7842
|
+
return filePath.replace(/\.md$/, ".prompt.md");
|
|
7843
|
+
}
|
|
7844
|
+
function isSameOrNestedPath(candidate, root) {
|
|
7845
|
+
if (typeof candidate !== "string" || typeof root !== "string" || !candidate.trim() || !root.trim()) return false;
|
|
7846
|
+
const resolvedCandidate = path2.resolve(candidate);
|
|
7847
|
+
const resolvedRoot = path2.resolve(root);
|
|
7848
|
+
const relative = path2.relative(resolvedRoot, resolvedCandidate);
|
|
7849
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path2.isAbsolute(relative);
|
|
7850
|
+
}
|
|
7851
|
+
function resolveWithin(baseDir, relativePath) {
|
|
7852
|
+
if (!relativePath) return null;
|
|
7853
|
+
if (path2.isAbsolute(relativePath)) return null;
|
|
7854
|
+
const resolved = path2.resolve(baseDir, relativePath);
|
|
7855
|
+
return isSameOrNestedPath(resolved, baseDir) ? resolved : null;
|
|
7856
|
+
}
|
|
7857
|
+
function withCompactPreference(paths, options = {}) {
|
|
7858
|
+
if (!options.preferCompactPromptDocs) return paths;
|
|
7859
|
+
const compactPaths = paths.map((filePath) => filePath ? compactPromptPath(filePath) : null).filter(Boolean);
|
|
7860
|
+
return [...compactPaths, ...paths];
|
|
7861
|
+
}
|
|
7862
|
+
function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
7863
|
+
const trimmed = String(includeRef || "").trim();
|
|
7864
|
+
const scopedMatch = trimmed.match(/^([a-z]+):(.*)$/i);
|
|
7865
|
+
const scope = scopedMatch?.[1]?.toLowerCase();
|
|
7866
|
+
const target = scopedMatch ? scopedMatch[2].trim() : trimmed;
|
|
7867
|
+
const optimaRoot = options.optimaRoot || path2.join(repoRoot, ".optima");
|
|
7868
|
+
const repoPolicyRoot = options.repoPolicyRoot || path2.join(optimaRoot, "policies");
|
|
7869
|
+
const bundlePolicyRoot = options.bundlePolicyRoot || path2.join(bundleRoot, "assets", "policies");
|
|
7870
|
+
if (scope === "plugin") {
|
|
7871
|
+
for (const filePath of withCompactPreference([resolveWithin(bundleRoot, target)], options)) {
|
|
7872
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7873
|
+
}
|
|
7874
|
+
return null;
|
|
7875
|
+
}
|
|
7876
|
+
if (scope === "repo") {
|
|
7877
|
+
for (const filePath of withCompactPreference([resolveWithin(optimaRoot, target)], options)) {
|
|
7878
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7879
|
+
}
|
|
7880
|
+
return null;
|
|
7881
|
+
}
|
|
7882
|
+
if (scope === "policy") {
|
|
7883
|
+
const candidates2 = withCompactPreference([
|
|
7884
|
+
resolveWithin(repoPolicyRoot, target),
|
|
7885
|
+
resolveWithin(bundlePolicyRoot, target)
|
|
7886
|
+
], options);
|
|
7887
|
+
for (const filePath of candidates2) {
|
|
7888
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7889
|
+
}
|
|
7890
|
+
return null;
|
|
7891
|
+
}
|
|
7892
|
+
const candidates = withCompactPreference([
|
|
7893
|
+
resolveWithin(repoRoot, target),
|
|
7894
|
+
resolveWithin(bundleRoot, target)
|
|
7895
|
+
], options);
|
|
7896
|
+
for (const filePath of candidates) {
|
|
7897
|
+
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
7898
|
+
}
|
|
7899
|
+
return null;
|
|
7900
|
+
}
|
|
7901
|
+
function resolveIncludes(text, repoRoot, bundleRoot, options = {}) {
|
|
7902
|
+
const includeDepth = Number(options.includeDepth || 0);
|
|
7903
|
+
const maxIncludeDepth = Number(options.maxIncludeDepth || 20);
|
|
7904
|
+
if (includeDepth > maxIncludeDepth) {
|
|
7905
|
+
return "\n\n# ERROR: Include recursion limit exceeded.\n\n";
|
|
7906
|
+
}
|
|
7907
|
+
const includeRegex = /<include:(.*?)>/g;
|
|
7908
|
+
return String(text || "").replace(includeRegex, (match, includeRef) => {
|
|
7909
|
+
const filePath = resolveIncludeFile(includeRef, repoRoot, bundleRoot, options);
|
|
7910
|
+
if (!filePath) {
|
|
7911
|
+
console.warn(`[Optima] Include file not found: ${includeRef}`);
|
|
7912
|
+
return `
|
|
7913
|
+
|
|
7914
|
+
# ERROR: Include file not found: ${includeRef}
|
|
7915
|
+
|
|
7916
|
+
`;
|
|
7917
|
+
}
|
|
7918
|
+
const content = fs2.readFileSync(filePath, "utf8");
|
|
7919
|
+
return resolveIncludes(content, repoRoot, bundleRoot, { ...options, includeDepth: includeDepth + 1, maxIncludeDepth });
|
|
7920
|
+
});
|
|
7921
|
+
}
|
|
7922
|
+
|
|
7923
|
+
// src/repair.js
|
|
7924
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
7925
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
7926
|
+
import fs3 from "node:fs";
|
|
7927
|
+
import path3 from "node:path";
|
|
7928
|
+
var CODEMAP_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7929
|
+
".js",
|
|
7930
|
+
".ts",
|
|
7931
|
+
".tsx",
|
|
7932
|
+
".jsx",
|
|
7933
|
+
".dart",
|
|
7934
|
+
".py",
|
|
7935
|
+
".go",
|
|
7936
|
+
".rs",
|
|
7937
|
+
".java",
|
|
7938
|
+
".c",
|
|
7939
|
+
".cpp",
|
|
7940
|
+
".cs",
|
|
7941
|
+
".php",
|
|
7942
|
+
".rb",
|
|
7943
|
+
".swift",
|
|
7944
|
+
".kt",
|
|
7945
|
+
".m",
|
|
7946
|
+
".sh",
|
|
7947
|
+
".sql",
|
|
7948
|
+
".yaml",
|
|
7949
|
+
".yml",
|
|
7950
|
+
".json",
|
|
7951
|
+
".md"
|
|
7952
|
+
]);
|
|
7953
|
+
var LEGACY_OPERATIONAL_ROOTS = /* @__PURE__ */ new Set([
|
|
7954
|
+
"tasks",
|
|
7955
|
+
"evidences",
|
|
7956
|
+
".orbita",
|
|
7957
|
+
".staticeng",
|
|
7958
|
+
".nomadworks",
|
|
7959
|
+
".nomadwork",
|
|
7960
|
+
"nomadworks",
|
|
7961
|
+
".codenomad"
|
|
7962
|
+
]);
|
|
7963
|
+
var OPTIMA_OPERATIONAL_FOLDERS = [".optima", "templates", "dist"];
|
|
7964
|
+
function relPath(worktree, targetPath) {
|
|
7965
|
+
return path3.relative(worktree, targetPath).split(path3.sep).join("/") || ".";
|
|
7966
|
+
}
|
|
7967
|
+
function normalizeCodemapRelPath(relPathValue) {
|
|
7968
|
+
return path3.normalize(relPathValue).replace(/\\/g, "/").replace(/\/$/, "");
|
|
7969
|
+
}
|
|
7970
|
+
function firstPathSegment(relPathValue) {
|
|
7971
|
+
return normalizeCodemapRelPath(relPathValue).split("/").filter(Boolean)[0] || "";
|
|
7972
|
+
}
|
|
7973
|
+
function isOperationalRelPath(relPathValue) {
|
|
7974
|
+
if (!relPathValue) return false;
|
|
7975
|
+
const normalized = normalizeCodemapRelPath(relPathValue);
|
|
7976
|
+
const firstSegment = firstPathSegment(normalized);
|
|
7977
|
+
if (LEGACY_OPERATIONAL_ROOTS.has(firstSegment)) return true;
|
|
7978
|
+
return OPTIMA_OPERATIONAL_FOLDERS.some((folder) => normalized === folder || normalized.startsWith(`${folder}/`));
|
|
7979
|
+
}
|
|
7980
|
+
function isHiddenTree(relPathValue) {
|
|
7981
|
+
if (!relPathValue) return false;
|
|
7982
|
+
return normalizeCodemapRelPath(relPathValue).split("/").some((part) => part.startsWith("."));
|
|
7983
|
+
}
|
|
7984
|
+
function loadGitIgnoreMatcher(worktree) {
|
|
7985
|
+
const ig = (0, import_ignore.default)();
|
|
7986
|
+
ig.add(".git");
|
|
7987
|
+
const gitignorePath = path3.join(worktree, ".gitignore");
|
|
7988
|
+
if (fs3.existsSync(gitignorePath)) ig.add(fs3.readFileSync(gitignorePath, "utf8"));
|
|
7989
|
+
return ig;
|
|
7990
|
+
}
|
|
7991
|
+
function codemapSectionEntries(value) {
|
|
7992
|
+
if (Array.isArray(value)) return value;
|
|
7993
|
+
if (value && typeof value === "object") return Object.values(value);
|
|
7994
|
+
return [];
|
|
7995
|
+
}
|
|
7996
|
+
function codemapIndexedPaths(map) {
|
|
7997
|
+
const indexed = /* @__PURE__ */ new Set();
|
|
7998
|
+
for (const section of ["modules", "entrypoints", "sources_of_truth", "links", "internals"]) {
|
|
7999
|
+
for (const item of codemapSectionEntries(map?.[section])) {
|
|
8000
|
+
if (item?.path) indexed.add(normalizeCodemapRelPath(item.path));
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
return indexed;
|
|
8004
|
+
}
|
|
8005
|
+
function readCodemap(filePath, unresolved, worktree) {
|
|
8006
|
+
try {
|
|
8007
|
+
const parsed = import_yaml.default.parse(fs3.readFileSync(filePath, "utf8"));
|
|
8008
|
+
if (!parsed || typeof parsed !== "object") {
|
|
8009
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, filePath), message: "CodeMap is empty or invalid; manual repair required." });
|
|
8010
|
+
return null;
|
|
8011
|
+
}
|
|
8012
|
+
return parsed;
|
|
8013
|
+
} catch {
|
|
8014
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, filePath), message: "CodeMap has invalid YAML; manual repair required." });
|
|
8015
|
+
return null;
|
|
8016
|
+
}
|
|
8017
|
+
}
|
|
8018
|
+
function writeCodemap(filePath, map) {
|
|
8019
|
+
fs3.writeFileSync(filePath, import_yaml.default.stringify(map), "utf8");
|
|
8020
|
+
}
|
|
8021
|
+
function isIgnoredPath(ig, relativePath) {
|
|
8022
|
+
const normalized = normalizeCodemapRelPath(relativePath);
|
|
8023
|
+
return Boolean(normalized) && ig.ignores(normalized);
|
|
8024
|
+
}
|
|
8025
|
+
function hasImmediateSourceFile(dirPath, ig, worktree) {
|
|
8026
|
+
if (!fs3.existsSync(dirPath)) return false;
|
|
8027
|
+
for (const item of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8028
|
+
const childPath = path3.join(dirPath, item.name);
|
|
8029
|
+
const relative = path3.relative(worktree, childPath);
|
|
8030
|
+
if (isIgnoredPath(ig, relative) || isOperationalRelPath(relative)) continue;
|
|
8031
|
+
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) return true;
|
|
8032
|
+
}
|
|
8033
|
+
return false;
|
|
8034
|
+
}
|
|
8035
|
+
function containsSource(dirPath, ig, worktree) {
|
|
8036
|
+
if (!fs3.existsSync(dirPath)) return false;
|
|
8037
|
+
const dirRelPath = path3.relative(worktree, dirPath);
|
|
8038
|
+
if (isOperationalRelPath(dirRelPath) || isHiddenTree(dirRelPath)) return false;
|
|
8039
|
+
for (const item of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8040
|
+
const childPath = path3.join(dirPath, item.name);
|
|
8041
|
+
const relative = path3.relative(worktree, childPath);
|
|
8042
|
+
if (isIgnoredPath(ig, relative) || isOperationalRelPath(relative) || isHiddenTree(relative)) continue;
|
|
8043
|
+
if (item.isFile() && CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) return true;
|
|
8044
|
+
if (item.isDirectory() && containsSource(childPath, ig, worktree)) return true;
|
|
8045
|
+
}
|
|
8046
|
+
return false;
|
|
8047
|
+
}
|
|
8048
|
+
function createModuleCodemap(dirPath, worktree, deps) {
|
|
8049
|
+
const entries = fs3.readdirSync(dirPath, { withFileTypes: true });
|
|
8050
|
+
const entrypoints = [];
|
|
8051
|
+
const internals = [];
|
|
8052
|
+
const relToRoot = path3.relative(dirPath, deps.optimaCodemapPath(worktree)).split(path3.sep).join("/");
|
|
8053
|
+
for (const item of entries) {
|
|
8054
|
+
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) continue;
|
|
8055
|
+
const bucket = /^index\.(js|ts|tsx|jsx)$|^main\.(js|ts|tsx|jsx|py|go|rs|java)$/.test(item.name) ? entrypoints : internals;
|
|
8056
|
+
bucket.push({ path: item.name });
|
|
8057
|
+
}
|
|
8058
|
+
const map = { scope: "module", parent: relToRoot };
|
|
8059
|
+
if (entrypoints.length > 0) map.entrypoints = entrypoints;
|
|
8060
|
+
map.internals = internals;
|
|
8061
|
+
return map;
|
|
8062
|
+
}
|
|
8063
|
+
function repairSingleCodemap(codemapPath, worktree, apply, registerAction, unresolved, deps) {
|
|
8064
|
+
const map = readCodemap(codemapPath, unresolved, worktree);
|
|
8065
|
+
if (!map) return;
|
|
8066
|
+
const isRootMap = codemapPath === deps.optimaCodemapPath(worktree);
|
|
8067
|
+
const mapDir = path3.dirname(codemapPath);
|
|
8068
|
+
const pathBase = isRootMap ? worktree : mapDir;
|
|
8069
|
+
let changed = false;
|
|
8070
|
+
for (const section of ["modules", "entrypoints", "sources_of_truth", "links", "internals"]) {
|
|
8071
|
+
const originalEntries = codemapSectionEntries(map[section]);
|
|
8072
|
+
if (!Array.isArray(map[section]) && originalEntries.length > 0) {
|
|
8073
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, codemapPath), message: `Section '${section}' is object-shaped; repair leaves it unchanged to avoid changing user structure.` });
|
|
8074
|
+
continue;
|
|
8075
|
+
}
|
|
8076
|
+
const nextEntries = [];
|
|
8077
|
+
for (const entry of originalEntries) {
|
|
8078
|
+
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://") || path3.isAbsolute(entry.path)) {
|
|
8079
|
+
nextEntries.push(entry);
|
|
8080
|
+
continue;
|
|
8081
|
+
}
|
|
8082
|
+
const normalizedPath = normalizeCodemapRelPath(entry.path);
|
|
8083
|
+
const absPath = path3.join(pathBase, normalizedPath);
|
|
8084
|
+
if (!fs3.existsSync(absPath)) {
|
|
8085
|
+
registerAction({ category: "codemap", action: apply ? "removed" : "would_remove", path: relPath(worktree, codemapPath), detail: `Remove broken ${section.slice(0, -1)} reference '${entry.path}'.` });
|
|
8086
|
+
changed = true;
|
|
8087
|
+
continue;
|
|
8088
|
+
}
|
|
8089
|
+
const parts = normalizedPath.split("/").filter((part) => part && part !== ".");
|
|
8090
|
+
const maxParts = isRootMap ? 2 : 1;
|
|
8091
|
+
if (parts.length > maxParts) {
|
|
8092
|
+
const localPath = isRootMap ? parts.slice(0, 2).join("/") : parts[0];
|
|
8093
|
+
const localAbs = path3.join(pathBase, localPath);
|
|
8094
|
+
if (fs3.existsSync(localAbs)) {
|
|
8095
|
+
nextEntries.push({ ...entry, path: localPath });
|
|
8096
|
+
changed = true;
|
|
8097
|
+
registerAction({ category: "codemap", action: apply ? "updated" : "would_update", path: relPath(worktree, codemapPath), detail: `Shorten '${entry.path}' to local path '${localPath}'.` });
|
|
8098
|
+
continue;
|
|
8099
|
+
}
|
|
8100
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, codemapPath), message: `Path '${entry.path}' violates Rule of Local Knowledge and no safe local replacement exists.` });
|
|
8101
|
+
nextEntries.push(entry);
|
|
8102
|
+
continue;
|
|
8103
|
+
}
|
|
8104
|
+
nextEntries.push(entry);
|
|
8105
|
+
}
|
|
8106
|
+
if (Array.isArray(map[section])) map[section] = nextEntries;
|
|
8107
|
+
}
|
|
8108
|
+
if (map.scope === "module" && !isOperationalRelPath(path3.relative(worktree, mapDir))) {
|
|
8109
|
+
const indexed = codemapIndexedPaths(map);
|
|
8110
|
+
const internals = Array.isArray(map.internals) ? map.internals : [];
|
|
8111
|
+
for (const item of fs3.readdirSync(mapDir, { withFileTypes: true })) {
|
|
8112
|
+
if (!item.isFile() || item.name === "codemap.yml" || !CODEMAP_SOURCE_EXTENSIONS.has(path3.extname(item.name))) continue;
|
|
8113
|
+
if (indexed.has(item.name)) continue;
|
|
8114
|
+
internals.push({ path: item.name });
|
|
8115
|
+
indexed.add(item.name);
|
|
8116
|
+
changed = true;
|
|
8117
|
+
registerAction({ category: "codemap", action: apply ? "updated" : "would_update", path: relPath(worktree, codemapPath), detail: `Categorize immediate source file '${item.name}' as internal.` });
|
|
8118
|
+
}
|
|
8119
|
+
if (internals.length > 0) map.internals = internals;
|
|
8120
|
+
}
|
|
8121
|
+
if (changed && apply) writeCodemap(codemapPath, map);
|
|
8122
|
+
}
|
|
8123
|
+
function repairCodemaps(worktree, apply, actions, unresolved, deps) {
|
|
8124
|
+
const ig = loadGitIgnoreMatcher(worktree);
|
|
8125
|
+
const rootCodemapPath = deps.optimaCodemapPath(worktree);
|
|
8126
|
+
const rootCodemapMissing = !fs3.existsSync(rootCodemapPath);
|
|
8127
|
+
if (rootCodemapMissing) {
|
|
8128
|
+
actions.push({ category: "codemap", action: apply ? "created" : "would_create", path: ".optima/codemap.yml", detail: "Create missing root CodeMap from template." });
|
|
8129
|
+
if (apply) deps.scaffoldOptimaRootCodemap(worktree);
|
|
8130
|
+
}
|
|
8131
|
+
if (fs3.existsSync(rootCodemapPath)) repairSingleCodemap(rootCodemapPath, worktree, apply, (action) => actions.push(action), unresolved, deps);
|
|
8132
|
+
const rootMap = fs3.existsSync(rootCodemapPath) ? readCodemap(rootCodemapPath, unresolved, worktree) : null;
|
|
8133
|
+
let rootChanged = false;
|
|
8134
|
+
const rootModulesRepairable = rootMap && (rootMap.modules === void 0 || Array.isArray(rootMap.modules));
|
|
8135
|
+
if (rootMap) {
|
|
8136
|
+
rootMap.scope ||= "repo";
|
|
8137
|
+
if (rootModulesRepairable) {
|
|
8138
|
+
rootMap.modules = Array.isArray(rootMap.modules) ? rootMap.modules : [];
|
|
8139
|
+
} else {
|
|
8140
|
+
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." });
|
|
8141
|
+
}
|
|
8142
|
+
}
|
|
8143
|
+
function walk(dirPath) {
|
|
8144
|
+
const relative = path3.relative(worktree, dirPath);
|
|
8145
|
+
if (relative && (isIgnoredPath(ig, relative) || isOperationalRelPath(relative) || isHiddenTree(relative))) return;
|
|
8146
|
+
const codemapPath = path3.join(dirPath, "codemap.yml");
|
|
8147
|
+
const hasCodemap = fs3.existsSync(codemapPath);
|
|
8148
|
+
const sourceDir = relative && containsSource(dirPath, ig, worktree);
|
|
8149
|
+
if (sourceDir && !hasCodemap && dirPath !== worktree) {
|
|
8150
|
+
if (hasImmediateSourceFile(dirPath, ig, worktree)) {
|
|
8151
|
+
actions.push({ category: "codemap", action: apply ? "created" : "would_create", path: relPath(worktree, codemapPath), detail: "Create missing module CodeMap for source directory." });
|
|
8152
|
+
if (apply) writeCodemap(codemapPath, createModuleCodemap(dirPath, worktree, deps));
|
|
8153
|
+
} else {
|
|
8154
|
+
unresolved.push({ category: "codemap", path: relPath(worktree, dirPath), message: "Directory contains source only in nested directories; create a local CodeMap after deciding module boundaries." });
|
|
8155
|
+
}
|
|
8156
|
+
}
|
|
8157
|
+
if (rootMap && rootModulesRepairable && relative && sourceDir && normalizeCodemapRelPath(relative).split("/").length === 1) {
|
|
8158
|
+
const modules = codemapSectionEntries(rootMap.modules);
|
|
8159
|
+
if (!modules.some((entry) => normalizeCodemapRelPath(entry.path) === normalizeCodemapRelPath(relative))) {
|
|
8160
|
+
rootMap.modules = modules;
|
|
8161
|
+
rootMap.modules.push({ path: normalizeCodemapRelPath(relative), summary: "Maintained source module." });
|
|
8162
|
+
rootChanged = true;
|
|
8163
|
+
actions.push({ category: "codemap", action: apply ? "updated" : "would_update", path: ".optima/codemap.yml", detail: `Register top-level source module '${normalizeCodemapRelPath(relative)}'.` });
|
|
8164
|
+
}
|
|
8165
|
+
}
|
|
8166
|
+
if (fs3.existsSync(codemapPath) && codemapPath !== rootCodemapPath) repairSingleCodemap(codemapPath, worktree, apply, (action) => actions.push(action), unresolved, deps);
|
|
8167
|
+
for (const item of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8168
|
+
if (item.isDirectory()) walk(path3.join(dirPath, item.name));
|
|
8169
|
+
}
|
|
8170
|
+
}
|
|
8171
|
+
if (fs3.existsSync(worktree)) walk(worktree);
|
|
8172
|
+
if (rootMap && rootChanged && apply) writeCodemap(rootCodemapPath, rootMap);
|
|
8173
|
+
}
|
|
8174
|
+
function walkMarkdownFiles(dirPath) {
|
|
8175
|
+
if (!fs3.existsSync(dirPath)) return [];
|
|
8176
|
+
const files = [];
|
|
8177
|
+
for (const entry of fs3.readdirSync(dirPath, { withFileTypes: true })) {
|
|
8178
|
+
const entryPath = path3.join(dirPath, entry.name);
|
|
8179
|
+
if (entry.isDirectory()) {
|
|
8180
|
+
files.push(...walkMarkdownFiles(entryPath));
|
|
8181
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
8182
|
+
files.push(entryPath);
|
|
8183
|
+
}
|
|
8184
|
+
}
|
|
8185
|
+
return files;
|
|
8186
|
+
}
|
|
8187
|
+
function isMarkdownReferenceNormalizationExempt(line) {
|
|
8188
|
+
const normalized = line.toLowerCase().replace(/[`*_~]/g, "");
|
|
8189
|
+
const pathToken = "(?:tasks/|evidences/|docs/scrs/|codemap\\.yml|logs/|screenshots/)";
|
|
8190
|
+
if (new RegExp(`\\b(root|legacy)\\s+${pathToken}`).test(normalized)) return true;
|
|
8191
|
+
if (new RegExp(`\\b(do not|don't|never|must not|should not|avoid|forbid|prohibit)\\b[^\\n]*${pathToken}`).test(normalized)) return true;
|
|
8192
|
+
if (/\b(historical|problem|prohibition)\b[^\n]*(tasks\/|evidences\/|docs\/scrs\/|codemap\.yml|logs\/|screenshots\/)/.test(normalized)) return true;
|
|
8193
|
+
return false;
|
|
8194
|
+
}
|
|
8195
|
+
function rewriteReferenceToken(text, fromPrefix, toPrefix) {
|
|
8196
|
+
const escapedPrefix = fromPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8197
|
+
const pattern = new RegExp("(^|[\\s([{\"'<`])" + escapedPrefix + `(?=[^\\s)\\]}>"']+)`, "g");
|
|
8198
|
+
return text.replace(pattern, `$1${toPrefix}`);
|
|
8199
|
+
}
|
|
8200
|
+
function rewriteOptimaMarkdownReferences(content, evidenceBasePath = null) {
|
|
8201
|
+
return content.split(/(\r?\n)/).map((part) => {
|
|
8202
|
+
if (part === "\n" || part === "\r\n" || isMarkdownReferenceNormalizationExempt(part)) return part;
|
|
8203
|
+
let next = part;
|
|
8204
|
+
next = rewriteReferenceToken(next, "tasks/", ".optima/tasks/");
|
|
8205
|
+
next = rewriteReferenceToken(next, "evidences/", ".optima/evidences/");
|
|
8206
|
+
next = rewriteReferenceToken(next, "docs/scrs/", ".optima/docs/scrs/");
|
|
8207
|
+
next = next.replace(/(^|[\s([{"'<`])codemap\.yml\b/g, "$1.optima/codemap.yml");
|
|
8208
|
+
if (evidenceBasePath) {
|
|
8209
|
+
next = rewriteReferenceToken(next, "logs/", `${evidenceBasePath}/logs/`);
|
|
8210
|
+
next = rewriteReferenceToken(next, "screenshots/", `${evidenceBasePath}/screenshots/`);
|
|
8211
|
+
}
|
|
8212
|
+
return next;
|
|
8213
|
+
}).join("");
|
|
8214
|
+
}
|
|
8215
|
+
function optimaEvidenceBaseForFile(worktree, filePath, deps) {
|
|
8216
|
+
const evidenceRoot = deps.optimaEvidencesDir(worktree);
|
|
8217
|
+
const relative = path3.relative(evidenceRoot, filePath).split(path3.sep).join("/");
|
|
8218
|
+
const [taskId] = relative.split("/");
|
|
8219
|
+
if (!taskId || taskId === ".." || relative.startsWith("../")) return null;
|
|
8220
|
+
return `.optima/evidences/${taskId}`;
|
|
8221
|
+
}
|
|
8222
|
+
function normalizeOptimaMarkdownReferences(worktree, deps) {
|
|
8223
|
+
const scanRoots = [
|
|
8224
|
+
deps.optimaTasksDir(worktree),
|
|
8225
|
+
deps.optimaEvidencesDir(worktree),
|
|
8226
|
+
deps.optimaScrsDir(worktree)
|
|
8227
|
+
];
|
|
8228
|
+
const changed = [];
|
|
8229
|
+
for (const filePath of scanRoots.flatMap(walkMarkdownFiles)) {
|
|
8230
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
8231
|
+
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) + path3.sep) ? optimaEvidenceBaseForFile(worktree, filePath, deps) : null;
|
|
8232
|
+
const rewritten = rewriteOptimaMarkdownReferences(raw, evidenceBasePath);
|
|
8233
|
+
if (rewritten !== raw) {
|
|
8234
|
+
fs3.writeFileSync(filePath, rewritten, "utf8");
|
|
8235
|
+
changed.push(relPath(worktree, filePath));
|
|
8236
|
+
}
|
|
8237
|
+
}
|
|
8238
|
+
return changed;
|
|
8239
|
+
}
|
|
8240
|
+
function missingOptimaGitignoreRules(worktree, deps) {
|
|
8241
|
+
if (!deps.isGitRepository(worktree)) return [];
|
|
8242
|
+
const gitignorePath = path3.join(worktree, ".gitignore");
|
|
8243
|
+
const existing = fs3.existsSync(gitignorePath) ? fs3.readFileSync(gitignorePath, "utf8") : "";
|
|
8244
|
+
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
8245
|
+
return deps.optimaGitignoreRules.filter((rule) => !existingRules.has(rule));
|
|
8246
|
+
}
|
|
8247
|
+
function repairMode(args = {}) {
|
|
8248
|
+
const value = args.apply ?? args.mode;
|
|
8249
|
+
if (value === true || value === "apply") return "apply";
|
|
8250
|
+
return "dry-run";
|
|
8251
|
+
}
|
|
8252
|
+
function pushRepairAction(actions, category, action, pathValue, detail) {
|
|
8253
|
+
actions.push({ category, action, path: pathValue, detail });
|
|
8254
|
+
}
|
|
8255
|
+
function planOptimaRepair(worktree, args = {}, deps) {
|
|
8256
|
+
const mode = repairMode(args);
|
|
8257
|
+
const apply = mode === "apply";
|
|
8258
|
+
const actions = [];
|
|
8259
|
+
const unresolved = [];
|
|
8260
|
+
const hadOptima = fs3.existsSync(deps.optimaDir(worktree));
|
|
8261
|
+
const missingGitignoreRules = missingOptimaGitignoreRules(worktree, deps);
|
|
8262
|
+
const markdownBefore = apply ? [] : walkMarkdownFiles(deps.optimaDir(worktree)).filter((filePath) => {
|
|
8263
|
+
const raw = fs3.readFileSync(filePath, "utf8");
|
|
8264
|
+
const evidenceBasePath = filePath.startsWith(deps.optimaEvidencesDir(worktree) + path3.sep) ? optimaEvidenceBaseForFile(worktree, filePath, deps) : null;
|
|
8265
|
+
return rewriteOptimaMarkdownReferences(raw, evidenceBasePath) !== raw;
|
|
8266
|
+
}).map((filePath) => relPath(worktree, filePath));
|
|
8267
|
+
if (!hadOptima) pushRepairAction(actions, "operational", apply ? "created" : "would_create", ".optima/", "Create Optima operational root.");
|
|
8268
|
+
const legacySources = [
|
|
8269
|
+
[".orbita/", deps.legacyOrbitaDir(worktree)],
|
|
8270
|
+
[".staticeng/", deps.legacyStaticEngDir(worktree)],
|
|
8271
|
+
[".nomadwork/", deps.legacyNomadworkDir(worktree)],
|
|
8272
|
+
[".nomadworks/", deps.legacyNomadworksDir(worktree)],
|
|
8273
|
+
["tasks/", path3.join(worktree, "tasks")],
|
|
8274
|
+
["evidences/", path3.join(worktree, "evidences")],
|
|
8275
|
+
["docs/scrs/", path3.join(worktree, "docs", "scrs")],
|
|
8276
|
+
["codemap.yml", path3.join(worktree, "codemap.yml")],
|
|
8277
|
+
["codemap.yaml", path3.join(worktree, "codemap.yaml")]
|
|
8278
|
+
];
|
|
8279
|
+
if (!deps.isOptimaPluginPackageWorktree(worktree)) legacySources.push(["policies/", path3.join(worktree, "policies")]);
|
|
8280
|
+
for (const [display, sourcePath] of legacySources) {
|
|
8281
|
+
if (fs3.existsSync(sourcePath)) pushRepairAction(actions, "legacy", apply ? "migrated" : "would_migrate", display, "Move or merge legacy/root Optima artifact into .optima using preservation safeguards.");
|
|
8282
|
+
}
|
|
8283
|
+
if (apply) deps.migrateLegacyOptimaLayout(worktree);
|
|
8284
|
+
const configMissing = !fs3.existsSync(deps.repoConfigPath(worktree));
|
|
8285
|
+
if (configMissing) pushRepairAction(actions, "config", apply ? "created" : "would_create", ".optima/.config/optima.yaml", "Create missing local Optima config from template.");
|
|
8286
|
+
if (apply) deps.scaffoldOptimaConfig(worktree, args.team_mode || "full");
|
|
8287
|
+
const requiredFiles = [
|
|
8288
|
+
[path3.join(deps.optimaTasksDir(worktree), "current.md"), "registry", ".optima/tasks/current.md", deps.currentTasksRegistryContent()],
|
|
8289
|
+
[path3.join(deps.optimaTasksDir(worktree), "done.md"), "registry", ".optima/tasks/done.md", deps.doneTasksRegistryContent()],
|
|
8290
|
+
[path3.join(deps.optimaScrsDir(worktree), "current.md"), "registry", ".optima/docs/scrs/current.md", deps.currentScrRegistryContent()],
|
|
8291
|
+
[path3.join(deps.optimaScrsDir(worktree), "done.md"), "registry", ".optima/docs/scrs/done.md", deps.doneScrRegistryContent()],
|
|
8292
|
+
[path3.join(deps.optimaTasksDir(worktree), "task-template.md"), "template", ".optima/tasks/task-template.md", deps.taskTemplateContent()],
|
|
8293
|
+
[path3.join(deps.optimaTasksDir(worktree), "subtask-template.md"), "template", ".optima/tasks/subtask-template.md", deps.subtaskTemplateContent()],
|
|
8294
|
+
[path3.join(deps.repoPoliciesDir(worktree), "README.md"), "policy", ".optima/policies/README.md", deps.repoLocalPoliciesReadme]
|
|
8295
|
+
];
|
|
8296
|
+
for (const [filePath, category, displayPath, content] of requiredFiles) {
|
|
8297
|
+
if (!fs3.existsSync(filePath)) {
|
|
8298
|
+
pushRepairAction(actions, category, apply ? "created" : "would_create", displayPath, "Create missing Optima scaffold file without overwriting existing content.");
|
|
8299
|
+
if (apply) deps.ensureFileIfMissing(filePath, content);
|
|
8300
|
+
}
|
|
8301
|
+
}
|
|
8302
|
+
if (apply) {
|
|
8303
|
+
deps.scaffoldOptimaReadmes(worktree);
|
|
8304
|
+
deps.ensureOptimaRegistries(worktree);
|
|
8305
|
+
normalizeOptimaMarkdownReferences(worktree, deps).forEach((filePath) => {
|
|
8306
|
+
pushRepairAction(actions, "markdown", "normalized", filePath, "Normalize Optima artifact paths in Markdown.");
|
|
8307
|
+
});
|
|
8308
|
+
} else {
|
|
8309
|
+
markdownBefore.forEach((filePath) => pushRepairAction(actions, "markdown", "would_normalize", filePath, "Normalize Optima artifact paths in Markdown."));
|
|
8310
|
+
}
|
|
8311
|
+
if (missingGitignoreRules.length > 0) {
|
|
8312
|
+
pushRepairAction(actions, "gitignore", apply ? "updated" : "would_update", ".gitignore", `Add ${missingGitignoreRules.length} Optima local/private rule(s).`);
|
|
8313
|
+
if (apply) deps.ensureOptimaGitignoreRules(worktree);
|
|
8314
|
+
}
|
|
8315
|
+
repairCodemaps(worktree, apply, actions, unresolved, deps);
|
|
8316
|
+
return { mode, actions, unresolved };
|
|
8317
|
+
}
|
|
8318
|
+
function formatRepairResult(plan, validationResult = null, formatValidationResult2 = (res) => JSON.stringify(res, null, 2)) {
|
|
8319
|
+
const lines = [
|
|
8320
|
+
`Optima repair ${plan.mode === "apply" ? "applied" : "dry-run"}.`,
|
|
8321
|
+
"Scope inspected: config, registries, templates, policy scaffolding, legacy/root artifact leakage, Markdown path normalization, gitignore rules, and CodeMap integrity.",
|
|
8322
|
+
"",
|
|
8323
|
+
"Planned/Applied fixes:"
|
|
8324
|
+
];
|
|
8325
|
+
if (plan.actions.length === 0) {
|
|
8326
|
+
lines.push("- None");
|
|
8327
|
+
} else {
|
|
8328
|
+
for (const action of plan.actions) {
|
|
8329
|
+
lines.push(`- [${action.category}] ${action.action} ${action.path}: ${action.detail}`);
|
|
8330
|
+
}
|
|
8331
|
+
}
|
|
8332
|
+
lines.push("", "Unresolved issues:");
|
|
8333
|
+
if (plan.unresolved.length === 0) {
|
|
8334
|
+
lines.push("- None");
|
|
8335
|
+
} else {
|
|
8336
|
+
for (const issue of plan.unresolved) {
|
|
8337
|
+
lines.push(`- [${issue.category}] ${issue.path}: ${issue.message}`);
|
|
8338
|
+
}
|
|
8339
|
+
}
|
|
8340
|
+
if (validationResult) {
|
|
8341
|
+
lines.push("", "Validation after apply:", formatValidationResult2(validationResult));
|
|
8342
|
+
} else {
|
|
8343
|
+
lines.push("", "Validation after apply: not run in dry-run mode.");
|
|
8344
|
+
}
|
|
8345
|
+
return lines.join("\n");
|
|
8346
|
+
}
|
|
8347
|
+
|
|
8348
|
+
// src/validate_logic.js
|
|
8349
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
8350
|
+
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
8351
|
+
import fs4 from "node:fs";
|
|
8352
|
+
import path4 from "node:path";
|
|
7727
8353
|
async function optima_validate_logic(worktree) {
|
|
7728
|
-
const rootCodemapPath =
|
|
7729
|
-
if (!
|
|
8354
|
+
const rootCodemapPath = path4.join(worktree, ".optima", "codemap.yml");
|
|
8355
|
+
if (!fs4.existsSync(rootCodemapPath)) return { ok: false, errors: [".optima/codemap.yml not found."], warnings: [] };
|
|
7730
8356
|
const errors = [];
|
|
7731
8357
|
const warnings = [];
|
|
7732
|
-
const ig = (0,
|
|
8358
|
+
const ig = (0, import_ignore2.default)();
|
|
7733
8359
|
ig.add(".git");
|
|
7734
|
-
const gitignorePath =
|
|
7735
|
-
if (
|
|
7736
|
-
ig.add(
|
|
8360
|
+
const gitignorePath = path4.join(worktree, ".gitignore");
|
|
8361
|
+
if (fs4.existsSync(gitignorePath)) {
|
|
8362
|
+
ig.add(fs4.readFileSync(gitignorePath, "utf8"));
|
|
7737
8363
|
}
|
|
7738
8364
|
const sourceExtensions = [
|
|
7739
8365
|
".js",
|
|
@@ -7771,32 +8397,32 @@ async function optima_validate_logic(worktree) {
|
|
|
7771
8397
|
".codenomad"
|
|
7772
8398
|
]);
|
|
7773
8399
|
const operationalFolders = [".optima", "templates", "dist"];
|
|
7774
|
-
const
|
|
7775
|
-
if (!
|
|
7776
|
-
return
|
|
8400
|
+
const isHiddenTree2 = (relPath2) => {
|
|
8401
|
+
if (!relPath2) return false;
|
|
8402
|
+
return relPath2.split(path4.sep).some((part) => part.startsWith("."));
|
|
7777
8403
|
};
|
|
7778
|
-
const
|
|
7779
|
-
const
|
|
7780
|
-
if (!
|
|
7781
|
-
const firstSegment =
|
|
8404
|
+
const firstPathSegment2 = (relPath2) => relPath2.split(path4.sep).filter(Boolean)[0] || "";
|
|
8405
|
+
const isOperationalRelPath2 = (relPath2) => {
|
|
8406
|
+
if (!relPath2) return false;
|
|
8407
|
+
const firstSegment = firstPathSegment2(relPath2);
|
|
7782
8408
|
if (legacyOperationalRoots.has(firstSegment)) return true;
|
|
7783
|
-
return operationalFolders.some((f) =>
|
|
8409
|
+
return operationalFolders.some((f) => relPath2 === f || relPath2.startsWith(f + path4.sep));
|
|
7784
8410
|
};
|
|
7785
8411
|
const getSectionEntries = (value) => {
|
|
7786
8412
|
if (Array.isArray(value)) return value;
|
|
7787
8413
|
if (value && typeof value === "object") return Object.values(value);
|
|
7788
8414
|
return [];
|
|
7789
8415
|
};
|
|
7790
|
-
const normalizeRelativePath = (
|
|
8416
|
+
const normalizeRelativePath = (relPath2) => path4.normalize(relPath2).replace(/\\/g, "/").replace(/\/$/, "");
|
|
7791
8417
|
const isSourceDir = (dirPath) => {
|
|
7792
|
-
const dirRelPath =
|
|
7793
|
-
if (
|
|
7794
|
-
const items =
|
|
8418
|
+
const dirRelPath = path4.relative(worktree, dirPath);
|
|
8419
|
+
if (isOperationalRelPath2(dirRelPath)) return false;
|
|
8420
|
+
const items = fs4.readdirSync(dirPath, { withFileTypes: true });
|
|
7795
8421
|
for (const item of items) {
|
|
7796
|
-
const childPath =
|
|
7797
|
-
const
|
|
7798
|
-
if (ig.ignores(
|
|
7799
|
-
if (item.isFile() && sourceExtensions.includes(
|
|
8422
|
+
const childPath = path4.join(dirPath, item.name);
|
|
8423
|
+
const relPath2 = path4.relative(worktree, childPath);
|
|
8424
|
+
if (ig.ignores(relPath2) || isOperationalRelPath2(relPath2)) continue;
|
|
8425
|
+
if (item.isFile() && sourceExtensions.includes(path4.extname(item.name))) return true;
|
|
7800
8426
|
if (item.isDirectory()) {
|
|
7801
8427
|
if (isSourceDir(childPath)) return true;
|
|
7802
8428
|
}
|
|
@@ -7804,10 +8430,10 @@ async function optima_validate_logic(worktree) {
|
|
|
7804
8430
|
return false;
|
|
7805
8431
|
};
|
|
7806
8432
|
function validateMap(filePath) {
|
|
7807
|
-
const content =
|
|
8433
|
+
const content = fs4.readFileSync(filePath, "utf8");
|
|
7808
8434
|
let map;
|
|
7809
8435
|
try {
|
|
7810
|
-
map =
|
|
8436
|
+
map = import_yaml2.default.parse(content);
|
|
7811
8437
|
if (!map || typeof map !== "object") {
|
|
7812
8438
|
errors.push(`${filePath}: CodeMap is empty or invalid.`);
|
|
7813
8439
|
return;
|
|
@@ -7816,32 +8442,32 @@ async function optima_validate_logic(worktree) {
|
|
|
7816
8442
|
errors.push(`${filePath}: Invalid YAML.`);
|
|
7817
8443
|
return;
|
|
7818
8444
|
}
|
|
7819
|
-
const dir =
|
|
8445
|
+
const dir = path4.dirname(filePath);
|
|
7820
8446
|
const pathBase = filePath === rootCodemapPath ? worktree : dir;
|
|
7821
8447
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
7822
8448
|
const sectionsToVerify = ["modules", "entrypoints", "sources_of_truth", "links", "internals"];
|
|
7823
8449
|
for (const section of sectionsToVerify) {
|
|
7824
8450
|
for (const item of getSectionEntries(map[section])) {
|
|
7825
8451
|
if (item?.path) {
|
|
7826
|
-
indexedPaths.add(
|
|
8452
|
+
indexedPaths.add(path4.normalize(item.path));
|
|
7827
8453
|
if (section === "links" && (item.path.startsWith("http://") || item.path.startsWith("https://"))) {
|
|
7828
8454
|
continue;
|
|
7829
8455
|
}
|
|
7830
|
-
const absPath =
|
|
7831
|
-
if (!
|
|
8456
|
+
const absPath = path4.isAbsolute(item.path) ? item.path : path4.join(pathBase, item.path);
|
|
8457
|
+
if (!fs4.existsSync(absPath)) {
|
|
7832
8458
|
errors.push(`${filePath}: ${section.slice(0, -1)} path does not exist: ${item.path}`);
|
|
7833
8459
|
}
|
|
7834
8460
|
}
|
|
7835
8461
|
}
|
|
7836
8462
|
}
|
|
7837
|
-
const relDir =
|
|
7838
|
-
const isOperational =
|
|
8463
|
+
const relDir = path4.relative(worktree, dir);
|
|
8464
|
+
const isOperational = isOperationalRelPath2(relDir);
|
|
7839
8465
|
if (map.scope === "module" && !isOperational) {
|
|
7840
|
-
const items =
|
|
8466
|
+
const items = fs4.readdirSync(dir, { withFileTypes: true });
|
|
7841
8467
|
for (const item of items) {
|
|
7842
|
-
if (item.isFile() && sourceExtensions.includes(
|
|
8468
|
+
if (item.isFile() && sourceExtensions.includes(path4.extname(item.name))) {
|
|
7843
8469
|
if (item.name === "codemap.yml") continue;
|
|
7844
|
-
if (!indexedPaths.has(
|
|
8470
|
+
if (!indexedPaths.has(path4.normalize(item.name))) {
|
|
7845
8471
|
errors.push(`${filePath}: Unindexed source file found: '${item.name}'. Every source file must be categorized in a section (e.g., 'internals').`);
|
|
7846
8472
|
}
|
|
7847
8473
|
}
|
|
@@ -7851,7 +8477,7 @@ async function optima_validate_logic(worktree) {
|
|
|
7851
8477
|
for (const key of pathKeys) {
|
|
7852
8478
|
for (const entry of getSectionEntries(map[key])) {
|
|
7853
8479
|
if (!entry?.path || entry.path.startsWith("http://") || entry.path.startsWith("https://")) continue;
|
|
7854
|
-
if (
|
|
8480
|
+
if (path4.isAbsolute(entry.path)) continue;
|
|
7855
8481
|
const normalizedPath = normalizeRelativePath(entry.path);
|
|
7856
8482
|
if (!normalizedPath || normalizedPath === ".") continue;
|
|
7857
8483
|
const parts = normalizedPath.split("/").filter((p) => p && p !== ".");
|
|
@@ -7863,30 +8489,30 @@ async function optima_validate_logic(worktree) {
|
|
|
7863
8489
|
}
|
|
7864
8490
|
}
|
|
7865
8491
|
const walk = (dir) => {
|
|
7866
|
-
const relDir =
|
|
8492
|
+
const relDir = path4.relative(worktree, dir);
|
|
7867
8493
|
if (relDir && ig.ignores(relDir)) return;
|
|
7868
|
-
if (
|
|
7869
|
-
if (
|
|
7870
|
-
const hasCodemap =
|
|
7871
|
-
const items =
|
|
7872
|
-
if (!relDir.startsWith(
|
|
8494
|
+
if (isOperationalRelPath2(relDir)) return;
|
|
8495
|
+
if (isHiddenTree2(relDir)) return;
|
|
8496
|
+
const hasCodemap = fs4.existsSync(path4.join(dir, "codemap.yml"));
|
|
8497
|
+
const items = fs4.readdirSync(dir, { withFileTypes: true });
|
|
8498
|
+
if (!relDir.startsWith(path4.join(".optima", "tasks", "done"))) {
|
|
7873
8499
|
for (const item of items) {
|
|
7874
8500
|
if (item.isFile() && item.name.endsWith(".md")) {
|
|
7875
|
-
const content =
|
|
8501
|
+
const content = fs4.readFileSync(path4.join(dir, item.name), "utf8");
|
|
7876
8502
|
if (content.includes("[To be defined]") || content.includes("[Insert ")) {
|
|
7877
|
-
const relFilePath =
|
|
8503
|
+
const relFilePath = path4.join(relDir, item.name);
|
|
7878
8504
|
errors.push(`Documentation Placeholder found: '${relFilePath}' still contains [To be defined] or [Insert ...] placeholders.`);
|
|
7879
8505
|
}
|
|
7880
8506
|
}
|
|
7881
8507
|
}
|
|
7882
8508
|
}
|
|
7883
|
-
const isOperational =
|
|
8509
|
+
const isOperational = isOperationalRelPath2(relDir);
|
|
7884
8510
|
if (relDir !== "" && !hasCodemap && isSourceDir(dir) && !isOperational) {
|
|
7885
8511
|
errors.push(`Missing CodeMap: Directory '${relDir}' contains source but has no codemap.yml.`);
|
|
7886
8512
|
}
|
|
7887
|
-
if (hasCodemap) validateMap(
|
|
8513
|
+
if (hasCodemap) validateMap(path4.join(dir, "codemap.yml"));
|
|
7888
8514
|
for (const item of items) {
|
|
7889
|
-
if (item.isDirectory()) walk(
|
|
8515
|
+
if (item.isDirectory()) walk(path4.join(dir, item.name));
|
|
7890
8516
|
}
|
|
7891
8517
|
};
|
|
7892
8518
|
validateMap(rootCodemapPath);
|
|
@@ -7899,12 +8525,12 @@ async function optima_validate_logic(worktree) {
|
|
|
7899
8525
|
}
|
|
7900
8526
|
|
|
7901
8527
|
// src/index.js
|
|
7902
|
-
var PKG_ROOT =
|
|
7903
|
-
var BUNDLE_ASSETS_DIR =
|
|
7904
|
-
var BUNDLE_AGENTS_DIR =
|
|
7905
|
-
var BUNDLE_POLICIES_DIR =
|
|
7906
|
-
var TEMPLATES_DIR =
|
|
7907
|
-
var HUMANS_REGISTRY_PATH =
|
|
8528
|
+
var PKG_ROOT = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8529
|
+
var BUNDLE_ASSETS_DIR = path5.join(PKG_ROOT, "assets");
|
|
8530
|
+
var BUNDLE_AGENTS_DIR = path5.join(BUNDLE_ASSETS_DIR, "agents");
|
|
8531
|
+
var BUNDLE_POLICIES_DIR = path5.join(BUNDLE_ASSETS_DIR, "policies");
|
|
8532
|
+
var TEMPLATES_DIR = path5.join(PKG_ROOT, "templates");
|
|
8533
|
+
var HUMANS_REGISTRY_PATH = path5.join(PKG_ROOT, "docs", "core", "humans.md");
|
|
7908
8534
|
var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
7909
8535
|
var MINI_MODE_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
7910
8536
|
var CLICKUP_IGNORED_TASK_TYPES = ["Idea", "Backlog", "Hito", "Nota de reuni\xF3n", "Respuesta del formulario"];
|
|
@@ -8040,18 +8666,18 @@ function objectIdentity(value) {
|
|
|
8040
8666
|
return objectIdentityMap.get(value);
|
|
8041
8667
|
}
|
|
8042
8668
|
function isRootDirectory(candidate) {
|
|
8043
|
-
const resolved =
|
|
8044
|
-
return resolved ===
|
|
8669
|
+
const resolved = path5.resolve(candidate);
|
|
8670
|
+
return resolved === path5.parse(resolved).root;
|
|
8045
8671
|
}
|
|
8046
8672
|
function isSafeWritableDirectory(candidate) {
|
|
8047
8673
|
if (typeof candidate !== "string" || !candidate.trim()) return false;
|
|
8048
|
-
if (!
|
|
8049
|
-
const resolved =
|
|
8674
|
+
if (!path5.isAbsolute(candidate)) return false;
|
|
8675
|
+
const resolved = path5.resolve(candidate);
|
|
8050
8676
|
if (isRootDirectory(resolved)) return false;
|
|
8051
8677
|
try {
|
|
8052
|
-
const stat =
|
|
8678
|
+
const stat = fs5.statSync(resolved);
|
|
8053
8679
|
if (!stat.isDirectory()) return false;
|
|
8054
|
-
|
|
8680
|
+
fs5.accessSync(resolved, fs5.constants.W_OK);
|
|
8055
8681
|
return true;
|
|
8056
8682
|
} catch {
|
|
8057
8683
|
return false;
|
|
@@ -8067,7 +8693,7 @@ function resolveSafeWorktree(context = {}, pluginWorktree = null) {
|
|
|
8067
8693
|
];
|
|
8068
8694
|
for (const candidate of candidates) {
|
|
8069
8695
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8070
|
-
const resolved =
|
|
8696
|
+
const resolved = path5.resolve(candidate);
|
|
8071
8697
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8072
8698
|
}
|
|
8073
8699
|
throw new Error(SAFE_WORKTREE_FAILURE);
|
|
@@ -8082,13 +8708,13 @@ function safeWorktreeOrFailure(context, pluginWorktree) {
|
|
|
8082
8708
|
function explicitSafeInputWorktree(input = {}) {
|
|
8083
8709
|
for (const candidate of [input?.worktree, input?.directory]) {
|
|
8084
8710
|
if (typeof candidate !== "string" || !candidate.trim()) continue;
|
|
8085
|
-
const resolved =
|
|
8711
|
+
const resolved = path5.resolve(candidate);
|
|
8086
8712
|
if (isSafeWritableDirectory(resolved)) return resolved;
|
|
8087
8713
|
}
|
|
8088
8714
|
return null;
|
|
8089
8715
|
}
|
|
8090
8716
|
function isGitRepository(worktree) {
|
|
8091
|
-
return
|
|
8717
|
+
return fs5.existsSync(path5.join(worktree, ".git"));
|
|
8092
8718
|
}
|
|
8093
8719
|
function normalizeLooseToken(value) {
|
|
8094
8720
|
return String(value ?? "").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -8122,8 +8748,8 @@ function parseHumansRegistry(markdown = "") {
|
|
|
8122
8748
|
return roles;
|
|
8123
8749
|
}
|
|
8124
8750
|
function loadHumansRegistry(registryPath = HUMANS_REGISTRY_PATH) {
|
|
8125
|
-
if (!
|
|
8126
|
-
return parseHumansRegistry(
|
|
8751
|
+
if (!fs5.existsSync(registryPath)) return {};
|
|
8752
|
+
return parseHumansRegistry(fs5.readFileSync(registryPath, "utf8"));
|
|
8127
8753
|
}
|
|
8128
8754
|
function resolveHumanRoles(roles = CLICKUP_FINAL_APPROVER_ROLES, registry = loadHumansRegistry()) {
|
|
8129
8755
|
return [...new Set(roles.map((role) => registry[String(role ?? "").trim()] || "").filter(Boolean))];
|
|
@@ -8290,7 +8916,7 @@ function parseMarkdownArtifact(markdown = "", { requiredSections = [] } = {}) {
|
|
|
8290
8916
|
};
|
|
8291
8917
|
}
|
|
8292
8918
|
function readMarkdownArtifact(filePath, options = {}) {
|
|
8293
|
-
const markdown =
|
|
8919
|
+
const markdown = fs5.readFileSync(filePath, "utf8");
|
|
8294
8920
|
return parseMarkdownArtifact(markdown, options);
|
|
8295
8921
|
}
|
|
8296
8922
|
function stripRawLogSections(sections = {}) {
|
|
@@ -8576,7 +9202,7 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
|
|
|
8576
9202
|
function deriveClickUpWorktree({ baseWorktree = "", taskId, taskType, parentTaskId, subtaskId } = {}) {
|
|
8577
9203
|
const branch = deriveClickUpBranchName({ taskType, parentTaskId, subtaskId, taskId });
|
|
8578
9204
|
const root = baseWorktree || process.cwd();
|
|
8579
|
-
return
|
|
9205
|
+
return path5.join(path5.dirname(root), `${path5.basename(root)}-${branch.replace(/\//g, "-")}`);
|
|
8580
9206
|
}
|
|
8581
9207
|
function clickUpCustomFieldValue(task = {}, names = []) {
|
|
8582
9208
|
const fields = Array.isArray(task.custom_fields) ? task.custom_fields : [];
|
|
@@ -8600,12 +9226,12 @@ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
|
|
|
8600
9226
|
const taskMetadata = metadataTaskRouting(metadata);
|
|
8601
9227
|
const existingWorktree = typeof taskMetadata.worktree === "string" ? taskMetadata.worktree.trim() : "";
|
|
8602
9228
|
const existingBranch = typeof taskMetadata.branch === "string" ? taskMetadata.branch.trim() : "";
|
|
8603
|
-
if (!existingWorktree || !
|
|
9229
|
+
if (!existingWorktree || !path5.isAbsolute(existingWorktree) || !fs5.existsSync(existingWorktree)) return null;
|
|
8604
9230
|
try {
|
|
8605
|
-
const stat =
|
|
9231
|
+
const stat = fs5.statSync(existingWorktree);
|
|
8606
9232
|
if (!stat.isDirectory()) return null;
|
|
8607
9233
|
if (branch && existingBranch && existingBranch !== branch) return null;
|
|
8608
|
-
return { branch: existingBranch || branch, worktree:
|
|
9234
|
+
return { branch: existingBranch || branch, worktree: path5.resolve(existingWorktree), reused: true };
|
|
8609
9235
|
} catch {
|
|
8610
9236
|
return null;
|
|
8611
9237
|
}
|
|
@@ -8616,10 +9242,10 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
8616
9242
|
const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
|
|
8617
9243
|
if (existing) return { ...existing, branch };
|
|
8618
9244
|
const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
8619
|
-
if (
|
|
9245
|
+
if (fs5.existsSync(worktreePath)) return { branch, worktree: path5.resolve(worktreePath), reused: true };
|
|
8620
9246
|
if (allowNonGitFallback) {
|
|
8621
|
-
|
|
8622
|
-
return { branch, worktree:
|
|
9247
|
+
fs5.mkdirSync(worktreePath, { recursive: true });
|
|
9248
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, fallback: "non_git" };
|
|
8623
9249
|
}
|
|
8624
9250
|
const startPoint = (() => {
|
|
8625
9251
|
try {
|
|
@@ -8630,7 +9256,7 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
|
|
|
8630
9256
|
}
|
|
8631
9257
|
})();
|
|
8632
9258
|
runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
|
|
8633
|
-
return { branch, worktree:
|
|
9259
|
+
return { branch, worktree: path5.resolve(worktreePath), reused: false, startPoint };
|
|
8634
9260
|
}
|
|
8635
9261
|
function normalizeClickUpDefinitionDocParent(parent = {}) {
|
|
8636
9262
|
const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
|
|
@@ -8765,76 +9391,10 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
8765
9391
|
}
|
|
8766
9392
|
};
|
|
8767
9393
|
}
|
|
8768
|
-
function toGitPath(worktree, targetPath) {
|
|
8769
|
-
const relativePath = path2.relative(worktree, targetPath);
|
|
8770
|
-
if (!relativePath || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) return null;
|
|
8771
|
-
return relativePath.split(path2.sep).join("/");
|
|
8772
|
-
}
|
|
8773
|
-
function runGit(worktree, args) {
|
|
8774
|
-
return execFileSync("git", args, {
|
|
8775
|
-
cwd: worktree,
|
|
8776
|
-
encoding: "utf8",
|
|
8777
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
8778
|
-
});
|
|
8779
|
-
}
|
|
8780
|
-
function gitMigrationState(worktree) {
|
|
8781
|
-
try {
|
|
8782
|
-
const root = runGit(worktree, ["rev-parse", "--show-toplevel"]).trim();
|
|
8783
|
-
if (path2.resolve(root) !== path2.resolve(worktree)) return null;
|
|
8784
|
-
return { worktree };
|
|
8785
|
-
} catch {
|
|
8786
|
-
return null;
|
|
8787
|
-
}
|
|
8788
|
-
}
|
|
8789
|
-
function isGitTracked(gitState, targetPath) {
|
|
8790
|
-
if (!gitState) return false;
|
|
8791
|
-
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
8792
|
-
if (!gitPath) return false;
|
|
8793
|
-
try {
|
|
8794
|
-
runGit(gitState.worktree, ["ls-files", "--error-unmatch", "--", gitPath]);
|
|
8795
|
-
return true;
|
|
8796
|
-
} catch {
|
|
8797
|
-
return false;
|
|
8798
|
-
}
|
|
8799
|
-
}
|
|
8800
|
-
function hasGitTrackedChildren(gitState, targetPath) {
|
|
8801
|
-
if (!gitState) return false;
|
|
8802
|
-
const gitPath = toGitPath(gitState.worktree, targetPath);
|
|
8803
|
-
if (!gitPath) return false;
|
|
8804
|
-
try {
|
|
8805
|
-
return runGit(gitState.worktree, ["ls-files", "--", `${gitPath}/`]).trim().length > 0;
|
|
8806
|
-
} catch {
|
|
8807
|
-
return false;
|
|
8808
|
-
}
|
|
8809
|
-
}
|
|
8810
|
-
function gitAwareMoveIfTracked(sourcePath, destinationPath, gitState) {
|
|
8811
|
-
const sourceIsTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
8812
|
-
if (!gitState || !sourceIsTracked || fs2.existsSync(destinationPath)) return false;
|
|
8813
|
-
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
8814
|
-
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
8815
|
-
if (!sourceGitPath || !destinationGitPath) return false;
|
|
8816
|
-
fs2.mkdirSync(path2.dirname(destinationPath), { recursive: true });
|
|
8817
|
-
try {
|
|
8818
|
-
runGit(gitState.worktree, ["mv", "--", sourceGitPath, destinationGitPath]);
|
|
8819
|
-
return true;
|
|
8820
|
-
} catch {
|
|
8821
|
-
return false;
|
|
8822
|
-
}
|
|
8823
|
-
}
|
|
8824
|
-
function stageGitAwareMerge(sourcePath, destinationPath, gitState) {
|
|
8825
|
-
if (!gitState || !isGitTracked(gitState, sourcePath)) return;
|
|
8826
|
-
const sourceGitPath = toGitPath(gitState.worktree, sourcePath);
|
|
8827
|
-
const destinationGitPath = toGitPath(gitState.worktree, destinationPath);
|
|
8828
|
-
if (!sourceGitPath || !destinationGitPath) return;
|
|
8829
|
-
try {
|
|
8830
|
-
runGit(gitState.worktree, ["add", "-A", "--", sourceGitPath, destinationGitPath]);
|
|
8831
|
-
} catch {
|
|
8832
|
-
}
|
|
8833
|
-
}
|
|
8834
9394
|
function ensureOptimaGitignoreRules(worktree) {
|
|
8835
9395
|
if (!isGitRepository(worktree)) return { touched: false, added: [] };
|
|
8836
|
-
const gitignorePath =
|
|
8837
|
-
const existing =
|
|
9396
|
+
const gitignorePath = path5.join(worktree, ".gitignore");
|
|
9397
|
+
const existing = fs5.existsSync(gitignorePath) ? fs5.readFileSync(gitignorePath, "utf8") : "";
|
|
8838
9398
|
const existingRules = new Set(existing.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
8839
9399
|
const missingRules = OPTIMA_GITIGNORE_RULES.filter((rule) => !existingRules.has(rule));
|
|
8840
9400
|
if (missingRules.length === 0) return { touched: false, added: [] };
|
|
@@ -8844,49 +9404,45 @@ function ensureOptimaGitignoreRules(worktree) {
|
|
|
8844
9404
|
"# Optima local/private state",
|
|
8845
9405
|
...missingRules
|
|
8846
9406
|
].join("\n");
|
|
8847
|
-
|
|
9407
|
+
fs5.writeFileSync(gitignorePath, `${existing}${prefix}${separator}${block}
|
|
8848
9408
|
`, "utf8");
|
|
8849
9409
|
return { touched: true, added: missingRules };
|
|
8850
9410
|
}
|
|
8851
|
-
function compactPromptPath(filePath) {
|
|
8852
|
-
if (!filePath.endsWith(".md") || filePath.endsWith(".prompt.md")) return null;
|
|
8853
|
-
return filePath.replace(/\.md$/, ".prompt.md");
|
|
8854
|
-
}
|
|
8855
9411
|
function optimaDir(worktree) {
|
|
8856
|
-
return
|
|
9412
|
+
return path5.join(worktree, OPTIMA_DIRNAME);
|
|
8857
9413
|
}
|
|
8858
9414
|
function optimaLocalConfigDir(worktree) {
|
|
8859
|
-
return
|
|
9415
|
+
return path5.join(optimaDir(worktree), ".config");
|
|
8860
9416
|
}
|
|
8861
9417
|
function optimaConfigDir(worktree) {
|
|
8862
9418
|
return optimaLocalConfigDir(worktree);
|
|
8863
9419
|
}
|
|
8864
9420
|
function optimaCodemapPath(worktree) {
|
|
8865
|
-
return
|
|
9421
|
+
return path5.join(optimaDir(worktree), "codemap.yml");
|
|
8866
9422
|
}
|
|
8867
9423
|
function optimaTasksDir(worktree) {
|
|
8868
|
-
return
|
|
9424
|
+
return path5.join(optimaDir(worktree), "tasks");
|
|
8869
9425
|
}
|
|
8870
9426
|
function optimaEvidencesDir(worktree) {
|
|
8871
|
-
return
|
|
9427
|
+
return path5.join(optimaDir(worktree), "evidences");
|
|
8872
9428
|
}
|
|
8873
9429
|
function optimaScrsDir(worktree) {
|
|
8874
|
-
return
|
|
9430
|
+
return path5.join(optimaDir(worktree), "docs", "scrs");
|
|
8875
9431
|
}
|
|
8876
9432
|
function legacyNomadworkDir(worktree) {
|
|
8877
|
-
return
|
|
9433
|
+
return path5.join(worktree, LEGACY_NOMADWORK_DIRNAME);
|
|
8878
9434
|
}
|
|
8879
9435
|
function legacyNomadworksDir(worktree) {
|
|
8880
|
-
return
|
|
9436
|
+
return path5.join(worktree, LEGACY_NOMADWORKS_DIRNAME);
|
|
8881
9437
|
}
|
|
8882
9438
|
function legacyOrbitaDir(worktree) {
|
|
8883
|
-
return
|
|
9439
|
+
return path5.join(worktree, LEGACY_ORBITA_DIRNAME);
|
|
8884
9440
|
}
|
|
8885
9441
|
function legacyStaticEngDir(worktree) {
|
|
8886
|
-
return
|
|
9442
|
+
return path5.join(worktree, LEGACY_STATICENG_DIRNAME);
|
|
8887
9443
|
}
|
|
8888
9444
|
function repoConfigPath(worktree) {
|
|
8889
|
-
return
|
|
9445
|
+
return path5.join(optimaConfigDir(worktree), "optima.yaml");
|
|
8890
9446
|
}
|
|
8891
9447
|
function normalizeLegacyDiscussionEntry(entry) {
|
|
8892
9448
|
if (!entry || typeof entry !== "object") return entry;
|
|
@@ -8899,25 +9455,25 @@ function normalizeLegacyDiscussionEntry(entry) {
|
|
|
8899
9455
|
return next;
|
|
8900
9456
|
}
|
|
8901
9457
|
function repoPoliciesDir(worktree) {
|
|
8902
|
-
return
|
|
9458
|
+
return path5.join(optimaDir(worktree), "policies");
|
|
8903
9459
|
}
|
|
8904
9460
|
function generatedPoliciesDir(worktree) {
|
|
8905
|
-
return
|
|
9461
|
+
return path5.join(optimaLocalConfigDir(worktree), "generated", "policies");
|
|
8906
9462
|
}
|
|
8907
9463
|
function generatedAgentsDir(worktree) {
|
|
8908
|
-
return
|
|
9464
|
+
return path5.join(optimaLocalConfigDir(worktree), "generated", "agents");
|
|
8909
9465
|
}
|
|
8910
9466
|
function repoAgentsDir(worktree) {
|
|
8911
|
-
return
|
|
9467
|
+
return path5.join(optimaDir(worktree), "agents");
|
|
8912
9468
|
}
|
|
8913
9469
|
function repoAgentAdditionsDir(worktree) {
|
|
8914
|
-
return
|
|
9470
|
+
return path5.join(optimaDir(worktree), "agent-additions");
|
|
8915
9471
|
}
|
|
8916
9472
|
function legacyRepoAgentsDir(worktree) {
|
|
8917
|
-
return
|
|
9473
|
+
return path5.join(legacyNomadworksDir(worktree), "agents");
|
|
8918
9474
|
}
|
|
8919
9475
|
function runtimeDiscussionRegistryPath(worktree) {
|
|
8920
|
-
return
|
|
9476
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime", "discussions.json");
|
|
8921
9477
|
}
|
|
8922
9478
|
function resolveConfigPath(worktree) {
|
|
8923
9479
|
return repoConfigPath(worktree);
|
|
@@ -8960,32 +9516,32 @@ function mergeClickUpAgentMetadata(existing, update = {}) {
|
|
|
8960
9516
|
return JSON.stringify(sortJsonValue(merged), null, 2);
|
|
8961
9517
|
}
|
|
8962
9518
|
function optimaRuntimeDir(worktree) {
|
|
8963
|
-
return
|
|
9519
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime");
|
|
8964
9520
|
}
|
|
8965
9521
|
function clickUpWebhookStatePath(worktree) {
|
|
8966
|
-
return
|
|
9522
|
+
return path5.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
|
|
8967
9523
|
}
|
|
8968
9524
|
function clickUpCommentLedgerPath(worktree) {
|
|
8969
|
-
return
|
|
9525
|
+
return path5.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
|
|
8970
9526
|
}
|
|
8971
9527
|
function clickUpWebhookLogPath(worktree) {
|
|
8972
|
-
return
|
|
9528
|
+
return path5.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
8973
9529
|
}
|
|
8974
9530
|
function normalizeClickUpWebhookLogLevel(value) {
|
|
8975
9531
|
const level = String(value || "info").trim().toLowerCase();
|
|
8976
9532
|
return CLICKUP_WEBHOOK_LOG_LEVELS.has(level) ? level : "info";
|
|
8977
9533
|
}
|
|
8978
9534
|
function clickUpWebhookAuditLogDir() {
|
|
8979
|
-
const dataHome = process.env.XDG_DATA_HOME &&
|
|
8980
|
-
return
|
|
9535
|
+
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");
|
|
9536
|
+
return path5.join(dataHome, "opencode-optima");
|
|
8981
9537
|
}
|
|
8982
9538
|
function clickUpWebhookAuditLogPath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8983
|
-
return
|
|
9539
|
+
return path5.join(logDir, `clickup-webhook-${at.toISOString().slice(0, 10)}.jsonl`);
|
|
8984
9540
|
}
|
|
8985
9541
|
function clickUpWebhookRequestFilePath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8986
9542
|
const stamp = at.toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
8987
9543
|
const shortId = crypto.randomBytes(4).toString("hex");
|
|
8988
|
-
return
|
|
9544
|
+
return path5.join(logDir, "requests", `clickup-webhook-${stamp}-${shortId}.json`);
|
|
8989
9545
|
}
|
|
8990
9546
|
function findOptimaPluginTupleOptions(pluginEntries = []) {
|
|
8991
9547
|
if (!Array.isArray(pluginEntries)) return null;
|
|
@@ -9029,6 +9585,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9029
9585
|
const ignoredStatuses = Array.isArray(routing.ignored_statuses) && routing.ignored_statuses.length > 0 ? routing.ignored_statuses : CLICKUP_WEBHOOK_TERMINAL_STATUSES;
|
|
9030
9586
|
const config = {
|
|
9031
9587
|
enabled: raw.enabled === true,
|
|
9588
|
+
test: raw.test === true || raw.test_mode === true || raw.testMode === true,
|
|
9032
9589
|
basePath: String(raw.base_path || raw.basePath || worktree || "").trim(),
|
|
9033
9590
|
teamId: String(raw.team_id || raw.teamId || "").trim(),
|
|
9034
9591
|
apiToken: String(raw.api_token || raw.apiToken || "").trim(),
|
|
@@ -9067,7 +9624,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9067
9624
|
};
|
|
9068
9625
|
const errors = [];
|
|
9069
9626
|
if (!config.enabled) errors.push("clickup.enabled must be true");
|
|
9070
|
-
if (!config.basePath || !
|
|
9627
|
+
if (!config.basePath || !path5.isAbsolute(config.basePath)) errors.push("clickup.base_path must be an absolute path");
|
|
9071
9628
|
if (!config.teamId) errors.push("clickup.team_id is required");
|
|
9072
9629
|
if (!config.apiToken) errors.push("clickup.api_token is required");
|
|
9073
9630
|
if (!config.webhook.publicUrl) errors.push("clickup.webhook.public_url is required");
|
|
@@ -9109,21 +9666,21 @@ function sanitizeClickUpWebhookState(state = {}, config = null) {
|
|
|
9109
9666
|
}
|
|
9110
9667
|
function readClickUpWebhookState(worktree, config = null) {
|
|
9111
9668
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9112
|
-
if (!
|
|
9669
|
+
if (!fs5.existsSync(statePath)) return sanitizeClickUpWebhookState({}, config);
|
|
9113
9670
|
try {
|
|
9114
|
-
return sanitizeClickUpWebhookState(JSON.parse(
|
|
9671
|
+
return sanitizeClickUpWebhookState(JSON.parse(fs5.readFileSync(statePath, "utf8")), config);
|
|
9115
9672
|
} catch {
|
|
9116
9673
|
return sanitizeClickUpWebhookState({}, config);
|
|
9117
9674
|
}
|
|
9118
9675
|
}
|
|
9119
9676
|
function writeClickUpWebhookState(worktree, state, config = null) {
|
|
9120
9677
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9121
|
-
|
|
9678
|
+
fs5.mkdirSync(path5.dirname(statePath), { recursive: true, mode: 448 });
|
|
9122
9679
|
const next = sanitizeClickUpWebhookState(state, config);
|
|
9123
|
-
|
|
9680
|
+
fs5.writeFileSync(statePath, `${JSON.stringify(next, null, 2)}
|
|
9124
9681
|
`, { encoding: "utf8", mode: 384 });
|
|
9125
9682
|
try {
|
|
9126
|
-
|
|
9683
|
+
fs5.chmodSync(statePath, 384);
|
|
9127
9684
|
} catch {
|
|
9128
9685
|
}
|
|
9129
9686
|
return next;
|
|
@@ -9139,7 +9696,7 @@ function isClickUpWebhookStateActive(state, config) {
|
|
|
9139
9696
|
function expandHomePath(value = "") {
|
|
9140
9697
|
const input = String(value || "").trim();
|
|
9141
9698
|
if (input === "~") return os.homedir();
|
|
9142
|
-
if (input.startsWith("~/")) return
|
|
9699
|
+
if (input.startsWith("~/")) return path5.join(os.homedir(), input.slice(2));
|
|
9143
9700
|
return input;
|
|
9144
9701
|
}
|
|
9145
9702
|
function resolveSecretReference(value = "") {
|
|
@@ -9149,7 +9706,7 @@ function resolveSecretReference(value = "") {
|
|
|
9149
9706
|
const fileMatch = raw.match(/^\{file:(.+)\}$/);
|
|
9150
9707
|
if (fileMatch) {
|
|
9151
9708
|
try {
|
|
9152
|
-
return
|
|
9709
|
+
return fs5.readFileSync(expandHomePath(fileMatch[1]), "utf8").trim();
|
|
9153
9710
|
} catch {
|
|
9154
9711
|
return "";
|
|
9155
9712
|
}
|
|
@@ -9226,6 +9783,40 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9226
9783
|
}
|
|
9227
9784
|
};
|
|
9228
9785
|
}
|
|
9786
|
+
function createTestClickUpApiClient(config) {
|
|
9787
|
+
const metadata = /* @__PURE__ */ new Map();
|
|
9788
|
+
return {
|
|
9789
|
+
async createWebhook({ endpoint, events, location } = {}) {
|
|
9790
|
+
return {
|
|
9791
|
+
webhook: {
|
|
9792
|
+
id: "optima-test-webhook",
|
|
9793
|
+
endpoint: endpoint || config.webhook.publicUrl,
|
|
9794
|
+
events: events || config.webhook.events,
|
|
9795
|
+
location: location || config.webhook.location,
|
|
9796
|
+
secret: "optima-test-secret",
|
|
9797
|
+
active: true
|
|
9798
|
+
}
|
|
9799
|
+
};
|
|
9800
|
+
},
|
|
9801
|
+
async getTask(taskId) {
|
|
9802
|
+
return null;
|
|
9803
|
+
},
|
|
9804
|
+
async updateTaskMetadata({ taskId, value }) {
|
|
9805
|
+
metadata.set(String(taskId || ""), value);
|
|
9806
|
+
return { ok: true };
|
|
9807
|
+
},
|
|
9808
|
+
async postTaskComment() {
|
|
9809
|
+
return { ok: true };
|
|
9810
|
+
},
|
|
9811
|
+
async addTaskTag() {
|
|
9812
|
+
return { ok: true };
|
|
9813
|
+
},
|
|
9814
|
+
async listAssignedTasks() {
|
|
9815
|
+
return { tasks: [] };
|
|
9816
|
+
},
|
|
9817
|
+
__metadata: metadata
|
|
9818
|
+
};
|
|
9819
|
+
}
|
|
9229
9820
|
function normalizeClickUpWebhookApiResponse(response = {}, fallbackConfig = null) {
|
|
9230
9821
|
const webhook = response?.webhook || response?.data || response || {};
|
|
9231
9822
|
const healthStatus = String(webhook.health?.status || webhook.health_status || "").trim().toLowerCase();
|
|
@@ -9405,9 +9996,9 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
|
|
|
9405
9996
|
}
|
|
9406
9997
|
function readClickUpCommentLedger(ledgerPath) {
|
|
9407
9998
|
const processed = /* @__PURE__ */ new Set();
|
|
9408
|
-
if (!ledgerPath || !
|
|
9999
|
+
if (!ledgerPath || !fs5.existsSync(ledgerPath)) return processed;
|
|
9409
10000
|
try {
|
|
9410
|
-
const raw =
|
|
10001
|
+
const raw = fs5.readFileSync(ledgerPath, "utf8");
|
|
9411
10002
|
for (const [index, line] of raw.split(/\r?\n/).entries()) {
|
|
9412
10003
|
if (!line.trim()) continue;
|
|
9413
10004
|
try {
|
|
@@ -9424,11 +10015,11 @@ function readClickUpCommentLedger(ledgerPath) {
|
|
|
9424
10015
|
}
|
|
9425
10016
|
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
9426
10017
|
if (!ledgerPath || !entry.key) return;
|
|
9427
|
-
|
|
9428
|
-
|
|
10018
|
+
fs5.mkdirSync(path5.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
10019
|
+
fs5.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
9429
10020
|
`, { encoding: "utf8", mode: 384 });
|
|
9430
10021
|
try {
|
|
9431
|
-
|
|
10022
|
+
fs5.chmodSync(ledgerPath, 384);
|
|
9432
10023
|
} catch {
|
|
9433
10024
|
}
|
|
9434
10025
|
}
|
|
@@ -10144,9 +10735,9 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
10144
10735
|
}
|
|
10145
10736
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
10146
10737
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
10147
|
-
|
|
10738
|
+
fs5.mkdirSync(path5.dirname(logPath), { recursive: true });
|
|
10148
10739
|
const safeEntry = { ...entry, at: entry.at || (/* @__PURE__ */ new Date()).toISOString() };
|
|
10149
|
-
|
|
10740
|
+
fs5.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
10150
10741
|
`, "utf8");
|
|
10151
10742
|
}
|
|
10152
10743
|
function clickUpWebhookLifecycleLog(worktree, entry) {
|
|
@@ -10175,7 +10766,7 @@ function closeClickUpWebhookServer(server) {
|
|
|
10175
10766
|
});
|
|
10176
10767
|
}
|
|
10177
10768
|
function managedClickUpWebhookKey({ worktree, state, config } = {}) {
|
|
10178
|
-
return [
|
|
10769
|
+
return [path5.resolve(worktree || process.cwd()), state?.webhookId || "", config?.webhook?.publicUrl || ""].join("|");
|
|
10179
10770
|
}
|
|
10180
10771
|
async function deleteClickUpWebhookBestEffort({ webhookId, clickupClient, worktree, reason = "cleanup" } = {}) {
|
|
10181
10772
|
const id = String(webhookId || "").trim();
|
|
@@ -10274,13 +10865,13 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
10274
10865
|
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
10275
10866
|
if (level === "error" && !failed) return;
|
|
10276
10867
|
const logDir = clickUpWebhookAuditLogDir();
|
|
10277
|
-
|
|
10868
|
+
fs5.mkdirSync(logDir, { recursive: true });
|
|
10278
10869
|
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
10279
10870
|
let requestFile;
|
|
10280
10871
|
if (level === "verbose") {
|
|
10281
10872
|
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
10282
|
-
|
|
10283
|
-
requestFile =
|
|
10873
|
+
fs5.mkdirSync(path5.dirname(absoluteRequestFile), { recursive: true });
|
|
10874
|
+
requestFile = path5.relative(logDir, absoluteRequestFile).split(path5.sep).join("/");
|
|
10284
10875
|
const parsedBody = payload || (() => {
|
|
10285
10876
|
try {
|
|
10286
10877
|
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
@@ -10296,11 +10887,11 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
10296
10887
|
headers,
|
|
10297
10888
|
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
10298
10889
|
}, secretValues);
|
|
10299
|
-
|
|
10890
|
+
fs5.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
10300
10891
|
`, "utf8");
|
|
10301
10892
|
}
|
|
10302
10893
|
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
10303
|
-
|
|
10894
|
+
fs5.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
10304
10895
|
`, "utf8");
|
|
10305
10896
|
} catch {
|
|
10306
10897
|
}
|
|
@@ -10374,7 +10965,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
10374
10965
|
const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
|
|
10375
10966
|
let taskRoute;
|
|
10376
10967
|
try {
|
|
10377
|
-
taskRoute = ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" });
|
|
10968
|
+
taskRoute = ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true });
|
|
10378
10969
|
} catch (error) {
|
|
10379
10970
|
const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
|
|
10380
10971
|
appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
|
|
@@ -10656,7 +11247,7 @@ function clickUpListenerFingerprint({ config, state, worktree, clickupClient, op
|
|
|
10656
11247
|
return JSON.stringify({
|
|
10657
11248
|
publicUrl: config?.webhook?.publicUrl || "",
|
|
10658
11249
|
path: clickUpWebhookExpectedPath(config),
|
|
10659
|
-
worktree:
|
|
11250
|
+
worktree: path5.resolve(worktree || process.cwd()),
|
|
10660
11251
|
webhookId: state?.webhookId || "",
|
|
10661
11252
|
secret: state?.secret || "",
|
|
10662
11253
|
events: config?.webhook?.events || [],
|
|
@@ -10753,17 +11344,17 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, o
|
|
|
10753
11344
|
return result;
|
|
10754
11345
|
}
|
|
10755
11346
|
function legacyVariantPath(destinationPath) {
|
|
10756
|
-
const parsed =
|
|
11347
|
+
const parsed = path5.parse(destinationPath);
|
|
10757
11348
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
10758
|
-
if (parsed.ext) return
|
|
10759
|
-
return
|
|
11349
|
+
if (parsed.ext) return path5.join(parsed.dir, `${parsed.name}.legacy-${stamp}${parsed.ext}`);
|
|
11350
|
+
return path5.join(parsed.dir, `${parsed.base}.legacy-${stamp}`);
|
|
10760
11351
|
}
|
|
10761
11352
|
function normalizeWorkflowTaskPath(taskPath) {
|
|
10762
11353
|
if (typeof taskPath !== "string") return { ok: false, message: "Error: task_path is required." };
|
|
10763
11354
|
const trimmed = taskPath.trim();
|
|
10764
11355
|
if (!trimmed) return { ok: false, message: "Error: task_path is required." };
|
|
10765
11356
|
const normalized = trimmed.replace(/\\/g, "/");
|
|
10766
|
-
if (
|
|
11357
|
+
if (path5.isAbsolute(trimmed)) return { ok: true, taskPath: trimmed };
|
|
10767
11358
|
if (normalized === "tasks" || normalized.startsWith("tasks/")) {
|
|
10768
11359
|
return {
|
|
10769
11360
|
ok: false,
|
|
@@ -10779,76 +11370,80 @@ function normalizeWorkflowTaskPath(taskPath) {
|
|
|
10779
11370
|
return { ok: true, taskPath: trimmed };
|
|
10780
11371
|
}
|
|
10781
11372
|
function mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState = null) {
|
|
11373
|
+
const sourceWasTracked = isGitTracked(gitState, sourcePath);
|
|
10782
11374
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
10783
|
-
if (!
|
|
10784
|
-
|
|
10785
|
-
|
|
11375
|
+
if (!fs5.existsSync(destinationPath)) {
|
|
11376
|
+
fs5.mkdirSync(path5.dirname(destinationPath), { recursive: true });
|
|
11377
|
+
fs5.renameSync(sourcePath, destinationPath);
|
|
11378
|
+
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10786
11379
|
return;
|
|
10787
11380
|
}
|
|
10788
|
-
const ext =
|
|
11381
|
+
const ext = path5.extname(destinationPath).toLowerCase();
|
|
10789
11382
|
if ([".yaml", ".yml", ".json"].includes(ext)) {
|
|
10790
|
-
const sourceRaw =
|
|
10791
|
-
const destRaw =
|
|
10792
|
-
const parser = ext === ".json" ? JSON :
|
|
11383
|
+
const sourceRaw = fs5.readFileSync(sourcePath, "utf8");
|
|
11384
|
+
const destRaw = fs5.readFileSync(destinationPath, "utf8");
|
|
11385
|
+
const parser = ext === ".json" ? JSON : import_yaml3.default;
|
|
10793
11386
|
const sourceValue = parser.parse(sourceRaw) || {};
|
|
10794
11387
|
const destValue = parser.parse(destRaw) || {};
|
|
10795
11388
|
const merged = mergeStructuredValues(sourceValue, destValue);
|
|
10796
11389
|
const serialized = ext === ".json" ? `${JSON.stringify(merged, null, 2)}
|
|
10797
|
-
` :
|
|
10798
|
-
|
|
10799
|
-
|
|
11390
|
+
` : import_yaml3.default.stringify(merged);
|
|
11391
|
+
fs5.writeFileSync(destinationPath, serialized, "utf8");
|
|
11392
|
+
fs5.unlinkSync(sourcePath);
|
|
10800
11393
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10801
11394
|
return;
|
|
10802
11395
|
}
|
|
10803
11396
|
if (ext === ".md") {
|
|
10804
|
-
const sourceRaw =
|
|
10805
|
-
const destRaw =
|
|
11397
|
+
const sourceRaw = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
11398
|
+
const destRaw = fs5.readFileSync(destinationPath, "utf8").trimEnd();
|
|
10806
11399
|
if (sourceRaw && !destRaw.includes(sourceRaw)) {
|
|
10807
11400
|
const marker = `## Legacy Content From ${relativeSource}`;
|
|
10808
|
-
|
|
11401
|
+
fs5.writeFileSync(destinationPath, `${destRaw}
|
|
10809
11402
|
|
|
10810
11403
|
${marker}
|
|
10811
11404
|
|
|
10812
11405
|
${sourceRaw}
|
|
10813
11406
|
`, "utf8");
|
|
10814
11407
|
}
|
|
10815
|
-
|
|
11408
|
+
fs5.unlinkSync(sourcePath);
|
|
10816
11409
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10817
11410
|
return;
|
|
10818
11411
|
}
|
|
10819
11412
|
const preservedPath = legacyVariantPath(destinationPath);
|
|
10820
|
-
|
|
11413
|
+
fs5.renameSync(sourcePath, preservedPath);
|
|
10821
11414
|
stageGitAwareMerge(sourcePath, preservedPath, gitState);
|
|
10822
11415
|
}
|
|
10823
|
-
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
10824
|
-
if (!
|
|
10825
|
-
const stat =
|
|
11416
|
+
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource = path5.basename(sourcePath), gitState = null) {
|
|
11417
|
+
if (!fs5.existsSync(sourcePath)) return;
|
|
11418
|
+
const stat = fs5.statSync(sourcePath);
|
|
10826
11419
|
if (stat.isDirectory()) {
|
|
11420
|
+
const sourceWasTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
10827
11421
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
10828
|
-
if (!
|
|
10829
|
-
|
|
10830
|
-
|
|
11422
|
+
if (!fs5.existsSync(destinationPath)) {
|
|
11423
|
+
fs5.mkdirSync(path5.dirname(destinationPath), { recursive: true });
|
|
11424
|
+
fs5.renameSync(sourcePath, destinationPath);
|
|
11425
|
+
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10831
11426
|
return;
|
|
10832
11427
|
}
|
|
10833
|
-
|
|
10834
|
-
for (const entry of
|
|
11428
|
+
fs5.mkdirSync(destinationPath, { recursive: true });
|
|
11429
|
+
for (const entry of fs5.readdirSync(sourcePath)) {
|
|
10835
11430
|
mergePathIntoDestination(
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
11431
|
+
path5.join(sourcePath, entry),
|
|
11432
|
+
path5.join(destinationPath, entry),
|
|
11433
|
+
path5.join(relativeSource, entry),
|
|
10839
11434
|
gitState
|
|
10840
11435
|
);
|
|
10841
11436
|
}
|
|
10842
|
-
|
|
11437
|
+
fs5.rmSync(sourcePath, { recursive: true, force: true });
|
|
10843
11438
|
return;
|
|
10844
11439
|
}
|
|
10845
11440
|
mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState);
|
|
10846
11441
|
}
|
|
10847
11442
|
function isOptimaPluginPackageWorktree(worktree) {
|
|
10848
|
-
const packageJsonPath =
|
|
10849
|
-
if (!
|
|
11443
|
+
const packageJsonPath = path5.join(worktree, "package.json");
|
|
11444
|
+
if (!fs5.existsSync(packageJsonPath)) return false;
|
|
10850
11445
|
try {
|
|
10851
|
-
const pkg = JSON.parse(
|
|
11446
|
+
const pkg = JSON.parse(fs5.readFileSync(packageJsonPath, "utf8"));
|
|
10852
11447
|
return pkg?.name === "@defend-tech/opencode-optima";
|
|
10853
11448
|
} catch {
|
|
10854
11449
|
return false;
|
|
@@ -10857,59 +11452,59 @@ function isOptimaPluginPackageWorktree(worktree) {
|
|
|
10857
11452
|
function migrateLegacyOptimaLayout(worktree) {
|
|
10858
11453
|
const gitState = gitMigrationState(worktree);
|
|
10859
11454
|
const migrations = [
|
|
10860
|
-
[
|
|
10861
|
-
[
|
|
10862
|
-
[
|
|
10863
|
-
[
|
|
10864
|
-
[
|
|
10865
|
-
[
|
|
11455
|
+
[path5.join(legacyOrbitaDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".orbita", "orbita.yaml")],
|
|
11456
|
+
[path5.join(legacyOrbitaDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".orbita", "staticeng.yaml")],
|
|
11457
|
+
[path5.join(legacyOrbitaDir(worktree), ".config"), optimaLocalConfigDir(worktree), path5.join(".orbita", ".config")],
|
|
11458
|
+
[path5.join(legacyOrbitaDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".orbita", "config")],
|
|
11459
|
+
[path5.join(legacyOrbitaDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".orbita", "runtime")],
|
|
11460
|
+
[path5.join(legacyOrbitaDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".orbita", "generated")],
|
|
10866
11461
|
[legacyOrbitaDir(worktree), optimaDir(worktree), ".orbita"],
|
|
10867
|
-
[
|
|
10868
|
-
[
|
|
10869
|
-
[
|
|
10870
|
-
[
|
|
10871
|
-
[
|
|
10872
|
-
[
|
|
11462
|
+
[path5.join(legacyStaticEngDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".staticeng", "staticeng.yaml")],
|
|
11463
|
+
[path5.join(legacyStaticEngDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".staticeng", "orbita.yaml")],
|
|
11464
|
+
[path5.join(legacyStaticEngDir(worktree), ".config"), optimaLocalConfigDir(worktree), path5.join(".staticeng", ".config")],
|
|
11465
|
+
[path5.join(legacyStaticEngDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".staticeng", "config")],
|
|
11466
|
+
[path5.join(legacyStaticEngDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".staticeng", "runtime")],
|
|
11467
|
+
[path5.join(legacyStaticEngDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".staticeng", "generated")],
|
|
10873
11468
|
[legacyStaticEngDir(worktree), optimaDir(worktree), ".staticeng"],
|
|
10874
|
-
[
|
|
10875
|
-
[
|
|
10876
|
-
[
|
|
10877
|
-
[
|
|
10878
|
-
[
|
|
10879
|
-
[
|
|
10880
|
-
[
|
|
10881
|
-
[
|
|
10882
|
-
[
|
|
10883
|
-
[
|
|
10884
|
-
[
|
|
10885
|
-
[
|
|
11469
|
+
[path5.join(legacyNomadworkDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "nomadworks.yaml")],
|
|
11470
|
+
[path5.join(legacyNomadworksDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "nomadworks.yaml")],
|
|
11471
|
+
[path5.join(legacyNomadworkDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "staticeng.yaml")],
|
|
11472
|
+
[path5.join(legacyNomadworksDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "staticeng.yaml")],
|
|
11473
|
+
[path5.join(legacyNomadworkDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "orbita.yaml")],
|
|
11474
|
+
[path5.join(legacyNomadworksDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "orbita.yaml")],
|
|
11475
|
+
[path5.join(legacyNomadworkDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".nomadwork", "runtime")],
|
|
11476
|
+
[path5.join(legacyNomadworksDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".nomadworks", "runtime")],
|
|
11477
|
+
[path5.join(legacyNomadworkDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".nomadwork", "generated")],
|
|
11478
|
+
[path5.join(legacyNomadworksDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".nomadworks", "generated")],
|
|
11479
|
+
[path5.join(legacyNomadworkDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".nomadwork", "config")],
|
|
11480
|
+
[path5.join(legacyNomadworksDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".nomadworks", "config")],
|
|
10886
11481
|
[legacyNomadworkDir(worktree), optimaDir(worktree), ".nomadwork"],
|
|
10887
11482
|
[legacyNomadworksDir(worktree), optimaDir(worktree), ".nomadworks"],
|
|
10888
|
-
[
|
|
10889
|
-
[
|
|
10890
|
-
[
|
|
10891
|
-
[
|
|
10892
|
-
[
|
|
11483
|
+
[path5.join(worktree, "tasks"), optimaTasksDir(worktree), "tasks"],
|
|
11484
|
+
[path5.join(worktree, "evidences"), optimaEvidencesDir(worktree), "evidences"],
|
|
11485
|
+
[path5.join(worktree, "docs", "scrs"), optimaScrsDir(worktree), path5.join("docs", "scrs")],
|
|
11486
|
+
[path5.join(worktree, "codemap.yml"), optimaCodemapPath(worktree), "codemap.yml"],
|
|
11487
|
+
[path5.join(worktree, "codemap.yaml"), optimaCodemapPath(worktree), "codemap.yaml"]
|
|
10893
11488
|
];
|
|
10894
11489
|
if (!isOptimaPluginPackageWorktree(worktree)) {
|
|
10895
|
-
migrations.push([
|
|
11490
|
+
migrations.push([path5.join(worktree, "policies"), repoPoliciesDir(worktree), "policies"]);
|
|
10896
11491
|
}
|
|
10897
11492
|
for (const [source, destination, relativeSource] of migrations) {
|
|
10898
|
-
if (
|
|
11493
|
+
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
10899
11494
|
}
|
|
10900
11495
|
for (const [source, destination, relativeSource] of [
|
|
10901
|
-
[
|
|
10902
|
-
[
|
|
10903
|
-
[
|
|
10904
|
-
[
|
|
11496
|
+
[path5.join(optimaDir(worktree), "optima.yaml"), repoConfigPath(worktree), path5.join(".optima", "optima.yaml")],
|
|
11497
|
+
[path5.join(optimaDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".optima", "config")],
|
|
11498
|
+
[path5.join(optimaDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".optima", "runtime")],
|
|
11499
|
+
[path5.join(optimaDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".optima", "generated")]
|
|
10905
11500
|
]) {
|
|
10906
|
-
if (
|
|
11501
|
+
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
10907
11502
|
}
|
|
10908
11503
|
}
|
|
10909
11504
|
function listMarkdownFiles(dirPath) {
|
|
10910
|
-
if (!
|
|
11505
|
+
if (!fs5.existsSync(dirPath)) return [];
|
|
10911
11506
|
try {
|
|
10912
|
-
return
|
|
11507
|
+
return fs5.readdirSync(dirPath).filter((file) => file.endsWith(".md") && file.toLowerCase() !== "readme.md");
|
|
10913
11508
|
} catch (e) {
|
|
10914
11509
|
console.error(`[Optima] Failed to read markdown files from ${dirPath}:`, e);
|
|
10915
11510
|
return [];
|
|
@@ -10919,67 +11514,6 @@ function normalizePolicyExtraction(value) {
|
|
|
10919
11514
|
if (typeof value !== "string") return "none";
|
|
10920
11515
|
return value.trim().toLowerCase() === "all" ? "all" : "none";
|
|
10921
11516
|
}
|
|
10922
|
-
function resolveIncludeFile(includeRef, repoRoot, bundleRoot, options = {}) {
|
|
10923
|
-
const trimmed = includeRef.trim();
|
|
10924
|
-
const scopedMatch = trimmed.match(/^([a-z]+):(.*)$/i);
|
|
10925
|
-
const scope = scopedMatch?.[1]?.toLowerCase();
|
|
10926
|
-
const target = scopedMatch ? scopedMatch[2].trim() : trimmed;
|
|
10927
|
-
const resolveRelative = (baseDir, relativePath) => {
|
|
10928
|
-
if (!relativePath) return null;
|
|
10929
|
-
return path2.isAbsolute(relativePath) ? relativePath : path2.join(baseDir, relativePath);
|
|
10930
|
-
};
|
|
10931
|
-
const withCompactPreference = (paths) => {
|
|
10932
|
-
if (!options.preferCompactPromptDocs) return paths;
|
|
10933
|
-
const compactPaths = paths.map((filePath) => filePath ? compactPromptPath(filePath) : null).filter(Boolean);
|
|
10934
|
-
return [...compactPaths, ...paths];
|
|
10935
|
-
};
|
|
10936
|
-
if (scope === "plugin") {
|
|
10937
|
-
for (const filePath of withCompactPreference([resolveRelative(bundleRoot, target)])) {
|
|
10938
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10939
|
-
}
|
|
10940
|
-
return null;
|
|
10941
|
-
}
|
|
10942
|
-
if (scope === "repo") {
|
|
10943
|
-
for (const filePath of withCompactPreference([resolveRelative(optimaDir(repoRoot), target)])) {
|
|
10944
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10945
|
-
}
|
|
10946
|
-
return null;
|
|
10947
|
-
}
|
|
10948
|
-
if (scope === "policy") {
|
|
10949
|
-
const candidates2 = withCompactPreference([
|
|
10950
|
-
resolveRelative(repoPoliciesDir(repoRoot), target),
|
|
10951
|
-
resolveRelative(BUNDLE_POLICIES_DIR, target)
|
|
10952
|
-
]);
|
|
10953
|
-
for (const filePath of candidates2) {
|
|
10954
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10955
|
-
}
|
|
10956
|
-
return null;
|
|
10957
|
-
}
|
|
10958
|
-
const candidates = withCompactPreference([
|
|
10959
|
-
resolveRelative(repoRoot, target),
|
|
10960
|
-
resolveRelative(bundleRoot, target)
|
|
10961
|
-
]);
|
|
10962
|
-
for (const filePath of candidates) {
|
|
10963
|
-
if (filePath && fs2.existsSync(filePath)) return filePath;
|
|
10964
|
-
}
|
|
10965
|
-
return null;
|
|
10966
|
-
}
|
|
10967
|
-
function resolveIncludes(text, repoRoot, bundleRoot, options = {}) {
|
|
10968
|
-
const includeRegex = /<include:(.*?)>/g;
|
|
10969
|
-
return text.replace(includeRegex, (match, includeRef) => {
|
|
10970
|
-
const filePath = resolveIncludeFile(includeRef, repoRoot, bundleRoot, options);
|
|
10971
|
-
if (!filePath) {
|
|
10972
|
-
console.warn(`[Optima] Include file not found: ${includeRef}`);
|
|
10973
|
-
return `
|
|
10974
|
-
|
|
10975
|
-
# ERROR: Include file not found: ${includeRef}
|
|
10976
|
-
|
|
10977
|
-
`;
|
|
10978
|
-
}
|
|
10979
|
-
const content = fs2.readFileSync(filePath, "utf8");
|
|
10980
|
-
return resolveIncludes(content, repoRoot, bundleRoot, options);
|
|
10981
|
-
});
|
|
10982
|
-
}
|
|
10983
11517
|
function parseFrontmatter(mdText) {
|
|
10984
11518
|
const lines = mdText.split(/\r?\n/);
|
|
10985
11519
|
if (lines[0]?.trim() !== "---") return { data: {}, body: mdText };
|
|
@@ -10994,7 +11528,7 @@ function parseFrontmatter(mdText) {
|
|
|
10994
11528
|
const fmText = lines.slice(1, end).join("\n");
|
|
10995
11529
|
const body = lines.slice(end + 1).join("\n");
|
|
10996
11530
|
try {
|
|
10997
|
-
return { data:
|
|
11531
|
+
return { data: import_yaml3.default.parse(fmText) || {}, body };
|
|
10998
11532
|
} catch {
|
|
10999
11533
|
return { data: {}, body };
|
|
11000
11534
|
}
|
|
@@ -11011,10 +11545,10 @@ function toModelString(provider, model) {
|
|
|
11011
11545
|
}
|
|
11012
11546
|
function readTaskMetadata(taskPath, worktree) {
|
|
11013
11547
|
if (!taskPath) return {};
|
|
11014
|
-
const absoluteTaskPath =
|
|
11015
|
-
if (!
|
|
11548
|
+
const absoluteTaskPath = path5.isAbsolute(taskPath) ? taskPath : path5.join(worktree, taskPath);
|
|
11549
|
+
if (!fs5.existsSync(absoluteTaskPath)) return {};
|
|
11016
11550
|
try {
|
|
11017
|
-
const raw =
|
|
11551
|
+
const raw = fs5.readFileSync(absoluteTaskPath, "utf8");
|
|
11018
11552
|
const { data } = parseFrontmatter(raw);
|
|
11019
11553
|
return {
|
|
11020
11554
|
complexity: typeof data.complexity === "string" ? data.complexity.trim().toLowerCase() : void 0,
|
|
@@ -11039,11 +11573,11 @@ function slugifyTitle(input) {
|
|
|
11039
11573
|
}
|
|
11040
11574
|
function loadDiscussionRegistry(worktree) {
|
|
11041
11575
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
11042
|
-
if (!
|
|
11576
|
+
if (!fs5.existsSync(registryPath)) {
|
|
11043
11577
|
return { version: 1, active: {} };
|
|
11044
11578
|
}
|
|
11045
11579
|
try {
|
|
11046
|
-
const parsed = JSON.parse(
|
|
11580
|
+
const parsed = JSON.parse(fs5.readFileSync(registryPath, "utf8"));
|
|
11047
11581
|
const registry = {
|
|
11048
11582
|
version: 1,
|
|
11049
11583
|
active: Object.fromEntries(Object.entries(parsed.active || {}).map(([key, value]) => [key, normalizeLegacyDiscussionEntry(value)]))
|
|
@@ -11056,34 +11590,34 @@ function loadDiscussionRegistry(worktree) {
|
|
|
11056
11590
|
}
|
|
11057
11591
|
function saveDiscussionRegistry(worktree, registry) {
|
|
11058
11592
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
11059
|
-
const runtimeDir =
|
|
11060
|
-
if (!
|
|
11061
|
-
|
|
11593
|
+
const runtimeDir = path5.dirname(registryPath);
|
|
11594
|
+
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
11595
|
+
fs5.writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf8");
|
|
11062
11596
|
}
|
|
11063
11597
|
function runtimeDiscussionsDir(worktree) {
|
|
11064
|
-
return
|
|
11598
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime", "discussions");
|
|
11065
11599
|
}
|
|
11066
11600
|
function archivedRuntimeDiscussionsDir(worktree) {
|
|
11067
|
-
return
|
|
11601
|
+
return path5.join(runtimeDiscussionsDir(worktree), "archive");
|
|
11068
11602
|
}
|
|
11069
11603
|
function finalDiscussionsDir(worktree) {
|
|
11070
|
-
return
|
|
11604
|
+
return path5.join(optimaTasksDir(worktree), "discussions");
|
|
11071
11605
|
}
|
|
11072
11606
|
function nextDiscussionIdentity(worktree, title) {
|
|
11073
11607
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
11074
11608
|
const runtimeDir = runtimeDiscussionsDir(worktree);
|
|
11075
|
-
if (!
|
|
11076
|
-
if (!
|
|
11609
|
+
if (!fs5.existsSync(discussionsDir)) fs5.mkdirSync(discussionsDir, { recursive: true });
|
|
11610
|
+
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
11077
11611
|
let sequence = 1;
|
|
11078
11612
|
while (true) {
|
|
11079
11613
|
const id = `DISCUSSION-${String(sequence).padStart(3, "0")}`;
|
|
11080
11614
|
const filename = `${id}-${slugifyTitle(title)}.md`;
|
|
11081
|
-
const summaryRelativePath =
|
|
11082
|
-
const summaryAbsolutePath =
|
|
11615
|
+
const summaryRelativePath = path5.join(".optima", "tasks", "discussions", filename);
|
|
11616
|
+
const summaryAbsolutePath = path5.join(worktree, summaryRelativePath);
|
|
11083
11617
|
const transcriptFilename = `${id}-transcript.md`;
|
|
11084
|
-
const transcriptRelativePath =
|
|
11085
|
-
const transcriptAbsolutePath =
|
|
11086
|
-
if (!
|
|
11618
|
+
const transcriptRelativePath = path5.join(".optima", ".config", "runtime", "discussions", transcriptFilename);
|
|
11619
|
+
const transcriptAbsolutePath = path5.join(worktree, transcriptRelativePath);
|
|
11620
|
+
if (!fs5.existsSync(summaryAbsolutePath) && !fs5.existsSync(transcriptAbsolutePath)) {
|
|
11087
11621
|
return {
|
|
11088
11622
|
id,
|
|
11089
11623
|
filename,
|
|
@@ -11099,37 +11633,37 @@ function nextDiscussionIdentity(worktree, title) {
|
|
|
11099
11633
|
}
|
|
11100
11634
|
function findDiscussionById(worktree, discussionID) {
|
|
11101
11635
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
11102
|
-
if (!
|
|
11103
|
-
const entries =
|
|
11636
|
+
if (!fs5.existsSync(discussionsDir)) return null;
|
|
11637
|
+
const entries = fs5.readdirSync(discussionsDir).filter((name) => name.startsWith(`${discussionID}-`) && name.endsWith(".md"));
|
|
11104
11638
|
if (entries.length === 0) return null;
|
|
11105
11639
|
const filename = entries.sort()[0];
|
|
11106
11640
|
const transcriptFilename = `${discussionID}-transcript.md`;
|
|
11107
11641
|
return {
|
|
11108
11642
|
id: discussionID,
|
|
11109
11643
|
filename,
|
|
11110
|
-
summaryRelativePath:
|
|
11111
|
-
summaryAbsolutePath:
|
|
11644
|
+
summaryRelativePath: path5.join(".optima", "tasks", "discussions", filename),
|
|
11645
|
+
summaryAbsolutePath: path5.join(discussionsDir, filename),
|
|
11112
11646
|
transcriptFilename,
|
|
11113
|
-
transcriptRelativePath:
|
|
11114
|
-
transcriptAbsolutePath:
|
|
11647
|
+
transcriptRelativePath: path5.join(".optima", ".config", "runtime", "discussions", transcriptFilename),
|
|
11648
|
+
transcriptAbsolutePath: path5.join(runtimeDiscussionsDir(worktree), transcriptFilename)
|
|
11115
11649
|
};
|
|
11116
11650
|
}
|
|
11117
11651
|
function parseDiscussionFile(filePath) {
|
|
11118
|
-
const raw =
|
|
11652
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
11119
11653
|
const { data, body } = parseFrontmatter(raw);
|
|
11120
11654
|
return { data, body: body.trimStart() };
|
|
11121
11655
|
}
|
|
11122
11656
|
function writeDiscussionFile(filePath, frontmatter, body) {
|
|
11123
11657
|
const serialized = `---
|
|
11124
|
-
${
|
|
11658
|
+
${import_yaml3.default.stringify(frontmatter).trim()}
|
|
11125
11659
|
---
|
|
11126
11660
|
|
|
11127
11661
|
${body.trimEnd()}
|
|
11128
11662
|
`;
|
|
11129
|
-
|
|
11663
|
+
fs5.writeFileSync(filePath, serialized, "utf8");
|
|
11130
11664
|
}
|
|
11131
11665
|
function setDiscussionStatus(filePath, status) {
|
|
11132
|
-
if (!
|
|
11666
|
+
if (!fs5.existsSync(filePath)) return;
|
|
11133
11667
|
const { data, body } = parseDiscussionFile(filePath);
|
|
11134
11668
|
writeDiscussionFile(filePath, { ...data, status }, body);
|
|
11135
11669
|
}
|
|
@@ -11194,18 +11728,18 @@ async function appendMessageIfNeeded(client, worktree, registry, sessionID, mess
|
|
|
11194
11728
|
});
|
|
11195
11729
|
const text = extractTextParts(response.data.parts || []);
|
|
11196
11730
|
if (!text) return;
|
|
11197
|
-
appendDiscussionMessage(
|
|
11731
|
+
appendDiscussionMessage(path5.join(worktree, discussion.transcriptPath), speaker, text, messageID);
|
|
11198
11732
|
discussion.appendedMessageIDs ??= [];
|
|
11199
11733
|
discussion.appendedMessageIDs.push(messageID);
|
|
11200
11734
|
saveDiscussionRegistry(worktree, registry);
|
|
11201
11735
|
}
|
|
11202
11736
|
async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
11203
|
-
const transcriptPath =
|
|
11204
|
-
const summaryPath =
|
|
11205
|
-
const summaryDir =
|
|
11206
|
-
if (!
|
|
11207
|
-
const hasExistingSummary =
|
|
11208
|
-
const priorMtimeMs = hasExistingSummary ?
|
|
11737
|
+
const transcriptPath = path5.join(worktree, discussion.transcriptPath);
|
|
11738
|
+
const summaryPath = path5.join(worktree, discussion.summaryPath);
|
|
11739
|
+
const summaryDir = path5.dirname(summaryPath);
|
|
11740
|
+
if (!fs5.existsSync(summaryDir)) fs5.mkdirSync(summaryDir, { recursive: true });
|
|
11741
|
+
const hasExistingSummary = fs5.existsSync(summaryPath);
|
|
11742
|
+
const priorMtimeMs = hasExistingSummary ? fs5.statSync(summaryPath).mtimeMs : null;
|
|
11209
11743
|
const summarizerSession = await client.session.create({
|
|
11210
11744
|
body: { title: `Discussion Summary: ${discussion.id}` }
|
|
11211
11745
|
});
|
|
@@ -11305,30 +11839,30 @@ async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
|
11305
11839
|
return { confirmation, summaryPath, transcriptPath, hasExistingSummary, priorMtimeMs };
|
|
11306
11840
|
}
|
|
11307
11841
|
function archiveDiscussionTranscript(worktree, transcriptRelativePath) {
|
|
11308
|
-
const sourcePath =
|
|
11309
|
-
if (!
|
|
11842
|
+
const sourcePath = path5.join(worktree, transcriptRelativePath);
|
|
11843
|
+
if (!fs5.existsSync(sourcePath)) return null;
|
|
11310
11844
|
const archiveDir = archivedRuntimeDiscussionsDir(worktree);
|
|
11311
|
-
if (!
|
|
11312
|
-
const targetPath =
|
|
11313
|
-
|
|
11845
|
+
if (!fs5.existsSync(archiveDir)) fs5.mkdirSync(archiveDir, { recursive: true });
|
|
11846
|
+
const targetPath = path5.join(archiveDir, path5.basename(sourcePath));
|
|
11847
|
+
fs5.renameSync(sourcePath, targetPath);
|
|
11314
11848
|
return targetPath;
|
|
11315
11849
|
}
|
|
11316
11850
|
async function finalizeClosingDiscussion(client, worktree, registry, sessionID, discussion) {
|
|
11317
11851
|
const { confirmation, summaryPath, hasExistingSummary, priorMtimeMs } = await summarizeDiscussionWithBA(client, worktree, discussion);
|
|
11318
|
-
if (!
|
|
11852
|
+
if (!fs5.existsSync(summaryPath)) {
|
|
11319
11853
|
throw new Error(`Discussion summary was not written to ${discussion.summaryPath}`);
|
|
11320
11854
|
}
|
|
11321
11855
|
if (hasExistingSummary) {
|
|
11322
|
-
const currentMtimeMs =
|
|
11856
|
+
const currentMtimeMs = fs5.statSync(summaryPath).mtimeMs;
|
|
11323
11857
|
if (currentMtimeMs <= priorMtimeMs) {
|
|
11324
11858
|
throw new Error(`Discussion summary file was not updated at ${discussion.summaryPath}`);
|
|
11325
11859
|
}
|
|
11326
11860
|
}
|
|
11327
|
-
const summaryContent =
|
|
11861
|
+
const summaryContent = fs5.readFileSync(summaryPath, "utf8").trim();
|
|
11328
11862
|
if (!summaryContent) {
|
|
11329
11863
|
throw new Error(`Discussion summary file is empty at ${discussion.summaryPath}`);
|
|
11330
11864
|
}
|
|
11331
|
-
const transcriptPath =
|
|
11865
|
+
const transcriptPath = path5.join(worktree, discussion.transcriptPath);
|
|
11332
11866
|
setDiscussionStatus(transcriptPath, "closed");
|
|
11333
11867
|
const archivedTranscriptPath = archiveDiscussionTranscript(worktree, discussion.transcriptPath);
|
|
11334
11868
|
delete registry.active[sessionID];
|
|
@@ -11336,7 +11870,7 @@ async function finalizeClosingDiscussion(client, worktree, registry, sessionID,
|
|
|
11336
11870
|
return {
|
|
11337
11871
|
confirmation,
|
|
11338
11872
|
summaryPath: discussion.summaryPath,
|
|
11339
|
-
archivedTranscriptPath: archivedTranscriptPath ?
|
|
11873
|
+
archivedTranscriptPath: archivedTranscriptPath ? path5.relative(worktree, archivedTranscriptPath) : path5.join(".optima", ".config", "runtime", "discussions", "archive", path5.basename(discussion.transcriptPath))
|
|
11340
11874
|
};
|
|
11341
11875
|
}
|
|
11342
11876
|
function normalizeTeamMode(value) {
|
|
@@ -11375,13 +11909,13 @@ function getOperatingTeamMode(repoCfg) {
|
|
|
11375
11909
|
}
|
|
11376
11910
|
function readResolvedFile(relativePath, worktree, options = {}) {
|
|
11377
11911
|
const filePath = resolveIncludeFile(`plugin:${relativePath}`, worktree, PKG_ROOT, options);
|
|
11378
|
-
if (!filePath || !
|
|
11379
|
-
return resolveIncludes(
|
|
11912
|
+
if (!filePath || !fs5.existsSync(filePath)) return "";
|
|
11913
|
+
return resolveIncludes(fs5.readFileSync(filePath, "utf8"), worktree, PKG_ROOT, options).trim();
|
|
11380
11914
|
}
|
|
11381
11915
|
function loadMarkdownFragment(filePath, worktree) {
|
|
11382
|
-
if (!
|
|
11916
|
+
if (!fs5.existsSync(filePath)) return "";
|
|
11383
11917
|
try {
|
|
11384
|
-
const raw =
|
|
11918
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
11385
11919
|
const { body } = parseFrontmatter(raw);
|
|
11386
11920
|
return resolveIncludes(body.trim(), worktree, PKG_ROOT);
|
|
11387
11921
|
} catch (e) {
|
|
@@ -11390,9 +11924,9 @@ function loadMarkdownFragment(filePath, worktree) {
|
|
|
11390
11924
|
}
|
|
11391
11925
|
}
|
|
11392
11926
|
function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
11393
|
-
if (!
|
|
11927
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
11394
11928
|
try {
|
|
11395
|
-
const rawContent =
|
|
11929
|
+
const rawContent = fs5.readFileSync(filePath, "utf8");
|
|
11396
11930
|
const { data, body } = parseFrontmatter(rawContent);
|
|
11397
11931
|
const prompt = resolveIncludes(body.trim(), worktree, PKG_ROOT, options);
|
|
11398
11932
|
return { data, prompt };
|
|
@@ -11403,13 +11937,13 @@ function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
|
11403
11937
|
}
|
|
11404
11938
|
function syncGeneratedPolicies(worktree, repoCfg) {
|
|
11405
11939
|
if (repoCfg.policies?.extract_defaults !== "all") return;
|
|
11406
|
-
if (!
|
|
11940
|
+
if (!fs5.existsSync(BUNDLE_POLICIES_DIR)) return;
|
|
11407
11941
|
const generatedDir = generatedPoliciesDir(worktree);
|
|
11408
|
-
if (!
|
|
11409
|
-
const policyFiles =
|
|
11942
|
+
if (!fs5.existsSync(generatedDir)) fs5.mkdirSync(generatedDir, { recursive: true });
|
|
11943
|
+
const policyFiles = fs5.readdirSync(BUNDLE_POLICIES_DIR).filter((file) => file.endsWith(".md") && file !== "README.md");
|
|
11410
11944
|
for (const file of policyFiles) {
|
|
11411
|
-
const sourcePath =
|
|
11412
|
-
const source =
|
|
11945
|
+
const sourcePath = path5.join(BUNDLE_POLICIES_DIR, file);
|
|
11946
|
+
const source = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
11413
11947
|
const generated = [
|
|
11414
11948
|
"<!--",
|
|
11415
11949
|
"Generated from Optima plugin defaults.",
|
|
@@ -11420,19 +11954,31 @@ function syncGeneratedPolicies(worktree, repoCfg) {
|
|
|
11420
11954
|
source,
|
|
11421
11955
|
""
|
|
11422
11956
|
].join("\n");
|
|
11423
|
-
|
|
11957
|
+
fs5.writeFileSync(path5.join(generatedDir, file), generated, "utf8");
|
|
11424
11958
|
}
|
|
11425
11959
|
}
|
|
11426
11960
|
function ensureReadmeFile(dirPath, content) {
|
|
11427
|
-
if (!
|
|
11428
|
-
const readmePath =
|
|
11429
|
-
if (!
|
|
11430
|
-
|
|
11961
|
+
if (!fs5.existsSync(dirPath)) fs5.mkdirSync(dirPath, { recursive: true });
|
|
11962
|
+
const readmePath = path5.join(dirPath, "README.md");
|
|
11963
|
+
if (!fs5.existsSync(readmePath)) {
|
|
11964
|
+
fs5.writeFileSync(readmePath, content, "utf8");
|
|
11431
11965
|
}
|
|
11432
11966
|
}
|
|
11433
11967
|
function ensureFileIfMissing(filePath, content) {
|
|
11434
|
-
if (!
|
|
11435
|
-
if (!
|
|
11968
|
+
if (!fs5.existsSync(path5.dirname(filePath))) fs5.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
11969
|
+
if (!fs5.existsSync(filePath)) fs5.writeFileSync(filePath, content, "utf8");
|
|
11970
|
+
}
|
|
11971
|
+
function currentTasksRegistryContent() {
|
|
11972
|
+
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";
|
|
11973
|
+
}
|
|
11974
|
+
function doneTasksRegistryContent() {
|
|
11975
|
+
return "# Completed Tasks (Registry)\n\n| Date | Task ID | SCR ID | Commit | Summary |\n| :--- | :--- | :--- | :--- | :--- |\n";
|
|
11976
|
+
}
|
|
11977
|
+
function currentScrRegistryContent() {
|
|
11978
|
+
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";
|
|
11979
|
+
}
|
|
11980
|
+
function doneScrRegistryContent() {
|
|
11981
|
+
return "# Implemented Spec Change Requests\n\n| Date | SCR ID | Title | Related Feature | Task ID |\n| :--- | :--- | :--- | :--- | :--- |\n";
|
|
11436
11982
|
}
|
|
11437
11983
|
function taskTemplateContent() {
|
|
11438
11984
|
return `---
|
|
@@ -11506,19 +12052,58 @@ Never place implementation evidence under root \`evidences/\`.
|
|
|
11506
12052
|
}
|
|
11507
12053
|
function ensureOptimaTaskTemplates(worktree) {
|
|
11508
12054
|
const tasksDir = optimaTasksDir(worktree);
|
|
11509
|
-
ensureFileIfMissing(
|
|
11510
|
-
ensureFileIfMissing(
|
|
12055
|
+
ensureFileIfMissing(path5.join(tasksDir, "task-template.md"), taskTemplateContent());
|
|
12056
|
+
ensureFileIfMissing(path5.join(tasksDir, "subtask-template.md"), subtaskTemplateContent());
|
|
12057
|
+
}
|
|
12058
|
+
function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
12059
|
+
const configPath = repoConfigPath(worktree);
|
|
12060
|
+
if (fs5.existsSync(configPath)) return false;
|
|
12061
|
+
const templatePath = path5.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12062
|
+
if (!fs5.existsSync(templatePath)) return false;
|
|
12063
|
+
const agentIds = fs5.existsSync(BUNDLE_AGENTS_DIR) ? fs5.readdirSync(BUNDLE_AGENTS_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")) : [];
|
|
12064
|
+
let optimaConfig = fs5.readFileSync(templatePath, "utf8");
|
|
12065
|
+
optimaConfig = optimaConfig.replace("{{teamMode}}", normalizeTeamMode(teamMode));
|
|
12066
|
+
let agentsSection = "";
|
|
12067
|
+
for (const id of agentIds) {
|
|
12068
|
+
const enabled = isAgentEnabledForTeamMode(id, normalizeTeamMode(teamMode)) ? "true" : "false";
|
|
12069
|
+
agentsSection += ` ${id}:
|
|
12070
|
+
enabled: ${enabled}
|
|
12071
|
+
`;
|
|
12072
|
+
}
|
|
12073
|
+
optimaConfig = optimaConfig.replace(/^agents:/m, "agents:\n" + agentsSection);
|
|
12074
|
+
ensureFileIfMissing(configPath, optimaConfig);
|
|
12075
|
+
return fs5.existsSync(configPath);
|
|
12076
|
+
}
|
|
12077
|
+
function scaffoldOptimaRootCodemap(worktree) {
|
|
12078
|
+
const rootCodemapPath = optimaCodemapPath(worktree);
|
|
12079
|
+
if (fs5.existsSync(rootCodemapPath)) return false;
|
|
12080
|
+
const templatePath = path5.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12081
|
+
if (!fs5.existsSync(templatePath)) return false;
|
|
12082
|
+
const codemapConfig = fs5.readFileSync(templatePath, "utf8").replace("{{projectName}}", path5.basename(worktree));
|
|
12083
|
+
ensureFileIfMissing(rootCodemapPath, codemapConfig);
|
|
12084
|
+
return fs5.existsSync(rootCodemapPath);
|
|
12085
|
+
}
|
|
12086
|
+
function ensureOptimaRegistries(worktree) {
|
|
12087
|
+
const tasksDir = optimaTasksDir(worktree);
|
|
12088
|
+
const scrsDir = optimaScrsDir(worktree);
|
|
12089
|
+
if (!fs5.existsSync(tasksDir)) fs5.mkdirSync(tasksDir, { recursive: true });
|
|
12090
|
+
if (!fs5.existsSync(scrsDir)) fs5.mkdirSync(scrsDir, { recursive: true });
|
|
12091
|
+
ensureFileIfMissing(path5.join(tasksDir, "current.md"), currentTasksRegistryContent());
|
|
12092
|
+
ensureFileIfMissing(path5.join(tasksDir, "done.md"), doneTasksRegistryContent());
|
|
12093
|
+
ensureFileIfMissing(path5.join(scrsDir, "current.md"), currentScrRegistryContent());
|
|
12094
|
+
ensureFileIfMissing(path5.join(scrsDir, "done.md"), doneScrRegistryContent());
|
|
11511
12095
|
}
|
|
11512
12096
|
function scaffoldOptimaReadmes(worktree) {
|
|
11513
12097
|
ensureReadmeFile(repoPoliciesDir(worktree), REPO_LOCAL_POLICIES_README);
|
|
11514
12098
|
ensureReadmeFile(repoAgentsDir(worktree), [
|
|
11515
12099
|
"# Repository Agents",
|
|
11516
12100
|
"",
|
|
11517
|
-
"Place full repository-local agent definitions here.",
|
|
12101
|
+
"Place full repository-local agent definitions here only when explicitly requested by the user.",
|
|
11518
12102
|
"",
|
|
11519
|
-
"- Use `.optima/agents/<agent>.md` to override a bundled agent's full base definition.",
|
|
11520
|
-
"- Use `.optima/agents/<agent>.md` to define a brand new custom repository agent.",
|
|
11521
|
-
"- Files in this folder are treated as full agent definitions.",
|
|
12103
|
+
"- Use `.optima/agents/<agent>.md` only to intentionally override a bundled agent's full base definition.",
|
|
12104
|
+
"- Use `.optima/agents/<agent>.md` only to define a brand new custom repository agent.",
|
|
12105
|
+
"- Files in this folder are treated as full agent definitions and block bundled prompt updates for that agent.",
|
|
12106
|
+
"- Prefer `.optima/agent-additions/<agent>.md` for normal repository-specific guidance.",
|
|
11522
12107
|
"- `README.md` is ignored by agent discovery.",
|
|
11523
12108
|
"",
|
|
11524
12109
|
"## Include Types Available In Custom Agents",
|
|
@@ -11612,6 +12197,38 @@ function scaffoldOptimaReadmes(worktree) {
|
|
|
11612
12197
|
].join("\n"));
|
|
11613
12198
|
ensureOptimaTaskTemplates(worktree);
|
|
11614
12199
|
}
|
|
12200
|
+
function optimaRepairDependencies() {
|
|
12201
|
+
return {
|
|
12202
|
+
currentScrRegistryContent,
|
|
12203
|
+
currentTasksRegistryContent,
|
|
12204
|
+
doneScrRegistryContent,
|
|
12205
|
+
doneTasksRegistryContent,
|
|
12206
|
+
ensureFileIfMissing,
|
|
12207
|
+
ensureOptimaGitignoreRules,
|
|
12208
|
+
ensureOptimaRegistries,
|
|
12209
|
+
isGitRepository,
|
|
12210
|
+
isOptimaPluginPackageWorktree,
|
|
12211
|
+
legacyNomadworkDir,
|
|
12212
|
+
legacyNomadworksDir,
|
|
12213
|
+
legacyOrbitaDir,
|
|
12214
|
+
legacyStaticEngDir,
|
|
12215
|
+
migrateLegacyOptimaLayout,
|
|
12216
|
+
optimaCodemapPath,
|
|
12217
|
+
optimaDir,
|
|
12218
|
+
optimaEvidencesDir,
|
|
12219
|
+
optimaGitignoreRules: OPTIMA_GITIGNORE_RULES,
|
|
12220
|
+
optimaScrsDir,
|
|
12221
|
+
optimaTasksDir,
|
|
12222
|
+
repoConfigPath,
|
|
12223
|
+
repoLocalPoliciesReadme: REPO_LOCAL_POLICIES_README,
|
|
12224
|
+
repoPoliciesDir,
|
|
12225
|
+
scaffoldOptimaConfig,
|
|
12226
|
+
scaffoldOptimaReadmes,
|
|
12227
|
+
scaffoldOptimaRootCodemap,
|
|
12228
|
+
subtaskTemplateContent,
|
|
12229
|
+
taskTemplateContent
|
|
12230
|
+
};
|
|
12231
|
+
}
|
|
11615
12232
|
function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
11616
12233
|
const fragmentMap = {
|
|
11617
12234
|
product_manager: {
|
|
@@ -11627,13 +12244,6 @@ function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
|
11627
12244
|
if (!fragmentPath) return "";
|
|
11628
12245
|
return readResolvedFile(fragmentPath, worktree, { preferCompactPromptDocs: true });
|
|
11629
12246
|
}
|
|
11630
|
-
function isSameOrNestedPath(candidate, root) {
|
|
11631
|
-
if (typeof candidate !== "string" || typeof root !== "string" || !candidate.trim() || !root.trim()) return false;
|
|
11632
|
-
const resolvedCandidate = path2.resolve(candidate);
|
|
11633
|
-
const resolvedRoot = path2.resolve(root);
|
|
11634
|
-
const relative = path2.relative(resolvedRoot, resolvedCandidate);
|
|
11635
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !path2.isAbsolute(relative);
|
|
11636
|
-
}
|
|
11637
12247
|
function shouldRegisterWorkflowProductManager(options = {}, worktree = process.cwd()) {
|
|
11638
12248
|
if (options.clickUpWebhookActive === true) return true;
|
|
11639
12249
|
const validation = options.clickUpWebhookValidation;
|
|
@@ -11642,12 +12252,12 @@ function shouldRegisterWorkflowProductManager(options = {}, worktree = process.c
|
|
|
11642
12252
|
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
11643
12253
|
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
11644
12254
|
if (!safe.ok) return { ok: false, error: safe.message };
|
|
11645
|
-
const requested = String(requestedDirectory || "").trim() ?
|
|
12255
|
+
const requested = String(requestedDirectory || "").trim() ? path5.resolve(safe.worktree, String(requestedDirectory).trim()) : safe.worktree;
|
|
11646
12256
|
if (!isSafeWritableDirectory(requested)) return { ok: false, error: `Directory is not a safe writable directory: ${requested}` };
|
|
11647
12257
|
if (isSameOrNestedPath(requested, safe.worktree)) return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "context_worktree" };
|
|
11648
12258
|
const clickUpBasePath = clickUpWebhookValidation?.complete === true && clickUpWebhookValidation?.ok !== false ? clickUpWebhookValidation.config?.basePath : "";
|
|
11649
12259
|
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
11650
|
-
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath:
|
|
12260
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
11651
12261
|
}
|
|
11652
12262
|
return {
|
|
11653
12263
|
ok: false,
|
|
@@ -11681,8 +12291,8 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
11681
12291
|
if (!enabled) continue;
|
|
11682
12292
|
}
|
|
11683
12293
|
const promptOptions = { preferCompactPromptDocs: repoCfg.features?.compact_prompt_docs !== false };
|
|
11684
|
-
const bundledDefinition = loadAgentDefinition(
|
|
11685
|
-
const repoDefinition = loadAgentDefinition(
|
|
12294
|
+
const bundledDefinition = loadAgentDefinition(path5.join(BUNDLE_AGENTS_DIR, file), worktree, promptOptions);
|
|
12295
|
+
const repoDefinition = loadAgentDefinition(path5.join(repoAgentDefinitions, file), worktree, promptOptions) || loadAgentDefinition(path5.join(legacyAgentsDir, file), worktree, promptOptions);
|
|
11686
12296
|
const activeDefinition = repoDefinition || bundledDefinition;
|
|
11687
12297
|
if (!activeDefinition) continue;
|
|
11688
12298
|
const { data } = activeDefinition;
|
|
@@ -11691,7 +12301,7 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
11691
12301
|
if (modePromptFragment) finalPrompt = `${finalPrompt}
|
|
11692
12302
|
|
|
11693
12303
|
${modePromptFragment}`;
|
|
11694
|
-
const additionFragment = loadMarkdownFragment(
|
|
12304
|
+
const additionFragment = loadMarkdownFragment(path5.join(repoAgentAdditions, file), worktree);
|
|
11695
12305
|
if (additionFragment) {
|
|
11696
12306
|
finalPrompt = `${finalPrompt}
|
|
11697
12307
|
|
|
@@ -11744,14 +12354,14 @@ Use this Optima-provided fallback when the current task worktree lacks docs/core
|
|
|
11744
12354
|
}
|
|
11745
12355
|
ourAgents[id] = agentConfig;
|
|
11746
12356
|
if (repoCfg.features?.debug_dumps !== false) {
|
|
11747
|
-
const debugPath =
|
|
12357
|
+
const debugPath = path5.join(debugDir, `${id}.md`);
|
|
11748
12358
|
const { prompt, ...dumpConfig } = agentConfig;
|
|
11749
12359
|
const debugHeader = `---
|
|
11750
|
-
${
|
|
12360
|
+
${import_yaml3.default.stringify(dumpConfig).trim()}
|
|
11751
12361
|
---`;
|
|
11752
12362
|
try {
|
|
11753
|
-
if (!
|
|
11754
|
-
|
|
12363
|
+
if (!fs5.existsSync(debugDir)) fs5.mkdirSync(debugDir, { recursive: true });
|
|
12364
|
+
fs5.writeFileSync(debugPath, `${debugHeader}
|
|
11755
12365
|
|
|
11756
12366
|
${prompt}`, "utf8");
|
|
11757
12367
|
} catch (e) {
|
|
@@ -11775,9 +12385,9 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11775
12385
|
const configPath = resolveConfigPath(worktree);
|
|
11776
12386
|
const discussionRegistry = loadDiscussionRegistry(worktree);
|
|
11777
12387
|
let repoCfg = { agents: {}, defaults: {}, features: {} };
|
|
11778
|
-
if (
|
|
12388
|
+
if (fs5.existsSync(configPath)) {
|
|
11779
12389
|
try {
|
|
11780
|
-
repoCfg =
|
|
12390
|
+
repoCfg = import_yaml3.default.parse(fs5.readFileSync(configPath, "utf8")) || repoCfg;
|
|
11781
12391
|
} catch (e) {
|
|
11782
12392
|
console.error(`[Optima] Failed to parse config at ${configPath}:`, e);
|
|
11783
12393
|
}
|
|
@@ -11790,13 +12400,14 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11790
12400
|
syncGeneratedPolicies(worktree, repoCfg);
|
|
11791
12401
|
const operatingTeamMode = getOperatingTeamMode(repoCfg);
|
|
11792
12402
|
const clickUpWebhookValidation = normalizeClickUpWebhookConfig(pickConfiguredClickUp(input, repoCfg, resolvedPluginOptions), worktree);
|
|
12403
|
+
const runtimeClickUpClient = input.clickupClient || (clickUpWebhookValidation.config?.test === true ? createTestClickUpApiClient(clickUpWebhookValidation.config) : createClickUpApiClient(clickUpWebhookValidation.config, input.fetch));
|
|
11793
12404
|
let clickUpWebhookRuntime = { active: false, valid: false, reason: "not_configured" };
|
|
11794
12405
|
if (clickUpWebhookValidation.complete) {
|
|
11795
12406
|
try {
|
|
11796
12407
|
clickUpWebhookRuntime = await ensureClickUpWebhookSubscription({
|
|
11797
12408
|
validation: clickUpWebhookValidation,
|
|
11798
12409
|
worktree,
|
|
11799
|
-
clickupClient:
|
|
12410
|
+
clickupClient: runtimeClickUpClient
|
|
11800
12411
|
});
|
|
11801
12412
|
} catch (error) {
|
|
11802
12413
|
clickUpWebhookRuntime = { active: false, valid: false, reason: "subscription_failed", error: error.message };
|
|
@@ -11806,7 +12417,7 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11806
12417
|
let clickUpWebhookActive = clickUpWebhookRuntime.active === true && clickUpWebhookRuntime.valid === true;
|
|
11807
12418
|
if (clickUpWebhookActive && input.startClickUpWebhookListener !== false) {
|
|
11808
12419
|
try {
|
|
11809
|
-
const lifecycleClickUpClient =
|
|
12420
|
+
const lifecycleClickUpClient = runtimeClickUpClient;
|
|
11810
12421
|
const listenerRegistry = input.clickUpWebhookListenerRegistry || activeClickUpWebhookListeners;
|
|
11811
12422
|
const listenerState = clickUpWebhookRuntime.state || readClickUpWebhookState(worktree, clickUpWebhookValidation.config);
|
|
11812
12423
|
const listener = startClickUpWebhookListener({
|
|
@@ -11884,54 +12495,16 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11884
12495
|
}
|
|
11885
12496
|
migrateLegacyOptimaLayout(toolWorktree);
|
|
11886
12497
|
const cfgDir = optimaConfigDir(toolWorktree);
|
|
11887
|
-
if (!
|
|
11888
|
-
const
|
|
11889
|
-
const
|
|
11890
|
-
|
|
11891
|
-
if (!fs2.existsSync(optimaTmplPath) || !fs2.existsSync(codemapTmplPath)) {
|
|
12498
|
+
if (!fs5.existsSync(cfgDir)) fs5.mkdirSync(cfgDir, { recursive: true });
|
|
12499
|
+
const optimaTmplPath = path5.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12500
|
+
const codemapTmplPath = path5.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12501
|
+
if (!fs5.existsSync(optimaTmplPath) || !fs5.existsSync(codemapTmplPath)) {
|
|
11892
12502
|
return "Error: Initialization templates not found in plugin.";
|
|
11893
12503
|
}
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
let agentsSection = "";
|
|
11897
|
-
for (const id of agentIds) {
|
|
11898
|
-
const enabled = isAgentEnabledForTeamMode(id, requestedTeamMode) ? "true" : "false";
|
|
11899
|
-
agentsSection += ` ${id}:
|
|
11900
|
-
enabled: ${enabled}
|
|
11901
|
-
`;
|
|
11902
|
-
}
|
|
11903
|
-
optimaConfig = optimaConfig.replace(/^agents:/m, "agents:\n" + agentsSection);
|
|
11904
|
-
let codemapConfig = fs2.readFileSync(codemapTmplPath, "utf8");
|
|
11905
|
-
codemapConfig = codemapConfig.replace("{{projectName}}", path2.basename(toolWorktree));
|
|
11906
|
-
const cfgFilePath = repoConfigPath(toolWorktree);
|
|
11907
|
-
const rootCodemapPath = optimaCodemapPath(toolWorktree);
|
|
11908
|
-
if (!fs2.existsSync(cfgFilePath)) {
|
|
11909
|
-
fs2.writeFileSync(cfgFilePath, optimaConfig, "utf8");
|
|
11910
|
-
}
|
|
11911
|
-
if (!fs2.existsSync(rootCodemapPath)) {
|
|
11912
|
-
fs2.writeFileSync(rootCodemapPath, codemapConfig, "utf8");
|
|
11913
|
-
}
|
|
12504
|
+
scaffoldOptimaConfig(toolWorktree, requestedTeamMode);
|
|
12505
|
+
scaffoldOptimaRootCodemap(toolWorktree);
|
|
11914
12506
|
scaffoldOptimaReadmes(toolWorktree);
|
|
11915
|
-
|
|
11916
|
-
const scrsDir = optimaScrsDir(toolWorktree);
|
|
11917
|
-
if (!fs2.existsSync(tasksDir)) fs2.mkdirSync(tasksDir, { recursive: true });
|
|
11918
|
-
if (!fs2.existsSync(scrsDir)) fs2.mkdirSync(scrsDir, { recursive: true });
|
|
11919
|
-
const currentPath = path2.join(tasksDir, "current.md");
|
|
11920
|
-
const donePath = path2.join(tasksDir, "done.md");
|
|
11921
|
-
const scrsCurrentPath = path2.join(scrsDir, "current.md");
|
|
11922
|
-
const scrsDonePath = path2.join(scrsDir, "done.md");
|
|
11923
|
-
if (!fs2.existsSync(currentPath)) {
|
|
11924
|
-
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");
|
|
11925
|
-
}
|
|
11926
|
-
if (!fs2.existsSync(donePath)) {
|
|
11927
|
-
fs2.writeFileSync(donePath, "# Completed Tasks (Registry)\n\n| Date | Task ID | SCR ID | Commit | Summary |\n| :--- | :--- | :--- | :--- | :--- |\n", "utf8");
|
|
11928
|
-
}
|
|
11929
|
-
if (!fs2.existsSync(scrsCurrentPath)) {
|
|
11930
|
-
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");
|
|
11931
|
-
}
|
|
11932
|
-
if (!fs2.existsSync(scrsDonePath)) {
|
|
11933
|
-
fs2.writeFileSync(scrsDonePath, "# Implemented Spec Change Requests\n\n| Date | SCR ID | Title | Related Feature | Task ID |\n| :--- | :--- | :--- | :--- | :--- |\n", "utf8");
|
|
11934
|
-
}
|
|
12507
|
+
ensureOptimaRegistries(toolWorktree);
|
|
11935
12508
|
const gitignoreResult = ensureOptimaGitignoreRules(toolWorktree);
|
|
11936
12509
|
const gitignoreNote = gitignoreResult.added.length > 0 ? ` Added ${gitignoreResult.added.length} Optima local/private .gitignore rule(s).` : "";
|
|
11937
12510
|
const initSummary = `Optima initialized in '${requestedTeamMode}' team mode: .optima/.config/optima.yaml, repo policy/agent folders, registries, and .optima/codemap.yml created.${gitignoreNote}`;
|
|
@@ -12046,14 +12619,14 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
12046
12619
|
async execute(args, context) {
|
|
12047
12620
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12048
12621
|
if (!safe.ok) return safe.message;
|
|
12049
|
-
const summaryPath =
|
|
12050
|
-
if (!
|
|
12051
|
-
const taskPath = args.task_path ?
|
|
12622
|
+
const summaryPath = path5.resolve(safe.worktree, args.summary_path || "");
|
|
12623
|
+
if (!fs5.existsSync(summaryPath)) return `FAIL: summary_path not found: ${summaryPath}`;
|
|
12624
|
+
const taskPath = args.task_path ? path5.resolve(safe.worktree, args.task_path) : "";
|
|
12052
12625
|
const payload = buildClickUpSummaryPayload({
|
|
12053
|
-
summaryMarkdown:
|
|
12054
|
-
summaryPath:
|
|
12055
|
-
taskMarkdown: taskPath &&
|
|
12056
|
-
taskPath: taskPath ?
|
|
12626
|
+
summaryMarkdown: fs5.readFileSync(summaryPath, "utf8"),
|
|
12627
|
+
summaryPath: path5.relative(safe.worktree, summaryPath),
|
|
12628
|
+
taskMarkdown: taskPath && fs5.existsSync(taskPath) ? fs5.readFileSync(taskPath, "utf8") : "",
|
|
12629
|
+
taskPath: taskPath ? path5.relative(safe.worktree, taskPath) : "",
|
|
12057
12630
|
branch: args.branch,
|
|
12058
12631
|
worktree: args.worktree,
|
|
12059
12632
|
pr: args.pr
|
|
@@ -12132,12 +12705,12 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
12132
12705
|
async execute(args, context) {
|
|
12133
12706
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12134
12707
|
if (!safe.ok) return safe.message;
|
|
12135
|
-
const markdownPath =
|
|
12136
|
-
if (!
|
|
12708
|
+
const markdownPath = path5.resolve(safe.worktree, args.markdown_path || "");
|
|
12709
|
+
if (!fs5.existsSync(markdownPath)) return `FAIL: markdown_path not found: ${markdownPath}`;
|
|
12137
12710
|
const payload = buildClickUpCreateSubtasksPayload({
|
|
12138
12711
|
parentTaskId: args.parent_task_id,
|
|
12139
|
-
markdown:
|
|
12140
|
-
sourcePath:
|
|
12712
|
+
markdown: fs5.readFileSync(markdownPath, "utf8"),
|
|
12713
|
+
sourcePath: path5.relative(safe.worktree, markdownPath),
|
|
12141
12714
|
parentBranch: args.parent_branch,
|
|
12142
12715
|
parentTaskType: args.parent_task_type || "Tarea",
|
|
12143
12716
|
apply: String(args.apply || "").toLowerCase() === "true"
|
|
@@ -12169,6 +12742,21 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
12169
12742
|
return formatValidationResult(res);
|
|
12170
12743
|
}
|
|
12171
12744
|
}),
|
|
12745
|
+
optima_repair: tool({
|
|
12746
|
+
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.",
|
|
12747
|
+
args: {
|
|
12748
|
+
apply: tool.schema.boolean().optional().describe("Set true to apply deterministic Optima metadata repairs. Defaults to false dry-run."),
|
|
12749
|
+
mode: tool.schema.string().optional().describe("Optional mode: 'dry-run' or 'apply'. Dry-run is the default."),
|
|
12750
|
+
team_mode: tool.schema.string().optional().describe("Team mode for a newly created config when missing: mini or full. Defaults to full.")
|
|
12751
|
+
},
|
|
12752
|
+
async execute(args, context) {
|
|
12753
|
+
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12754
|
+
if (!safe.ok) return safe.message;
|
|
12755
|
+
const plan = planOptimaRepair(safe.worktree, args, optimaRepairDependencies());
|
|
12756
|
+
const validationResult = plan.mode === "apply" ? await optima_validate_logic(safe.worktree) : null;
|
|
12757
|
+
return formatRepairResult(plan, validationResult, formatValidationResult);
|
|
12758
|
+
}
|
|
12759
|
+
}),
|
|
12172
12760
|
optima_start_discussion: tool({
|
|
12173
12761
|
description: "Start an automatic discussion transcript for this session",
|
|
12174
12762
|
args: {
|
|
@@ -12321,7 +12909,7 @@ Backfilled messages: ${backfilled}`;
|
|
|
12321
12909
|
if (!existing) {
|
|
12322
12910
|
return "FAIL: No active discussion exists for this session.";
|
|
12323
12911
|
}
|
|
12324
|
-
const discussionPath =
|
|
12912
|
+
const discussionPath = path5.join(toolWorktree, existing.transcriptPath);
|
|
12325
12913
|
setDiscussionStatus(discussionPath, "summarizing");
|
|
12326
12914
|
existing.status = "summarizing";
|
|
12327
12915
|
saveDiscussionRegistry(toolWorktree, toolRegistry);
|
|
@@ -12373,7 +12961,7 @@ Reason: ${err.message}`;
|
|
|
12373
12961
|
try {
|
|
12374
12962
|
const sessionResult = await client.session.create({
|
|
12375
12963
|
query: { directory: workflowDirectory },
|
|
12376
|
-
body: { title: `Workflow Run: ${
|
|
12964
|
+
body: { title: `Workflow Run: ${path5.basename(workflowTaskPath)}` }
|
|
12377
12965
|
});
|
|
12378
12966
|
const sessionId = sessionResult.data.id;
|
|
12379
12967
|
activeWorkflows.set(sessionId, { pmaSessionId, taskPath: workflowTaskPath, track: workflowTrack, directory: workflowDirectory });
|
|
@@ -12502,7 +13090,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
12502
13090
|
}
|
|
12503
13091
|
};
|
|
12504
13092
|
}
|
|
12505
|
-
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, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, 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 };
|
|
13093
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12506
13094
|
export {
|
|
12507
13095
|
OptimaPlugin as default
|
|
12508
13096
|
};
|