@defend-tech/opencode-optima 0.1.55 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Agents_Common.md +2 -0
- package/Agents_Common.prompt.md +2 -0
- package/README.md +10 -0
- package/assets/agents/business_analyst.md +10 -5
- package/assets/agents/developer.md +9 -2
- package/assets/agents/ops_product_manager.md +7 -7
- package/assets/agents/product_manager.md +12 -4
- package/assets/agents/qa_engineer.md +8 -0
- package/assets/agents/tech_lead.md +8 -0
- package/assets/agents/technical_architect.md +8 -1
- package/assets/agents/ui_ux_designer.md +8 -0
- package/assets/agents/workflow_product_manager.md +20 -20
- package/assets/agents/workflow_runner.md +9 -1
- package/dist/index.js +1174 -622
- package/dist/sanitize_cli.js +1207 -655
- 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;
|
|
@@ -9067,7 +9623,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
9067
9623
|
};
|
|
9068
9624
|
const errors = [];
|
|
9069
9625
|
if (!config.enabled) errors.push("clickup.enabled must be true");
|
|
9070
|
-
if (!config.basePath || !
|
|
9626
|
+
if (!config.basePath || !path5.isAbsolute(config.basePath)) errors.push("clickup.base_path must be an absolute path");
|
|
9071
9627
|
if (!config.teamId) errors.push("clickup.team_id is required");
|
|
9072
9628
|
if (!config.apiToken) errors.push("clickup.api_token is required");
|
|
9073
9629
|
if (!config.webhook.publicUrl) errors.push("clickup.webhook.public_url is required");
|
|
@@ -9109,21 +9665,21 @@ function sanitizeClickUpWebhookState(state = {}, config = null) {
|
|
|
9109
9665
|
}
|
|
9110
9666
|
function readClickUpWebhookState(worktree, config = null) {
|
|
9111
9667
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9112
|
-
if (!
|
|
9668
|
+
if (!fs5.existsSync(statePath)) return sanitizeClickUpWebhookState({}, config);
|
|
9113
9669
|
try {
|
|
9114
|
-
return sanitizeClickUpWebhookState(JSON.parse(
|
|
9670
|
+
return sanitizeClickUpWebhookState(JSON.parse(fs5.readFileSync(statePath, "utf8")), config);
|
|
9115
9671
|
} catch {
|
|
9116
9672
|
return sanitizeClickUpWebhookState({}, config);
|
|
9117
9673
|
}
|
|
9118
9674
|
}
|
|
9119
9675
|
function writeClickUpWebhookState(worktree, state, config = null) {
|
|
9120
9676
|
const statePath = clickUpWebhookStatePath(worktree);
|
|
9121
|
-
|
|
9677
|
+
fs5.mkdirSync(path5.dirname(statePath), { recursive: true, mode: 448 });
|
|
9122
9678
|
const next = sanitizeClickUpWebhookState(state, config);
|
|
9123
|
-
|
|
9679
|
+
fs5.writeFileSync(statePath, `${JSON.stringify(next, null, 2)}
|
|
9124
9680
|
`, { encoding: "utf8", mode: 384 });
|
|
9125
9681
|
try {
|
|
9126
|
-
|
|
9682
|
+
fs5.chmodSync(statePath, 384);
|
|
9127
9683
|
} catch {
|
|
9128
9684
|
}
|
|
9129
9685
|
return next;
|
|
@@ -9139,7 +9695,7 @@ function isClickUpWebhookStateActive(state, config) {
|
|
|
9139
9695
|
function expandHomePath(value = "") {
|
|
9140
9696
|
const input = String(value || "").trim();
|
|
9141
9697
|
if (input === "~") return os.homedir();
|
|
9142
|
-
if (input.startsWith("~/")) return
|
|
9698
|
+
if (input.startsWith("~/")) return path5.join(os.homedir(), input.slice(2));
|
|
9143
9699
|
return input;
|
|
9144
9700
|
}
|
|
9145
9701
|
function resolveSecretReference(value = "") {
|
|
@@ -9149,7 +9705,7 @@ function resolveSecretReference(value = "") {
|
|
|
9149
9705
|
const fileMatch = raw.match(/^\{file:(.+)\}$/);
|
|
9150
9706
|
if (fileMatch) {
|
|
9151
9707
|
try {
|
|
9152
|
-
return
|
|
9708
|
+
return fs5.readFileSync(expandHomePath(fileMatch[1]), "utf8").trim();
|
|
9153
9709
|
} catch {
|
|
9154
9710
|
return "";
|
|
9155
9711
|
}
|
|
@@ -9405,9 +9961,9 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
|
|
|
9405
9961
|
}
|
|
9406
9962
|
function readClickUpCommentLedger(ledgerPath) {
|
|
9407
9963
|
const processed = /* @__PURE__ */ new Set();
|
|
9408
|
-
if (!ledgerPath || !
|
|
9964
|
+
if (!ledgerPath || !fs5.existsSync(ledgerPath)) return processed;
|
|
9409
9965
|
try {
|
|
9410
|
-
const raw =
|
|
9966
|
+
const raw = fs5.readFileSync(ledgerPath, "utf8");
|
|
9411
9967
|
for (const [index, line] of raw.split(/\r?\n/).entries()) {
|
|
9412
9968
|
if (!line.trim()) continue;
|
|
9413
9969
|
try {
|
|
@@ -9424,11 +9980,11 @@ function readClickUpCommentLedger(ledgerPath) {
|
|
|
9424
9980
|
}
|
|
9425
9981
|
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
9426
9982
|
if (!ledgerPath || !entry.key) return;
|
|
9427
|
-
|
|
9428
|
-
|
|
9983
|
+
fs5.mkdirSync(path5.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
9984
|
+
fs5.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
9429
9985
|
`, { encoding: "utf8", mode: 384 });
|
|
9430
9986
|
try {
|
|
9431
|
-
|
|
9987
|
+
fs5.chmodSync(ledgerPath, 384);
|
|
9432
9988
|
} catch {
|
|
9433
9989
|
}
|
|
9434
9990
|
}
|
|
@@ -10144,9 +10700,9 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
|
|
|
10144
10700
|
}
|
|
10145
10701
|
function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
10146
10702
|
const logPath = clickUpWebhookLogPath(worktree);
|
|
10147
|
-
|
|
10703
|
+
fs5.mkdirSync(path5.dirname(logPath), { recursive: true });
|
|
10148
10704
|
const safeEntry = { ...entry, at: entry.at || (/* @__PURE__ */ new Date()).toISOString() };
|
|
10149
|
-
|
|
10705
|
+
fs5.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
10150
10706
|
`, "utf8");
|
|
10151
10707
|
}
|
|
10152
10708
|
function clickUpWebhookLifecycleLog(worktree, entry) {
|
|
@@ -10175,7 +10731,7 @@ function closeClickUpWebhookServer(server) {
|
|
|
10175
10731
|
});
|
|
10176
10732
|
}
|
|
10177
10733
|
function managedClickUpWebhookKey({ worktree, state, config } = {}) {
|
|
10178
|
-
return [
|
|
10734
|
+
return [path5.resolve(worktree || process.cwd()), state?.webhookId || "", config?.webhook?.publicUrl || ""].join("|");
|
|
10179
10735
|
}
|
|
10180
10736
|
async function deleteClickUpWebhookBestEffort({ webhookId, clickupClient, worktree, reason = "cleanup" } = {}) {
|
|
10181
10737
|
const id = String(webhookId || "").trim();
|
|
@@ -10274,13 +10830,13 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
10274
10830
|
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
10275
10831
|
if (level === "error" && !failed) return;
|
|
10276
10832
|
const logDir = clickUpWebhookAuditLogDir();
|
|
10277
|
-
|
|
10833
|
+
fs5.mkdirSync(logDir, { recursive: true });
|
|
10278
10834
|
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
10279
10835
|
let requestFile;
|
|
10280
10836
|
if (level === "verbose") {
|
|
10281
10837
|
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
10282
|
-
|
|
10283
|
-
requestFile =
|
|
10838
|
+
fs5.mkdirSync(path5.dirname(absoluteRequestFile), { recursive: true });
|
|
10839
|
+
requestFile = path5.relative(logDir, absoluteRequestFile).split(path5.sep).join("/");
|
|
10284
10840
|
const parsedBody = payload || (() => {
|
|
10285
10841
|
try {
|
|
10286
10842
|
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
@@ -10296,11 +10852,11 @@ function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "",
|
|
|
10296
10852
|
headers,
|
|
10297
10853
|
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
10298
10854
|
}, secretValues);
|
|
10299
|
-
|
|
10855
|
+
fs5.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
10300
10856
|
`, "utf8");
|
|
10301
10857
|
}
|
|
10302
10858
|
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
10303
|
-
|
|
10859
|
+
fs5.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
10304
10860
|
`, "utf8");
|
|
10305
10861
|
} catch {
|
|
10306
10862
|
}
|
|
@@ -10656,7 +11212,7 @@ function clickUpListenerFingerprint({ config, state, worktree, clickupClient, op
|
|
|
10656
11212
|
return JSON.stringify({
|
|
10657
11213
|
publicUrl: config?.webhook?.publicUrl || "",
|
|
10658
11214
|
path: clickUpWebhookExpectedPath(config),
|
|
10659
|
-
worktree:
|
|
11215
|
+
worktree: path5.resolve(worktree || process.cwd()),
|
|
10660
11216
|
webhookId: state?.webhookId || "",
|
|
10661
11217
|
secret: state?.secret || "",
|
|
10662
11218
|
events: config?.webhook?.events || [],
|
|
@@ -10753,17 +11309,17 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, o
|
|
|
10753
11309
|
return result;
|
|
10754
11310
|
}
|
|
10755
11311
|
function legacyVariantPath(destinationPath) {
|
|
10756
|
-
const parsed =
|
|
11312
|
+
const parsed = path5.parse(destinationPath);
|
|
10757
11313
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
10758
|
-
if (parsed.ext) return
|
|
10759
|
-
return
|
|
11314
|
+
if (parsed.ext) return path5.join(parsed.dir, `${parsed.name}.legacy-${stamp}${parsed.ext}`);
|
|
11315
|
+
return path5.join(parsed.dir, `${parsed.base}.legacy-${stamp}`);
|
|
10760
11316
|
}
|
|
10761
11317
|
function normalizeWorkflowTaskPath(taskPath) {
|
|
10762
11318
|
if (typeof taskPath !== "string") return { ok: false, message: "Error: task_path is required." };
|
|
10763
11319
|
const trimmed = taskPath.trim();
|
|
10764
11320
|
if (!trimmed) return { ok: false, message: "Error: task_path is required." };
|
|
10765
11321
|
const normalized = trimmed.replace(/\\/g, "/");
|
|
10766
|
-
if (
|
|
11322
|
+
if (path5.isAbsolute(trimmed)) return { ok: true, taskPath: trimmed };
|
|
10767
11323
|
if (normalized === "tasks" || normalized.startsWith("tasks/")) {
|
|
10768
11324
|
return {
|
|
10769
11325
|
ok: false,
|
|
@@ -10779,76 +11335,80 @@ function normalizeWorkflowTaskPath(taskPath) {
|
|
|
10779
11335
|
return { ok: true, taskPath: trimmed };
|
|
10780
11336
|
}
|
|
10781
11337
|
function mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState = null) {
|
|
11338
|
+
const sourceWasTracked = isGitTracked(gitState, sourcePath);
|
|
10782
11339
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
10783
|
-
if (!
|
|
10784
|
-
|
|
10785
|
-
|
|
11340
|
+
if (!fs5.existsSync(destinationPath)) {
|
|
11341
|
+
fs5.mkdirSync(path5.dirname(destinationPath), { recursive: true });
|
|
11342
|
+
fs5.renameSync(sourcePath, destinationPath);
|
|
11343
|
+
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10786
11344
|
return;
|
|
10787
11345
|
}
|
|
10788
|
-
const ext =
|
|
11346
|
+
const ext = path5.extname(destinationPath).toLowerCase();
|
|
10789
11347
|
if ([".yaml", ".yml", ".json"].includes(ext)) {
|
|
10790
|
-
const sourceRaw =
|
|
10791
|
-
const destRaw =
|
|
10792
|
-
const parser = ext === ".json" ? JSON :
|
|
11348
|
+
const sourceRaw = fs5.readFileSync(sourcePath, "utf8");
|
|
11349
|
+
const destRaw = fs5.readFileSync(destinationPath, "utf8");
|
|
11350
|
+
const parser = ext === ".json" ? JSON : import_yaml3.default;
|
|
10793
11351
|
const sourceValue = parser.parse(sourceRaw) || {};
|
|
10794
11352
|
const destValue = parser.parse(destRaw) || {};
|
|
10795
11353
|
const merged = mergeStructuredValues(sourceValue, destValue);
|
|
10796
11354
|
const serialized = ext === ".json" ? `${JSON.stringify(merged, null, 2)}
|
|
10797
|
-
` :
|
|
10798
|
-
|
|
10799
|
-
|
|
11355
|
+
` : import_yaml3.default.stringify(merged);
|
|
11356
|
+
fs5.writeFileSync(destinationPath, serialized, "utf8");
|
|
11357
|
+
fs5.unlinkSync(sourcePath);
|
|
10800
11358
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10801
11359
|
return;
|
|
10802
11360
|
}
|
|
10803
11361
|
if (ext === ".md") {
|
|
10804
|
-
const sourceRaw =
|
|
10805
|
-
const destRaw =
|
|
11362
|
+
const sourceRaw = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
11363
|
+
const destRaw = fs5.readFileSync(destinationPath, "utf8").trimEnd();
|
|
10806
11364
|
if (sourceRaw && !destRaw.includes(sourceRaw)) {
|
|
10807
11365
|
const marker = `## Legacy Content From ${relativeSource}`;
|
|
10808
|
-
|
|
11366
|
+
fs5.writeFileSync(destinationPath, `${destRaw}
|
|
10809
11367
|
|
|
10810
11368
|
${marker}
|
|
10811
11369
|
|
|
10812
11370
|
${sourceRaw}
|
|
10813
11371
|
`, "utf8");
|
|
10814
11372
|
}
|
|
10815
|
-
|
|
11373
|
+
fs5.unlinkSync(sourcePath);
|
|
10816
11374
|
stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10817
11375
|
return;
|
|
10818
11376
|
}
|
|
10819
11377
|
const preservedPath = legacyVariantPath(destinationPath);
|
|
10820
|
-
|
|
11378
|
+
fs5.renameSync(sourcePath, preservedPath);
|
|
10821
11379
|
stageGitAwareMerge(sourcePath, preservedPath, gitState);
|
|
10822
11380
|
}
|
|
10823
|
-
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource =
|
|
10824
|
-
if (!
|
|
10825
|
-
const stat =
|
|
11381
|
+
function mergePathIntoDestination(sourcePath, destinationPath, relativeSource = path5.basename(sourcePath), gitState = null) {
|
|
11382
|
+
if (!fs5.existsSync(sourcePath)) return;
|
|
11383
|
+
const stat = fs5.statSync(sourcePath);
|
|
10826
11384
|
if (stat.isDirectory()) {
|
|
11385
|
+
const sourceWasTracked = isGitTracked(gitState, sourcePath) || hasGitTrackedChildren(gitState, sourcePath);
|
|
10827
11386
|
if (gitAwareMoveIfTracked(sourcePath, destinationPath, gitState)) return;
|
|
10828
|
-
if (!
|
|
10829
|
-
|
|
10830
|
-
|
|
11387
|
+
if (!fs5.existsSync(destinationPath)) {
|
|
11388
|
+
fs5.mkdirSync(path5.dirname(destinationPath), { recursive: true });
|
|
11389
|
+
fs5.renameSync(sourcePath, destinationPath);
|
|
11390
|
+
if (sourceWasTracked) stageGitAwareMerge(sourcePath, destinationPath, gitState);
|
|
10831
11391
|
return;
|
|
10832
11392
|
}
|
|
10833
|
-
|
|
10834
|
-
for (const entry of
|
|
11393
|
+
fs5.mkdirSync(destinationPath, { recursive: true });
|
|
11394
|
+
for (const entry of fs5.readdirSync(sourcePath)) {
|
|
10835
11395
|
mergePathIntoDestination(
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
11396
|
+
path5.join(sourcePath, entry),
|
|
11397
|
+
path5.join(destinationPath, entry),
|
|
11398
|
+
path5.join(relativeSource, entry),
|
|
10839
11399
|
gitState
|
|
10840
11400
|
);
|
|
10841
11401
|
}
|
|
10842
|
-
|
|
11402
|
+
fs5.rmSync(sourcePath, { recursive: true, force: true });
|
|
10843
11403
|
return;
|
|
10844
11404
|
}
|
|
10845
11405
|
mergeFileIntoDestination(sourcePath, destinationPath, relativeSource, gitState);
|
|
10846
11406
|
}
|
|
10847
11407
|
function isOptimaPluginPackageWorktree(worktree) {
|
|
10848
|
-
const packageJsonPath =
|
|
10849
|
-
if (!
|
|
11408
|
+
const packageJsonPath = path5.join(worktree, "package.json");
|
|
11409
|
+
if (!fs5.existsSync(packageJsonPath)) return false;
|
|
10850
11410
|
try {
|
|
10851
|
-
const pkg = JSON.parse(
|
|
11411
|
+
const pkg = JSON.parse(fs5.readFileSync(packageJsonPath, "utf8"));
|
|
10852
11412
|
return pkg?.name === "@defend-tech/opencode-optima";
|
|
10853
11413
|
} catch {
|
|
10854
11414
|
return false;
|
|
@@ -10857,59 +11417,59 @@ function isOptimaPluginPackageWorktree(worktree) {
|
|
|
10857
11417
|
function migrateLegacyOptimaLayout(worktree) {
|
|
10858
11418
|
const gitState = gitMigrationState(worktree);
|
|
10859
11419
|
const migrations = [
|
|
10860
|
-
[
|
|
10861
|
-
[
|
|
10862
|
-
[
|
|
10863
|
-
[
|
|
10864
|
-
[
|
|
10865
|
-
[
|
|
11420
|
+
[path5.join(legacyOrbitaDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".orbita", "orbita.yaml")],
|
|
11421
|
+
[path5.join(legacyOrbitaDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".orbita", "staticeng.yaml")],
|
|
11422
|
+
[path5.join(legacyOrbitaDir(worktree), ".config"), optimaLocalConfigDir(worktree), path5.join(".orbita", ".config")],
|
|
11423
|
+
[path5.join(legacyOrbitaDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".orbita", "config")],
|
|
11424
|
+
[path5.join(legacyOrbitaDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".orbita", "runtime")],
|
|
11425
|
+
[path5.join(legacyOrbitaDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".orbita", "generated")],
|
|
10866
11426
|
[legacyOrbitaDir(worktree), optimaDir(worktree), ".orbita"],
|
|
10867
|
-
[
|
|
10868
|
-
[
|
|
10869
|
-
[
|
|
10870
|
-
[
|
|
10871
|
-
[
|
|
10872
|
-
[
|
|
11427
|
+
[path5.join(legacyStaticEngDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".staticeng", "staticeng.yaml")],
|
|
11428
|
+
[path5.join(legacyStaticEngDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".staticeng", "orbita.yaml")],
|
|
11429
|
+
[path5.join(legacyStaticEngDir(worktree), ".config"), optimaLocalConfigDir(worktree), path5.join(".staticeng", ".config")],
|
|
11430
|
+
[path5.join(legacyStaticEngDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".staticeng", "config")],
|
|
11431
|
+
[path5.join(legacyStaticEngDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".staticeng", "runtime")],
|
|
11432
|
+
[path5.join(legacyStaticEngDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".staticeng", "generated")],
|
|
10873
11433
|
[legacyStaticEngDir(worktree), optimaDir(worktree), ".staticeng"],
|
|
10874
|
-
[
|
|
10875
|
-
[
|
|
10876
|
-
[
|
|
10877
|
-
[
|
|
10878
|
-
[
|
|
10879
|
-
[
|
|
10880
|
-
[
|
|
10881
|
-
[
|
|
10882
|
-
[
|
|
10883
|
-
[
|
|
10884
|
-
[
|
|
10885
|
-
[
|
|
11434
|
+
[path5.join(legacyNomadworkDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "nomadworks.yaml")],
|
|
11435
|
+
[path5.join(legacyNomadworksDir(worktree), "nomadworks.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "nomadworks.yaml")],
|
|
11436
|
+
[path5.join(legacyNomadworkDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "staticeng.yaml")],
|
|
11437
|
+
[path5.join(legacyNomadworksDir(worktree), "staticeng.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "staticeng.yaml")],
|
|
11438
|
+
[path5.join(legacyNomadworkDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".nomadwork", "orbita.yaml")],
|
|
11439
|
+
[path5.join(legacyNomadworksDir(worktree), "orbita.yaml"), repoConfigPath(worktree), path5.join(".nomadworks", "orbita.yaml")],
|
|
11440
|
+
[path5.join(legacyNomadworkDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".nomadwork", "runtime")],
|
|
11441
|
+
[path5.join(legacyNomadworksDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".nomadworks", "runtime")],
|
|
11442
|
+
[path5.join(legacyNomadworkDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".nomadwork", "generated")],
|
|
11443
|
+
[path5.join(legacyNomadworksDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".nomadworks", "generated")],
|
|
11444
|
+
[path5.join(legacyNomadworkDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".nomadwork", "config")],
|
|
11445
|
+
[path5.join(legacyNomadworksDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".nomadworks", "config")],
|
|
10886
11446
|
[legacyNomadworkDir(worktree), optimaDir(worktree), ".nomadwork"],
|
|
10887
11447
|
[legacyNomadworksDir(worktree), optimaDir(worktree), ".nomadworks"],
|
|
10888
|
-
[
|
|
10889
|
-
[
|
|
10890
|
-
[
|
|
10891
|
-
[
|
|
10892
|
-
[
|
|
11448
|
+
[path5.join(worktree, "tasks"), optimaTasksDir(worktree), "tasks"],
|
|
11449
|
+
[path5.join(worktree, "evidences"), optimaEvidencesDir(worktree), "evidences"],
|
|
11450
|
+
[path5.join(worktree, "docs", "scrs"), optimaScrsDir(worktree), path5.join("docs", "scrs")],
|
|
11451
|
+
[path5.join(worktree, "codemap.yml"), optimaCodemapPath(worktree), "codemap.yml"],
|
|
11452
|
+
[path5.join(worktree, "codemap.yaml"), optimaCodemapPath(worktree), "codemap.yaml"]
|
|
10893
11453
|
];
|
|
10894
11454
|
if (!isOptimaPluginPackageWorktree(worktree)) {
|
|
10895
|
-
migrations.push([
|
|
11455
|
+
migrations.push([path5.join(worktree, "policies"), repoPoliciesDir(worktree), "policies"]);
|
|
10896
11456
|
}
|
|
10897
11457
|
for (const [source, destination, relativeSource] of migrations) {
|
|
10898
|
-
if (
|
|
11458
|
+
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
10899
11459
|
}
|
|
10900
11460
|
for (const [source, destination, relativeSource] of [
|
|
10901
|
-
[
|
|
10902
|
-
[
|
|
10903
|
-
[
|
|
10904
|
-
[
|
|
11461
|
+
[path5.join(optimaDir(worktree), "optima.yaml"), repoConfigPath(worktree), path5.join(".optima", "optima.yaml")],
|
|
11462
|
+
[path5.join(optimaDir(worktree), "config"), optimaLocalConfigDir(worktree), path5.join(".optima", "config")],
|
|
11463
|
+
[path5.join(optimaDir(worktree), "runtime"), path5.join(optimaLocalConfigDir(worktree), "runtime"), path5.join(".optima", "runtime")],
|
|
11464
|
+
[path5.join(optimaDir(worktree), "generated"), path5.join(optimaLocalConfigDir(worktree), "generated"), path5.join(".optima", "generated")]
|
|
10905
11465
|
]) {
|
|
10906
|
-
if (
|
|
11466
|
+
if (fs5.existsSync(source)) mergePathIntoDestination(source, destination, relativeSource, gitState);
|
|
10907
11467
|
}
|
|
10908
11468
|
}
|
|
10909
11469
|
function listMarkdownFiles(dirPath) {
|
|
10910
|
-
if (!
|
|
11470
|
+
if (!fs5.existsSync(dirPath)) return [];
|
|
10911
11471
|
try {
|
|
10912
|
-
return
|
|
11472
|
+
return fs5.readdirSync(dirPath).filter((file) => file.endsWith(".md") && file.toLowerCase() !== "readme.md");
|
|
10913
11473
|
} catch (e) {
|
|
10914
11474
|
console.error(`[Optima] Failed to read markdown files from ${dirPath}:`, e);
|
|
10915
11475
|
return [];
|
|
@@ -10919,67 +11479,6 @@ function normalizePolicyExtraction(value) {
|
|
|
10919
11479
|
if (typeof value !== "string") return "none";
|
|
10920
11480
|
return value.trim().toLowerCase() === "all" ? "all" : "none";
|
|
10921
11481
|
}
|
|
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
11482
|
function parseFrontmatter(mdText) {
|
|
10984
11483
|
const lines = mdText.split(/\r?\n/);
|
|
10985
11484
|
if (lines[0]?.trim() !== "---") return { data: {}, body: mdText };
|
|
@@ -10994,7 +11493,7 @@ function parseFrontmatter(mdText) {
|
|
|
10994
11493
|
const fmText = lines.slice(1, end).join("\n");
|
|
10995
11494
|
const body = lines.slice(end + 1).join("\n");
|
|
10996
11495
|
try {
|
|
10997
|
-
return { data:
|
|
11496
|
+
return { data: import_yaml3.default.parse(fmText) || {}, body };
|
|
10998
11497
|
} catch {
|
|
10999
11498
|
return { data: {}, body };
|
|
11000
11499
|
}
|
|
@@ -11011,10 +11510,10 @@ function toModelString(provider, model) {
|
|
|
11011
11510
|
}
|
|
11012
11511
|
function readTaskMetadata(taskPath, worktree) {
|
|
11013
11512
|
if (!taskPath) return {};
|
|
11014
|
-
const absoluteTaskPath =
|
|
11015
|
-
if (!
|
|
11513
|
+
const absoluteTaskPath = path5.isAbsolute(taskPath) ? taskPath : path5.join(worktree, taskPath);
|
|
11514
|
+
if (!fs5.existsSync(absoluteTaskPath)) return {};
|
|
11016
11515
|
try {
|
|
11017
|
-
const raw =
|
|
11516
|
+
const raw = fs5.readFileSync(absoluteTaskPath, "utf8");
|
|
11018
11517
|
const { data } = parseFrontmatter(raw);
|
|
11019
11518
|
return {
|
|
11020
11519
|
complexity: typeof data.complexity === "string" ? data.complexity.trim().toLowerCase() : void 0,
|
|
@@ -11039,11 +11538,11 @@ function slugifyTitle(input) {
|
|
|
11039
11538
|
}
|
|
11040
11539
|
function loadDiscussionRegistry(worktree) {
|
|
11041
11540
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
11042
|
-
if (!
|
|
11541
|
+
if (!fs5.existsSync(registryPath)) {
|
|
11043
11542
|
return { version: 1, active: {} };
|
|
11044
11543
|
}
|
|
11045
11544
|
try {
|
|
11046
|
-
const parsed = JSON.parse(
|
|
11545
|
+
const parsed = JSON.parse(fs5.readFileSync(registryPath, "utf8"));
|
|
11047
11546
|
const registry = {
|
|
11048
11547
|
version: 1,
|
|
11049
11548
|
active: Object.fromEntries(Object.entries(parsed.active || {}).map(([key, value]) => [key, normalizeLegacyDiscussionEntry(value)]))
|
|
@@ -11056,34 +11555,34 @@ function loadDiscussionRegistry(worktree) {
|
|
|
11056
11555
|
}
|
|
11057
11556
|
function saveDiscussionRegistry(worktree, registry) {
|
|
11058
11557
|
const registryPath = runtimeDiscussionRegistryPath(worktree);
|
|
11059
|
-
const runtimeDir =
|
|
11060
|
-
if (!
|
|
11061
|
-
|
|
11558
|
+
const runtimeDir = path5.dirname(registryPath);
|
|
11559
|
+
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
11560
|
+
fs5.writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf8");
|
|
11062
11561
|
}
|
|
11063
11562
|
function runtimeDiscussionsDir(worktree) {
|
|
11064
|
-
return
|
|
11563
|
+
return path5.join(optimaLocalConfigDir(worktree), "runtime", "discussions");
|
|
11065
11564
|
}
|
|
11066
11565
|
function archivedRuntimeDiscussionsDir(worktree) {
|
|
11067
|
-
return
|
|
11566
|
+
return path5.join(runtimeDiscussionsDir(worktree), "archive");
|
|
11068
11567
|
}
|
|
11069
11568
|
function finalDiscussionsDir(worktree) {
|
|
11070
|
-
return
|
|
11569
|
+
return path5.join(optimaTasksDir(worktree), "discussions");
|
|
11071
11570
|
}
|
|
11072
11571
|
function nextDiscussionIdentity(worktree, title) {
|
|
11073
11572
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
11074
11573
|
const runtimeDir = runtimeDiscussionsDir(worktree);
|
|
11075
|
-
if (!
|
|
11076
|
-
if (!
|
|
11574
|
+
if (!fs5.existsSync(discussionsDir)) fs5.mkdirSync(discussionsDir, { recursive: true });
|
|
11575
|
+
if (!fs5.existsSync(runtimeDir)) fs5.mkdirSync(runtimeDir, { recursive: true });
|
|
11077
11576
|
let sequence = 1;
|
|
11078
11577
|
while (true) {
|
|
11079
11578
|
const id = `DISCUSSION-${String(sequence).padStart(3, "0")}`;
|
|
11080
11579
|
const filename = `${id}-${slugifyTitle(title)}.md`;
|
|
11081
|
-
const summaryRelativePath =
|
|
11082
|
-
const summaryAbsolutePath =
|
|
11580
|
+
const summaryRelativePath = path5.join(".optima", "tasks", "discussions", filename);
|
|
11581
|
+
const summaryAbsolutePath = path5.join(worktree, summaryRelativePath);
|
|
11083
11582
|
const transcriptFilename = `${id}-transcript.md`;
|
|
11084
|
-
const transcriptRelativePath =
|
|
11085
|
-
const transcriptAbsolutePath =
|
|
11086
|
-
if (!
|
|
11583
|
+
const transcriptRelativePath = path5.join(".optima", ".config", "runtime", "discussions", transcriptFilename);
|
|
11584
|
+
const transcriptAbsolutePath = path5.join(worktree, transcriptRelativePath);
|
|
11585
|
+
if (!fs5.existsSync(summaryAbsolutePath) && !fs5.existsSync(transcriptAbsolutePath)) {
|
|
11087
11586
|
return {
|
|
11088
11587
|
id,
|
|
11089
11588
|
filename,
|
|
@@ -11099,37 +11598,37 @@ function nextDiscussionIdentity(worktree, title) {
|
|
|
11099
11598
|
}
|
|
11100
11599
|
function findDiscussionById(worktree, discussionID) {
|
|
11101
11600
|
const discussionsDir = finalDiscussionsDir(worktree);
|
|
11102
|
-
if (!
|
|
11103
|
-
const entries =
|
|
11601
|
+
if (!fs5.existsSync(discussionsDir)) return null;
|
|
11602
|
+
const entries = fs5.readdirSync(discussionsDir).filter((name) => name.startsWith(`${discussionID}-`) && name.endsWith(".md"));
|
|
11104
11603
|
if (entries.length === 0) return null;
|
|
11105
11604
|
const filename = entries.sort()[0];
|
|
11106
11605
|
const transcriptFilename = `${discussionID}-transcript.md`;
|
|
11107
11606
|
return {
|
|
11108
11607
|
id: discussionID,
|
|
11109
11608
|
filename,
|
|
11110
|
-
summaryRelativePath:
|
|
11111
|
-
summaryAbsolutePath:
|
|
11609
|
+
summaryRelativePath: path5.join(".optima", "tasks", "discussions", filename),
|
|
11610
|
+
summaryAbsolutePath: path5.join(discussionsDir, filename),
|
|
11112
11611
|
transcriptFilename,
|
|
11113
|
-
transcriptRelativePath:
|
|
11114
|
-
transcriptAbsolutePath:
|
|
11612
|
+
transcriptRelativePath: path5.join(".optima", ".config", "runtime", "discussions", transcriptFilename),
|
|
11613
|
+
transcriptAbsolutePath: path5.join(runtimeDiscussionsDir(worktree), transcriptFilename)
|
|
11115
11614
|
};
|
|
11116
11615
|
}
|
|
11117
11616
|
function parseDiscussionFile(filePath) {
|
|
11118
|
-
const raw =
|
|
11617
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
11119
11618
|
const { data, body } = parseFrontmatter(raw);
|
|
11120
11619
|
return { data, body: body.trimStart() };
|
|
11121
11620
|
}
|
|
11122
11621
|
function writeDiscussionFile(filePath, frontmatter, body) {
|
|
11123
11622
|
const serialized = `---
|
|
11124
|
-
${
|
|
11623
|
+
${import_yaml3.default.stringify(frontmatter).trim()}
|
|
11125
11624
|
---
|
|
11126
11625
|
|
|
11127
11626
|
${body.trimEnd()}
|
|
11128
11627
|
`;
|
|
11129
|
-
|
|
11628
|
+
fs5.writeFileSync(filePath, serialized, "utf8");
|
|
11130
11629
|
}
|
|
11131
11630
|
function setDiscussionStatus(filePath, status) {
|
|
11132
|
-
if (!
|
|
11631
|
+
if (!fs5.existsSync(filePath)) return;
|
|
11133
11632
|
const { data, body } = parseDiscussionFile(filePath);
|
|
11134
11633
|
writeDiscussionFile(filePath, { ...data, status }, body);
|
|
11135
11634
|
}
|
|
@@ -11194,18 +11693,18 @@ async function appendMessageIfNeeded(client, worktree, registry, sessionID, mess
|
|
|
11194
11693
|
});
|
|
11195
11694
|
const text = extractTextParts(response.data.parts || []);
|
|
11196
11695
|
if (!text) return;
|
|
11197
|
-
appendDiscussionMessage(
|
|
11696
|
+
appendDiscussionMessage(path5.join(worktree, discussion.transcriptPath), speaker, text, messageID);
|
|
11198
11697
|
discussion.appendedMessageIDs ??= [];
|
|
11199
11698
|
discussion.appendedMessageIDs.push(messageID);
|
|
11200
11699
|
saveDiscussionRegistry(worktree, registry);
|
|
11201
11700
|
}
|
|
11202
11701
|
async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
11203
|
-
const transcriptPath =
|
|
11204
|
-
const summaryPath =
|
|
11205
|
-
const summaryDir =
|
|
11206
|
-
if (!
|
|
11207
|
-
const hasExistingSummary =
|
|
11208
|
-
const priorMtimeMs = hasExistingSummary ?
|
|
11702
|
+
const transcriptPath = path5.join(worktree, discussion.transcriptPath);
|
|
11703
|
+
const summaryPath = path5.join(worktree, discussion.summaryPath);
|
|
11704
|
+
const summaryDir = path5.dirname(summaryPath);
|
|
11705
|
+
if (!fs5.existsSync(summaryDir)) fs5.mkdirSync(summaryDir, { recursive: true });
|
|
11706
|
+
const hasExistingSummary = fs5.existsSync(summaryPath);
|
|
11707
|
+
const priorMtimeMs = hasExistingSummary ? fs5.statSync(summaryPath).mtimeMs : null;
|
|
11209
11708
|
const summarizerSession = await client.session.create({
|
|
11210
11709
|
body: { title: `Discussion Summary: ${discussion.id}` }
|
|
11211
11710
|
});
|
|
@@ -11305,30 +11804,30 @@ async function summarizeDiscussionWithBA(client, worktree, discussion) {
|
|
|
11305
11804
|
return { confirmation, summaryPath, transcriptPath, hasExistingSummary, priorMtimeMs };
|
|
11306
11805
|
}
|
|
11307
11806
|
function archiveDiscussionTranscript(worktree, transcriptRelativePath) {
|
|
11308
|
-
const sourcePath =
|
|
11309
|
-
if (!
|
|
11807
|
+
const sourcePath = path5.join(worktree, transcriptRelativePath);
|
|
11808
|
+
if (!fs5.existsSync(sourcePath)) return null;
|
|
11310
11809
|
const archiveDir = archivedRuntimeDiscussionsDir(worktree);
|
|
11311
|
-
if (!
|
|
11312
|
-
const targetPath =
|
|
11313
|
-
|
|
11810
|
+
if (!fs5.existsSync(archiveDir)) fs5.mkdirSync(archiveDir, { recursive: true });
|
|
11811
|
+
const targetPath = path5.join(archiveDir, path5.basename(sourcePath));
|
|
11812
|
+
fs5.renameSync(sourcePath, targetPath);
|
|
11314
11813
|
return targetPath;
|
|
11315
11814
|
}
|
|
11316
11815
|
async function finalizeClosingDiscussion(client, worktree, registry, sessionID, discussion) {
|
|
11317
11816
|
const { confirmation, summaryPath, hasExistingSummary, priorMtimeMs } = await summarizeDiscussionWithBA(client, worktree, discussion);
|
|
11318
|
-
if (!
|
|
11817
|
+
if (!fs5.existsSync(summaryPath)) {
|
|
11319
11818
|
throw new Error(`Discussion summary was not written to ${discussion.summaryPath}`);
|
|
11320
11819
|
}
|
|
11321
11820
|
if (hasExistingSummary) {
|
|
11322
|
-
const currentMtimeMs =
|
|
11821
|
+
const currentMtimeMs = fs5.statSync(summaryPath).mtimeMs;
|
|
11323
11822
|
if (currentMtimeMs <= priorMtimeMs) {
|
|
11324
11823
|
throw new Error(`Discussion summary file was not updated at ${discussion.summaryPath}`);
|
|
11325
11824
|
}
|
|
11326
11825
|
}
|
|
11327
|
-
const summaryContent =
|
|
11826
|
+
const summaryContent = fs5.readFileSync(summaryPath, "utf8").trim();
|
|
11328
11827
|
if (!summaryContent) {
|
|
11329
11828
|
throw new Error(`Discussion summary file is empty at ${discussion.summaryPath}`);
|
|
11330
11829
|
}
|
|
11331
|
-
const transcriptPath =
|
|
11830
|
+
const transcriptPath = path5.join(worktree, discussion.transcriptPath);
|
|
11332
11831
|
setDiscussionStatus(transcriptPath, "closed");
|
|
11333
11832
|
const archivedTranscriptPath = archiveDiscussionTranscript(worktree, discussion.transcriptPath);
|
|
11334
11833
|
delete registry.active[sessionID];
|
|
@@ -11336,7 +11835,7 @@ async function finalizeClosingDiscussion(client, worktree, registry, sessionID,
|
|
|
11336
11835
|
return {
|
|
11337
11836
|
confirmation,
|
|
11338
11837
|
summaryPath: discussion.summaryPath,
|
|
11339
|
-
archivedTranscriptPath: archivedTranscriptPath ?
|
|
11838
|
+
archivedTranscriptPath: archivedTranscriptPath ? path5.relative(worktree, archivedTranscriptPath) : path5.join(".optima", ".config", "runtime", "discussions", "archive", path5.basename(discussion.transcriptPath))
|
|
11340
11839
|
};
|
|
11341
11840
|
}
|
|
11342
11841
|
function normalizeTeamMode(value) {
|
|
@@ -11375,13 +11874,13 @@ function getOperatingTeamMode(repoCfg) {
|
|
|
11375
11874
|
}
|
|
11376
11875
|
function readResolvedFile(relativePath, worktree, options = {}) {
|
|
11377
11876
|
const filePath = resolveIncludeFile(`plugin:${relativePath}`, worktree, PKG_ROOT, options);
|
|
11378
|
-
if (!filePath || !
|
|
11379
|
-
return resolveIncludes(
|
|
11877
|
+
if (!filePath || !fs5.existsSync(filePath)) return "";
|
|
11878
|
+
return resolveIncludes(fs5.readFileSync(filePath, "utf8"), worktree, PKG_ROOT, options).trim();
|
|
11380
11879
|
}
|
|
11381
11880
|
function loadMarkdownFragment(filePath, worktree) {
|
|
11382
|
-
if (!
|
|
11881
|
+
if (!fs5.existsSync(filePath)) return "";
|
|
11383
11882
|
try {
|
|
11384
|
-
const raw =
|
|
11883
|
+
const raw = fs5.readFileSync(filePath, "utf8");
|
|
11385
11884
|
const { body } = parseFrontmatter(raw);
|
|
11386
11885
|
return resolveIncludes(body.trim(), worktree, PKG_ROOT);
|
|
11387
11886
|
} catch (e) {
|
|
@@ -11390,9 +11889,9 @@ function loadMarkdownFragment(filePath, worktree) {
|
|
|
11390
11889
|
}
|
|
11391
11890
|
}
|
|
11392
11891
|
function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
11393
|
-
if (!
|
|
11892
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
11394
11893
|
try {
|
|
11395
|
-
const rawContent =
|
|
11894
|
+
const rawContent = fs5.readFileSync(filePath, "utf8");
|
|
11396
11895
|
const { data, body } = parseFrontmatter(rawContent);
|
|
11397
11896
|
const prompt = resolveIncludes(body.trim(), worktree, PKG_ROOT, options);
|
|
11398
11897
|
return { data, prompt };
|
|
@@ -11403,13 +11902,13 @@ function loadAgentDefinition(filePath, worktree, options = {}) {
|
|
|
11403
11902
|
}
|
|
11404
11903
|
function syncGeneratedPolicies(worktree, repoCfg) {
|
|
11405
11904
|
if (repoCfg.policies?.extract_defaults !== "all") return;
|
|
11406
|
-
if (!
|
|
11905
|
+
if (!fs5.existsSync(BUNDLE_POLICIES_DIR)) return;
|
|
11407
11906
|
const generatedDir = generatedPoliciesDir(worktree);
|
|
11408
|
-
if (!
|
|
11409
|
-
const policyFiles =
|
|
11907
|
+
if (!fs5.existsSync(generatedDir)) fs5.mkdirSync(generatedDir, { recursive: true });
|
|
11908
|
+
const policyFiles = fs5.readdirSync(BUNDLE_POLICIES_DIR).filter((file) => file.endsWith(".md") && file !== "README.md");
|
|
11410
11909
|
for (const file of policyFiles) {
|
|
11411
|
-
const sourcePath =
|
|
11412
|
-
const source =
|
|
11910
|
+
const sourcePath = path5.join(BUNDLE_POLICIES_DIR, file);
|
|
11911
|
+
const source = fs5.readFileSync(sourcePath, "utf8").trimEnd();
|
|
11413
11912
|
const generated = [
|
|
11414
11913
|
"<!--",
|
|
11415
11914
|
"Generated from Optima plugin defaults.",
|
|
@@ -11420,19 +11919,31 @@ function syncGeneratedPolicies(worktree, repoCfg) {
|
|
|
11420
11919
|
source,
|
|
11421
11920
|
""
|
|
11422
11921
|
].join("\n");
|
|
11423
|
-
|
|
11922
|
+
fs5.writeFileSync(path5.join(generatedDir, file), generated, "utf8");
|
|
11424
11923
|
}
|
|
11425
11924
|
}
|
|
11426
11925
|
function ensureReadmeFile(dirPath, content) {
|
|
11427
|
-
if (!
|
|
11428
|
-
const readmePath =
|
|
11429
|
-
if (!
|
|
11430
|
-
|
|
11926
|
+
if (!fs5.existsSync(dirPath)) fs5.mkdirSync(dirPath, { recursive: true });
|
|
11927
|
+
const readmePath = path5.join(dirPath, "README.md");
|
|
11928
|
+
if (!fs5.existsSync(readmePath)) {
|
|
11929
|
+
fs5.writeFileSync(readmePath, content, "utf8");
|
|
11431
11930
|
}
|
|
11432
11931
|
}
|
|
11433
11932
|
function ensureFileIfMissing(filePath, content) {
|
|
11434
|
-
if (!
|
|
11435
|
-
if (!
|
|
11933
|
+
if (!fs5.existsSync(path5.dirname(filePath))) fs5.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
11934
|
+
if (!fs5.existsSync(filePath)) fs5.writeFileSync(filePath, content, "utf8");
|
|
11935
|
+
}
|
|
11936
|
+
function currentTasksRegistryContent() {
|
|
11937
|
+
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";
|
|
11938
|
+
}
|
|
11939
|
+
function doneTasksRegistryContent() {
|
|
11940
|
+
return "# Completed Tasks (Registry)\n\n| Date | Task ID | SCR ID | Commit | Summary |\n| :--- | :--- | :--- | :--- | :--- |\n";
|
|
11941
|
+
}
|
|
11942
|
+
function currentScrRegistryContent() {
|
|
11943
|
+
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";
|
|
11944
|
+
}
|
|
11945
|
+
function doneScrRegistryContent() {
|
|
11946
|
+
return "# Implemented Spec Change Requests\n\n| Date | SCR ID | Title | Related Feature | Task ID |\n| :--- | :--- | :--- | :--- | :--- |\n";
|
|
11436
11947
|
}
|
|
11437
11948
|
function taskTemplateContent() {
|
|
11438
11949
|
return `---
|
|
@@ -11506,19 +12017,58 @@ Never place implementation evidence under root \`evidences/\`.
|
|
|
11506
12017
|
}
|
|
11507
12018
|
function ensureOptimaTaskTemplates(worktree) {
|
|
11508
12019
|
const tasksDir = optimaTasksDir(worktree);
|
|
11509
|
-
ensureFileIfMissing(
|
|
11510
|
-
ensureFileIfMissing(
|
|
12020
|
+
ensureFileIfMissing(path5.join(tasksDir, "task-template.md"), taskTemplateContent());
|
|
12021
|
+
ensureFileIfMissing(path5.join(tasksDir, "subtask-template.md"), subtaskTemplateContent());
|
|
12022
|
+
}
|
|
12023
|
+
function scaffoldOptimaConfig(worktree, teamMode = "full") {
|
|
12024
|
+
const configPath = repoConfigPath(worktree);
|
|
12025
|
+
if (fs5.existsSync(configPath)) return false;
|
|
12026
|
+
const templatePath = path5.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12027
|
+
if (!fs5.existsSync(templatePath)) return false;
|
|
12028
|
+
const agentIds = fs5.existsSync(BUNDLE_AGENTS_DIR) ? fs5.readdirSync(BUNDLE_AGENTS_DIR).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")) : [];
|
|
12029
|
+
let optimaConfig = fs5.readFileSync(templatePath, "utf8");
|
|
12030
|
+
optimaConfig = optimaConfig.replace("{{teamMode}}", normalizeTeamMode(teamMode));
|
|
12031
|
+
let agentsSection = "";
|
|
12032
|
+
for (const id of agentIds) {
|
|
12033
|
+
const enabled = isAgentEnabledForTeamMode(id, normalizeTeamMode(teamMode)) ? "true" : "false";
|
|
12034
|
+
agentsSection += ` ${id}:
|
|
12035
|
+
enabled: ${enabled}
|
|
12036
|
+
`;
|
|
12037
|
+
}
|
|
12038
|
+
optimaConfig = optimaConfig.replace(/^agents:/m, "agents:\n" + agentsSection);
|
|
12039
|
+
ensureFileIfMissing(configPath, optimaConfig);
|
|
12040
|
+
return fs5.existsSync(configPath);
|
|
12041
|
+
}
|
|
12042
|
+
function scaffoldOptimaRootCodemap(worktree) {
|
|
12043
|
+
const rootCodemapPath = optimaCodemapPath(worktree);
|
|
12044
|
+
if (fs5.existsSync(rootCodemapPath)) return false;
|
|
12045
|
+
const templatePath = path5.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12046
|
+
if (!fs5.existsSync(templatePath)) return false;
|
|
12047
|
+
const codemapConfig = fs5.readFileSync(templatePath, "utf8").replace("{{projectName}}", path5.basename(worktree));
|
|
12048
|
+
ensureFileIfMissing(rootCodemapPath, codemapConfig);
|
|
12049
|
+
return fs5.existsSync(rootCodemapPath);
|
|
12050
|
+
}
|
|
12051
|
+
function ensureOptimaRegistries(worktree) {
|
|
12052
|
+
const tasksDir = optimaTasksDir(worktree);
|
|
12053
|
+
const scrsDir = optimaScrsDir(worktree);
|
|
12054
|
+
if (!fs5.existsSync(tasksDir)) fs5.mkdirSync(tasksDir, { recursive: true });
|
|
12055
|
+
if (!fs5.existsSync(scrsDir)) fs5.mkdirSync(scrsDir, { recursive: true });
|
|
12056
|
+
ensureFileIfMissing(path5.join(tasksDir, "current.md"), currentTasksRegistryContent());
|
|
12057
|
+
ensureFileIfMissing(path5.join(tasksDir, "done.md"), doneTasksRegistryContent());
|
|
12058
|
+
ensureFileIfMissing(path5.join(scrsDir, "current.md"), currentScrRegistryContent());
|
|
12059
|
+
ensureFileIfMissing(path5.join(scrsDir, "done.md"), doneScrRegistryContent());
|
|
11511
12060
|
}
|
|
11512
12061
|
function scaffoldOptimaReadmes(worktree) {
|
|
11513
12062
|
ensureReadmeFile(repoPoliciesDir(worktree), REPO_LOCAL_POLICIES_README);
|
|
11514
12063
|
ensureReadmeFile(repoAgentsDir(worktree), [
|
|
11515
12064
|
"# Repository Agents",
|
|
11516
12065
|
"",
|
|
11517
|
-
"Place full repository-local agent definitions here.",
|
|
12066
|
+
"Place full repository-local agent definitions here only when explicitly requested by the user.",
|
|
11518
12067
|
"",
|
|
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.",
|
|
12068
|
+
"- Use `.optima/agents/<agent>.md` only to intentionally override a bundled agent's full base definition.",
|
|
12069
|
+
"- Use `.optima/agents/<agent>.md` only to define a brand new custom repository agent.",
|
|
12070
|
+
"- Files in this folder are treated as full agent definitions and block bundled prompt updates for that agent.",
|
|
12071
|
+
"- Prefer `.optima/agent-additions/<agent>.md` for normal repository-specific guidance.",
|
|
11522
12072
|
"- `README.md` is ignored by agent discovery.",
|
|
11523
12073
|
"",
|
|
11524
12074
|
"## Include Types Available In Custom Agents",
|
|
@@ -11612,6 +12162,38 @@ function scaffoldOptimaReadmes(worktree) {
|
|
|
11612
12162
|
].join("\n"));
|
|
11613
12163
|
ensureOptimaTaskTemplates(worktree);
|
|
11614
12164
|
}
|
|
12165
|
+
function optimaRepairDependencies() {
|
|
12166
|
+
return {
|
|
12167
|
+
currentScrRegistryContent,
|
|
12168
|
+
currentTasksRegistryContent,
|
|
12169
|
+
doneScrRegistryContent,
|
|
12170
|
+
doneTasksRegistryContent,
|
|
12171
|
+
ensureFileIfMissing,
|
|
12172
|
+
ensureOptimaGitignoreRules,
|
|
12173
|
+
ensureOptimaRegistries,
|
|
12174
|
+
isGitRepository,
|
|
12175
|
+
isOptimaPluginPackageWorktree,
|
|
12176
|
+
legacyNomadworkDir,
|
|
12177
|
+
legacyNomadworksDir,
|
|
12178
|
+
legacyOrbitaDir,
|
|
12179
|
+
legacyStaticEngDir,
|
|
12180
|
+
migrateLegacyOptimaLayout,
|
|
12181
|
+
optimaCodemapPath,
|
|
12182
|
+
optimaDir,
|
|
12183
|
+
optimaEvidencesDir,
|
|
12184
|
+
optimaGitignoreRules: OPTIMA_GITIGNORE_RULES,
|
|
12185
|
+
optimaScrsDir,
|
|
12186
|
+
optimaTasksDir,
|
|
12187
|
+
repoConfigPath,
|
|
12188
|
+
repoLocalPoliciesReadme: REPO_LOCAL_POLICIES_README,
|
|
12189
|
+
repoPoliciesDir,
|
|
12190
|
+
scaffoldOptimaConfig,
|
|
12191
|
+
scaffoldOptimaReadmes,
|
|
12192
|
+
scaffoldOptimaRootCodemap,
|
|
12193
|
+
subtaskTemplateContent,
|
|
12194
|
+
taskTemplateContent
|
|
12195
|
+
};
|
|
12196
|
+
}
|
|
11615
12197
|
function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
11616
12198
|
const fragmentMap = {
|
|
11617
12199
|
product_manager: {
|
|
@@ -11627,13 +12209,6 @@ function getModePromptFragment(agentId, operatingTeamMode, worktree) {
|
|
|
11627
12209
|
if (!fragmentPath) return "";
|
|
11628
12210
|
return readResolvedFile(fragmentPath, worktree, { preferCompactPromptDocs: true });
|
|
11629
12211
|
}
|
|
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
12212
|
function shouldRegisterWorkflowProductManager(options = {}, worktree = process.cwd()) {
|
|
11638
12213
|
if (options.clickUpWebhookActive === true) return true;
|
|
11639
12214
|
const validation = options.clickUpWebhookValidation;
|
|
@@ -11642,12 +12217,12 @@ function shouldRegisterWorkflowProductManager(options = {}, worktree = process.c
|
|
|
11642
12217
|
function resolveSessionToolDirectory({ requestedDirectory, context, pluginWorktree, clickUpWebhookValidation } = {}) {
|
|
11643
12218
|
const safe = safeWorktreeOrFailure(context, pluginWorktree);
|
|
11644
12219
|
if (!safe.ok) return { ok: false, error: safe.message };
|
|
11645
|
-
const requested = String(requestedDirectory || "").trim() ?
|
|
12220
|
+
const requested = String(requestedDirectory || "").trim() ? path5.resolve(safe.worktree, String(requestedDirectory).trim()) : safe.worktree;
|
|
11646
12221
|
if (!isSafeWritableDirectory(requested)) return { ok: false, error: `Directory is not a safe writable directory: ${requested}` };
|
|
11647
12222
|
if (isSameOrNestedPath(requested, safe.worktree)) return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "context_worktree" };
|
|
11648
12223
|
const clickUpBasePath = clickUpWebhookValidation?.complete === true && clickUpWebhookValidation?.ok !== false ? clickUpWebhookValidation.config?.basePath : "";
|
|
11649
12224
|
if (clickUpBasePath && isSameOrNestedPath(requested, clickUpBasePath)) {
|
|
11650
|
-
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath:
|
|
12225
|
+
return { ok: true, directory: requested, safeWorktree: safe.worktree, scope: "clickup_base_path", clickupBasePath: path5.resolve(clickUpBasePath) };
|
|
11651
12226
|
}
|
|
11652
12227
|
return {
|
|
11653
12228
|
ok: false,
|
|
@@ -11681,8 +12256,8 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
11681
12256
|
if (!enabled) continue;
|
|
11682
12257
|
}
|
|
11683
12258
|
const promptOptions = { preferCompactPromptDocs: repoCfg.features?.compact_prompt_docs !== false };
|
|
11684
|
-
const bundledDefinition = loadAgentDefinition(
|
|
11685
|
-
const repoDefinition = loadAgentDefinition(
|
|
12259
|
+
const bundledDefinition = loadAgentDefinition(path5.join(BUNDLE_AGENTS_DIR, file), worktree, promptOptions);
|
|
12260
|
+
const repoDefinition = loadAgentDefinition(path5.join(repoAgentDefinitions, file), worktree, promptOptions) || loadAgentDefinition(path5.join(legacyAgentsDir, file), worktree, promptOptions);
|
|
11686
12261
|
const activeDefinition = repoDefinition || bundledDefinition;
|
|
11687
12262
|
if (!activeDefinition) continue;
|
|
11688
12263
|
const { data } = activeDefinition;
|
|
@@ -11691,7 +12266,7 @@ function buildOptimaAgents(repoCfg, operatingTeamMode, worktree, debugDir, optio
|
|
|
11691
12266
|
if (modePromptFragment) finalPrompt = `${finalPrompt}
|
|
11692
12267
|
|
|
11693
12268
|
${modePromptFragment}`;
|
|
11694
|
-
const additionFragment = loadMarkdownFragment(
|
|
12269
|
+
const additionFragment = loadMarkdownFragment(path5.join(repoAgentAdditions, file), worktree);
|
|
11695
12270
|
if (additionFragment) {
|
|
11696
12271
|
finalPrompt = `${finalPrompt}
|
|
11697
12272
|
|
|
@@ -11744,14 +12319,14 @@ Use this Optima-provided fallback when the current task worktree lacks docs/core
|
|
|
11744
12319
|
}
|
|
11745
12320
|
ourAgents[id] = agentConfig;
|
|
11746
12321
|
if (repoCfg.features?.debug_dumps !== false) {
|
|
11747
|
-
const debugPath =
|
|
12322
|
+
const debugPath = path5.join(debugDir, `${id}.md`);
|
|
11748
12323
|
const { prompt, ...dumpConfig } = agentConfig;
|
|
11749
12324
|
const debugHeader = `---
|
|
11750
|
-
${
|
|
12325
|
+
${import_yaml3.default.stringify(dumpConfig).trim()}
|
|
11751
12326
|
---`;
|
|
11752
12327
|
try {
|
|
11753
|
-
if (!
|
|
11754
|
-
|
|
12328
|
+
if (!fs5.existsSync(debugDir)) fs5.mkdirSync(debugDir, { recursive: true });
|
|
12329
|
+
fs5.writeFileSync(debugPath, `${debugHeader}
|
|
11755
12330
|
|
|
11756
12331
|
${prompt}`, "utf8");
|
|
11757
12332
|
} catch (e) {
|
|
@@ -11775,9 +12350,9 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11775
12350
|
const configPath = resolveConfigPath(worktree);
|
|
11776
12351
|
const discussionRegistry = loadDiscussionRegistry(worktree);
|
|
11777
12352
|
let repoCfg = { agents: {}, defaults: {}, features: {} };
|
|
11778
|
-
if (
|
|
12353
|
+
if (fs5.existsSync(configPath)) {
|
|
11779
12354
|
try {
|
|
11780
|
-
repoCfg =
|
|
12355
|
+
repoCfg = import_yaml3.default.parse(fs5.readFileSync(configPath, "utf8")) || repoCfg;
|
|
11781
12356
|
} catch (e) {
|
|
11782
12357
|
console.error(`[Optima] Failed to parse config at ${configPath}:`, e);
|
|
11783
12358
|
}
|
|
@@ -11884,54 +12459,16 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
|
|
|
11884
12459
|
}
|
|
11885
12460
|
migrateLegacyOptimaLayout(toolWorktree);
|
|
11886
12461
|
const cfgDir = optimaConfigDir(toolWorktree);
|
|
11887
|
-
if (!
|
|
11888
|
-
const
|
|
11889
|
-
const
|
|
11890
|
-
|
|
11891
|
-
if (!fs2.existsSync(optimaTmplPath) || !fs2.existsSync(codemapTmplPath)) {
|
|
12462
|
+
if (!fs5.existsSync(cfgDir)) fs5.mkdirSync(cfgDir, { recursive: true });
|
|
12463
|
+
const optimaTmplPath = path5.join(TEMPLATES_DIR, "optima.yaml.template");
|
|
12464
|
+
const codemapTmplPath = path5.join(TEMPLATES_DIR, "codemap.yml.template");
|
|
12465
|
+
if (!fs5.existsSync(optimaTmplPath) || !fs5.existsSync(codemapTmplPath)) {
|
|
11892
12466
|
return "Error: Initialization templates not found in plugin.";
|
|
11893
12467
|
}
|
|
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
|
-
}
|
|
12468
|
+
scaffoldOptimaConfig(toolWorktree, requestedTeamMode);
|
|
12469
|
+
scaffoldOptimaRootCodemap(toolWorktree);
|
|
11914
12470
|
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
|
-
}
|
|
12471
|
+
ensureOptimaRegistries(toolWorktree);
|
|
11935
12472
|
const gitignoreResult = ensureOptimaGitignoreRules(toolWorktree);
|
|
11936
12473
|
const gitignoreNote = gitignoreResult.added.length > 0 ? ` Added ${gitignoreResult.added.length} Optima local/private .gitignore rule(s).` : "";
|
|
11937
12474
|
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 +12583,14 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
12046
12583
|
async execute(args, context) {
|
|
12047
12584
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12048
12585
|
if (!safe.ok) return safe.message;
|
|
12049
|
-
const summaryPath =
|
|
12050
|
-
if (!
|
|
12051
|
-
const taskPath = args.task_path ?
|
|
12586
|
+
const summaryPath = path5.resolve(safe.worktree, args.summary_path || "");
|
|
12587
|
+
if (!fs5.existsSync(summaryPath)) return `FAIL: summary_path not found: ${summaryPath}`;
|
|
12588
|
+
const taskPath = args.task_path ? path5.resolve(safe.worktree, args.task_path) : "";
|
|
12052
12589
|
const payload = buildClickUpSummaryPayload({
|
|
12053
|
-
summaryMarkdown:
|
|
12054
|
-
summaryPath:
|
|
12055
|
-
taskMarkdown: taskPath &&
|
|
12056
|
-
taskPath: taskPath ?
|
|
12590
|
+
summaryMarkdown: fs5.readFileSync(summaryPath, "utf8"),
|
|
12591
|
+
summaryPath: path5.relative(safe.worktree, summaryPath),
|
|
12592
|
+
taskMarkdown: taskPath && fs5.existsSync(taskPath) ? fs5.readFileSync(taskPath, "utf8") : "",
|
|
12593
|
+
taskPath: taskPath ? path5.relative(safe.worktree, taskPath) : "",
|
|
12057
12594
|
branch: args.branch,
|
|
12058
12595
|
worktree: args.worktree,
|
|
12059
12596
|
pr: args.pr
|
|
@@ -12132,12 +12669,12 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
12132
12669
|
async execute(args, context) {
|
|
12133
12670
|
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12134
12671
|
if (!safe.ok) return safe.message;
|
|
12135
|
-
const markdownPath =
|
|
12136
|
-
if (!
|
|
12672
|
+
const markdownPath = path5.resolve(safe.worktree, args.markdown_path || "");
|
|
12673
|
+
if (!fs5.existsSync(markdownPath)) return `FAIL: markdown_path not found: ${markdownPath}`;
|
|
12137
12674
|
const payload = buildClickUpCreateSubtasksPayload({
|
|
12138
12675
|
parentTaskId: args.parent_task_id,
|
|
12139
|
-
markdown:
|
|
12140
|
-
sourcePath:
|
|
12676
|
+
markdown: fs5.readFileSync(markdownPath, "utf8"),
|
|
12677
|
+
sourcePath: path5.relative(safe.worktree, markdownPath),
|
|
12141
12678
|
parentBranch: args.parent_branch,
|
|
12142
12679
|
parentTaskType: args.parent_task_type || "Tarea",
|
|
12143
12680
|
apply: String(args.apply || "").toLowerCase() === "true"
|
|
@@ -12169,6 +12706,21 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
|
|
|
12169
12706
|
return formatValidationResult(res);
|
|
12170
12707
|
}
|
|
12171
12708
|
}),
|
|
12709
|
+
optima_repair: tool({
|
|
12710
|
+
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.",
|
|
12711
|
+
args: {
|
|
12712
|
+
apply: tool.schema.boolean().optional().describe("Set true to apply deterministic Optima metadata repairs. Defaults to false dry-run."),
|
|
12713
|
+
mode: tool.schema.string().optional().describe("Optional mode: 'dry-run' or 'apply'. Dry-run is the default."),
|
|
12714
|
+
team_mode: tool.schema.string().optional().describe("Team mode for a newly created config when missing: mini or full. Defaults to full.")
|
|
12715
|
+
},
|
|
12716
|
+
async execute(args, context) {
|
|
12717
|
+
const safe = safeWorktreeOrFailure(context, worktree);
|
|
12718
|
+
if (!safe.ok) return safe.message;
|
|
12719
|
+
const plan = planOptimaRepair(safe.worktree, args, optimaRepairDependencies());
|
|
12720
|
+
const validationResult = plan.mode === "apply" ? await optima_validate_logic(safe.worktree) : null;
|
|
12721
|
+
return formatRepairResult(plan, validationResult, formatValidationResult);
|
|
12722
|
+
}
|
|
12723
|
+
}),
|
|
12172
12724
|
optima_start_discussion: tool({
|
|
12173
12725
|
description: "Start an automatic discussion transcript for this session",
|
|
12174
12726
|
args: {
|
|
@@ -12321,7 +12873,7 @@ Backfilled messages: ${backfilled}`;
|
|
|
12321
12873
|
if (!existing) {
|
|
12322
12874
|
return "FAIL: No active discussion exists for this session.";
|
|
12323
12875
|
}
|
|
12324
|
-
const discussionPath =
|
|
12876
|
+
const discussionPath = path5.join(toolWorktree, existing.transcriptPath);
|
|
12325
12877
|
setDiscussionStatus(discussionPath, "summarizing");
|
|
12326
12878
|
existing.status = "summarizing";
|
|
12327
12879
|
saveDiscussionRegistry(toolWorktree, toolRegistry);
|
|
@@ -12373,7 +12925,7 @@ Reason: ${err.message}`;
|
|
|
12373
12925
|
try {
|
|
12374
12926
|
const sessionResult = await client.session.create({
|
|
12375
12927
|
query: { directory: workflowDirectory },
|
|
12376
|
-
body: { title: `Workflow Run: ${
|
|
12928
|
+
body: { title: `Workflow Run: ${path5.basename(workflowTaskPath)}` }
|
|
12377
12929
|
});
|
|
12378
12930
|
const sessionId = sessionResult.data.id;
|
|
12379
12931
|
activeWorkflows.set(sessionId, { pmaSessionId, taskPath: workflowTaskPath, track: workflowTrack, directory: workflowDirectory });
|
|
@@ -12502,7 +13054,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
12502
13054
|
}
|
|
12503
13055
|
};
|
|
12504
13056
|
}
|
|
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 };
|
|
13057
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
12506
13058
|
export {
|
|
12507
13059
|
OptimaPlugin as default
|
|
12508
13060
|
};
|