@opencode_weave/weave 0.7.4-preview.1 → 0.7.4
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/dist/agents/tapestry/prompt-composer.d.ts +0 -2
- package/dist/config/schema.d.ts +6 -0
- package/dist/index.js +1150 -340
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/log.d.ts +11 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,11 +1,814 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { join as
|
|
2
|
+
import { join as join12 } from "path";
|
|
3
3
|
|
|
4
4
|
// src/config/loader.ts
|
|
5
|
-
import { existsSync
|
|
6
|
-
import { join
|
|
7
|
-
import { homedir
|
|
8
|
-
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
|
|
9
|
+
// node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
10
|
+
function createScanner(text, ignoreTrivia = false) {
|
|
11
|
+
const len = text.length;
|
|
12
|
+
let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
|
|
13
|
+
function scanHexDigits(count, exact) {
|
|
14
|
+
let digits = 0;
|
|
15
|
+
let value2 = 0;
|
|
16
|
+
while (digits < count || !exact) {
|
|
17
|
+
let ch = text.charCodeAt(pos);
|
|
18
|
+
if (ch >= 48 && ch <= 57) {
|
|
19
|
+
value2 = value2 * 16 + ch - 48;
|
|
20
|
+
} else if (ch >= 65 && ch <= 70) {
|
|
21
|
+
value2 = value2 * 16 + ch - 65 + 10;
|
|
22
|
+
} else if (ch >= 97 && ch <= 102) {
|
|
23
|
+
value2 = value2 * 16 + ch - 97 + 10;
|
|
24
|
+
} else {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
pos++;
|
|
28
|
+
digits++;
|
|
29
|
+
}
|
|
30
|
+
if (digits < count) {
|
|
31
|
+
value2 = -1;
|
|
32
|
+
}
|
|
33
|
+
return value2;
|
|
34
|
+
}
|
|
35
|
+
function setPosition(newPosition) {
|
|
36
|
+
pos = newPosition;
|
|
37
|
+
value = "";
|
|
38
|
+
tokenOffset = 0;
|
|
39
|
+
token = 16;
|
|
40
|
+
scanError = 0;
|
|
41
|
+
}
|
|
42
|
+
function scanNumber() {
|
|
43
|
+
let start = pos;
|
|
44
|
+
if (text.charCodeAt(pos) === 48) {
|
|
45
|
+
pos++;
|
|
46
|
+
} else {
|
|
47
|
+
pos++;
|
|
48
|
+
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
49
|
+
pos++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (pos < text.length && text.charCodeAt(pos) === 46) {
|
|
53
|
+
pos++;
|
|
54
|
+
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
55
|
+
pos++;
|
|
56
|
+
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
57
|
+
pos++;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
scanError = 3;
|
|
61
|
+
return text.substring(start, pos);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let end = pos;
|
|
65
|
+
if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
|
|
66
|
+
pos++;
|
|
67
|
+
if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
|
|
68
|
+
pos++;
|
|
69
|
+
}
|
|
70
|
+
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
71
|
+
pos++;
|
|
72
|
+
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
73
|
+
pos++;
|
|
74
|
+
}
|
|
75
|
+
end = pos;
|
|
76
|
+
} else {
|
|
77
|
+
scanError = 3;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return text.substring(start, end);
|
|
81
|
+
}
|
|
82
|
+
function scanString() {
|
|
83
|
+
let result = "", start = pos;
|
|
84
|
+
while (true) {
|
|
85
|
+
if (pos >= len) {
|
|
86
|
+
result += text.substring(start, pos);
|
|
87
|
+
scanError = 2;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
const ch = text.charCodeAt(pos);
|
|
91
|
+
if (ch === 34) {
|
|
92
|
+
result += text.substring(start, pos);
|
|
93
|
+
pos++;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
if (ch === 92) {
|
|
97
|
+
result += text.substring(start, pos);
|
|
98
|
+
pos++;
|
|
99
|
+
if (pos >= len) {
|
|
100
|
+
scanError = 2;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
const ch2 = text.charCodeAt(pos++);
|
|
104
|
+
switch (ch2) {
|
|
105
|
+
case 34:
|
|
106
|
+
result += '"';
|
|
107
|
+
break;
|
|
108
|
+
case 92:
|
|
109
|
+
result += "\\";
|
|
110
|
+
break;
|
|
111
|
+
case 47:
|
|
112
|
+
result += "/";
|
|
113
|
+
break;
|
|
114
|
+
case 98:
|
|
115
|
+
result += "\b";
|
|
116
|
+
break;
|
|
117
|
+
case 102:
|
|
118
|
+
result += "\f";
|
|
119
|
+
break;
|
|
120
|
+
case 110:
|
|
121
|
+
result += `
|
|
122
|
+
`;
|
|
123
|
+
break;
|
|
124
|
+
case 114:
|
|
125
|
+
result += "\r";
|
|
126
|
+
break;
|
|
127
|
+
case 116:
|
|
128
|
+
result += "\t";
|
|
129
|
+
break;
|
|
130
|
+
case 117:
|
|
131
|
+
const ch3 = scanHexDigits(4, true);
|
|
132
|
+
if (ch3 >= 0) {
|
|
133
|
+
result += String.fromCharCode(ch3);
|
|
134
|
+
} else {
|
|
135
|
+
scanError = 4;
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
scanError = 5;
|
|
140
|
+
}
|
|
141
|
+
start = pos;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (ch >= 0 && ch <= 31) {
|
|
145
|
+
if (isLineBreak(ch)) {
|
|
146
|
+
result += text.substring(start, pos);
|
|
147
|
+
scanError = 2;
|
|
148
|
+
break;
|
|
149
|
+
} else {
|
|
150
|
+
scanError = 6;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
pos++;
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
function scanNext() {
|
|
158
|
+
value = "";
|
|
159
|
+
scanError = 0;
|
|
160
|
+
tokenOffset = pos;
|
|
161
|
+
lineStartOffset = lineNumber;
|
|
162
|
+
prevTokenLineStartOffset = tokenLineStartOffset;
|
|
163
|
+
if (pos >= len) {
|
|
164
|
+
tokenOffset = len;
|
|
165
|
+
return token = 17;
|
|
166
|
+
}
|
|
167
|
+
let code = text.charCodeAt(pos);
|
|
168
|
+
if (isWhiteSpace(code)) {
|
|
169
|
+
do {
|
|
170
|
+
pos++;
|
|
171
|
+
value += String.fromCharCode(code);
|
|
172
|
+
code = text.charCodeAt(pos);
|
|
173
|
+
} while (isWhiteSpace(code));
|
|
174
|
+
return token = 15;
|
|
175
|
+
}
|
|
176
|
+
if (isLineBreak(code)) {
|
|
177
|
+
pos++;
|
|
178
|
+
value += String.fromCharCode(code);
|
|
179
|
+
if (code === 13 && text.charCodeAt(pos) === 10) {
|
|
180
|
+
pos++;
|
|
181
|
+
value += `
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
lineNumber++;
|
|
185
|
+
tokenLineStartOffset = pos;
|
|
186
|
+
return token = 14;
|
|
187
|
+
}
|
|
188
|
+
switch (code) {
|
|
189
|
+
case 123:
|
|
190
|
+
pos++;
|
|
191
|
+
return token = 1;
|
|
192
|
+
case 125:
|
|
193
|
+
pos++;
|
|
194
|
+
return token = 2;
|
|
195
|
+
case 91:
|
|
196
|
+
pos++;
|
|
197
|
+
return token = 3;
|
|
198
|
+
case 93:
|
|
199
|
+
pos++;
|
|
200
|
+
return token = 4;
|
|
201
|
+
case 58:
|
|
202
|
+
pos++;
|
|
203
|
+
return token = 6;
|
|
204
|
+
case 44:
|
|
205
|
+
pos++;
|
|
206
|
+
return token = 5;
|
|
207
|
+
case 34:
|
|
208
|
+
pos++;
|
|
209
|
+
value = scanString();
|
|
210
|
+
return token = 10;
|
|
211
|
+
case 47:
|
|
212
|
+
const start = pos - 1;
|
|
213
|
+
if (text.charCodeAt(pos + 1) === 47) {
|
|
214
|
+
pos += 2;
|
|
215
|
+
while (pos < len) {
|
|
216
|
+
if (isLineBreak(text.charCodeAt(pos))) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
pos++;
|
|
220
|
+
}
|
|
221
|
+
value = text.substring(start, pos);
|
|
222
|
+
return token = 12;
|
|
223
|
+
}
|
|
224
|
+
if (text.charCodeAt(pos + 1) === 42) {
|
|
225
|
+
pos += 2;
|
|
226
|
+
const safeLength = len - 1;
|
|
227
|
+
let commentClosed = false;
|
|
228
|
+
while (pos < safeLength) {
|
|
229
|
+
const ch = text.charCodeAt(pos);
|
|
230
|
+
if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
|
|
231
|
+
pos += 2;
|
|
232
|
+
commentClosed = true;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
pos++;
|
|
236
|
+
if (isLineBreak(ch)) {
|
|
237
|
+
if (ch === 13 && text.charCodeAt(pos) === 10) {
|
|
238
|
+
pos++;
|
|
239
|
+
}
|
|
240
|
+
lineNumber++;
|
|
241
|
+
tokenLineStartOffset = pos;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (!commentClosed) {
|
|
245
|
+
pos++;
|
|
246
|
+
scanError = 1;
|
|
247
|
+
}
|
|
248
|
+
value = text.substring(start, pos);
|
|
249
|
+
return token = 13;
|
|
250
|
+
}
|
|
251
|
+
value += String.fromCharCode(code);
|
|
252
|
+
pos++;
|
|
253
|
+
return token = 16;
|
|
254
|
+
case 45:
|
|
255
|
+
value += String.fromCharCode(code);
|
|
256
|
+
pos++;
|
|
257
|
+
if (pos === len || !isDigit(text.charCodeAt(pos))) {
|
|
258
|
+
return token = 16;
|
|
259
|
+
}
|
|
260
|
+
case 48:
|
|
261
|
+
case 49:
|
|
262
|
+
case 50:
|
|
263
|
+
case 51:
|
|
264
|
+
case 52:
|
|
265
|
+
case 53:
|
|
266
|
+
case 54:
|
|
267
|
+
case 55:
|
|
268
|
+
case 56:
|
|
269
|
+
case 57:
|
|
270
|
+
value += scanNumber();
|
|
271
|
+
return token = 11;
|
|
272
|
+
default:
|
|
273
|
+
while (pos < len && isUnknownContentCharacter(code)) {
|
|
274
|
+
pos++;
|
|
275
|
+
code = text.charCodeAt(pos);
|
|
276
|
+
}
|
|
277
|
+
if (tokenOffset !== pos) {
|
|
278
|
+
value = text.substring(tokenOffset, pos);
|
|
279
|
+
switch (value) {
|
|
280
|
+
case "true":
|
|
281
|
+
return token = 8;
|
|
282
|
+
case "false":
|
|
283
|
+
return token = 9;
|
|
284
|
+
case "null":
|
|
285
|
+
return token = 7;
|
|
286
|
+
}
|
|
287
|
+
return token = 16;
|
|
288
|
+
}
|
|
289
|
+
value += String.fromCharCode(code);
|
|
290
|
+
pos++;
|
|
291
|
+
return token = 16;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function isUnknownContentCharacter(code) {
|
|
295
|
+
if (isWhiteSpace(code) || isLineBreak(code)) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
switch (code) {
|
|
299
|
+
case 125:
|
|
300
|
+
case 93:
|
|
301
|
+
case 123:
|
|
302
|
+
case 91:
|
|
303
|
+
case 34:
|
|
304
|
+
case 58:
|
|
305
|
+
case 44:
|
|
306
|
+
case 47:
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
function scanNextNonTrivia() {
|
|
312
|
+
let result;
|
|
313
|
+
do {
|
|
314
|
+
result = scanNext();
|
|
315
|
+
} while (result >= 12 && result <= 15);
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
setPosition,
|
|
320
|
+
getPosition: () => pos,
|
|
321
|
+
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
|
|
322
|
+
getToken: () => token,
|
|
323
|
+
getTokenValue: () => value,
|
|
324
|
+
getTokenOffset: () => tokenOffset,
|
|
325
|
+
getTokenLength: () => pos - tokenOffset,
|
|
326
|
+
getTokenStartLine: () => lineStartOffset,
|
|
327
|
+
getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
|
|
328
|
+
getTokenError: () => scanError
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function isWhiteSpace(ch) {
|
|
332
|
+
return ch === 32 || ch === 9;
|
|
333
|
+
}
|
|
334
|
+
function isLineBreak(ch) {
|
|
335
|
+
return ch === 10 || ch === 13;
|
|
336
|
+
}
|
|
337
|
+
function isDigit(ch) {
|
|
338
|
+
return ch >= 48 && ch <= 57;
|
|
339
|
+
}
|
|
340
|
+
var CharacterCodes;
|
|
341
|
+
(function(CharacterCodes2) {
|
|
342
|
+
CharacterCodes2[CharacterCodes2["lineFeed"] = 10] = "lineFeed";
|
|
343
|
+
CharacterCodes2[CharacterCodes2["carriageReturn"] = 13] = "carriageReturn";
|
|
344
|
+
CharacterCodes2[CharacterCodes2["space"] = 32] = "space";
|
|
345
|
+
CharacterCodes2[CharacterCodes2["_0"] = 48] = "_0";
|
|
346
|
+
CharacterCodes2[CharacterCodes2["_1"] = 49] = "_1";
|
|
347
|
+
CharacterCodes2[CharacterCodes2["_2"] = 50] = "_2";
|
|
348
|
+
CharacterCodes2[CharacterCodes2["_3"] = 51] = "_3";
|
|
349
|
+
CharacterCodes2[CharacterCodes2["_4"] = 52] = "_4";
|
|
350
|
+
CharacterCodes2[CharacterCodes2["_5"] = 53] = "_5";
|
|
351
|
+
CharacterCodes2[CharacterCodes2["_6"] = 54] = "_6";
|
|
352
|
+
CharacterCodes2[CharacterCodes2["_7"] = 55] = "_7";
|
|
353
|
+
CharacterCodes2[CharacterCodes2["_8"] = 56] = "_8";
|
|
354
|
+
CharacterCodes2[CharacterCodes2["_9"] = 57] = "_9";
|
|
355
|
+
CharacterCodes2[CharacterCodes2["a"] = 97] = "a";
|
|
356
|
+
CharacterCodes2[CharacterCodes2["b"] = 98] = "b";
|
|
357
|
+
CharacterCodes2[CharacterCodes2["c"] = 99] = "c";
|
|
358
|
+
CharacterCodes2[CharacterCodes2["d"] = 100] = "d";
|
|
359
|
+
CharacterCodes2[CharacterCodes2["e"] = 101] = "e";
|
|
360
|
+
CharacterCodes2[CharacterCodes2["f"] = 102] = "f";
|
|
361
|
+
CharacterCodes2[CharacterCodes2["g"] = 103] = "g";
|
|
362
|
+
CharacterCodes2[CharacterCodes2["h"] = 104] = "h";
|
|
363
|
+
CharacterCodes2[CharacterCodes2["i"] = 105] = "i";
|
|
364
|
+
CharacterCodes2[CharacterCodes2["j"] = 106] = "j";
|
|
365
|
+
CharacterCodes2[CharacterCodes2["k"] = 107] = "k";
|
|
366
|
+
CharacterCodes2[CharacterCodes2["l"] = 108] = "l";
|
|
367
|
+
CharacterCodes2[CharacterCodes2["m"] = 109] = "m";
|
|
368
|
+
CharacterCodes2[CharacterCodes2["n"] = 110] = "n";
|
|
369
|
+
CharacterCodes2[CharacterCodes2["o"] = 111] = "o";
|
|
370
|
+
CharacterCodes2[CharacterCodes2["p"] = 112] = "p";
|
|
371
|
+
CharacterCodes2[CharacterCodes2["q"] = 113] = "q";
|
|
372
|
+
CharacterCodes2[CharacterCodes2["r"] = 114] = "r";
|
|
373
|
+
CharacterCodes2[CharacterCodes2["s"] = 115] = "s";
|
|
374
|
+
CharacterCodes2[CharacterCodes2["t"] = 116] = "t";
|
|
375
|
+
CharacterCodes2[CharacterCodes2["u"] = 117] = "u";
|
|
376
|
+
CharacterCodes2[CharacterCodes2["v"] = 118] = "v";
|
|
377
|
+
CharacterCodes2[CharacterCodes2["w"] = 119] = "w";
|
|
378
|
+
CharacterCodes2[CharacterCodes2["x"] = 120] = "x";
|
|
379
|
+
CharacterCodes2[CharacterCodes2["y"] = 121] = "y";
|
|
380
|
+
CharacterCodes2[CharacterCodes2["z"] = 122] = "z";
|
|
381
|
+
CharacterCodes2[CharacterCodes2["A"] = 65] = "A";
|
|
382
|
+
CharacterCodes2[CharacterCodes2["B"] = 66] = "B";
|
|
383
|
+
CharacterCodes2[CharacterCodes2["C"] = 67] = "C";
|
|
384
|
+
CharacterCodes2[CharacterCodes2["D"] = 68] = "D";
|
|
385
|
+
CharacterCodes2[CharacterCodes2["E"] = 69] = "E";
|
|
386
|
+
CharacterCodes2[CharacterCodes2["F"] = 70] = "F";
|
|
387
|
+
CharacterCodes2[CharacterCodes2["G"] = 71] = "G";
|
|
388
|
+
CharacterCodes2[CharacterCodes2["H"] = 72] = "H";
|
|
389
|
+
CharacterCodes2[CharacterCodes2["I"] = 73] = "I";
|
|
390
|
+
CharacterCodes2[CharacterCodes2["J"] = 74] = "J";
|
|
391
|
+
CharacterCodes2[CharacterCodes2["K"] = 75] = "K";
|
|
392
|
+
CharacterCodes2[CharacterCodes2["L"] = 76] = "L";
|
|
393
|
+
CharacterCodes2[CharacterCodes2["M"] = 77] = "M";
|
|
394
|
+
CharacterCodes2[CharacterCodes2["N"] = 78] = "N";
|
|
395
|
+
CharacterCodes2[CharacterCodes2["O"] = 79] = "O";
|
|
396
|
+
CharacterCodes2[CharacterCodes2["P"] = 80] = "P";
|
|
397
|
+
CharacterCodes2[CharacterCodes2["Q"] = 81] = "Q";
|
|
398
|
+
CharacterCodes2[CharacterCodes2["R"] = 82] = "R";
|
|
399
|
+
CharacterCodes2[CharacterCodes2["S"] = 83] = "S";
|
|
400
|
+
CharacterCodes2[CharacterCodes2["T"] = 84] = "T";
|
|
401
|
+
CharacterCodes2[CharacterCodes2["U"] = 85] = "U";
|
|
402
|
+
CharacterCodes2[CharacterCodes2["V"] = 86] = "V";
|
|
403
|
+
CharacterCodes2[CharacterCodes2["W"] = 87] = "W";
|
|
404
|
+
CharacterCodes2[CharacterCodes2["X"] = 88] = "X";
|
|
405
|
+
CharacterCodes2[CharacterCodes2["Y"] = 89] = "Y";
|
|
406
|
+
CharacterCodes2[CharacterCodes2["Z"] = 90] = "Z";
|
|
407
|
+
CharacterCodes2[CharacterCodes2["asterisk"] = 42] = "asterisk";
|
|
408
|
+
CharacterCodes2[CharacterCodes2["backslash"] = 92] = "backslash";
|
|
409
|
+
CharacterCodes2[CharacterCodes2["closeBrace"] = 125] = "closeBrace";
|
|
410
|
+
CharacterCodes2[CharacterCodes2["closeBracket"] = 93] = "closeBracket";
|
|
411
|
+
CharacterCodes2[CharacterCodes2["colon"] = 58] = "colon";
|
|
412
|
+
CharacterCodes2[CharacterCodes2["comma"] = 44] = "comma";
|
|
413
|
+
CharacterCodes2[CharacterCodes2["dot"] = 46] = "dot";
|
|
414
|
+
CharacterCodes2[CharacterCodes2["doubleQuote"] = 34] = "doubleQuote";
|
|
415
|
+
CharacterCodes2[CharacterCodes2["minus"] = 45] = "minus";
|
|
416
|
+
CharacterCodes2[CharacterCodes2["openBrace"] = 123] = "openBrace";
|
|
417
|
+
CharacterCodes2[CharacterCodes2["openBracket"] = 91] = "openBracket";
|
|
418
|
+
CharacterCodes2[CharacterCodes2["plus"] = 43] = "plus";
|
|
419
|
+
CharacterCodes2[CharacterCodes2["slash"] = 47] = "slash";
|
|
420
|
+
CharacterCodes2[CharacterCodes2["formFeed"] = 12] = "formFeed";
|
|
421
|
+
CharacterCodes2[CharacterCodes2["tab"] = 9] = "tab";
|
|
422
|
+
})(CharacterCodes || (CharacterCodes = {}));
|
|
423
|
+
|
|
424
|
+
// node_modules/jsonc-parser/lib/esm/impl/string-intern.js
|
|
425
|
+
var cachedSpaces = new Array(20).fill(0).map((_, index) => {
|
|
426
|
+
return " ".repeat(index);
|
|
427
|
+
});
|
|
428
|
+
var maxCachedValues = 200;
|
|
429
|
+
var cachedBreakLinesWithSpaces = {
|
|
430
|
+
" ": {
|
|
431
|
+
"\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
432
|
+
return `
|
|
433
|
+
` + " ".repeat(index);
|
|
434
|
+
}),
|
|
435
|
+
"\r": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
436
|
+
return "\r" + " ".repeat(index);
|
|
437
|
+
}),
|
|
438
|
+
"\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
439
|
+
return `\r
|
|
440
|
+
` + " ".repeat(index);
|
|
441
|
+
})
|
|
442
|
+
},
|
|
443
|
+
"\t": {
|
|
444
|
+
"\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
445
|
+
return `
|
|
446
|
+
` + "\t".repeat(index);
|
|
447
|
+
}),
|
|
448
|
+
"\r": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
449
|
+
return "\r" + "\t".repeat(index);
|
|
450
|
+
}),
|
|
451
|
+
"\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
452
|
+
return `\r
|
|
453
|
+
` + "\t".repeat(index);
|
|
454
|
+
})
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// node_modules/jsonc-parser/lib/esm/impl/parser.js
|
|
459
|
+
var ParseOptions;
|
|
460
|
+
(function(ParseOptions2) {
|
|
461
|
+
ParseOptions2.DEFAULT = {
|
|
462
|
+
allowTrailingComma: false
|
|
463
|
+
};
|
|
464
|
+
})(ParseOptions || (ParseOptions = {}));
|
|
465
|
+
function parse(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
466
|
+
let currentProperty = null;
|
|
467
|
+
let currentParent = [];
|
|
468
|
+
const previousParents = [];
|
|
469
|
+
function onValue(value) {
|
|
470
|
+
if (Array.isArray(currentParent)) {
|
|
471
|
+
currentParent.push(value);
|
|
472
|
+
} else if (currentProperty !== null) {
|
|
473
|
+
currentParent[currentProperty] = value;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const visitor = {
|
|
477
|
+
onObjectBegin: () => {
|
|
478
|
+
const object = {};
|
|
479
|
+
onValue(object);
|
|
480
|
+
previousParents.push(currentParent);
|
|
481
|
+
currentParent = object;
|
|
482
|
+
currentProperty = null;
|
|
483
|
+
},
|
|
484
|
+
onObjectProperty: (name) => {
|
|
485
|
+
currentProperty = name;
|
|
486
|
+
},
|
|
487
|
+
onObjectEnd: () => {
|
|
488
|
+
currentParent = previousParents.pop();
|
|
489
|
+
},
|
|
490
|
+
onArrayBegin: () => {
|
|
491
|
+
const array = [];
|
|
492
|
+
onValue(array);
|
|
493
|
+
previousParents.push(currentParent);
|
|
494
|
+
currentParent = array;
|
|
495
|
+
currentProperty = null;
|
|
496
|
+
},
|
|
497
|
+
onArrayEnd: () => {
|
|
498
|
+
currentParent = previousParents.pop();
|
|
499
|
+
},
|
|
500
|
+
onLiteralValue: onValue,
|
|
501
|
+
onError: (error, offset, length) => {
|
|
502
|
+
errors.push({ error, offset, length });
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
visit(text, visitor, options);
|
|
506
|
+
return currentParent[0];
|
|
507
|
+
}
|
|
508
|
+
function visit(text, visitor, options = ParseOptions.DEFAULT) {
|
|
509
|
+
const _scanner = createScanner(text, false);
|
|
510
|
+
const _jsonPath = [];
|
|
511
|
+
let suppressedCallbacks = 0;
|
|
512
|
+
function toNoArgVisit(visitFunction) {
|
|
513
|
+
return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
|
|
514
|
+
}
|
|
515
|
+
function toOneArgVisit(visitFunction) {
|
|
516
|
+
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
|
|
517
|
+
}
|
|
518
|
+
function toOneArgVisitWithPath(visitFunction) {
|
|
519
|
+
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
|
|
520
|
+
}
|
|
521
|
+
function toBeginVisit(visitFunction) {
|
|
522
|
+
return visitFunction ? () => {
|
|
523
|
+
if (suppressedCallbacks > 0) {
|
|
524
|
+
suppressedCallbacks++;
|
|
525
|
+
} else {
|
|
526
|
+
let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
|
|
527
|
+
if (cbReturn === false) {
|
|
528
|
+
suppressedCallbacks = 1;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
} : () => true;
|
|
532
|
+
}
|
|
533
|
+
function toEndVisit(visitFunction) {
|
|
534
|
+
return visitFunction ? () => {
|
|
535
|
+
if (suppressedCallbacks > 0) {
|
|
536
|
+
suppressedCallbacks--;
|
|
537
|
+
}
|
|
538
|
+
if (suppressedCallbacks === 0) {
|
|
539
|
+
visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
|
|
540
|
+
}
|
|
541
|
+
} : () => true;
|
|
542
|
+
}
|
|
543
|
+
const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
|
|
544
|
+
const disallowComments = options && options.disallowComments;
|
|
545
|
+
const allowTrailingComma = options && options.allowTrailingComma;
|
|
546
|
+
function scanNext() {
|
|
547
|
+
while (true) {
|
|
548
|
+
const token = _scanner.scan();
|
|
549
|
+
switch (_scanner.getTokenError()) {
|
|
550
|
+
case 4:
|
|
551
|
+
handleError(14);
|
|
552
|
+
break;
|
|
553
|
+
case 5:
|
|
554
|
+
handleError(15);
|
|
555
|
+
break;
|
|
556
|
+
case 3:
|
|
557
|
+
handleError(13);
|
|
558
|
+
break;
|
|
559
|
+
case 1:
|
|
560
|
+
if (!disallowComments) {
|
|
561
|
+
handleError(11);
|
|
562
|
+
}
|
|
563
|
+
break;
|
|
564
|
+
case 2:
|
|
565
|
+
handleError(12);
|
|
566
|
+
break;
|
|
567
|
+
case 6:
|
|
568
|
+
handleError(16);
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
switch (token) {
|
|
572
|
+
case 12:
|
|
573
|
+
case 13:
|
|
574
|
+
if (disallowComments) {
|
|
575
|
+
handleError(10);
|
|
576
|
+
} else {
|
|
577
|
+
onComment();
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
case 16:
|
|
581
|
+
handleError(1);
|
|
582
|
+
break;
|
|
583
|
+
case 15:
|
|
584
|
+
case 14:
|
|
585
|
+
break;
|
|
586
|
+
default:
|
|
587
|
+
return token;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function handleError(error, skipUntilAfter = [], skipUntil = []) {
|
|
592
|
+
onError(error);
|
|
593
|
+
if (skipUntilAfter.length + skipUntil.length > 0) {
|
|
594
|
+
let token = _scanner.getToken();
|
|
595
|
+
while (token !== 17) {
|
|
596
|
+
if (skipUntilAfter.indexOf(token) !== -1) {
|
|
597
|
+
scanNext();
|
|
598
|
+
break;
|
|
599
|
+
} else if (skipUntil.indexOf(token) !== -1) {
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
token = scanNext();
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function parseString(isValue) {
|
|
607
|
+
const value = _scanner.getTokenValue();
|
|
608
|
+
if (isValue) {
|
|
609
|
+
onLiteralValue(value);
|
|
610
|
+
} else {
|
|
611
|
+
onObjectProperty(value);
|
|
612
|
+
_jsonPath.push(value);
|
|
613
|
+
}
|
|
614
|
+
scanNext();
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
function parseLiteral() {
|
|
618
|
+
switch (_scanner.getToken()) {
|
|
619
|
+
case 11:
|
|
620
|
+
const tokenValue = _scanner.getTokenValue();
|
|
621
|
+
let value = Number(tokenValue);
|
|
622
|
+
if (isNaN(value)) {
|
|
623
|
+
handleError(2);
|
|
624
|
+
value = 0;
|
|
625
|
+
}
|
|
626
|
+
onLiteralValue(value);
|
|
627
|
+
break;
|
|
628
|
+
case 7:
|
|
629
|
+
onLiteralValue(null);
|
|
630
|
+
break;
|
|
631
|
+
case 8:
|
|
632
|
+
onLiteralValue(true);
|
|
633
|
+
break;
|
|
634
|
+
case 9:
|
|
635
|
+
onLiteralValue(false);
|
|
636
|
+
break;
|
|
637
|
+
default:
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
scanNext();
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
function parseProperty() {
|
|
644
|
+
if (_scanner.getToken() !== 10) {
|
|
645
|
+
handleError(3, [], [2, 5]);
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
parseString(false);
|
|
649
|
+
if (_scanner.getToken() === 6) {
|
|
650
|
+
onSeparator(":");
|
|
651
|
+
scanNext();
|
|
652
|
+
if (!parseValue()) {
|
|
653
|
+
handleError(4, [], [2, 5]);
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
handleError(5, [], [2, 5]);
|
|
657
|
+
}
|
|
658
|
+
_jsonPath.pop();
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
function parseObject() {
|
|
662
|
+
onObjectBegin();
|
|
663
|
+
scanNext();
|
|
664
|
+
let needsComma = false;
|
|
665
|
+
while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
|
|
666
|
+
if (_scanner.getToken() === 5) {
|
|
667
|
+
if (!needsComma) {
|
|
668
|
+
handleError(4, [], []);
|
|
669
|
+
}
|
|
670
|
+
onSeparator(",");
|
|
671
|
+
scanNext();
|
|
672
|
+
if (_scanner.getToken() === 2 && allowTrailingComma) {
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
} else if (needsComma) {
|
|
676
|
+
handleError(6, [], []);
|
|
677
|
+
}
|
|
678
|
+
if (!parseProperty()) {
|
|
679
|
+
handleError(4, [], [2, 5]);
|
|
680
|
+
}
|
|
681
|
+
needsComma = true;
|
|
682
|
+
}
|
|
683
|
+
onObjectEnd();
|
|
684
|
+
if (_scanner.getToken() !== 2) {
|
|
685
|
+
handleError(7, [2], []);
|
|
686
|
+
} else {
|
|
687
|
+
scanNext();
|
|
688
|
+
}
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
function parseArray() {
|
|
692
|
+
onArrayBegin();
|
|
693
|
+
scanNext();
|
|
694
|
+
let isFirstElement = true;
|
|
695
|
+
let needsComma = false;
|
|
696
|
+
while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
|
|
697
|
+
if (_scanner.getToken() === 5) {
|
|
698
|
+
if (!needsComma) {
|
|
699
|
+
handleError(4, [], []);
|
|
700
|
+
}
|
|
701
|
+
onSeparator(",");
|
|
702
|
+
scanNext();
|
|
703
|
+
if (_scanner.getToken() === 4 && allowTrailingComma) {
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
} else if (needsComma) {
|
|
707
|
+
handleError(6, [], []);
|
|
708
|
+
}
|
|
709
|
+
if (isFirstElement) {
|
|
710
|
+
_jsonPath.push(0);
|
|
711
|
+
isFirstElement = false;
|
|
712
|
+
} else {
|
|
713
|
+
_jsonPath[_jsonPath.length - 1]++;
|
|
714
|
+
}
|
|
715
|
+
if (!parseValue()) {
|
|
716
|
+
handleError(4, [], [4, 5]);
|
|
717
|
+
}
|
|
718
|
+
needsComma = true;
|
|
719
|
+
}
|
|
720
|
+
onArrayEnd();
|
|
721
|
+
if (!isFirstElement) {
|
|
722
|
+
_jsonPath.pop();
|
|
723
|
+
}
|
|
724
|
+
if (_scanner.getToken() !== 4) {
|
|
725
|
+
handleError(8, [4], []);
|
|
726
|
+
} else {
|
|
727
|
+
scanNext();
|
|
728
|
+
}
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
function parseValue() {
|
|
732
|
+
switch (_scanner.getToken()) {
|
|
733
|
+
case 3:
|
|
734
|
+
return parseArray();
|
|
735
|
+
case 1:
|
|
736
|
+
return parseObject();
|
|
737
|
+
case 10:
|
|
738
|
+
return parseString(true);
|
|
739
|
+
default:
|
|
740
|
+
return parseLiteral();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
scanNext();
|
|
744
|
+
if (_scanner.getToken() === 17) {
|
|
745
|
+
if (options.allowEmptyContent) {
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
handleError(4, [], []);
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
if (!parseValue()) {
|
|
752
|
+
handleError(4, [], []);
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
if (_scanner.getToken() !== 17) {
|
|
756
|
+
handleError(9, [], []);
|
|
757
|
+
}
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// node_modules/jsonc-parser/lib/esm/main.js
|
|
762
|
+
var ScanError;
|
|
763
|
+
(function(ScanError2) {
|
|
764
|
+
ScanError2[ScanError2["None"] = 0] = "None";
|
|
765
|
+
ScanError2[ScanError2["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
|
|
766
|
+
ScanError2[ScanError2["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
|
|
767
|
+
ScanError2[ScanError2["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
|
|
768
|
+
ScanError2[ScanError2["InvalidUnicode"] = 4] = "InvalidUnicode";
|
|
769
|
+
ScanError2[ScanError2["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
|
|
770
|
+
ScanError2[ScanError2["InvalidCharacter"] = 6] = "InvalidCharacter";
|
|
771
|
+
})(ScanError || (ScanError = {}));
|
|
772
|
+
var SyntaxKind;
|
|
773
|
+
(function(SyntaxKind2) {
|
|
774
|
+
SyntaxKind2[SyntaxKind2["OpenBraceToken"] = 1] = "OpenBraceToken";
|
|
775
|
+
SyntaxKind2[SyntaxKind2["CloseBraceToken"] = 2] = "CloseBraceToken";
|
|
776
|
+
SyntaxKind2[SyntaxKind2["OpenBracketToken"] = 3] = "OpenBracketToken";
|
|
777
|
+
SyntaxKind2[SyntaxKind2["CloseBracketToken"] = 4] = "CloseBracketToken";
|
|
778
|
+
SyntaxKind2[SyntaxKind2["CommaToken"] = 5] = "CommaToken";
|
|
779
|
+
SyntaxKind2[SyntaxKind2["ColonToken"] = 6] = "ColonToken";
|
|
780
|
+
SyntaxKind2[SyntaxKind2["NullKeyword"] = 7] = "NullKeyword";
|
|
781
|
+
SyntaxKind2[SyntaxKind2["TrueKeyword"] = 8] = "TrueKeyword";
|
|
782
|
+
SyntaxKind2[SyntaxKind2["FalseKeyword"] = 9] = "FalseKeyword";
|
|
783
|
+
SyntaxKind2[SyntaxKind2["StringLiteral"] = 10] = "StringLiteral";
|
|
784
|
+
SyntaxKind2[SyntaxKind2["NumericLiteral"] = 11] = "NumericLiteral";
|
|
785
|
+
SyntaxKind2[SyntaxKind2["LineCommentTrivia"] = 12] = "LineCommentTrivia";
|
|
786
|
+
SyntaxKind2[SyntaxKind2["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
|
|
787
|
+
SyntaxKind2[SyntaxKind2["LineBreakTrivia"] = 14] = "LineBreakTrivia";
|
|
788
|
+
SyntaxKind2[SyntaxKind2["Trivia"] = 15] = "Trivia";
|
|
789
|
+
SyntaxKind2[SyntaxKind2["Unknown"] = 16] = "Unknown";
|
|
790
|
+
SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
|
|
791
|
+
})(SyntaxKind || (SyntaxKind = {}));
|
|
792
|
+
var parse2 = parse;
|
|
793
|
+
var ParseErrorCode;
|
|
794
|
+
(function(ParseErrorCode2) {
|
|
795
|
+
ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
796
|
+
ParseErrorCode2[ParseErrorCode2["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
|
|
797
|
+
ParseErrorCode2[ParseErrorCode2["PropertyNameExpected"] = 3] = "PropertyNameExpected";
|
|
798
|
+
ParseErrorCode2[ParseErrorCode2["ValueExpected"] = 4] = "ValueExpected";
|
|
799
|
+
ParseErrorCode2[ParseErrorCode2["ColonExpected"] = 5] = "ColonExpected";
|
|
800
|
+
ParseErrorCode2[ParseErrorCode2["CommaExpected"] = 6] = "CommaExpected";
|
|
801
|
+
ParseErrorCode2[ParseErrorCode2["CloseBraceExpected"] = 7] = "CloseBraceExpected";
|
|
802
|
+
ParseErrorCode2[ParseErrorCode2["CloseBracketExpected"] = 8] = "CloseBracketExpected";
|
|
803
|
+
ParseErrorCode2[ParseErrorCode2["EndOfFileExpected"] = 9] = "EndOfFileExpected";
|
|
804
|
+
ParseErrorCode2[ParseErrorCode2["InvalidCommentToken"] = 10] = "InvalidCommentToken";
|
|
805
|
+
ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
|
|
806
|
+
ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
|
|
807
|
+
ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
|
|
808
|
+
ParseErrorCode2[ParseErrorCode2["InvalidUnicode"] = 14] = "InvalidUnicode";
|
|
809
|
+
ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
|
|
810
|
+
ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
|
|
811
|
+
})(ParseErrorCode || (ParseErrorCode = {}));
|
|
9
812
|
|
|
10
813
|
// src/config/schema.ts
|
|
11
814
|
import { z } from "zod";
|
|
@@ -101,7 +904,8 @@ var WeaveConfigSchema = z.object({
|
|
|
101
904
|
analytics: AnalyticsConfigSchema.optional(),
|
|
102
905
|
tmux: TmuxConfigSchema.optional(),
|
|
103
906
|
experimental: ExperimentalConfigSchema.optional(),
|
|
104
|
-
workflows: WorkflowConfigSchema.optional()
|
|
907
|
+
workflows: WorkflowConfigSchema.optional(),
|
|
908
|
+
log_level: z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).optional()
|
|
105
909
|
});
|
|
106
910
|
|
|
107
911
|
// src/config/merge.ts
|
|
@@ -141,30 +945,56 @@ function mergeConfigs(user, project) {
|
|
|
141
945
|
}
|
|
142
946
|
|
|
143
947
|
// src/shared/log.ts
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
948
|
+
var LEVEL_PRIORITY = {
|
|
949
|
+
DEBUG: 0,
|
|
950
|
+
INFO: 1,
|
|
951
|
+
WARN: 2,
|
|
952
|
+
ERROR: 3
|
|
953
|
+
};
|
|
954
|
+
function parseLogLevel(value) {
|
|
955
|
+
if (value === "DEBUG" || value === "INFO" || value === "WARN" || value === "ERROR") {
|
|
956
|
+
return value;
|
|
957
|
+
}
|
|
958
|
+
return "INFO";
|
|
150
959
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
960
|
+
var activeLevel = parseLogLevel(process.env.WEAVE_LOG_LEVEL);
|
|
961
|
+
var client = null;
|
|
962
|
+
function setClient(c) {
|
|
963
|
+
client = c;
|
|
964
|
+
}
|
|
965
|
+
function setLogLevel(level) {
|
|
966
|
+
activeLevel = level;
|
|
967
|
+
}
|
|
968
|
+
function shouldLog(level) {
|
|
969
|
+
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[activeLevel];
|
|
970
|
+
}
|
|
971
|
+
function emit(level, message, data) {
|
|
972
|
+
if (!shouldLog(level))
|
|
973
|
+
return;
|
|
974
|
+
const appRef = client?.app;
|
|
975
|
+
if (appRef && typeof appRef.log === "function") {
|
|
976
|
+
const extra = data !== undefined ? typeof data === "object" && data !== null ? data : { value: data } : undefined;
|
|
977
|
+
appRef.log({ body: { service: "weave", level: level.toLowerCase(), message, extra } }).catch(() => {});
|
|
978
|
+
} else {
|
|
979
|
+
if (level === "ERROR" || level === "WARN") {
|
|
980
|
+
console.error(`[weave:${level}] ${message}`, data ?? "");
|
|
156
981
|
}
|
|
157
|
-
}
|
|
158
|
-
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function debug(message, data) {
|
|
985
|
+
emit("DEBUG", message, data);
|
|
986
|
+
}
|
|
987
|
+
function info(message, data) {
|
|
988
|
+
emit("INFO", message, data);
|
|
989
|
+
}
|
|
990
|
+
function warn(message, data) {
|
|
991
|
+
emit("WARN", message, data);
|
|
992
|
+
}
|
|
993
|
+
function error(message, data) {
|
|
994
|
+
emit("ERROR", message, data);
|
|
159
995
|
}
|
|
160
|
-
var LOG_FILE = resolveLogFile();
|
|
161
996
|
function log(message, data) {
|
|
162
|
-
|
|
163
|
-
const timestamp = new Date().toISOString();
|
|
164
|
-
const entry = `[${timestamp}] ${message}${data !== undefined ? " " + JSON.stringify(data) : ""}
|
|
165
|
-
`;
|
|
166
|
-
fs.appendFileSync(LOG_FILE, entry);
|
|
167
|
-
} catch {}
|
|
997
|
+
info(message, data);
|
|
168
998
|
}
|
|
169
999
|
function logDelegation(event) {
|
|
170
1000
|
const prefix = `[delegation:${event.phase}]`;
|
|
@@ -181,38 +1011,49 @@ function readJsoncFile(filePath) {
|
|
|
181
1011
|
try {
|
|
182
1012
|
const text = readFileSync(filePath, "utf-8");
|
|
183
1013
|
const errors = [];
|
|
184
|
-
const parsed =
|
|
1014
|
+
const parsed = parse2(text, errors);
|
|
185
1015
|
if (errors.length > 0) {
|
|
186
|
-
|
|
1016
|
+
warn(`JSONC parse warnings in ${filePath}: ${errors.length} issue(s)`);
|
|
187
1017
|
}
|
|
188
1018
|
return parsed ?? {};
|
|
189
1019
|
} catch (e) {
|
|
190
|
-
|
|
1020
|
+
error(`Failed to read config file ${filePath}`, e);
|
|
191
1021
|
return {};
|
|
192
1022
|
}
|
|
193
1023
|
}
|
|
194
1024
|
function detectConfigFile(basePath) {
|
|
195
1025
|
const jsoncPath = basePath + ".jsonc";
|
|
196
|
-
if (
|
|
1026
|
+
if (existsSync(jsoncPath))
|
|
197
1027
|
return jsoncPath;
|
|
198
1028
|
const jsonPath = basePath + ".json";
|
|
199
|
-
if (
|
|
1029
|
+
if (existsSync(jsonPath))
|
|
200
1030
|
return jsonPath;
|
|
201
1031
|
return null;
|
|
202
1032
|
}
|
|
203
1033
|
function loadWeaveConfig(directory, _ctx, _homeDir) {
|
|
204
|
-
const userBasePath =
|
|
205
|
-
const projectBasePath =
|
|
1034
|
+
const userBasePath = join(_homeDir ?? homedir(), ".config", "opencode", "weave-opencode");
|
|
1035
|
+
const projectBasePath = join(directory, ".opencode", "weave-opencode");
|
|
206
1036
|
const userConfigPath = detectConfigFile(userBasePath);
|
|
207
1037
|
const projectConfigPath = detectConfigFile(projectBasePath);
|
|
1038
|
+
debug("Loading Weave config", {
|
|
1039
|
+
userConfig: userConfigPath ?? "(none)",
|
|
1040
|
+
projectConfig: projectConfigPath ?? "(none)"
|
|
1041
|
+
});
|
|
208
1042
|
const userRaw = userConfigPath ? readJsoncFile(userConfigPath) : {};
|
|
209
1043
|
const projectRaw = projectConfigPath ? readJsoncFile(projectConfigPath) : {};
|
|
210
1044
|
const merged = mergeConfigs(userRaw, projectRaw);
|
|
211
1045
|
const result = WeaveConfigSchema.safeParse(merged);
|
|
212
1046
|
if (!result.success) {
|
|
213
|
-
|
|
1047
|
+
error("WeaveConfig validation errors — using defaults", result.error.issues);
|
|
214
1048
|
return WeaveConfigSchema.parse({});
|
|
215
1049
|
}
|
|
1050
|
+
debug("Weave config loaded successfully", {
|
|
1051
|
+
hasAgentOverrides: !!result.data.agents && Object.keys(result.data.agents).length > 0,
|
|
1052
|
+
disabledAgents: result.data.disabled_agents ?? [],
|
|
1053
|
+
customAgents: result.data.custom_agents ? Object.keys(result.data.custom_agents) : [],
|
|
1054
|
+
logLevel: result.data.log_level ?? "(default)",
|
|
1055
|
+
analyticsEnabled: result.data.analytics?.enabled ?? false
|
|
1056
|
+
});
|
|
216
1057
|
return result.data;
|
|
217
1058
|
}
|
|
218
1059
|
|
|
@@ -510,9 +1351,9 @@ class BackgroundManager {
|
|
|
510
1351
|
}
|
|
511
1352
|
|
|
512
1353
|
// src/managers/skill-mcp-manager.ts
|
|
513
|
-
function createStdioClient(config,
|
|
1354
|
+
function createStdioClient(config, info2) {
|
|
514
1355
|
const { command, args = [] } = config;
|
|
515
|
-
const { serverName, skillName } =
|
|
1356
|
+
const { serverName, skillName } = info2;
|
|
516
1357
|
if (!command) {
|
|
517
1358
|
throw new Error(`missing 'command' field for stdio MCP server '${serverName}' in skill '${skillName}'`);
|
|
518
1359
|
}
|
|
@@ -541,28 +1382,28 @@ function createStdioClient(config, info) {
|
|
|
541
1382
|
}
|
|
542
1383
|
};
|
|
543
1384
|
}
|
|
544
|
-
function getClientKey(
|
|
545
|
-
return `${
|
|
1385
|
+
function getClientKey(info2) {
|
|
1386
|
+
return `${info2.sessionID}:${info2.skillName}:${info2.serverName}`;
|
|
546
1387
|
}
|
|
547
1388
|
|
|
548
1389
|
class SkillMcpManager {
|
|
549
1390
|
clients = new Map;
|
|
550
|
-
async getOrCreateClient(
|
|
551
|
-
const key = getClientKey(
|
|
1391
|
+
async getOrCreateClient(info2, config) {
|
|
1392
|
+
const key = getClientKey(info2);
|
|
552
1393
|
const existing = this.clients.get(key);
|
|
553
1394
|
if (existing) {
|
|
554
1395
|
return existing;
|
|
555
1396
|
}
|
|
556
|
-
const { serverName, skillName } =
|
|
1397
|
+
const { serverName, skillName } = info2;
|
|
557
1398
|
if (config.type === "http") {
|
|
558
1399
|
throw new Error("HTTP MCP not supported in v1");
|
|
559
1400
|
}
|
|
560
1401
|
if (!config.command) {
|
|
561
1402
|
throw new Error(`missing 'command' field for stdio MCP server '${serverName}' in skill '${skillName}'`);
|
|
562
1403
|
}
|
|
563
|
-
const
|
|
564
|
-
this.clients.set(key,
|
|
565
|
-
return
|
|
1404
|
+
const client2 = createStdioClient(config, info2);
|
|
1405
|
+
this.clients.set(key, client2);
|
|
1406
|
+
return client2;
|
|
566
1407
|
}
|
|
567
1408
|
async disconnectSession(sessionID) {
|
|
568
1409
|
const prefix = `${sessionID}:`;
|
|
@@ -573,16 +1414,16 @@ class SkillMcpManager {
|
|
|
573
1414
|
}
|
|
574
1415
|
}
|
|
575
1416
|
await Promise.all(keysToRemove.map(async (key) => {
|
|
576
|
-
const
|
|
577
|
-
if (
|
|
578
|
-
await
|
|
1417
|
+
const client2 = this.clients.get(key);
|
|
1418
|
+
if (client2) {
|
|
1419
|
+
await client2.close();
|
|
579
1420
|
this.clients.delete(key);
|
|
580
1421
|
}
|
|
581
1422
|
}));
|
|
582
1423
|
}
|
|
583
1424
|
async disconnectAll() {
|
|
584
|
-
await Promise.all(Array.from(this.clients.entries()).map(async ([key,
|
|
585
|
-
await
|
|
1425
|
+
await Promise.all(Array.from(this.clients.entries()).map(async ([key, client2]) => {
|
|
1426
|
+
await client2.close();
|
|
586
1427
|
this.clients.delete(key);
|
|
587
1428
|
}));
|
|
588
1429
|
this.clients.clear();
|
|
@@ -590,19 +1431,19 @@ class SkillMcpManager {
|
|
|
590
1431
|
getConnectedServers() {
|
|
591
1432
|
return Array.from(this.clients.keys());
|
|
592
1433
|
}
|
|
593
|
-
isConnected(
|
|
594
|
-
return this.clients.has(getClientKey(
|
|
1434
|
+
isConnected(info2) {
|
|
1435
|
+
return this.clients.has(getClientKey(info2));
|
|
595
1436
|
}
|
|
596
|
-
async callTool(
|
|
1437
|
+
async callTool(info2, config, name, args) {
|
|
597
1438
|
const maxAttempts = 3;
|
|
598
1439
|
let lastError = null;
|
|
599
1440
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
600
1441
|
try {
|
|
601
|
-
const
|
|
602
|
-
const result = await
|
|
1442
|
+
const client2 = await this.getOrCreateClient(info2, config);
|
|
1443
|
+
const result = await client2.callTool({ name, arguments: args });
|
|
603
1444
|
return result.content;
|
|
604
|
-
} catch (
|
|
605
|
-
lastError =
|
|
1445
|
+
} catch (error2) {
|
|
1446
|
+
lastError = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
606
1447
|
if (!lastError.message.toLowerCase().includes("not connected")) {
|
|
607
1448
|
throw lastError;
|
|
608
1449
|
}
|
|
@@ -729,8 +1570,6 @@ function buildDelegationSection(disabled) {
|
|
|
729
1570
|
lines.push("- MUST use Warp for security audits when changes touch auth, crypto, certificates, tokens, signatures, input validation, secrets, passwords, sessions, CORS, CSP, .env files, or OAuth/OIDC/SAML flows — not optional.");
|
|
730
1571
|
}
|
|
731
1572
|
lines.push("- Delegate aggressively to keep your context lean");
|
|
732
|
-
lines.push("");
|
|
733
|
-
lines.push('RATIONALIZATION CHECK: If you catch yourself thinking "this is just a quick fix" but it touches 3+ files — delegate. Quick fixes that grow are the most common failure mode. When in doubt, delegate.');
|
|
734
1573
|
return `<Delegation>
|
|
735
1574
|
${lines.join(`
|
|
736
1575
|
`)}
|
|
@@ -995,33 +1834,6 @@ After completing work for each task — BEFORE marking \`- [ ]\` → \`- [x]\`:
|
|
|
995
1834
|
**Gate**: Only mark complete when ALL checks pass. If ANY check fails, fix first.
|
|
996
1835
|
</Verification>`;
|
|
997
1836
|
}
|
|
998
|
-
function buildTapestryVerificationGateSection() {
|
|
999
|
-
return `<VerificationGate>
|
|
1000
|
-
BEFORE claiming ANY status — "done", "passes", "works", "fixed", "complete":
|
|
1001
|
-
|
|
1002
|
-
1. IDENTIFY: What command proves this claim? (test runner, build, linter, curl, etc.)
|
|
1003
|
-
2. RUN: Execute the command NOW — fresh, complete, in this message
|
|
1004
|
-
3. READ: Check exit code, count failures, read full output
|
|
1005
|
-
4. VERIFY: Does the output confirm the claim?
|
|
1006
|
-
- YES → State the claim WITH the evidence
|
|
1007
|
-
- NO → State actual status with evidence. Fix. Re-run.
|
|
1008
|
-
|
|
1009
|
-
| Claim | Requires | NOT Sufficient |
|
|
1010
|
-
|-------|----------|----------------|
|
|
1011
|
-
| "Tests pass" | Test command output showing 0 failures | Previous run, "should pass", partial suite |
|
|
1012
|
-
| "Build succeeds" | Build command with exit 0 | Linter passing, "looks correct" |
|
|
1013
|
-
| "Bug is fixed" | Failing test now passes | "Code changed, should be fixed" |
|
|
1014
|
-
| "No regressions" | Full test suite output | Spot-checking a few files |
|
|
1015
|
-
|
|
1016
|
-
RED FLAGS — if you catch yourself writing these, STOP:
|
|
1017
|
-
- "should", "probably", "seems to", "looks correct"
|
|
1018
|
-
- "Great!", "Done!", "Perfect!" before running verification
|
|
1019
|
-
- Claiming completion based on a previous run
|
|
1020
|
-
- Trusting your own Edit/Write calls without reading the result
|
|
1021
|
-
|
|
1022
|
-
**Verification you didn't run in this message does not exist.**
|
|
1023
|
-
</VerificationGate>`;
|
|
1024
|
-
}
|
|
1025
1837
|
function buildTapestryPostExecutionReviewSection(disabled) {
|
|
1026
1838
|
const hasWeft = isAgentEnabled("weft", disabled);
|
|
1027
1839
|
const hasWarp = isAgentEnabled("warp", disabled);
|
|
@@ -1067,30 +1879,6 @@ function buildTapestryExecutionSection() {
|
|
|
1067
1879
|
- Report completion with evidence (test output, file paths, commands run)
|
|
1068
1880
|
</Execution>`;
|
|
1069
1881
|
}
|
|
1070
|
-
function buildTapestryDebuggingSection() {
|
|
1071
|
-
return `<WhenStuck>
|
|
1072
|
-
When a task fails or produces unexpected results:
|
|
1073
|
-
|
|
1074
|
-
1. **Read error messages completely** — stack traces, line numbers, exit codes. They often contain the answer.
|
|
1075
|
-
2. **Form a single hypothesis** — "I think X is the root cause because Y." Be specific.
|
|
1076
|
-
3. **Make the smallest possible change** to test that hypothesis. One variable at a time.
|
|
1077
|
-
4. **Verify** — did it work? If yes, continue. If no, form a NEW hypothesis.
|
|
1078
|
-
|
|
1079
|
-
ESCALATION RULE:
|
|
1080
|
-
- Fix attempt #1 failed → re-read errors, try different hypothesis
|
|
1081
|
-
- Fix attempt #2 failed → step back, trace the data flow from source to error
|
|
1082
|
-
- Fix attempt #3 failed → **STOP. Do NOT attempt fix #4.**
|
|
1083
|
-
- Document: what you tried, what happened, what you think the root cause is
|
|
1084
|
-
- Report to the user: "Blocked after 3 attempts on task N. Here's what I've tried: [...]"
|
|
1085
|
-
- This is likely an architectural issue, not a code bug. The user needs to decide.
|
|
1086
|
-
|
|
1087
|
-
RED FLAGS — you are debugging wrong if you:
|
|
1088
|
-
- Propose fixes without reading the error message carefully
|
|
1089
|
-
- Change multiple things at once ("shotgun debugging")
|
|
1090
|
-
- Re-try the same approach hoping for a different result
|
|
1091
|
-
- Think "just one more fix" after 2 failures
|
|
1092
|
-
</WhenStuck>`;
|
|
1093
|
-
}
|
|
1094
1882
|
function buildTapestryStyleSection() {
|
|
1095
1883
|
return `<Style>
|
|
1096
1884
|
- Terse status updates only
|
|
@@ -1106,10 +1894,8 @@ function composeTapestryPrompt(options = {}) {
|
|
|
1106
1894
|
buildTapestrySidebarTodosSection(),
|
|
1107
1895
|
buildTapestryPlanExecutionSection(disabled),
|
|
1108
1896
|
buildTapestryVerificationSection(),
|
|
1109
|
-
buildTapestryVerificationGateSection(),
|
|
1110
1897
|
buildTapestryPostExecutionReviewSection(disabled),
|
|
1111
1898
|
buildTapestryExecutionSection(),
|
|
1112
|
-
buildTapestryDebuggingSection(),
|
|
1113
1899
|
buildTapestryStyleSection()
|
|
1114
1900
|
];
|
|
1115
1901
|
return sections.join(`
|
|
@@ -1270,30 +2056,6 @@ FILES FIELD: For verification-only tasks that have no associated files (e.g., "r
|
|
|
1270
2056
|
- After completing a plan, tell the user: "Plan saved to \`.weave/plans/{name}.md\`. Run /start-work to begin execution."
|
|
1271
2057
|
</Constraints>
|
|
1272
2058
|
|
|
1273
|
-
<NoPlaceholders>
|
|
1274
|
-
Every task must contain the actual detail an engineer needs to start working. These are PLAN FAILURES — never write them:
|
|
1275
|
-
|
|
1276
|
-
- "TBD", "TODO", "implement later", "fill in details"
|
|
1277
|
-
- "Add appropriate error handling" / "add validation" / "handle edge cases"
|
|
1278
|
-
- "Write tests for the above" (without describing what to test)
|
|
1279
|
-
- "Similar to Task N" (repeat the detail — the executor may read tasks independently)
|
|
1280
|
-
- Steps that describe WHAT to do without specifying HOW (file paths, approach, acceptance criteria required)
|
|
1281
|
-
- References to types, functions, or files that aren't defined or explained in any task
|
|
1282
|
-
|
|
1283
|
-
If you can't specify something concretely, you haven't researched enough. Go read more code.
|
|
1284
|
-
</NoPlaceholders>
|
|
1285
|
-
|
|
1286
|
-
<SelfReview>
|
|
1287
|
-
After writing the complete plan, review it with fresh eyes:
|
|
1288
|
-
|
|
1289
|
-
1. **Requirement coverage**: Re-read the original request. Can you point to a task for each requirement? List any gaps.
|
|
1290
|
-
2. **Placeholder scan**: Search your plan for any patterns from the \`<NoPlaceholders>\` list above. Fix them.
|
|
1291
|
-
3. **Name consistency**: Do file paths, function names, and type names used in later tasks match what you defined in earlier tasks? A function called \`createUser()\` in Task 2 but \`addUser()\` in Task 5 is a bug.
|
|
1292
|
-
4. **Dependency order**: Can each task be started after completing only the tasks before it? If Task 4 depends on Task 6, reorder.
|
|
1293
|
-
|
|
1294
|
-
Fix any issues inline. Then report the plan as complete.
|
|
1295
|
-
</SelfReview>
|
|
1296
|
-
|
|
1297
2059
|
<Research>
|
|
1298
2060
|
- Read relevant files before planning
|
|
1299
2061
|
- Check existing patterns in the codebase
|
|
@@ -1422,10 +2184,9 @@ You operate in two modes depending on what you're asked to review:
|
|
|
1422
2184
|
|
|
1423
2185
|
**Work Review** (reviewing completed implementation):
|
|
1424
2186
|
- Read every changed file (use git diff --stat, then Read each file)
|
|
1425
|
-
-
|
|
1426
|
-
-
|
|
1427
|
-
-
|
|
1428
|
-
- Verify tests exist and test real behavior (not mocks of mocks)
|
|
2187
|
+
- Check the code actually does what the task required
|
|
2188
|
+
- Look for stubs, TODOs, placeholders, hardcoded values
|
|
2189
|
+
- Verify tests exist and test real behavior
|
|
1429
2190
|
- Check for scope creep (changes outside the task spec)
|
|
1430
2191
|
</ReviewModes>
|
|
1431
2192
|
|
|
@@ -1733,34 +2494,47 @@ var AGENT_MODEL_REQUIREMENTS = {
|
|
|
1733
2494
|
function resolveAgentModel(agentName, options) {
|
|
1734
2495
|
const { availableModels, agentMode, uiSelectedModel, categoryModel, overrideModel, systemDefaultModel, customFallbackChain } = options;
|
|
1735
2496
|
const requirement = AGENT_MODEL_REQUIREMENTS[agentName];
|
|
1736
|
-
if (overrideModel)
|
|
2497
|
+
if (overrideModel) {
|
|
2498
|
+
debug(`Model resolved for "${agentName}"`, { via: "override", model: overrideModel });
|
|
1737
2499
|
return overrideModel;
|
|
2500
|
+
}
|
|
1738
2501
|
if (uiSelectedModel && (agentMode === "primary" || agentMode === "all")) {
|
|
2502
|
+
debug(`Model resolved for "${agentName}"`, { via: "ui-selection", model: uiSelectedModel, agentMode });
|
|
1739
2503
|
return uiSelectedModel;
|
|
1740
2504
|
}
|
|
1741
|
-
if (categoryModel && availableModels.has(categoryModel))
|
|
2505
|
+
if (categoryModel && availableModels.has(categoryModel)) {
|
|
2506
|
+
debug(`Model resolved for "${agentName}"`, { via: "category", model: categoryModel });
|
|
1742
2507
|
return categoryModel;
|
|
2508
|
+
}
|
|
1743
2509
|
const fallbackChain = requirement?.fallbackChain ?? customFallbackChain;
|
|
1744
2510
|
if (fallbackChain) {
|
|
1745
2511
|
for (const entry of fallbackChain) {
|
|
1746
2512
|
for (const provider of entry.providers) {
|
|
1747
2513
|
const qualified = `${provider}/${entry.model}`;
|
|
1748
|
-
if (availableModels.has(qualified))
|
|
2514
|
+
if (availableModels.has(qualified)) {
|
|
2515
|
+
debug(`Model resolved for "${agentName}"`, { via: "fallback-chain", model: qualified });
|
|
1749
2516
|
return qualified;
|
|
1750
|
-
|
|
2517
|
+
}
|
|
2518
|
+
if (availableModels.has(entry.model)) {
|
|
2519
|
+
debug(`Model resolved for "${agentName}"`, { via: "fallback-chain", model: entry.model });
|
|
1751
2520
|
return entry.model;
|
|
2521
|
+
}
|
|
1752
2522
|
}
|
|
1753
2523
|
}
|
|
1754
2524
|
}
|
|
1755
|
-
if (systemDefaultModel)
|
|
2525
|
+
if (systemDefaultModel) {
|
|
2526
|
+
debug(`Model resolved for "${agentName}"`, { via: "system-default", model: systemDefaultModel });
|
|
1756
2527
|
return systemDefaultModel;
|
|
2528
|
+
}
|
|
1757
2529
|
if (fallbackChain && fallbackChain.length > 0) {
|
|
1758
2530
|
const first = fallbackChain[0];
|
|
1759
2531
|
if (first.providers.length > 0) {
|
|
1760
|
-
|
|
2532
|
+
const guessed = `${first.providers[0]}/${first.model}`;
|
|
2533
|
+
debug(`Model resolved for "${agentName}" (offline best-guess — no available models matched)`, { via: "offline-guess", model: guessed });
|
|
2534
|
+
return guessed;
|
|
1761
2535
|
}
|
|
1762
2536
|
}
|
|
1763
|
-
|
|
2537
|
+
warn(`No model resolved for agent "${agentName}" — falling back to default github-copilot/claude-opus-4.6`, { agentName });
|
|
1764
2538
|
return "github-copilot/claude-opus-4.6";
|
|
1765
2539
|
}
|
|
1766
2540
|
|
|
@@ -1873,8 +2647,10 @@ function createBuiltinAgents(options = {}) {
|
|
|
1873
2647
|
const disabledSet = new Set(disabledAgents);
|
|
1874
2648
|
const result = {};
|
|
1875
2649
|
for (const [name, factory] of Object.entries(AGENT_FACTORIES)) {
|
|
1876
|
-
if (disabledSet.has(name))
|
|
2650
|
+
if (disabledSet.has(name)) {
|
|
2651
|
+
debug(`Builtin agent "${name}" is disabled — skipping`);
|
|
1877
2652
|
continue;
|
|
2653
|
+
}
|
|
1878
2654
|
const override = agentOverrides[name];
|
|
1879
2655
|
const overrideModel = override?.model;
|
|
1880
2656
|
const resolvedModel = resolveAgentModel(name, {
|
|
@@ -1884,6 +2660,9 @@ function createBuiltinAgents(options = {}) {
|
|
|
1884
2660
|
systemDefaultModel,
|
|
1885
2661
|
overrideModel
|
|
1886
2662
|
});
|
|
2663
|
+
if (overrideModel) {
|
|
2664
|
+
debug(`Builtin agent "${name}" model overridden via config`, { model: resolvedModel });
|
|
2665
|
+
}
|
|
1887
2666
|
let built;
|
|
1888
2667
|
if (name === "loom") {
|
|
1889
2668
|
built = createLoomAgentWithOptions(resolvedModel, disabledSet, fingerprint, customAgentMetadata);
|
|
@@ -1921,7 +2700,7 @@ function createBuiltinAgents(options = {}) {
|
|
|
1921
2700
|
}
|
|
1922
2701
|
|
|
1923
2702
|
// src/agents/prompt-loader.ts
|
|
1924
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
2703
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
1925
2704
|
import { resolve, isAbsolute as isAbsolute2, normalize, sep } from "path";
|
|
1926
2705
|
function loadPromptFile(promptFilePath, basePath) {
|
|
1927
2706
|
if (isAbsolute2(promptFilePath)) {
|
|
@@ -1932,7 +2711,7 @@ function loadPromptFile(promptFilePath, basePath) {
|
|
|
1932
2711
|
if (!resolvedPath.startsWith(base + sep) && resolvedPath !== base) {
|
|
1933
2712
|
return null;
|
|
1934
2713
|
}
|
|
1935
|
-
if (!
|
|
2714
|
+
if (!existsSync2(resolvedPath)) {
|
|
1936
2715
|
return null;
|
|
1937
2716
|
}
|
|
1938
2717
|
return readFileSync2(resolvedPath, "utf-8").trim();
|
|
@@ -1968,11 +2747,17 @@ function buildCustomAgent(name, config, options = {}) {
|
|
|
1968
2747
|
}
|
|
1969
2748
|
const { resolveSkills, disabledSkills, availableModels = new Set, systemDefaultModel, uiSelectedModel, configDir } = options;
|
|
1970
2749
|
let prompt = config.prompt ?? "";
|
|
2750
|
+
let promptSource = "inline";
|
|
1971
2751
|
if (config.prompt_file) {
|
|
1972
2752
|
const fileContent = loadPromptFile(config.prompt_file, configDir);
|
|
1973
2753
|
if (fileContent) {
|
|
1974
2754
|
prompt = fileContent;
|
|
2755
|
+
promptSource = `file:${config.prompt_file}`;
|
|
2756
|
+
} else {
|
|
2757
|
+
promptSource = `file:${config.prompt_file} (not found — falling back to inline)`;
|
|
1975
2758
|
}
|
|
2759
|
+
} else if (config.skills?.length) {
|
|
2760
|
+
promptSource = `skills:[${config.skills.join(",")}]`;
|
|
1976
2761
|
}
|
|
1977
2762
|
if (config.skills?.length && resolveSkills) {
|
|
1978
2763
|
const skillContent = resolveSkills(config.skills, disabledSkills);
|
|
@@ -1995,6 +2780,13 @@ function buildCustomAgent(name, config, options = {}) {
|
|
|
1995
2780
|
const displayName = config.display_name ?? name;
|
|
1996
2781
|
registerAgentDisplayName(name, displayName);
|
|
1997
2782
|
registerAgentNameVariants(name, displayName !== name ? [name, displayName] : undefined);
|
|
2783
|
+
debug(`Custom agent "${name}" built`, {
|
|
2784
|
+
model,
|
|
2785
|
+
displayName,
|
|
2786
|
+
mode,
|
|
2787
|
+
promptSource,
|
|
2788
|
+
hasPrompt: !!prompt
|
|
2789
|
+
});
|
|
1998
2790
|
const agentConfig = {
|
|
1999
2791
|
model,
|
|
2000
2792
|
prompt: prompt || undefined,
|
|
@@ -2062,7 +2854,7 @@ function createManagers(options) {
|
|
|
2062
2854
|
}
|
|
2063
2855
|
} catch (err) {
|
|
2064
2856
|
if (err instanceof Error && err.message.includes("not a built-in agent")) {
|
|
2065
|
-
|
|
2857
|
+
debug(`Skipping display_name override for non-builtin agent "${name}"`);
|
|
2066
2858
|
} else {
|
|
2067
2859
|
throw err;
|
|
2068
2860
|
}
|
|
@@ -2095,8 +2887,8 @@ function createManagers(options) {
|
|
|
2095
2887
|
}
|
|
2096
2888
|
|
|
2097
2889
|
// src/features/skill-loader/loader.ts
|
|
2098
|
-
import * as
|
|
2099
|
-
import * as
|
|
2890
|
+
import * as path2 from "path";
|
|
2891
|
+
import * as os from "os";
|
|
2100
2892
|
|
|
2101
2893
|
// src/features/skill-loader/opencode-client.ts
|
|
2102
2894
|
function deriveScope(location) {
|
|
@@ -2111,11 +2903,11 @@ async function fetchSkillsFromOpenCode(serverUrl, directory) {
|
|
|
2111
2903
|
try {
|
|
2112
2904
|
response = await fetch(url, { signal: AbortSignal.timeout(3000) });
|
|
2113
2905
|
} catch (err) {
|
|
2114
|
-
|
|
2906
|
+
error("Failed to fetch skills from OpenCode — skills will not be loaded", { url, error: String(err) });
|
|
2115
2907
|
return [];
|
|
2116
2908
|
}
|
|
2117
2909
|
if (!response.ok) {
|
|
2118
|
-
|
|
2910
|
+
warn("OpenCode /skill endpoint returned non-OK status — skills will not be loaded", {
|
|
2119
2911
|
url,
|
|
2120
2912
|
status: response.status
|
|
2121
2913
|
});
|
|
@@ -2125,11 +2917,11 @@ async function fetchSkillsFromOpenCode(serverUrl, directory) {
|
|
|
2125
2917
|
try {
|
|
2126
2918
|
data = await response.json();
|
|
2127
2919
|
} catch (err) {
|
|
2128
|
-
|
|
2920
|
+
error("Failed to parse skills response from OpenCode", { url, error: String(err) });
|
|
2129
2921
|
return [];
|
|
2130
2922
|
}
|
|
2131
2923
|
if (!Array.isArray(data)) {
|
|
2132
|
-
|
|
2924
|
+
warn("Unexpected skills response shape from OpenCode — expected array", { url });
|
|
2133
2925
|
return [];
|
|
2134
2926
|
}
|
|
2135
2927
|
const skills = [];
|
|
@@ -2148,8 +2940,8 @@ async function fetchSkillsFromOpenCode(serverUrl, directory) {
|
|
|
2148
2940
|
}
|
|
2149
2941
|
|
|
2150
2942
|
// src/features/skill-loader/discovery.ts
|
|
2151
|
-
import * as
|
|
2152
|
-
import * as
|
|
2943
|
+
import * as fs from "fs";
|
|
2944
|
+
import * as path from "path";
|
|
2153
2945
|
function parseFrontmatter(text) {
|
|
2154
2946
|
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
|
|
2155
2947
|
const match = frontmatterRegex.exec(text);
|
|
@@ -2196,7 +2988,7 @@ function parseFrontmatter(text) {
|
|
|
2196
2988
|
i++;
|
|
2197
2989
|
}
|
|
2198
2990
|
} catch (err) {
|
|
2199
|
-
|
|
2991
|
+
warn("Failed to parse YAML frontmatter", { error: String(err) });
|
|
2200
2992
|
return { metadata: {}, content: text };
|
|
2201
2993
|
}
|
|
2202
2994
|
return { metadata, content: body };
|
|
@@ -2224,22 +3016,22 @@ function setMetadataField(metadata, key, value) {
|
|
|
2224
3016
|
}
|
|
2225
3017
|
function scanDirectory(options) {
|
|
2226
3018
|
const { directory, scope } = options;
|
|
2227
|
-
if (!
|
|
3019
|
+
if (!fs.existsSync(directory)) {
|
|
2228
3020
|
return [];
|
|
2229
3021
|
}
|
|
2230
3022
|
let entries;
|
|
2231
3023
|
try {
|
|
2232
|
-
entries =
|
|
3024
|
+
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
2233
3025
|
} catch (err) {
|
|
2234
|
-
|
|
3026
|
+
warn("Failed to read skills directory", { directory, error: String(err) });
|
|
2235
3027
|
return [];
|
|
2236
3028
|
}
|
|
2237
3029
|
const skills = [];
|
|
2238
3030
|
for (const entry of entries) {
|
|
2239
|
-
const fullPath =
|
|
3031
|
+
const fullPath = path.join(directory, entry.name);
|
|
2240
3032
|
if (entry.isDirectory()) {
|
|
2241
|
-
const skillFile =
|
|
2242
|
-
if (
|
|
3033
|
+
const skillFile = path.join(fullPath, "SKILL.md");
|
|
3034
|
+
if (fs.existsSync(skillFile)) {
|
|
2243
3035
|
const skill = loadSkillFile(skillFile, scope);
|
|
2244
3036
|
if (skill)
|
|
2245
3037
|
skills.push(skill);
|
|
@@ -2257,14 +3049,14 @@ function scanDirectory(options) {
|
|
|
2257
3049
|
function loadSkillFile(filePath, scope) {
|
|
2258
3050
|
let text;
|
|
2259
3051
|
try {
|
|
2260
|
-
text =
|
|
3052
|
+
text = fs.readFileSync(filePath, "utf8");
|
|
2261
3053
|
} catch (err) {
|
|
2262
|
-
|
|
3054
|
+
warn("Failed to read skill file", { filePath, error: String(err) });
|
|
2263
3055
|
return null;
|
|
2264
3056
|
}
|
|
2265
3057
|
const { metadata, content } = parseFrontmatter(text);
|
|
2266
3058
|
if (!metadata.name) {
|
|
2267
|
-
|
|
3059
|
+
debug("Skill file missing name in frontmatter — skipping", { filePath });
|
|
2268
3060
|
return null;
|
|
2269
3061
|
}
|
|
2270
3062
|
return { name: metadata.name, description: metadata.description ?? "", content, scope, path: filePath, model: metadata.model };
|
|
@@ -2292,8 +3084,8 @@ function resolveSafePath(dir, projectRoot) {
|
|
|
2292
3084
|
|
|
2293
3085
|
// src/features/skill-loader/loader.ts
|
|
2294
3086
|
function scanFilesystemSkills(directory, customDirs) {
|
|
2295
|
-
const userDir =
|
|
2296
|
-
const projectDir =
|
|
3087
|
+
const userDir = path2.join(os.homedir(), ".config", "opencode", "skills");
|
|
3088
|
+
const projectDir = path2.join(directory, ".opencode", "skills");
|
|
2297
3089
|
const userSkills = scanDirectory({ directory: userDir, scope: "user" });
|
|
2298
3090
|
const projectSkills = scanDirectory({ directory: projectDir, scope: "project" });
|
|
2299
3091
|
const customSkills = [];
|
|
@@ -2324,7 +3116,7 @@ async function loadSkills(options) {
|
|
|
2324
3116
|
const fsSkills = scanFilesystemSkills(directory, customDirs);
|
|
2325
3117
|
const skills = mergeSkillSources(apiSkills, fsSkills);
|
|
2326
3118
|
if (apiSkills.length === 0 && fsSkills.length > 0) {
|
|
2327
|
-
|
|
3119
|
+
debug("OpenCode API returned no skills — using filesystem fallback", {
|
|
2328
3120
|
fsSkillCount: fsSkills.length,
|
|
2329
3121
|
fsSkillNames: fsSkills.map((s) => s.name)
|
|
2330
3122
|
});
|
|
@@ -2382,11 +3174,11 @@ function checkContextWindow(state, thresholds = { warningPct: 0.8, criticalPct:
|
|
|
2382
3174
|
const usagePct = state.maxTokens > 0 ? state.usedTokens / state.maxTokens : 0;
|
|
2383
3175
|
if (usagePct >= thresholds.criticalPct) {
|
|
2384
3176
|
const message = buildRecoveryMessage(state, usagePct);
|
|
2385
|
-
|
|
3177
|
+
warn(`[context-window] CRITICAL ${(usagePct * 100).toFixed(1)}% used in session ${state.sessionId}`);
|
|
2386
3178
|
return { action: "recover", usagePct, message };
|
|
2387
3179
|
}
|
|
2388
3180
|
if (usagePct >= thresholds.warningPct) {
|
|
2389
|
-
|
|
3181
|
+
warn(`[context-window] WARNING ${(usagePct * 100).toFixed(1)}% used in session ${state.sessionId}`);
|
|
2390
3182
|
return { action: "warn", usagePct, message: buildWarningMessage(usagePct) };
|
|
2391
3183
|
}
|
|
2392
3184
|
return { action: "none", usagePct };
|
|
@@ -2411,7 +3203,7 @@ Update the sidebar: use todowrite to create a todo (in_progress, high priority):
|
|
|
2411
3203
|
}
|
|
2412
3204
|
|
|
2413
3205
|
// src/hooks/write-existing-file-guard.ts
|
|
2414
|
-
import * as
|
|
3206
|
+
import * as fs2 from "fs";
|
|
2415
3207
|
function createWriteGuardState() {
|
|
2416
3208
|
return { readFiles: new Set };
|
|
2417
3209
|
}
|
|
@@ -2419,14 +3211,14 @@ function trackFileRead(state, filePath) {
|
|
|
2419
3211
|
state.readFiles.add(filePath);
|
|
2420
3212
|
}
|
|
2421
3213
|
function checkWriteAllowed(state, filePath) {
|
|
2422
|
-
if (!
|
|
3214
|
+
if (!fs2.existsSync(filePath)) {
|
|
2423
3215
|
return { allowed: true };
|
|
2424
3216
|
}
|
|
2425
3217
|
if (state.readFiles.has(filePath)) {
|
|
2426
3218
|
return { allowed: true };
|
|
2427
3219
|
}
|
|
2428
3220
|
const warning = `⚠️ Write guard: Attempting to write to '${filePath}' without reading it first. Read the file before overwriting to avoid data loss.`;
|
|
2429
|
-
|
|
3221
|
+
warn(`[write-guard] BLOCKED write to unread file: ${filePath}`);
|
|
2430
3222
|
return { allowed: false, warning };
|
|
2431
3223
|
}
|
|
2432
3224
|
function createWriteGuard(state) {
|
|
@@ -2437,13 +3229,13 @@ function createWriteGuard(state) {
|
|
|
2437
3229
|
}
|
|
2438
3230
|
|
|
2439
3231
|
// src/hooks/rules-injector.ts
|
|
2440
|
-
import * as
|
|
2441
|
-
import * as
|
|
3232
|
+
import * as fs3 from "fs";
|
|
3233
|
+
import * as path3 from "path";
|
|
2442
3234
|
var RULES_FILENAMES = ["AGENTS.md", ".rules", "CLAUDE.md"];
|
|
2443
3235
|
function findRulesFile(directory) {
|
|
2444
3236
|
for (const filename of RULES_FILENAMES) {
|
|
2445
|
-
const candidate =
|
|
2446
|
-
if (
|
|
3237
|
+
const candidate = path3.join(directory, filename);
|
|
3238
|
+
if (fs3.existsSync(candidate)) {
|
|
2447
3239
|
return candidate;
|
|
2448
3240
|
}
|
|
2449
3241
|
}
|
|
@@ -2454,11 +3246,11 @@ function loadRulesForDirectory(directory) {
|
|
|
2454
3246
|
if (!rulesFile)
|
|
2455
3247
|
return;
|
|
2456
3248
|
try {
|
|
2457
|
-
const content =
|
|
2458
|
-
|
|
3249
|
+
const content = fs3.readFileSync(rulesFile, "utf8");
|
|
3250
|
+
debug(`[rules-injector] Loaded rules from ${rulesFile}`);
|
|
2459
3251
|
return content;
|
|
2460
3252
|
} catch {
|
|
2461
|
-
|
|
3253
|
+
warn(`[rules-injector] Failed to read rules file: ${rulesFile}`);
|
|
2462
3254
|
return;
|
|
2463
3255
|
}
|
|
2464
3256
|
}
|
|
@@ -2466,7 +3258,7 @@ function shouldInjectRules(toolName) {
|
|
|
2466
3258
|
return toolName === "read" || toolName === "write" || toolName === "edit";
|
|
2467
3259
|
}
|
|
2468
3260
|
function getDirectoryFromFilePath(filePath) {
|
|
2469
|
-
return
|
|
3261
|
+
return path3.dirname(path3.resolve(filePath));
|
|
2470
3262
|
}
|
|
2471
3263
|
function buildRulesInjection(rulesContent, directory) {
|
|
2472
3264
|
return `<rules source="${directory}">
|
|
@@ -2525,7 +3317,7 @@ function buildKeywordInjection(detected) {
|
|
|
2525
3317
|
function processMessageForKeywords(message, sessionId, actions) {
|
|
2526
3318
|
const detected = detectKeywords(message, actions);
|
|
2527
3319
|
if (detected.length > 0) {
|
|
2528
|
-
|
|
3320
|
+
debug(`[keyword-detector] Detected keywords in session ${sessionId}: ${detected.map((a) => a.keyword).join(", ")}`);
|
|
2529
3321
|
}
|
|
2530
3322
|
return buildKeywordInjection(detected);
|
|
2531
3323
|
}
|
|
@@ -2562,15 +3354,15 @@ var WORK_STATE_FILE = "state.json";
|
|
|
2562
3354
|
var WORK_STATE_PATH = `${WEAVE_DIR}/${WORK_STATE_FILE}`;
|
|
2563
3355
|
var PLANS_DIR = `${WEAVE_DIR}/plans`;
|
|
2564
3356
|
// src/features/work-state/storage.ts
|
|
2565
|
-
import { existsSync as
|
|
2566
|
-
import { join as
|
|
3357
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync, unlinkSync, mkdirSync, readdirSync as readdirSync2, statSync } from "fs";
|
|
3358
|
+
import { join as join5, basename } from "path";
|
|
2567
3359
|
import { execSync } from "child_process";
|
|
2568
3360
|
var UNCHECKED_RE = /^[-*]\s*\[\s*\]/gm;
|
|
2569
3361
|
var CHECKED_RE = /^[-*]\s*\[[xX]\]/gm;
|
|
2570
3362
|
function readWorkState(directory) {
|
|
2571
|
-
const filePath =
|
|
3363
|
+
const filePath = join5(directory, WEAVE_DIR, WORK_STATE_FILE);
|
|
2572
3364
|
try {
|
|
2573
|
-
if (!
|
|
3365
|
+
if (!existsSync6(filePath))
|
|
2574
3366
|
return null;
|
|
2575
3367
|
const raw = readFileSync5(filePath, "utf-8");
|
|
2576
3368
|
const parsed = JSON.parse(raw);
|
|
@@ -2588,20 +3380,20 @@ function readWorkState(directory) {
|
|
|
2588
3380
|
}
|
|
2589
3381
|
function writeWorkState(directory, state) {
|
|
2590
3382
|
try {
|
|
2591
|
-
const dir =
|
|
2592
|
-
if (!
|
|
2593
|
-
|
|
3383
|
+
const dir = join5(directory, WEAVE_DIR);
|
|
3384
|
+
if (!existsSync6(dir)) {
|
|
3385
|
+
mkdirSync(dir, { recursive: true });
|
|
2594
3386
|
}
|
|
2595
|
-
writeFileSync(
|
|
3387
|
+
writeFileSync(join5(dir, WORK_STATE_FILE), JSON.stringify(state, null, 2), "utf-8");
|
|
2596
3388
|
return true;
|
|
2597
3389
|
} catch {
|
|
2598
3390
|
return false;
|
|
2599
3391
|
}
|
|
2600
3392
|
}
|
|
2601
3393
|
function clearWorkState(directory) {
|
|
2602
|
-
const filePath =
|
|
3394
|
+
const filePath = join5(directory, WEAVE_DIR, WORK_STATE_FILE);
|
|
2603
3395
|
try {
|
|
2604
|
-
if (
|
|
3396
|
+
if (existsSync6(filePath)) {
|
|
2605
3397
|
unlinkSync(filePath);
|
|
2606
3398
|
}
|
|
2607
3399
|
return true;
|
|
@@ -2643,12 +3435,12 @@ function getHeadSha(directory) {
|
|
|
2643
3435
|
}
|
|
2644
3436
|
}
|
|
2645
3437
|
function findPlans(directory) {
|
|
2646
|
-
const plansDir =
|
|
3438
|
+
const plansDir = join5(directory, PLANS_DIR);
|
|
2647
3439
|
try {
|
|
2648
|
-
if (!
|
|
3440
|
+
if (!existsSync6(plansDir))
|
|
2649
3441
|
return [];
|
|
2650
3442
|
const files = readdirSync2(plansDir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
2651
|
-
const fullPath =
|
|
3443
|
+
const fullPath = join5(plansDir, f);
|
|
2652
3444
|
const stat = statSync(fullPath);
|
|
2653
3445
|
return { path: fullPath, mtime: stat.mtimeMs };
|
|
2654
3446
|
}).sort((a, b) => b.mtime - a.mtime).map((f) => f.path);
|
|
@@ -2658,7 +3450,7 @@ function findPlans(directory) {
|
|
|
2658
3450
|
}
|
|
2659
3451
|
}
|
|
2660
3452
|
function getPlanProgress(planPath) {
|
|
2661
|
-
if (!
|
|
3453
|
+
if (!existsSync6(planPath)) {
|
|
2662
3454
|
return { total: 0, completed: 0, isComplete: true };
|
|
2663
3455
|
}
|
|
2664
3456
|
try {
|
|
@@ -2694,7 +3486,7 @@ function resumeWork(directory) {
|
|
|
2694
3486
|
return writeWorkState(directory, state);
|
|
2695
3487
|
}
|
|
2696
3488
|
// src/features/work-state/validation.ts
|
|
2697
|
-
import { readFileSync as readFileSync6, existsSync as
|
|
3489
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
2698
3490
|
import { resolve as resolve4, sep as sep3 } from "path";
|
|
2699
3491
|
function validatePlan(planPath, projectDir) {
|
|
2700
3492
|
const errors = [];
|
|
@@ -2709,7 +3501,7 @@ function validatePlan(planPath, projectDir) {
|
|
|
2709
3501
|
});
|
|
2710
3502
|
return { valid: false, errors, warnings };
|
|
2711
3503
|
}
|
|
2712
|
-
if (!
|
|
3504
|
+
if (!existsSync7(resolvedPlanPath)) {
|
|
2713
3505
|
errors.push({
|
|
2714
3506
|
severity: "error",
|
|
2715
3507
|
category: "structure",
|
|
@@ -2899,7 +3691,7 @@ function validateFileReferences(content, projectDir, warnings) {
|
|
|
2899
3691
|
});
|
|
2900
3692
|
continue;
|
|
2901
3693
|
}
|
|
2902
|
-
if (!
|
|
3694
|
+
if (!existsSync7(absolutePath)) {
|
|
2903
3695
|
warnings.push({
|
|
2904
3696
|
severity: "warning",
|
|
2905
3697
|
category: "file-references",
|
|
@@ -2990,8 +3782,8 @@ var ACTIVE_INSTANCE_FILE = "active-instance.json";
|
|
|
2990
3782
|
var WORKFLOWS_DIR_PROJECT = ".opencode/workflows";
|
|
2991
3783
|
var WORKFLOWS_DIR_USER = "workflows";
|
|
2992
3784
|
// src/features/workflow/storage.ts
|
|
2993
|
-
import { existsSync as
|
|
2994
|
-
import { join as
|
|
3785
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync3 } from "fs";
|
|
3786
|
+
import { join as join6 } from "path";
|
|
2995
3787
|
import { randomBytes } from "node:crypto";
|
|
2996
3788
|
function generateInstanceId() {
|
|
2997
3789
|
return `wf_${randomBytes(4).toString("hex")}`;
|
|
@@ -3027,9 +3819,9 @@ function createWorkflowInstance(definition, definitionPath, goal, sessionId) {
|
|
|
3027
3819
|
};
|
|
3028
3820
|
}
|
|
3029
3821
|
function readWorkflowInstance(directory, instanceId) {
|
|
3030
|
-
const filePath =
|
|
3822
|
+
const filePath = join6(directory, WORKFLOWS_STATE_DIR, instanceId, INSTANCE_STATE_FILE);
|
|
3031
3823
|
try {
|
|
3032
|
-
if (!
|
|
3824
|
+
if (!existsSync8(filePath))
|
|
3033
3825
|
return null;
|
|
3034
3826
|
const raw = readFileSync7(filePath, "utf-8");
|
|
3035
3827
|
const parsed = JSON.parse(raw);
|
|
@@ -3044,20 +3836,20 @@ function readWorkflowInstance(directory, instanceId) {
|
|
|
3044
3836
|
}
|
|
3045
3837
|
function writeWorkflowInstance(directory, instance) {
|
|
3046
3838
|
try {
|
|
3047
|
-
const dir =
|
|
3048
|
-
if (!
|
|
3049
|
-
|
|
3839
|
+
const dir = join6(directory, WORKFLOWS_STATE_DIR, instance.instance_id);
|
|
3840
|
+
if (!existsSync8(dir)) {
|
|
3841
|
+
mkdirSync2(dir, { recursive: true });
|
|
3050
3842
|
}
|
|
3051
|
-
writeFileSync2(
|
|
3843
|
+
writeFileSync2(join6(dir, INSTANCE_STATE_FILE), JSON.stringify(instance, null, 2), "utf-8");
|
|
3052
3844
|
return true;
|
|
3053
3845
|
} catch {
|
|
3054
3846
|
return false;
|
|
3055
3847
|
}
|
|
3056
3848
|
}
|
|
3057
3849
|
function readActiveInstance(directory) {
|
|
3058
|
-
const filePath =
|
|
3850
|
+
const filePath = join6(directory, WORKFLOWS_STATE_DIR, ACTIVE_INSTANCE_FILE);
|
|
3059
3851
|
try {
|
|
3060
|
-
if (!
|
|
3852
|
+
if (!existsSync8(filePath))
|
|
3061
3853
|
return null;
|
|
3062
3854
|
const raw = readFileSync7(filePath, "utf-8");
|
|
3063
3855
|
const parsed = JSON.parse(raw);
|
|
@@ -3070,21 +3862,21 @@ function readActiveInstance(directory) {
|
|
|
3070
3862
|
}
|
|
3071
3863
|
function setActiveInstance(directory, instanceId) {
|
|
3072
3864
|
try {
|
|
3073
|
-
const dir =
|
|
3074
|
-
if (!
|
|
3075
|
-
|
|
3865
|
+
const dir = join6(directory, WORKFLOWS_STATE_DIR);
|
|
3866
|
+
if (!existsSync8(dir)) {
|
|
3867
|
+
mkdirSync2(dir, { recursive: true });
|
|
3076
3868
|
}
|
|
3077
3869
|
const pointer = { instance_id: instanceId };
|
|
3078
|
-
writeFileSync2(
|
|
3870
|
+
writeFileSync2(join6(dir, ACTIVE_INSTANCE_FILE), JSON.stringify(pointer, null, 2), "utf-8");
|
|
3079
3871
|
return true;
|
|
3080
3872
|
} catch {
|
|
3081
3873
|
return false;
|
|
3082
3874
|
}
|
|
3083
3875
|
}
|
|
3084
3876
|
function clearActiveInstance(directory) {
|
|
3085
|
-
const filePath =
|
|
3877
|
+
const filePath = join6(directory, WORKFLOWS_STATE_DIR, ACTIVE_INSTANCE_FILE);
|
|
3086
3878
|
try {
|
|
3087
|
-
if (
|
|
3879
|
+
if (existsSync8(filePath)) {
|
|
3088
3880
|
unlinkSync2(filePath);
|
|
3089
3881
|
}
|
|
3090
3882
|
return true;
|
|
@@ -3099,10 +3891,9 @@ function getActiveWorkflowInstance(directory) {
|
|
|
3099
3891
|
return readWorkflowInstance(directory, pointer.instance_id);
|
|
3100
3892
|
}
|
|
3101
3893
|
// src/features/workflow/discovery.ts
|
|
3102
|
-
import * as
|
|
3103
|
-
import * as
|
|
3104
|
-
import * as
|
|
3105
|
-
import { parse as parseJsonc } from "jsonc-parser";
|
|
3894
|
+
import * as fs4 from "fs";
|
|
3895
|
+
import * as path4 from "path";
|
|
3896
|
+
import * as os2 from "os";
|
|
3106
3897
|
|
|
3107
3898
|
// src/features/workflow/schema.ts
|
|
3108
3899
|
import { z as z2 } from "zod";
|
|
@@ -3140,21 +3931,21 @@ var WorkflowDefinitionSchema = z2.object({
|
|
|
3140
3931
|
function loadWorkflowDefinition(filePath) {
|
|
3141
3932
|
let raw;
|
|
3142
3933
|
try {
|
|
3143
|
-
raw =
|
|
3934
|
+
raw = fs4.readFileSync(filePath, "utf-8");
|
|
3144
3935
|
} catch (err) {
|
|
3145
|
-
|
|
3936
|
+
error("Failed to read workflow definition file", { filePath, error: String(err) });
|
|
3146
3937
|
return null;
|
|
3147
3938
|
}
|
|
3148
3939
|
let parsed;
|
|
3149
3940
|
try {
|
|
3150
|
-
parsed =
|
|
3941
|
+
parsed = parse2(raw);
|
|
3151
3942
|
} catch (err) {
|
|
3152
|
-
|
|
3943
|
+
error("Failed to parse workflow definition JSONC", { filePath, error: String(err) });
|
|
3153
3944
|
return null;
|
|
3154
3945
|
}
|
|
3155
3946
|
const result = WorkflowDefinitionSchema.safeParse(parsed);
|
|
3156
3947
|
if (!result.success) {
|
|
3157
|
-
|
|
3948
|
+
warn("Workflow definition failed validation", {
|
|
3158
3949
|
filePath,
|
|
3159
3950
|
errors: result.error.issues.map((i) => i.message)
|
|
3160
3951
|
});
|
|
@@ -3163,13 +3954,13 @@ function loadWorkflowDefinition(filePath) {
|
|
|
3163
3954
|
return result.data;
|
|
3164
3955
|
}
|
|
3165
3956
|
function scanWorkflowDirectory(directory, scope) {
|
|
3166
|
-
if (!
|
|
3957
|
+
if (!fs4.existsSync(directory))
|
|
3167
3958
|
return [];
|
|
3168
3959
|
let entries;
|
|
3169
3960
|
try {
|
|
3170
|
-
entries =
|
|
3961
|
+
entries = fs4.readdirSync(directory, { withFileTypes: true });
|
|
3171
3962
|
} catch (err) {
|
|
3172
|
-
|
|
3963
|
+
warn("Failed to read workflows directory", { directory, error: String(err) });
|
|
3173
3964
|
return [];
|
|
3174
3965
|
}
|
|
3175
3966
|
const workflows = [];
|
|
@@ -3178,7 +3969,7 @@ function scanWorkflowDirectory(directory, scope) {
|
|
|
3178
3969
|
continue;
|
|
3179
3970
|
if (!entry.name.endsWith(".jsonc") && !entry.name.endsWith(".json"))
|
|
3180
3971
|
continue;
|
|
3181
|
-
const filePath =
|
|
3972
|
+
const filePath = path4.join(directory, entry.name);
|
|
3182
3973
|
const definition = loadWorkflowDefinition(filePath);
|
|
3183
3974
|
if (definition) {
|
|
3184
3975
|
workflows.push({ definition, path: filePath, scope });
|
|
@@ -3187,8 +3978,8 @@ function scanWorkflowDirectory(directory, scope) {
|
|
|
3187
3978
|
return workflows;
|
|
3188
3979
|
}
|
|
3189
3980
|
function discoverWorkflows(directory, customDirs) {
|
|
3190
|
-
const projectDir =
|
|
3191
|
-
const userDir =
|
|
3981
|
+
const projectDir = path4.join(directory, WORKFLOWS_DIR_PROJECT);
|
|
3982
|
+
const userDir = path4.join(os2.homedir(), ".config", "opencode", WORKFLOWS_DIR_USER);
|
|
3192
3983
|
const userWorkflows = scanWorkflowDirectory(userDir, "user");
|
|
3193
3984
|
const projectWorkflows = scanWorkflowDirectory(projectDir, "project");
|
|
3194
3985
|
const customWorkflows = [];
|
|
@@ -3310,8 +4101,8 @@ function truncateSummary(text) {
|
|
|
3310
4101
|
return text.slice(0, maxLength - 3) + "...";
|
|
3311
4102
|
}
|
|
3312
4103
|
// src/features/workflow/completion.ts
|
|
3313
|
-
import { existsSync as
|
|
3314
|
-
import { join as
|
|
4104
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4105
|
+
import { join as join8 } from "path";
|
|
3315
4106
|
var DEFAULT_CONFIRM_KEYWORDS = ["confirmed", "approved", "continue", "done", "let's proceed", "looks good", "lgtm"];
|
|
3316
4107
|
var VERDICT_APPROVE_RE = /\[\s*APPROVE\s*\]/i;
|
|
3317
4108
|
var VERDICT_REJECT_RE = /\[\s*REJECT\s*\]/i;
|
|
@@ -3363,8 +4154,8 @@ function checkPlanCreated(context) {
|
|
|
3363
4154
|
summary: `Plan created at ${matchingPlan}`
|
|
3364
4155
|
};
|
|
3365
4156
|
}
|
|
3366
|
-
const directPath =
|
|
3367
|
-
if (
|
|
4157
|
+
const directPath = join8(directory, ".weave", "plans", `${planName}.md`);
|
|
4158
|
+
if (existsSync10(directPath)) {
|
|
3368
4159
|
return {
|
|
3369
4160
|
complete: true,
|
|
3370
4161
|
artifacts: { plan_path: directPath },
|
|
@@ -3379,8 +4170,8 @@ function checkPlanComplete(context) {
|
|
|
3379
4170
|
if (!planName) {
|
|
3380
4171
|
return { complete: false, reason: "plan_complete requires plan_name in completion config" };
|
|
3381
4172
|
}
|
|
3382
|
-
const planPath =
|
|
3383
|
-
if (!
|
|
4173
|
+
const planPath = join8(directory, ".weave", "plans", `${planName}.md`);
|
|
4174
|
+
if (!existsSync10(planPath)) {
|
|
3384
4175
|
return { complete: false, reason: `Plan file not found: ${planPath}` };
|
|
3385
4176
|
}
|
|
3386
4177
|
const progress = getPlanProgress(planPath);
|
|
@@ -3811,7 +4602,7 @@ ${available ? `Available workflows: ${available}` : "No workflow definitions ava
|
|
|
3811
4602
|
sessionId,
|
|
3812
4603
|
directory
|
|
3813
4604
|
});
|
|
3814
|
-
|
|
4605
|
+
info("Workflow started", {
|
|
3815
4606
|
workflowName: match.definition.name,
|
|
3816
4607
|
goal,
|
|
3817
4608
|
agent: action.agent
|
|
@@ -4176,8 +4967,8 @@ function formatValidationResults(result) {
|
|
|
4176
4967
|
if (result.errors.length > 0)
|
|
4177
4968
|
lines.push("");
|
|
4178
4969
|
lines.push("**Warnings:**");
|
|
4179
|
-
for (const
|
|
4180
|
-
lines.push(`- [${
|
|
4970
|
+
for (const warn2 of result.warnings) {
|
|
4971
|
+
lines.push(`- [${warn2.category}] ${warn2.message}`);
|
|
4181
4972
|
}
|
|
4182
4973
|
}
|
|
4183
4974
|
return lines.join(`
|
|
@@ -4376,21 +5167,21 @@ async function resolveTodoWriter() {
|
|
|
4376
5167
|
}
|
|
4377
5168
|
|
|
4378
5169
|
// src/hooks/compaction-todo-preserver.ts
|
|
4379
|
-
function createCompactionTodoPreserver(
|
|
5170
|
+
function createCompactionTodoPreserver(client2) {
|
|
4380
5171
|
const snapshots = new Map;
|
|
4381
5172
|
async function capture(sessionID) {
|
|
4382
5173
|
try {
|
|
4383
|
-
const response = await
|
|
5174
|
+
const response = await client2.session.todo({ path: { id: sessionID } });
|
|
4384
5175
|
const todos = response.data ?? [];
|
|
4385
5176
|
if (todos.length > 0) {
|
|
4386
5177
|
snapshots.set(sessionID, todos);
|
|
4387
|
-
|
|
5178
|
+
debug("[compaction-todo-preserver] Captured snapshot", {
|
|
4388
5179
|
sessionID,
|
|
4389
5180
|
count: todos.length
|
|
4390
5181
|
});
|
|
4391
5182
|
}
|
|
4392
5183
|
} catch (err) {
|
|
4393
|
-
|
|
5184
|
+
warn("[compaction-todo-preserver] Failed to capture snapshot (non-fatal)", {
|
|
4394
5185
|
sessionID,
|
|
4395
5186
|
error: String(err)
|
|
4396
5187
|
});
|
|
@@ -4402,10 +5193,10 @@ function createCompactionTodoPreserver(client) {
|
|
|
4402
5193
|
return;
|
|
4403
5194
|
}
|
|
4404
5195
|
try {
|
|
4405
|
-
const response = await
|
|
5196
|
+
const response = await client2.session.todo({ path: { id: sessionID } });
|
|
4406
5197
|
const currentTodos = response.data ?? [];
|
|
4407
5198
|
if (currentTodos.length > 0) {
|
|
4408
|
-
|
|
5199
|
+
debug("[compaction-todo-preserver] Todos survived compaction, skipping restore", {
|
|
4409
5200
|
sessionID,
|
|
4410
5201
|
currentCount: currentTodos.length
|
|
4411
5202
|
});
|
|
@@ -4415,18 +5206,18 @@ function createCompactionTodoPreserver(client) {
|
|
|
4415
5206
|
const todoWriter = await resolveTodoWriter();
|
|
4416
5207
|
if (todoWriter) {
|
|
4417
5208
|
todoWriter({ sessionID, todos: snapshot });
|
|
4418
|
-
|
|
5209
|
+
debug("[compaction-todo-preserver] Restored todos via direct write", {
|
|
4419
5210
|
sessionID,
|
|
4420
5211
|
count: snapshot.length
|
|
4421
5212
|
});
|
|
4422
5213
|
} else {
|
|
4423
|
-
|
|
5214
|
+
warn("[compaction-todo-preserver] Direct write unavailable — todos cannot be restored", {
|
|
4424
5215
|
sessionID,
|
|
4425
5216
|
count: snapshot.length
|
|
4426
5217
|
});
|
|
4427
5218
|
}
|
|
4428
5219
|
} catch (err) {
|
|
4429
|
-
|
|
5220
|
+
warn("[compaction-todo-preserver] Failed to restore todos (non-fatal)", {
|
|
4430
5221
|
sessionID,
|
|
4431
5222
|
error: String(err)
|
|
4432
5223
|
});
|
|
@@ -4447,7 +5238,7 @@ function createCompactionTodoPreserver(client) {
|
|
|
4447
5238
|
const sessionID = props?.sessionID ?? props?.info?.id ?? "";
|
|
4448
5239
|
if (sessionID) {
|
|
4449
5240
|
snapshots.delete(sessionID);
|
|
4450
|
-
|
|
5241
|
+
debug("[compaction-todo-preserver] Cleaned up snapshot on session delete", { sessionID });
|
|
4451
5242
|
}
|
|
4452
5243
|
return;
|
|
4453
5244
|
}
|
|
@@ -4459,7 +5250,7 @@ function createCompactionTodoPreserver(client) {
|
|
|
4459
5250
|
}
|
|
4460
5251
|
// src/hooks/todo-continuation-enforcer.ts
|
|
4461
5252
|
var FINALIZE_TODOS_MARKER = "<!-- weave:finalize-todos -->";
|
|
4462
|
-
function createTodoContinuationEnforcer(
|
|
5253
|
+
function createTodoContinuationEnforcer(client2, options) {
|
|
4463
5254
|
const todoFinalizedSessions = new Set;
|
|
4464
5255
|
let todoWriterPromise;
|
|
4465
5256
|
if (options !== undefined && "todoWriterOverride" in options) {
|
|
@@ -4469,9 +5260,9 @@ function createTodoContinuationEnforcer(client, options) {
|
|
|
4469
5260
|
}
|
|
4470
5261
|
todoWriterPromise.then((writer) => {
|
|
4471
5262
|
if (writer) {
|
|
4472
|
-
|
|
5263
|
+
debug("[todo-continuation-enforcer] Direct write: available");
|
|
4473
5264
|
} else {
|
|
4474
|
-
|
|
5265
|
+
debug("[todo-continuation-enforcer] Direct write: unavailable, will fall back to LLM prompt");
|
|
4475
5266
|
}
|
|
4476
5267
|
}).catch(() => {});
|
|
4477
5268
|
async function checkAndFinalize(sessionID) {
|
|
@@ -4479,7 +5270,7 @@ function createTodoContinuationEnforcer(client, options) {
|
|
|
4479
5270
|
return;
|
|
4480
5271
|
}
|
|
4481
5272
|
try {
|
|
4482
|
-
const todosResponse = await
|
|
5273
|
+
const todosResponse = await client2.session.todo({ path: { id: sessionID } });
|
|
4483
5274
|
const todos = todosResponse.data ?? [];
|
|
4484
5275
|
const inProgressTodos = todos.filter((t) => t.status === "in_progress");
|
|
4485
5276
|
if (inProgressTodos.length === 0) {
|
|
@@ -4490,14 +5281,14 @@ function createTodoContinuationEnforcer(client, options) {
|
|
|
4490
5281
|
if (todoWriter) {
|
|
4491
5282
|
const updatedTodos = todos.map((t) => t.status === "in_progress" ? { ...t, status: "completed" } : t);
|
|
4492
5283
|
todoWriter({ sessionID, todos: updatedTodos });
|
|
4493
|
-
|
|
5284
|
+
debug("[todo-continuation-enforcer] Finalized via direct write (0 tokens)", {
|
|
4494
5285
|
sessionID,
|
|
4495
5286
|
count: inProgressTodos.length
|
|
4496
5287
|
});
|
|
4497
5288
|
} else {
|
|
4498
5289
|
const inProgressItems = inProgressTodos.map((t) => ` - "${t.content}"`).join(`
|
|
4499
5290
|
`);
|
|
4500
|
-
await
|
|
5291
|
+
await client2.session.promptAsync({
|
|
4501
5292
|
path: { id: sessionID },
|
|
4502
5293
|
body: {
|
|
4503
5294
|
parts: [
|
|
@@ -4512,14 +5303,14 @@ Use todowrite NOW to mark all of them as "completed" (or "cancelled" if abandone
|
|
|
4512
5303
|
]
|
|
4513
5304
|
}
|
|
4514
5305
|
});
|
|
4515
|
-
|
|
5306
|
+
debug("[todo-continuation-enforcer] Finalized via LLM prompt (fallback)", {
|
|
4516
5307
|
sessionID,
|
|
4517
5308
|
count: inProgressTodos.length
|
|
4518
5309
|
});
|
|
4519
5310
|
}
|
|
4520
5311
|
} catch (err) {
|
|
4521
5312
|
todoFinalizedSessions.delete(sessionID);
|
|
4522
|
-
|
|
5313
|
+
warn("[todo-continuation-enforcer] Failed to check/finalize todos (non-fatal, will retry)", {
|
|
4523
5314
|
sessionID,
|
|
4524
5315
|
error: String(err)
|
|
4525
5316
|
});
|
|
@@ -4546,8 +5337,8 @@ Use todowrite NOW to mark all of them as "completed" (or "cancelled" if abandone
|
|
|
4546
5337
|
};
|
|
4547
5338
|
}
|
|
4548
5339
|
// src/features/analytics/storage.ts
|
|
4549
|
-
import { existsSync as
|
|
4550
|
-
import { join as
|
|
5340
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, appendFileSync, readFileSync as readFileSync9, writeFileSync as writeFileSync3, statSync as statSync2 } from "fs";
|
|
5341
|
+
import { join as join9 } from "path";
|
|
4551
5342
|
|
|
4552
5343
|
// src/features/analytics/types.ts
|
|
4553
5344
|
var ANALYTICS_DIR = ".weave/analytics";
|
|
@@ -4562,17 +5353,17 @@ function zeroTokenUsage() {
|
|
|
4562
5353
|
// src/features/analytics/storage.ts
|
|
4563
5354
|
var MAX_SESSION_ENTRIES = 1000;
|
|
4564
5355
|
function ensureAnalyticsDir(directory) {
|
|
4565
|
-
const dir =
|
|
4566
|
-
|
|
5356
|
+
const dir = join9(directory, ANALYTICS_DIR);
|
|
5357
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
4567
5358
|
return dir;
|
|
4568
5359
|
}
|
|
4569
5360
|
function appendSessionSummary(directory, summary) {
|
|
4570
5361
|
try {
|
|
4571
5362
|
const dir = ensureAnalyticsDir(directory);
|
|
4572
|
-
const filePath =
|
|
5363
|
+
const filePath = join9(dir, SESSION_SUMMARIES_FILE);
|
|
4573
5364
|
const line = JSON.stringify(summary) + `
|
|
4574
5365
|
`;
|
|
4575
|
-
|
|
5366
|
+
appendFileSync(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4576
5367
|
try {
|
|
4577
5368
|
const TYPICAL_ENTRY_BYTES = 200;
|
|
4578
5369
|
const rotationSizeThreshold = MAX_SESSION_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
@@ -4595,9 +5386,9 @@ function appendSessionSummary(directory, summary) {
|
|
|
4595
5386
|
}
|
|
4596
5387
|
}
|
|
4597
5388
|
function readSessionSummaries(directory) {
|
|
4598
|
-
const filePath =
|
|
5389
|
+
const filePath = join9(directory, ANALYTICS_DIR, SESSION_SUMMARIES_FILE);
|
|
4599
5390
|
try {
|
|
4600
|
-
if (!
|
|
5391
|
+
if (!existsSync11(filePath))
|
|
4601
5392
|
return [];
|
|
4602
5393
|
const content = readFileSync9(filePath, "utf-8");
|
|
4603
5394
|
const lines = content.split(`
|
|
@@ -4616,7 +5407,7 @@ function readSessionSummaries(directory) {
|
|
|
4616
5407
|
function writeFingerprint(directory, fingerprint) {
|
|
4617
5408
|
try {
|
|
4618
5409
|
const dir = ensureAnalyticsDir(directory);
|
|
4619
|
-
const filePath =
|
|
5410
|
+
const filePath = join9(dir, FINGERPRINT_FILE);
|
|
4620
5411
|
writeFileSync3(filePath, JSON.stringify(fingerprint, null, 2), { encoding: "utf-8", mode: 384 });
|
|
4621
5412
|
return true;
|
|
4622
5413
|
} catch {
|
|
@@ -4624,9 +5415,9 @@ function writeFingerprint(directory, fingerprint) {
|
|
|
4624
5415
|
}
|
|
4625
5416
|
}
|
|
4626
5417
|
function readFingerprint(directory) {
|
|
4627
|
-
const filePath =
|
|
5418
|
+
const filePath = join9(directory, ANALYTICS_DIR, FINGERPRINT_FILE);
|
|
4628
5419
|
try {
|
|
4629
|
-
if (!
|
|
5420
|
+
if (!existsSync11(filePath))
|
|
4630
5421
|
return null;
|
|
4631
5422
|
const content = readFileSync9(filePath, "utf-8");
|
|
4632
5423
|
const parsed = JSON.parse(content);
|
|
@@ -4640,10 +5431,10 @@ function readFingerprint(directory) {
|
|
|
4640
5431
|
function writeMetricsReport(directory, report) {
|
|
4641
5432
|
try {
|
|
4642
5433
|
const dir = ensureAnalyticsDir(directory);
|
|
4643
|
-
const filePath =
|
|
5434
|
+
const filePath = join9(dir, METRICS_REPORTS_FILE);
|
|
4644
5435
|
const line = JSON.stringify(report) + `
|
|
4645
5436
|
`;
|
|
4646
|
-
|
|
5437
|
+
appendFileSync(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
4647
5438
|
try {
|
|
4648
5439
|
const TYPICAL_ENTRY_BYTES = 200;
|
|
4649
5440
|
const rotationSizeThreshold = MAX_METRICS_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
|
|
@@ -4666,9 +5457,9 @@ function writeMetricsReport(directory, report) {
|
|
|
4666
5457
|
}
|
|
4667
5458
|
}
|
|
4668
5459
|
function readMetricsReports(directory) {
|
|
4669
|
-
const filePath =
|
|
5460
|
+
const filePath = join9(directory, ANALYTICS_DIR, METRICS_REPORTS_FILE);
|
|
4670
5461
|
try {
|
|
4671
|
-
if (!
|
|
5462
|
+
if (!existsSync11(filePath))
|
|
4672
5463
|
return [];
|
|
4673
5464
|
const content = readFileSync9(filePath, "utf-8");
|
|
4674
5465
|
const lines = content.split(`
|
|
@@ -5198,7 +5989,7 @@ function generateMetricsReport(directory, state) {
|
|
|
5198
5989
|
totalTokens
|
|
5199
5990
|
});
|
|
5200
5991
|
} catch (qualityErr) {
|
|
5201
|
-
|
|
5992
|
+
warn("[analytics] Failed to calculate quality score (non-fatal)", {
|
|
5202
5993
|
error: String(qualityErr)
|
|
5203
5994
|
});
|
|
5204
5995
|
}
|
|
@@ -5219,10 +6010,10 @@ function generateMetricsReport(directory, state) {
|
|
|
5219
6010
|
};
|
|
5220
6011
|
const written = writeMetricsReport(directory, report);
|
|
5221
6012
|
if (!written) {
|
|
5222
|
-
|
|
6013
|
+
warn("[analytics] Failed to write metrics report (non-fatal)");
|
|
5223
6014
|
return null;
|
|
5224
6015
|
}
|
|
5225
|
-
|
|
6016
|
+
debug("[analytics] Metrics report generated", {
|
|
5226
6017
|
plan: report.planName,
|
|
5227
6018
|
coverage: adherence.coverage,
|
|
5228
6019
|
precision: adherence.precision,
|
|
@@ -5230,7 +6021,7 @@ function generateMetricsReport(directory, state) {
|
|
|
5230
6021
|
});
|
|
5231
6022
|
return report;
|
|
5232
6023
|
} catch (err) {
|
|
5233
|
-
|
|
6024
|
+
warn("[analytics] Failed to generate metrics report (non-fatal)", {
|
|
5234
6025
|
error: String(err)
|
|
5235
6026
|
});
|
|
5236
6027
|
return null;
|
|
@@ -5239,11 +6030,11 @@ function generateMetricsReport(directory, state) {
|
|
|
5239
6030
|
|
|
5240
6031
|
// src/plugin/plugin-interface.ts
|
|
5241
6032
|
function createPluginInterface(args) {
|
|
5242
|
-
const { pluginConfig, hooks, tools, configHandler, agents, client, directory = "", tracker } = args;
|
|
6033
|
+
const { pluginConfig, hooks, tools, configHandler, agents, client: client2, directory = "", tracker } = args;
|
|
5243
6034
|
const lastAssistantMessageText = new Map;
|
|
5244
6035
|
const lastUserMessageText = new Map;
|
|
5245
|
-
const compactionPreserver = hooks.compactionTodoPreserverEnabled &&
|
|
5246
|
-
const todoContinuationEnforcer = hooks.todoContinuationEnforcerEnabled &&
|
|
6036
|
+
const compactionPreserver = hooks.compactionTodoPreserverEnabled && client2 ? createCompactionTodoPreserver(client2) : null;
|
|
6037
|
+
const todoContinuationEnforcer = hooks.todoContinuationEnforcerEnabled && client2 ? createTodoContinuationEnforcer(client2) : null;
|
|
5247
6038
|
return {
|
|
5248
6039
|
tool: tools,
|
|
5249
6040
|
config: async (config) => {
|
|
@@ -5254,14 +6045,14 @@ function createPluginInterface(args) {
|
|
|
5254
6045
|
});
|
|
5255
6046
|
const existingAgents = config.agent ?? {};
|
|
5256
6047
|
if (Object.keys(existingAgents).length > 0) {
|
|
5257
|
-
|
|
6048
|
+
debug("[config] Merging Weave agents over existing agents", {
|
|
5258
6049
|
existingCount: Object.keys(existingAgents).length,
|
|
5259
6050
|
weaveCount: Object.keys(result.agents).length,
|
|
5260
6051
|
existingKeys: Object.keys(existingAgents)
|
|
5261
6052
|
});
|
|
5262
6053
|
const collisions = Object.keys(result.agents).filter((key) => (key in existingAgents));
|
|
5263
6054
|
if (collisions.length > 0) {
|
|
5264
|
-
|
|
6055
|
+
info("[config] Weave agents overriding user-defined agents with same name", {
|
|
5265
6056
|
overriddenKeys: collisions
|
|
5266
6057
|
});
|
|
5267
6058
|
}
|
|
@@ -5300,6 +6091,12 @@ function createPluginInterface(args) {
|
|
|
5300
6091
|
const result = isWorkflowCommand ? { contextInjection: null, switchAgent: null } : hooks.startWork(promptText, sessionID);
|
|
5301
6092
|
if (result.switchAgent && message) {
|
|
5302
6093
|
message.agent = getAgentDisplayName(result.switchAgent);
|
|
6094
|
+
debug("[start-work] Switching agent for plan execution", {
|
|
6095
|
+
sessionId: sessionID,
|
|
6096
|
+
agent: result.switchAgent,
|
|
6097
|
+
displayName: message.agent,
|
|
6098
|
+
hasContextInjection: !!result.contextInjection
|
|
6099
|
+
});
|
|
5303
6100
|
}
|
|
5304
6101
|
if (result.contextInjection && parts) {
|
|
5305
6102
|
const idx = parts.findIndex((p) => p.type === "text" && p.text);
|
|
@@ -5322,6 +6119,11 @@ ${result.contextInjection}`;
|
|
|
5322
6119
|
const result = hooks.workflowStart(promptText, sessionID);
|
|
5323
6120
|
if (result.switchAgent && message) {
|
|
5324
6121
|
message.agent = getAgentDisplayName(result.switchAgent);
|
|
6122
|
+
debug("[workflow] Switching agent for workflow execution", {
|
|
6123
|
+
sessionId: sessionID,
|
|
6124
|
+
agent: result.switchAgent,
|
|
6125
|
+
displayName: message.agent
|
|
6126
|
+
});
|
|
5325
6127
|
}
|
|
5326
6128
|
if (result.contextInjection && parts) {
|
|
5327
6129
|
const idx = parts.findIndex((p) => p.type === "text" && p.text);
|
|
@@ -5371,6 +6173,10 @@ ${cmdResult.contextInjection}`;
|
|
|
5371
6173
|
}
|
|
5372
6174
|
if (cmdResult.switchAgent && message) {
|
|
5373
6175
|
message.agent = getAgentDisplayName(cmdResult.switchAgent);
|
|
6176
|
+
debug("[workflow] Switching agent via workflow command", {
|
|
6177
|
+
agent: cmdResult.switchAgent,
|
|
6178
|
+
displayName: message.agent
|
|
6179
|
+
});
|
|
5374
6180
|
}
|
|
5375
6181
|
}
|
|
5376
6182
|
}
|
|
@@ -5391,7 +6197,7 @@ ${cmdResult.contextInjection}`;
|
|
|
5391
6197
|
const state = readWorkState(directory);
|
|
5392
6198
|
if (state && !state.paused) {
|
|
5393
6199
|
pauseWork(directory);
|
|
5394
|
-
|
|
6200
|
+
info("[work-continuation] Auto-paused: user message received during active plan", { sessionId: sessionID });
|
|
5395
6201
|
}
|
|
5396
6202
|
}
|
|
5397
6203
|
}
|
|
@@ -5402,7 +6208,7 @@ ${cmdResult.contextInjection}`;
|
|
|
5402
6208
|
const maxTokens = input.model?.limit?.context ?? 0;
|
|
5403
6209
|
if (sessionId && maxTokens > 0) {
|
|
5404
6210
|
setContextLimit(sessionId, maxTokens);
|
|
5405
|
-
|
|
6211
|
+
debug("[context-window] Captured context limit", { sessionId, maxTokens });
|
|
5406
6212
|
}
|
|
5407
6213
|
if (tracker && hooks.analyticsEnabled && sessionId && input.agent) {
|
|
5408
6214
|
tracker.setAgentName(sessionId, input.agent);
|
|
@@ -5437,7 +6243,7 @@ ${cmdResult.contextInjection}`;
|
|
|
5437
6243
|
try {
|
|
5438
6244
|
tracker.endSession(evt.properties.info.id);
|
|
5439
6245
|
} catch (err) {
|
|
5440
|
-
|
|
6246
|
+
warn("[analytics] Failed to end session (non-fatal)", { error: String(err) });
|
|
5441
6247
|
}
|
|
5442
6248
|
if (directory) {
|
|
5443
6249
|
try {
|
|
@@ -5449,29 +6255,29 @@ ${cmdResult.contextInjection}`;
|
|
|
5449
6255
|
}
|
|
5450
6256
|
}
|
|
5451
6257
|
} catch (err) {
|
|
5452
|
-
|
|
6258
|
+
warn("[analytics] Failed to generate metrics report on session end (non-fatal)", { error: String(err) });
|
|
5453
6259
|
}
|
|
5454
6260
|
}
|
|
5455
6261
|
}
|
|
5456
6262
|
}
|
|
5457
6263
|
if (event.type === "message.updated") {
|
|
5458
6264
|
const evt = event;
|
|
5459
|
-
const
|
|
5460
|
-
if (
|
|
6265
|
+
const info2 = evt.properties?.info;
|
|
6266
|
+
if (info2?.role === "assistant" && info2.sessionID) {
|
|
5461
6267
|
if (hooks.checkContextWindow) {
|
|
5462
|
-
const inputTokens =
|
|
6268
|
+
const inputTokens = info2.tokens?.input ?? 0;
|
|
5463
6269
|
if (inputTokens > 0) {
|
|
5464
|
-
updateUsage(
|
|
5465
|
-
const tokenState = getState(
|
|
6270
|
+
updateUsage(info2.sessionID, inputTokens);
|
|
6271
|
+
const tokenState = getState(info2.sessionID);
|
|
5466
6272
|
if (tokenState && tokenState.maxTokens > 0) {
|
|
5467
6273
|
const result = hooks.checkContextWindow({
|
|
5468
6274
|
usedTokens: tokenState.usedTokens,
|
|
5469
6275
|
maxTokens: tokenState.maxTokens,
|
|
5470
|
-
sessionId:
|
|
6276
|
+
sessionId: info2.sessionID
|
|
5471
6277
|
});
|
|
5472
6278
|
if (result.action !== "none") {
|
|
5473
|
-
|
|
5474
|
-
sessionId:
|
|
6279
|
+
warn("[context-window] Threshold crossed", {
|
|
6280
|
+
sessionId: info2.sessionID,
|
|
5475
6281
|
action: result.action,
|
|
5476
6282
|
usagePct: result.usagePct
|
|
5477
6283
|
});
|
|
@@ -5483,18 +6289,18 @@ ${cmdResult.contextInjection}`;
|
|
|
5483
6289
|
}
|
|
5484
6290
|
if (event.type === "message.updated" && tracker && hooks.analyticsEnabled) {
|
|
5485
6291
|
const evt = event;
|
|
5486
|
-
const
|
|
5487
|
-
if (
|
|
5488
|
-
if (typeof
|
|
5489
|
-
tracker.trackCost(
|
|
6292
|
+
const info2 = evt.properties?.info;
|
|
6293
|
+
if (info2?.role === "assistant" && info2.sessionID) {
|
|
6294
|
+
if (typeof info2.cost === "number" && info2.cost > 0) {
|
|
6295
|
+
tracker.trackCost(info2.sessionID, info2.cost);
|
|
5490
6296
|
}
|
|
5491
|
-
if (
|
|
5492
|
-
tracker.trackTokenUsage(
|
|
5493
|
-
input:
|
|
5494
|
-
output:
|
|
5495
|
-
reasoning:
|
|
5496
|
-
cacheRead:
|
|
5497
|
-
cacheWrite:
|
|
6297
|
+
if (info2.tokens) {
|
|
6298
|
+
tracker.trackTokenUsage(info2.sessionID, {
|
|
6299
|
+
input: info2.tokens.input ?? 0,
|
|
6300
|
+
output: info2.tokens.output ?? 0,
|
|
6301
|
+
reasoning: info2.tokens.reasoning ?? 0,
|
|
6302
|
+
cacheRead: info2.tokens.cache?.read ?? 0,
|
|
6303
|
+
cacheWrite: info2.tokens.cache?.write ?? 0
|
|
5498
6304
|
});
|
|
5499
6305
|
}
|
|
5500
6306
|
}
|
|
@@ -5503,12 +6309,12 @@ ${cmdResult.contextInjection}`;
|
|
|
5503
6309
|
const evt = event;
|
|
5504
6310
|
if (evt.properties?.command === "session.interrupt") {
|
|
5505
6311
|
pauseWork(directory);
|
|
5506
|
-
|
|
6312
|
+
info("[work-continuation] User interrupt detected — work paused");
|
|
5507
6313
|
if (directory) {
|
|
5508
6314
|
const activeWorkflow = getActiveWorkflowInstance(directory);
|
|
5509
6315
|
if (activeWorkflow && activeWorkflow.status === "running") {
|
|
5510
6316
|
pauseWorkflow(directory, "User interrupt");
|
|
5511
|
-
|
|
6317
|
+
info("[workflow] User interrupt detected — workflow paused");
|
|
5512
6318
|
}
|
|
5513
6319
|
}
|
|
5514
6320
|
}
|
|
@@ -5530,21 +6336,21 @@ ${cmdResult.contextInjection}`;
|
|
|
5530
6336
|
const lastMsg = lastAssistantMessageText.get(sessionId) ?? undefined;
|
|
5531
6337
|
const lastUserMsg = lastUserMessageText.get(sessionId) ?? undefined;
|
|
5532
6338
|
const result = hooks.workflowContinuation(sessionId, lastMsg, lastUserMsg);
|
|
5533
|
-
if (result.continuationPrompt &&
|
|
6339
|
+
if (result.continuationPrompt && client2) {
|
|
5534
6340
|
try {
|
|
5535
|
-
await
|
|
6341
|
+
await client2.session.promptAsync({
|
|
5536
6342
|
path: { id: sessionId },
|
|
5537
6343
|
body: {
|
|
5538
6344
|
parts: [{ type: "text", text: result.continuationPrompt }],
|
|
5539
6345
|
...result.switchAgent ? { agent: getAgentDisplayName(result.switchAgent) } : {}
|
|
5540
6346
|
}
|
|
5541
6347
|
});
|
|
5542
|
-
|
|
6348
|
+
debug("[workflow] Injected workflow continuation prompt", {
|
|
5543
6349
|
sessionId,
|
|
5544
6350
|
agent: result.switchAgent
|
|
5545
6351
|
});
|
|
5546
6352
|
} catch (err) {
|
|
5547
|
-
|
|
6353
|
+
error("[workflow] Failed to inject workflow continuation", { sessionId, error: String(err) });
|
|
5548
6354
|
}
|
|
5549
6355
|
return;
|
|
5550
6356
|
}
|
|
@@ -5556,21 +6362,21 @@ ${cmdResult.contextInjection}`;
|
|
|
5556
6362
|
const sessionId = evt.properties?.sessionID ?? "";
|
|
5557
6363
|
if (sessionId) {
|
|
5558
6364
|
const result = hooks.workContinuation(sessionId);
|
|
5559
|
-
if (result.continuationPrompt &&
|
|
6365
|
+
if (result.continuationPrompt && client2) {
|
|
5560
6366
|
try {
|
|
5561
|
-
await
|
|
6367
|
+
await client2.session.promptAsync({
|
|
5562
6368
|
path: { id: sessionId },
|
|
5563
6369
|
body: {
|
|
5564
6370
|
parts: [{ type: "text", text: result.continuationPrompt }]
|
|
5565
6371
|
}
|
|
5566
6372
|
});
|
|
5567
|
-
|
|
6373
|
+
debug("[work-continuation] Injected continuation prompt", { sessionId });
|
|
5568
6374
|
continuationFired = true;
|
|
5569
6375
|
} catch (err) {
|
|
5570
|
-
|
|
6376
|
+
error("[work-continuation] Failed to inject continuation", { sessionId, error: String(err) });
|
|
5571
6377
|
}
|
|
5572
6378
|
} else if (result.continuationPrompt) {
|
|
5573
|
-
|
|
6379
|
+
debug("[work-continuation] continuationPrompt available but no client", { sessionId });
|
|
5574
6380
|
}
|
|
5575
6381
|
}
|
|
5576
6382
|
}
|
|
@@ -5674,14 +6480,14 @@ ${cmdResult.contextInjection}`;
|
|
|
5674
6480
|
};
|
|
5675
6481
|
}
|
|
5676
6482
|
// src/features/analytics/fingerprint.ts
|
|
5677
|
-
import { existsSync as
|
|
5678
|
-
import { join as
|
|
6483
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
|
|
6484
|
+
import { join as join11 } from "path";
|
|
5679
6485
|
import { arch } from "os";
|
|
5680
6486
|
|
|
5681
6487
|
// src/shared/version.ts
|
|
5682
6488
|
import { readFileSync as readFileSync11 } from "fs";
|
|
5683
6489
|
import { fileURLToPath } from "url";
|
|
5684
|
-
import { dirname as dirname2, join as
|
|
6490
|
+
import { dirname as dirname2, join as join10 } from "path";
|
|
5685
6491
|
var cachedVersion;
|
|
5686
6492
|
function getWeaveVersion() {
|
|
5687
6493
|
if (cachedVersion !== undefined)
|
|
@@ -5690,7 +6496,7 @@ function getWeaveVersion() {
|
|
|
5690
6496
|
const thisDir = dirname2(fileURLToPath(import.meta.url));
|
|
5691
6497
|
for (const rel of ["../../package.json", "../package.json"]) {
|
|
5692
6498
|
try {
|
|
5693
|
-
const pkg = JSON.parse(readFileSync11(
|
|
6499
|
+
const pkg = JSON.parse(readFileSync11(join10(thisDir, rel), "utf-8"));
|
|
5694
6500
|
if (pkg.name === "@opencode_weave/weave" && typeof pkg.version === "string") {
|
|
5695
6501
|
const version = pkg.version;
|
|
5696
6502
|
cachedVersion = version;
|
|
@@ -5795,7 +6601,7 @@ function detectStack(directory) {
|
|
|
5795
6601
|
const detected = [];
|
|
5796
6602
|
for (const marker of STACK_MARKERS) {
|
|
5797
6603
|
for (const file of marker.files) {
|
|
5798
|
-
if (
|
|
6604
|
+
if (existsSync12(join11(directory, file))) {
|
|
5799
6605
|
detected.push({
|
|
5800
6606
|
name: marker.name,
|
|
5801
6607
|
confidence: marker.confidence,
|
|
@@ -5806,8 +6612,8 @@ function detectStack(directory) {
|
|
|
5806
6612
|
}
|
|
5807
6613
|
}
|
|
5808
6614
|
try {
|
|
5809
|
-
const pkgPath =
|
|
5810
|
-
if (
|
|
6615
|
+
const pkgPath = join11(directory, "package.json");
|
|
6616
|
+
if (existsSync12(pkgPath)) {
|
|
5811
6617
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
5812
6618
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5813
6619
|
if (deps.react) {
|
|
@@ -5841,26 +6647,26 @@ function detectStack(directory) {
|
|
|
5841
6647
|
});
|
|
5842
6648
|
}
|
|
5843
6649
|
function detectPackageManager(directory) {
|
|
5844
|
-
if (
|
|
6650
|
+
if (existsSync12(join11(directory, "bun.lockb")))
|
|
5845
6651
|
return "bun";
|
|
5846
|
-
if (
|
|
6652
|
+
if (existsSync12(join11(directory, "pnpm-lock.yaml")))
|
|
5847
6653
|
return "pnpm";
|
|
5848
|
-
if (
|
|
6654
|
+
if (existsSync12(join11(directory, "yarn.lock")))
|
|
5849
6655
|
return "yarn";
|
|
5850
|
-
if (
|
|
6656
|
+
if (existsSync12(join11(directory, "package-lock.json")))
|
|
5851
6657
|
return "npm";
|
|
5852
|
-
if (
|
|
6658
|
+
if (existsSync12(join11(directory, "package.json")))
|
|
5853
6659
|
return "npm";
|
|
5854
6660
|
return;
|
|
5855
6661
|
}
|
|
5856
6662
|
function detectMonorepo(directory) {
|
|
5857
6663
|
for (const marker of MONOREPO_MARKERS) {
|
|
5858
|
-
if (
|
|
6664
|
+
if (existsSync12(join11(directory, marker)))
|
|
5859
6665
|
return true;
|
|
5860
6666
|
}
|
|
5861
6667
|
try {
|
|
5862
|
-
const pkgPath =
|
|
5863
|
-
if (
|
|
6668
|
+
const pkgPath = join11(directory, "package.json");
|
|
6669
|
+
if (existsSync12(pkgPath)) {
|
|
5864
6670
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
5865
6671
|
if (pkg.workspaces)
|
|
5866
6672
|
return true;
|
|
@@ -5895,14 +6701,14 @@ function fingerprintProject(directory) {
|
|
|
5895
6701
|
try {
|
|
5896
6702
|
const fingerprint = generateFingerprint(directory);
|
|
5897
6703
|
writeFingerprint(directory, fingerprint);
|
|
5898
|
-
|
|
6704
|
+
debug("[analytics] Project fingerprinted", {
|
|
5899
6705
|
stack: fingerprint.stack.map((s) => s.name),
|
|
5900
6706
|
primaryLanguage: fingerprint.primaryLanguage,
|
|
5901
6707
|
packageManager: fingerprint.packageManager
|
|
5902
6708
|
});
|
|
5903
6709
|
return fingerprint;
|
|
5904
6710
|
} catch (err) {
|
|
5905
|
-
|
|
6711
|
+
warn("[analytics] Fingerprinting failed (non-fatal)", { error: String(err) });
|
|
5906
6712
|
return null;
|
|
5907
6713
|
}
|
|
5908
6714
|
}
|
|
@@ -5914,14 +6720,14 @@ function getOrCreateFingerprint(directory) {
|
|
|
5914
6720
|
if (existing.weaveVersion === currentVersion) {
|
|
5915
6721
|
return existing;
|
|
5916
6722
|
}
|
|
5917
|
-
|
|
6723
|
+
debug("[analytics] Fingerprint version mismatch — regenerating", {
|
|
5918
6724
|
cached: existing.weaveVersion ?? "none",
|
|
5919
6725
|
current: currentVersion
|
|
5920
6726
|
});
|
|
5921
6727
|
}
|
|
5922
6728
|
return fingerprintProject(directory);
|
|
5923
6729
|
} catch (err) {
|
|
5924
|
-
|
|
6730
|
+
warn("[analytics] getOrCreateFingerprint failed (non-fatal)", { error: String(err) });
|
|
5925
6731
|
return null;
|
|
5926
6732
|
}
|
|
5927
6733
|
}
|
|
@@ -6045,7 +6851,7 @@ class SessionTracker {
|
|
|
6045
6851
|
};
|
|
6046
6852
|
try {
|
|
6047
6853
|
appendSessionSummary(this.directory, summary);
|
|
6048
|
-
|
|
6854
|
+
debug("[analytics] Session summary persisted", {
|
|
6049
6855
|
sessionId,
|
|
6050
6856
|
totalToolCalls,
|
|
6051
6857
|
totalDelegations: session.delegations.length,
|
|
@@ -6055,7 +6861,7 @@ class SessionTracker {
|
|
|
6055
6861
|
} : {}
|
|
6056
6862
|
});
|
|
6057
6863
|
} catch (err) {
|
|
6058
|
-
|
|
6864
|
+
warn("[analytics] Failed to persist session summary (non-fatal)", {
|
|
6059
6865
|
sessionId,
|
|
6060
6866
|
error: String(err)
|
|
6061
6867
|
});
|
|
@@ -6085,12 +6891,16 @@ function createAnalytics(directory, fingerprint) {
|
|
|
6085
6891
|
// src/index.ts
|
|
6086
6892
|
var WeavePlugin = async (ctx) => {
|
|
6087
6893
|
const pluginConfig = loadWeaveConfig(ctx.directory, ctx);
|
|
6894
|
+
setClient(ctx.client);
|
|
6895
|
+
if (pluginConfig.log_level) {
|
|
6896
|
+
setLogLevel(pluginConfig.log_level);
|
|
6897
|
+
}
|
|
6088
6898
|
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
6089
6899
|
const isHookEnabled = (name) => !disabledHooks.has(name);
|
|
6090
6900
|
const analyticsEnabled = pluginConfig.analytics?.enabled === true;
|
|
6091
6901
|
const fingerprintEnabled = analyticsEnabled && pluginConfig.analytics?.use_fingerprint === true;
|
|
6092
6902
|
const fingerprint = fingerprintEnabled ? getOrCreateFingerprint(ctx.directory) : null;
|
|
6093
|
-
const configDir =
|
|
6903
|
+
const configDir = join12(ctx.directory, ".opencode");
|
|
6094
6904
|
const toolsResult = await createTools({ ctx, pluginConfig });
|
|
6095
6905
|
const managers = createManagers({ ctx, pluginConfig, resolveSkills: toolsResult.resolveSkillsFn, fingerprint, configDir });
|
|
6096
6906
|
const hooks = createHooks({ pluginConfig, isHookEnabled, directory: ctx.directory, analyticsEnabled });
|