@opencode_weave/weave 0.7.4-preview.1 → 0.7.5

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/index.js CHANGED
@@ -1,11 +1,814 @@
1
1
  // src/index.ts
2
- import { join as join13 } from "path";
2
+ import { join as join12 } from "path";
3
3
 
4
4
  // src/config/loader.ts
5
- import { existsSync as existsSync2, readFileSync } from "node:fs";
6
- import { join as join2 } from "node:path";
7
- import { homedir as homedir2 } from "node:os";
8
- import { parse } from "jsonc-parser";
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
- import * as fs from "fs";
145
- import * as path from "path";
146
- import * as os from "os";
147
- function getLogDir() {
148
- const home = os.homedir();
149
- return path.join(home, ".opencode", "logs");
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
- function resolveLogFile() {
152
- const dir = getLogDir();
153
- try {
154
- if (!fs.existsSync(dir)) {
155
- fs.mkdirSync(dir, { recursive: true });
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
- } catch {}
158
- return path.join(dir, "weave-opencode.log");
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
- try {
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}]`;
@@ -177,44 +1007,127 @@ function logDelegation(event) {
177
1007
  }
178
1008
 
179
1009
  // src/config/loader.ts
1010
+ var lastLoadResult = null;
1011
+ function getLastConfigLoadResult() {
1012
+ return lastLoadResult;
1013
+ }
180
1014
  function readJsoncFile(filePath) {
181
1015
  try {
182
1016
  const text = readFileSync(filePath, "utf-8");
183
1017
  const errors = [];
184
- const parsed = parse(text, errors);
1018
+ const parsed = parse2(text, errors);
185
1019
  if (errors.length > 0) {
186
- log(`JSONC parse warnings in ${filePath}: ${errors.length} issue(s)`);
1020
+ warn(`JSONC parse warnings in ${filePath}: ${errors.length} issue(s)`);
187
1021
  }
188
1022
  return parsed ?? {};
189
1023
  } catch (e) {
190
- log(`Failed to read config file ${filePath}`, e);
1024
+ error(`Failed to read config file ${filePath}`, e);
191
1025
  return {};
192
1026
  }
193
1027
  }
194
1028
  function detectConfigFile(basePath) {
195
1029
  const jsoncPath = basePath + ".jsonc";
196
- if (existsSync2(jsoncPath))
1030
+ if (existsSync(jsoncPath))
197
1031
  return jsoncPath;
198
1032
  const jsonPath = basePath + ".json";
199
- if (existsSync2(jsonPath))
1033
+ if (existsSync(jsonPath))
200
1034
  return jsonPath;
201
1035
  return null;
202
1036
  }
203
1037
  function loadWeaveConfig(directory, _ctx, _homeDir) {
204
- const userBasePath = join2(_homeDir ?? homedir2(), ".config", "opencode", "weave-opencode");
205
- const projectBasePath = join2(directory, ".opencode", "weave-opencode");
1038
+ const userBasePath = join(_homeDir ?? homedir(), ".config", "opencode", "weave-opencode");
1039
+ const projectBasePath = join(directory, ".opencode", "weave-opencode");
206
1040
  const userConfigPath = detectConfigFile(userBasePath);
207
1041
  const projectConfigPath = detectConfigFile(projectBasePath);
1042
+ debug("Loading Weave config", {
1043
+ userConfig: userConfigPath ?? "(none)",
1044
+ projectConfig: projectConfigPath ?? "(none)"
1045
+ });
1046
+ const loadedFiles = [];
1047
+ if (userConfigPath)
1048
+ loadedFiles.push(userConfigPath);
1049
+ if (projectConfigPath)
1050
+ loadedFiles.push(projectConfigPath);
208
1051
  const userRaw = userConfigPath ? readJsoncFile(userConfigPath) : {};
209
1052
  const projectRaw = projectConfigPath ? readJsoncFile(projectConfigPath) : {};
210
1053
  const merged = mergeConfigs(userRaw, projectRaw);
211
1054
  const result = WeaveConfigSchema.safeParse(merged);
212
1055
  if (!result.success) {
213
- log("WeaveConfig validation errors — using defaults", result.error.issues);
214
- return WeaveConfigSchema.parse({});
215
- }
1056
+ const recovery = recoverValidSections(merged, result.error.issues);
1057
+ if (recovery) {
1058
+ lastLoadResult = { config: recovery.config, loadedFiles, diagnostics: recovery.diagnostics };
1059
+ return recovery.config;
1060
+ }
1061
+ const diagnostics = [{
1062
+ level: "error",
1063
+ section: "(root)",
1064
+ message: "Config validation failed entirely — using defaults",
1065
+ fields: result.error.issues.map((i) => ({
1066
+ path: i.path.join(".") || "(root)",
1067
+ message: i.message
1068
+ }))
1069
+ }];
1070
+ error("WeaveConfig validation errors — using defaults. Fix the issues below and restart.", result.error.issues.map((i) => ({
1071
+ path: i.path.join(".") || "(root)",
1072
+ message: i.message
1073
+ })));
1074
+ const fallback = WeaveConfigSchema.parse({});
1075
+ lastLoadResult = { config: fallback, loadedFiles, diagnostics };
1076
+ return fallback;
1077
+ }
1078
+ debug("Weave config loaded successfully", {
1079
+ hasAgentOverrides: !!result.data.agents && Object.keys(result.data.agents).length > 0,
1080
+ disabledAgents: result.data.disabled_agents ?? [],
1081
+ customAgents: result.data.custom_agents ? Object.keys(result.data.custom_agents) : [],
1082
+ logLevel: result.data.log_level ?? "(default)",
1083
+ analyticsEnabled: result.data.analytics?.enabled ?? false
1084
+ });
1085
+ lastLoadResult = { config: result.data, loadedFiles, diagnostics: [] };
216
1086
  return result.data;
217
1087
  }
1088
+ function recoverValidSections(merged, issues) {
1089
+ const failingKeys = new Set;
1090
+ for (const issue of issues) {
1091
+ if (issue.path.length > 0) {
1092
+ failingKeys.add(String(issue.path[0]));
1093
+ }
1094
+ }
1095
+ if (failingKeys.size === 0)
1096
+ return null;
1097
+ const diagnostics = [];
1098
+ for (const key of failingKeys) {
1099
+ const sectionIssues = issues.filter((i) => i.path.length > 0 && String(i.path[0]) === key);
1100
+ const fields = sectionIssues.map((i) => ({
1101
+ path: i.path.slice(1).join("."),
1102
+ message: i.message
1103
+ }));
1104
+ const details = fields.map((f) => f.path ? ` → ${f.path}: ${f.message}` : ` → ${f.message}`);
1105
+ diagnostics.push({
1106
+ level: "warn",
1107
+ section: key,
1108
+ message: `Section "${key}" was dropped due to validation errors`,
1109
+ fields
1110
+ });
1111
+ warn(`Config section "${key}" has validation errors and was dropped:
1112
+ ${details.join(`
1113
+ `)}
1114
+ Remaining config sections are preserved. Fix the errors above and restart.`);
1115
+ }
1116
+ const stripped = { ...merged };
1117
+ for (const key of failingKeys) {
1118
+ delete stripped[key];
1119
+ }
1120
+ const retry = WeaveConfigSchema.safeParse(stripped);
1121
+ if (retry.success) {
1122
+ debug("Config recovery succeeded", {
1123
+ droppedSections: [...failingKeys],
1124
+ hasAgentOverrides: !!retry.data.agents && Object.keys(retry.data.agents).length > 0,
1125
+ customAgents: retry.data.custom_agents ? Object.keys(retry.data.custom_agents) : []
1126
+ });
1127
+ return { config: retry.data, diagnostics };
1128
+ }
1129
+ return null;
1130
+ }
218
1131
 
219
1132
  // src/shared/agent-display-names.ts
220
1133
  var AGENT_DISPLAY_NAMES = {
@@ -377,6 +1290,16 @@ ${RUN_WORKFLOW_TEMPLATE}
377
1290
  <session-context>Session ID: $SESSION_ID Timestamp: $TIMESTAMP</session-context>
378
1291
  <user-request>$ARGUMENTS</user-request>`,
379
1292
  argumentHint: '<workflow-name> ["goal"]'
1293
+ },
1294
+ "weave-health": {
1295
+ name: "weave-health",
1296
+ description: "Show Weave config health and any validation issues",
1297
+ agent: "loom",
1298
+ template: `<command-instruction>
1299
+ Display the Weave health report below to the user. Present warnings and errors prominently.
1300
+ If there are no issues, confirm that Weave config is healthy.
1301
+ </command-instruction>
1302
+ <weave-health>$ARGUMENTS</weave-health>`
380
1303
  }
381
1304
  };
382
1305
  // src/managers/config-handler.ts
@@ -510,9 +1433,9 @@ class BackgroundManager {
510
1433
  }
511
1434
 
512
1435
  // src/managers/skill-mcp-manager.ts
513
- function createStdioClient(config, info) {
1436
+ function createStdioClient(config, info2) {
514
1437
  const { command, args = [] } = config;
515
- const { serverName, skillName } = info;
1438
+ const { serverName, skillName } = info2;
516
1439
  if (!command) {
517
1440
  throw new Error(`missing 'command' field for stdio MCP server '${serverName}' in skill '${skillName}'`);
518
1441
  }
@@ -541,28 +1464,28 @@ function createStdioClient(config, info) {
541
1464
  }
542
1465
  };
543
1466
  }
544
- function getClientKey(info) {
545
- return `${info.sessionID}:${info.skillName}:${info.serverName}`;
1467
+ function getClientKey(info2) {
1468
+ return `${info2.sessionID}:${info2.skillName}:${info2.serverName}`;
546
1469
  }
547
1470
 
548
1471
  class SkillMcpManager {
549
1472
  clients = new Map;
550
- async getOrCreateClient(info, config) {
551
- const key = getClientKey(info);
1473
+ async getOrCreateClient(info2, config) {
1474
+ const key = getClientKey(info2);
552
1475
  const existing = this.clients.get(key);
553
1476
  if (existing) {
554
1477
  return existing;
555
1478
  }
556
- const { serverName, skillName } = info;
1479
+ const { serverName, skillName } = info2;
557
1480
  if (config.type === "http") {
558
1481
  throw new Error("HTTP MCP not supported in v1");
559
1482
  }
560
1483
  if (!config.command) {
561
1484
  throw new Error(`missing 'command' field for stdio MCP server '${serverName}' in skill '${skillName}'`);
562
1485
  }
563
- const client = createStdioClient(config, info);
564
- this.clients.set(key, client);
565
- return client;
1486
+ const client2 = createStdioClient(config, info2);
1487
+ this.clients.set(key, client2);
1488
+ return client2;
566
1489
  }
567
1490
  async disconnectSession(sessionID) {
568
1491
  const prefix = `${sessionID}:`;
@@ -573,16 +1496,16 @@ class SkillMcpManager {
573
1496
  }
574
1497
  }
575
1498
  await Promise.all(keysToRemove.map(async (key) => {
576
- const client = this.clients.get(key);
577
- if (client) {
578
- await client.close();
1499
+ const client2 = this.clients.get(key);
1500
+ if (client2) {
1501
+ await client2.close();
579
1502
  this.clients.delete(key);
580
1503
  }
581
1504
  }));
582
1505
  }
583
1506
  async disconnectAll() {
584
- await Promise.all(Array.from(this.clients.entries()).map(async ([key, client]) => {
585
- await client.close();
1507
+ await Promise.all(Array.from(this.clients.entries()).map(async ([key, client2]) => {
1508
+ await client2.close();
586
1509
  this.clients.delete(key);
587
1510
  }));
588
1511
  this.clients.clear();
@@ -590,19 +1513,19 @@ class SkillMcpManager {
590
1513
  getConnectedServers() {
591
1514
  return Array.from(this.clients.keys());
592
1515
  }
593
- isConnected(info) {
594
- return this.clients.has(getClientKey(info));
1516
+ isConnected(info2) {
1517
+ return this.clients.has(getClientKey(info2));
595
1518
  }
596
- async callTool(info, config, name, args) {
1519
+ async callTool(info2, config, name, args) {
597
1520
  const maxAttempts = 3;
598
1521
  let lastError = null;
599
1522
  for (let attempt = 1;attempt <= maxAttempts; attempt++) {
600
1523
  try {
601
- const client = await this.getOrCreateClient(info, config);
602
- const result = await client.callTool({ name, arguments: args });
1524
+ const client2 = await this.getOrCreateClient(info2, config);
1525
+ const result = await client2.callTool({ name, arguments: args });
603
1526
  return result.content;
604
- } catch (error) {
605
- lastError = error instanceof Error ? error : new Error(String(error));
1527
+ } catch (error2) {
1528
+ lastError = error2 instanceof Error ? error2 : new Error(String(error2));
606
1529
  if (!lastError.message.toLowerCase().includes("not connected")) {
607
1530
  throw lastError;
608
1531
  }
@@ -729,8 +1652,6 @@ function buildDelegationSection(disabled) {
729
1652
  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
1653
  }
731
1654
  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
1655
  return `<Delegation>
735
1656
  ${lines.join(`
736
1657
  `)}
@@ -995,33 +1916,6 @@ After completing work for each task — BEFORE marking \`- [ ]\` → \`- [x]\`:
995
1916
  **Gate**: Only mark complete when ALL checks pass. If ANY check fails, fix first.
996
1917
  </Verification>`;
997
1918
  }
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
1919
  function buildTapestryPostExecutionReviewSection(disabled) {
1026
1920
  const hasWeft = isAgentEnabled("weft", disabled);
1027
1921
  const hasWarp = isAgentEnabled("warp", disabled);
@@ -1067,30 +1961,6 @@ function buildTapestryExecutionSection() {
1067
1961
  - Report completion with evidence (test output, file paths, commands run)
1068
1962
  </Execution>`;
1069
1963
  }
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
1964
  function buildTapestryStyleSection() {
1095
1965
  return `<Style>
1096
1966
  - Terse status updates only
@@ -1106,10 +1976,8 @@ function composeTapestryPrompt(options = {}) {
1106
1976
  buildTapestrySidebarTodosSection(),
1107
1977
  buildTapestryPlanExecutionSection(disabled),
1108
1978
  buildTapestryVerificationSection(),
1109
- buildTapestryVerificationGateSection(),
1110
1979
  buildTapestryPostExecutionReviewSection(disabled),
1111
1980
  buildTapestryExecutionSection(),
1112
- buildTapestryDebuggingSection(),
1113
1981
  buildTapestryStyleSection()
1114
1982
  ];
1115
1983
  return sections.join(`
@@ -1270,30 +2138,6 @@ FILES FIELD: For verification-only tasks that have no associated files (e.g., "r
1270
2138
  - After completing a plan, tell the user: "Plan saved to \`.weave/plans/{name}.md\`. Run /start-work to begin execution."
1271
2139
  </Constraints>
1272
2140
 
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
2141
  <Research>
1298
2142
  - Read relevant files before planning
1299
2143
  - Check existing patterns in the codebase
@@ -1422,10 +2266,9 @@ You operate in two modes depending on what you're asked to review:
1422
2266
 
1423
2267
  **Work Review** (reviewing completed implementation):
1424
2268
  - Read every changed file (use git diff --stat, then Read each file)
1425
- - Do NOT trust commit messages, PR descriptions, or task completion claims — the implementer may have been optimistic or incomplete. Verify everything by reading the actual code.
1426
- - Check spec compliance FIRST: does the code do what the task required? If it doesn't match requirements, reject before evaluating code quality.
1427
- - Then check code quality: look for stubs, TODOs, placeholders, hardcoded values
1428
- - Verify tests exist and test real behavior (not mocks of mocks)
2269
+ - Check the code actually does what the task required
2270
+ - Look for stubs, TODOs, placeholders, hardcoded values
2271
+ - Verify tests exist and test real behavior
1429
2272
  - Check for scope creep (changes outside the task spec)
1430
2273
  </ReviewModes>
1431
2274
 
@@ -1733,34 +2576,47 @@ var AGENT_MODEL_REQUIREMENTS = {
1733
2576
  function resolveAgentModel(agentName, options) {
1734
2577
  const { availableModels, agentMode, uiSelectedModel, categoryModel, overrideModel, systemDefaultModel, customFallbackChain } = options;
1735
2578
  const requirement = AGENT_MODEL_REQUIREMENTS[agentName];
1736
- if (overrideModel)
2579
+ if (overrideModel) {
2580
+ debug(`Model resolved for "${agentName}"`, { via: "override", model: overrideModel });
1737
2581
  return overrideModel;
2582
+ }
1738
2583
  if (uiSelectedModel && (agentMode === "primary" || agentMode === "all")) {
2584
+ debug(`Model resolved for "${agentName}"`, { via: "ui-selection", model: uiSelectedModel, agentMode });
1739
2585
  return uiSelectedModel;
1740
2586
  }
1741
- if (categoryModel && availableModels.has(categoryModel))
2587
+ if (categoryModel && availableModels.has(categoryModel)) {
2588
+ debug(`Model resolved for "${agentName}"`, { via: "category", model: categoryModel });
1742
2589
  return categoryModel;
2590
+ }
1743
2591
  const fallbackChain = requirement?.fallbackChain ?? customFallbackChain;
1744
2592
  if (fallbackChain) {
1745
2593
  for (const entry of fallbackChain) {
1746
2594
  for (const provider of entry.providers) {
1747
2595
  const qualified = `${provider}/${entry.model}`;
1748
- if (availableModels.has(qualified))
2596
+ if (availableModels.has(qualified)) {
2597
+ debug(`Model resolved for "${agentName}"`, { via: "fallback-chain", model: qualified });
1749
2598
  return qualified;
1750
- if (availableModels.has(entry.model))
2599
+ }
2600
+ if (availableModels.has(entry.model)) {
2601
+ debug(`Model resolved for "${agentName}"`, { via: "fallback-chain", model: entry.model });
1751
2602
  return entry.model;
2603
+ }
1752
2604
  }
1753
2605
  }
1754
2606
  }
1755
- if (systemDefaultModel)
2607
+ if (systemDefaultModel) {
2608
+ debug(`Model resolved for "${agentName}"`, { via: "system-default", model: systemDefaultModel });
1756
2609
  return systemDefaultModel;
2610
+ }
1757
2611
  if (fallbackChain && fallbackChain.length > 0) {
1758
2612
  const first = fallbackChain[0];
1759
2613
  if (first.providers.length > 0) {
1760
- return `${first.providers[0]}/${first.model}`;
2614
+ const guessed = `${first.providers[0]}/${first.model}`;
2615
+ debug(`Model resolved for "${agentName}" (offline best-guess — no available models matched)`, { via: "offline-guess", model: guessed });
2616
+ return guessed;
1761
2617
  }
1762
2618
  }
1763
- console.warn(`[weave] No model resolved for agent "${agentName}" — falling back to default github-copilot/claude-opus-4.6`);
2619
+ warn(`No model resolved for agent "${agentName}" — falling back to default github-copilot/claude-opus-4.6`, { agentName });
1764
2620
  return "github-copilot/claude-opus-4.6";
1765
2621
  }
1766
2622
 
@@ -1873,8 +2729,10 @@ function createBuiltinAgents(options = {}) {
1873
2729
  const disabledSet = new Set(disabledAgents);
1874
2730
  const result = {};
1875
2731
  for (const [name, factory] of Object.entries(AGENT_FACTORIES)) {
1876
- if (disabledSet.has(name))
2732
+ if (disabledSet.has(name)) {
2733
+ debug(`Builtin agent "${name}" is disabled — skipping`);
1877
2734
  continue;
2735
+ }
1878
2736
  const override = agentOverrides[name];
1879
2737
  const overrideModel = override?.model;
1880
2738
  const resolvedModel = resolveAgentModel(name, {
@@ -1884,6 +2742,9 @@ function createBuiltinAgents(options = {}) {
1884
2742
  systemDefaultModel,
1885
2743
  overrideModel
1886
2744
  });
2745
+ if (overrideModel) {
2746
+ debug(`Builtin agent "${name}" model overridden via config`, { model: resolvedModel });
2747
+ }
1887
2748
  let built;
1888
2749
  if (name === "loom") {
1889
2750
  built = createLoomAgentWithOptions(resolvedModel, disabledSet, fingerprint, customAgentMetadata);
@@ -1921,7 +2782,7 @@ function createBuiltinAgents(options = {}) {
1921
2782
  }
1922
2783
 
1923
2784
  // src/agents/prompt-loader.ts
1924
- import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
2785
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
1925
2786
  import { resolve, isAbsolute as isAbsolute2, normalize, sep } from "path";
1926
2787
  function loadPromptFile(promptFilePath, basePath) {
1927
2788
  if (isAbsolute2(promptFilePath)) {
@@ -1932,7 +2793,7 @@ function loadPromptFile(promptFilePath, basePath) {
1932
2793
  if (!resolvedPath.startsWith(base + sep) && resolvedPath !== base) {
1933
2794
  return null;
1934
2795
  }
1935
- if (!existsSync3(resolvedPath)) {
2796
+ if (!existsSync2(resolvedPath)) {
1936
2797
  return null;
1937
2798
  }
1938
2799
  return readFileSync2(resolvedPath, "utf-8").trim();
@@ -1968,11 +2829,17 @@ function buildCustomAgent(name, config, options = {}) {
1968
2829
  }
1969
2830
  const { resolveSkills, disabledSkills, availableModels = new Set, systemDefaultModel, uiSelectedModel, configDir } = options;
1970
2831
  let prompt = config.prompt ?? "";
2832
+ let promptSource = "inline";
1971
2833
  if (config.prompt_file) {
1972
2834
  const fileContent = loadPromptFile(config.prompt_file, configDir);
1973
2835
  if (fileContent) {
1974
2836
  prompt = fileContent;
2837
+ promptSource = `file:${config.prompt_file}`;
2838
+ } else {
2839
+ promptSource = `file:${config.prompt_file} (not found — falling back to inline)`;
1975
2840
  }
2841
+ } else if (config.skills?.length) {
2842
+ promptSource = `skills:[${config.skills.join(",")}]`;
1976
2843
  }
1977
2844
  if (config.skills?.length && resolveSkills) {
1978
2845
  const skillContent = resolveSkills(config.skills, disabledSkills);
@@ -1995,6 +2862,13 @@ function buildCustomAgent(name, config, options = {}) {
1995
2862
  const displayName = config.display_name ?? name;
1996
2863
  registerAgentDisplayName(name, displayName);
1997
2864
  registerAgentNameVariants(name, displayName !== name ? [name, displayName] : undefined);
2865
+ debug(`Custom agent "${name}" built`, {
2866
+ model,
2867
+ displayName,
2868
+ mode,
2869
+ promptSource,
2870
+ hasPrompt: !!prompt
2871
+ });
1998
2872
  const agentConfig = {
1999
2873
  model,
2000
2874
  prompt: prompt || undefined,
@@ -2062,7 +2936,7 @@ function createManagers(options) {
2062
2936
  }
2063
2937
  } catch (err) {
2064
2938
  if (err instanceof Error && err.message.includes("not a built-in agent")) {
2065
- log(`Skipping display_name override for non-builtin agent "${name}"`);
2939
+ debug(`Skipping display_name override for non-builtin agent "${name}"`);
2066
2940
  } else {
2067
2941
  throw err;
2068
2942
  }
@@ -2095,8 +2969,8 @@ function createManagers(options) {
2095
2969
  }
2096
2970
 
2097
2971
  // src/features/skill-loader/loader.ts
2098
- import * as path3 from "path";
2099
- import * as os2 from "os";
2972
+ import * as path2 from "path";
2973
+ import * as os from "os";
2100
2974
 
2101
2975
  // src/features/skill-loader/opencode-client.ts
2102
2976
  function deriveScope(location) {
@@ -2111,11 +2985,11 @@ async function fetchSkillsFromOpenCode(serverUrl, directory) {
2111
2985
  try {
2112
2986
  response = await fetch(url, { signal: AbortSignal.timeout(3000) });
2113
2987
  } catch (err) {
2114
- log("Failed to fetch skills from OpenCode — skills will not be loaded", { url, error: String(err) });
2988
+ error("Failed to fetch skills from OpenCode — skills will not be loaded", { url, error: String(err) });
2115
2989
  return [];
2116
2990
  }
2117
2991
  if (!response.ok) {
2118
- log("OpenCode /skill endpoint returned non-OK status — skills will not be loaded", {
2992
+ warn("OpenCode /skill endpoint returned non-OK status — skills will not be loaded", {
2119
2993
  url,
2120
2994
  status: response.status
2121
2995
  });
@@ -2125,11 +2999,11 @@ async function fetchSkillsFromOpenCode(serverUrl, directory) {
2125
2999
  try {
2126
3000
  data = await response.json();
2127
3001
  } catch (err) {
2128
- log("Failed to parse skills response from OpenCode", { url, error: String(err) });
3002
+ error("Failed to parse skills response from OpenCode", { url, error: String(err) });
2129
3003
  return [];
2130
3004
  }
2131
3005
  if (!Array.isArray(data)) {
2132
- log("Unexpected skills response shape from OpenCode — expected array", { url });
3006
+ warn("Unexpected skills response shape from OpenCode — expected array", { url });
2133
3007
  return [];
2134
3008
  }
2135
3009
  const skills = [];
@@ -2148,8 +3022,8 @@ async function fetchSkillsFromOpenCode(serverUrl, directory) {
2148
3022
  }
2149
3023
 
2150
3024
  // src/features/skill-loader/discovery.ts
2151
- import * as fs2 from "fs";
2152
- import * as path2 from "path";
3025
+ import * as fs from "fs";
3026
+ import * as path from "path";
2153
3027
  function parseFrontmatter(text) {
2154
3028
  const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
2155
3029
  const match = frontmatterRegex.exec(text);
@@ -2196,7 +3070,7 @@ function parseFrontmatter(text) {
2196
3070
  i++;
2197
3071
  }
2198
3072
  } catch (err) {
2199
- log("Failed to parse YAML frontmatter", { error: String(err) });
3073
+ warn("Failed to parse YAML frontmatter", { error: String(err) });
2200
3074
  return { metadata: {}, content: text };
2201
3075
  }
2202
3076
  return { metadata, content: body };
@@ -2224,22 +3098,22 @@ function setMetadataField(metadata, key, value) {
2224
3098
  }
2225
3099
  function scanDirectory(options) {
2226
3100
  const { directory, scope } = options;
2227
- if (!fs2.existsSync(directory)) {
3101
+ if (!fs.existsSync(directory)) {
2228
3102
  return [];
2229
3103
  }
2230
3104
  let entries;
2231
3105
  try {
2232
- entries = fs2.readdirSync(directory, { withFileTypes: true });
3106
+ entries = fs.readdirSync(directory, { withFileTypes: true });
2233
3107
  } catch (err) {
2234
- log("Failed to read skills directory", { directory, error: String(err) });
3108
+ warn("Failed to read skills directory", { directory, error: String(err) });
2235
3109
  return [];
2236
3110
  }
2237
3111
  const skills = [];
2238
3112
  for (const entry of entries) {
2239
- const fullPath = path2.join(directory, entry.name);
3113
+ const fullPath = path.join(directory, entry.name);
2240
3114
  if (entry.isDirectory()) {
2241
- const skillFile = path2.join(fullPath, "SKILL.md");
2242
- if (fs2.existsSync(skillFile)) {
3115
+ const skillFile = path.join(fullPath, "SKILL.md");
3116
+ if (fs.existsSync(skillFile)) {
2243
3117
  const skill = loadSkillFile(skillFile, scope);
2244
3118
  if (skill)
2245
3119
  skills.push(skill);
@@ -2257,14 +3131,14 @@ function scanDirectory(options) {
2257
3131
  function loadSkillFile(filePath, scope) {
2258
3132
  let text;
2259
3133
  try {
2260
- text = fs2.readFileSync(filePath, "utf8");
3134
+ text = fs.readFileSync(filePath, "utf8");
2261
3135
  } catch (err) {
2262
- log("Failed to read skill file", { filePath, error: String(err) });
3136
+ warn("Failed to read skill file", { filePath, error: String(err) });
2263
3137
  return null;
2264
3138
  }
2265
3139
  const { metadata, content } = parseFrontmatter(text);
2266
3140
  if (!metadata.name) {
2267
- log("Skill file missing name in frontmatter — skipping", { filePath });
3141
+ debug("Skill file missing name in frontmatter — skipping", { filePath });
2268
3142
  return null;
2269
3143
  }
2270
3144
  return { name: metadata.name, description: metadata.description ?? "", content, scope, path: filePath, model: metadata.model };
@@ -2292,8 +3166,8 @@ function resolveSafePath(dir, projectRoot) {
2292
3166
 
2293
3167
  // src/features/skill-loader/loader.ts
2294
3168
  function scanFilesystemSkills(directory, customDirs) {
2295
- const userDir = path3.join(os2.homedir(), ".config", "opencode", "skills");
2296
- const projectDir = path3.join(directory, ".opencode", "skills");
3169
+ const userDir = path2.join(os.homedir(), ".config", "opencode", "skills");
3170
+ const projectDir = path2.join(directory, ".opencode", "skills");
2297
3171
  const userSkills = scanDirectory({ directory: userDir, scope: "user" });
2298
3172
  const projectSkills = scanDirectory({ directory: projectDir, scope: "project" });
2299
3173
  const customSkills = [];
@@ -2324,7 +3198,7 @@ async function loadSkills(options) {
2324
3198
  const fsSkills = scanFilesystemSkills(directory, customDirs);
2325
3199
  const skills = mergeSkillSources(apiSkills, fsSkills);
2326
3200
  if (apiSkills.length === 0 && fsSkills.length > 0) {
2327
- log("OpenCode API returned no skills — using filesystem fallback", {
3201
+ debug("OpenCode API returned no skills — using filesystem fallback", {
2328
3202
  fsSkillCount: fsSkills.length,
2329
3203
  fsSkillNames: fsSkills.map((s) => s.name)
2330
3204
  });
@@ -2382,11 +3256,11 @@ function checkContextWindow(state, thresholds = { warningPct: 0.8, criticalPct:
2382
3256
  const usagePct = state.maxTokens > 0 ? state.usedTokens / state.maxTokens : 0;
2383
3257
  if (usagePct >= thresholds.criticalPct) {
2384
3258
  const message = buildRecoveryMessage(state, usagePct);
2385
- log(`[context-window] CRITICAL ${(usagePct * 100).toFixed(1)}% used in session ${state.sessionId}`);
3259
+ warn(`[context-window] CRITICAL ${(usagePct * 100).toFixed(1)}% used in session ${state.sessionId}`);
2386
3260
  return { action: "recover", usagePct, message };
2387
3261
  }
2388
3262
  if (usagePct >= thresholds.warningPct) {
2389
- log(`[context-window] WARNING ${(usagePct * 100).toFixed(1)}% used in session ${state.sessionId}`);
3263
+ warn(`[context-window] WARNING ${(usagePct * 100).toFixed(1)}% used in session ${state.sessionId}`);
2390
3264
  return { action: "warn", usagePct, message: buildWarningMessage(usagePct) };
2391
3265
  }
2392
3266
  return { action: "none", usagePct };
@@ -2411,7 +3285,7 @@ Update the sidebar: use todowrite to create a todo (in_progress, high priority):
2411
3285
  }
2412
3286
 
2413
3287
  // src/hooks/write-existing-file-guard.ts
2414
- import * as fs3 from "fs";
3288
+ import * as fs2 from "fs";
2415
3289
  function createWriteGuardState() {
2416
3290
  return { readFiles: new Set };
2417
3291
  }
@@ -2419,14 +3293,14 @@ function trackFileRead(state, filePath) {
2419
3293
  state.readFiles.add(filePath);
2420
3294
  }
2421
3295
  function checkWriteAllowed(state, filePath) {
2422
- if (!fs3.existsSync(filePath)) {
3296
+ if (!fs2.existsSync(filePath)) {
2423
3297
  return { allowed: true };
2424
3298
  }
2425
3299
  if (state.readFiles.has(filePath)) {
2426
3300
  return { allowed: true };
2427
3301
  }
2428
3302
  const warning = `⚠️ Write guard: Attempting to write to '${filePath}' without reading it first. Read the file before overwriting to avoid data loss.`;
2429
- log(`[write-guard] BLOCKED write to unread file: ${filePath}`);
3303
+ warn(`[write-guard] BLOCKED write to unread file: ${filePath}`);
2430
3304
  return { allowed: false, warning };
2431
3305
  }
2432
3306
  function createWriteGuard(state) {
@@ -2437,13 +3311,13 @@ function createWriteGuard(state) {
2437
3311
  }
2438
3312
 
2439
3313
  // src/hooks/rules-injector.ts
2440
- import * as fs4 from "fs";
2441
- import * as path4 from "path";
3314
+ import * as fs3 from "fs";
3315
+ import * as path3 from "path";
2442
3316
  var RULES_FILENAMES = ["AGENTS.md", ".rules", "CLAUDE.md"];
2443
3317
  function findRulesFile(directory) {
2444
3318
  for (const filename of RULES_FILENAMES) {
2445
- const candidate = path4.join(directory, filename);
2446
- if (fs4.existsSync(candidate)) {
3319
+ const candidate = path3.join(directory, filename);
3320
+ if (fs3.existsSync(candidate)) {
2447
3321
  return candidate;
2448
3322
  }
2449
3323
  }
@@ -2454,11 +3328,11 @@ function loadRulesForDirectory(directory) {
2454
3328
  if (!rulesFile)
2455
3329
  return;
2456
3330
  try {
2457
- const content = fs4.readFileSync(rulesFile, "utf8");
2458
- log(`[rules-injector] Loaded rules from ${rulesFile}`);
3331
+ const content = fs3.readFileSync(rulesFile, "utf8");
3332
+ debug(`[rules-injector] Loaded rules from ${rulesFile}`);
2459
3333
  return content;
2460
3334
  } catch {
2461
- log(`[rules-injector] Failed to read rules file: ${rulesFile}`);
3335
+ warn(`[rules-injector] Failed to read rules file: ${rulesFile}`);
2462
3336
  return;
2463
3337
  }
2464
3338
  }
@@ -2466,7 +3340,7 @@ function shouldInjectRules(toolName) {
2466
3340
  return toolName === "read" || toolName === "write" || toolName === "edit";
2467
3341
  }
2468
3342
  function getDirectoryFromFilePath(filePath) {
2469
- return path4.dirname(path4.resolve(filePath));
3343
+ return path3.dirname(path3.resolve(filePath));
2470
3344
  }
2471
3345
  function buildRulesInjection(rulesContent, directory) {
2472
3346
  return `<rules source="${directory}">
@@ -2525,7 +3399,7 @@ function buildKeywordInjection(detected) {
2525
3399
  function processMessageForKeywords(message, sessionId, actions) {
2526
3400
  const detected = detectKeywords(message, actions);
2527
3401
  if (detected.length > 0) {
2528
- log(`[keyword-detector] Detected keywords in session ${sessionId}: ${detected.map((a) => a.keyword).join(", ")}`);
3402
+ debug(`[keyword-detector] Detected keywords in session ${sessionId}: ${detected.map((a) => a.keyword).join(", ")}`);
2529
3403
  }
2530
3404
  return buildKeywordInjection(detected);
2531
3405
  }
@@ -2562,15 +3436,15 @@ var WORK_STATE_FILE = "state.json";
2562
3436
  var WORK_STATE_PATH = `${WEAVE_DIR}/${WORK_STATE_FILE}`;
2563
3437
  var PLANS_DIR = `${WEAVE_DIR}/plans`;
2564
3438
  // src/features/work-state/storage.ts
2565
- import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync, unlinkSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2, statSync } from "fs";
2566
- import { join as join6, basename } from "path";
3439
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync, unlinkSync, mkdirSync, readdirSync as readdirSync2, statSync } from "fs";
3440
+ import { join as join5, basename } from "path";
2567
3441
  import { execSync } from "child_process";
2568
3442
  var UNCHECKED_RE = /^[-*]\s*\[\s*\]/gm;
2569
3443
  var CHECKED_RE = /^[-*]\s*\[[xX]\]/gm;
2570
3444
  function readWorkState(directory) {
2571
- const filePath = join6(directory, WEAVE_DIR, WORK_STATE_FILE);
3445
+ const filePath = join5(directory, WEAVE_DIR, WORK_STATE_FILE);
2572
3446
  try {
2573
- if (!existsSync7(filePath))
3447
+ if (!existsSync6(filePath))
2574
3448
  return null;
2575
3449
  const raw = readFileSync5(filePath, "utf-8");
2576
3450
  const parsed = JSON.parse(raw);
@@ -2588,20 +3462,20 @@ function readWorkState(directory) {
2588
3462
  }
2589
3463
  function writeWorkState(directory, state) {
2590
3464
  try {
2591
- const dir = join6(directory, WEAVE_DIR);
2592
- if (!existsSync7(dir)) {
2593
- mkdirSync2(dir, { recursive: true });
3465
+ const dir = join5(directory, WEAVE_DIR);
3466
+ if (!existsSync6(dir)) {
3467
+ mkdirSync(dir, { recursive: true });
2594
3468
  }
2595
- writeFileSync(join6(dir, WORK_STATE_FILE), JSON.stringify(state, null, 2), "utf-8");
3469
+ writeFileSync(join5(dir, WORK_STATE_FILE), JSON.stringify(state, null, 2), "utf-8");
2596
3470
  return true;
2597
3471
  } catch {
2598
3472
  return false;
2599
3473
  }
2600
3474
  }
2601
3475
  function clearWorkState(directory) {
2602
- const filePath = join6(directory, WEAVE_DIR, WORK_STATE_FILE);
3476
+ const filePath = join5(directory, WEAVE_DIR, WORK_STATE_FILE);
2603
3477
  try {
2604
- if (existsSync7(filePath)) {
3478
+ if (existsSync6(filePath)) {
2605
3479
  unlinkSync(filePath);
2606
3480
  }
2607
3481
  return true;
@@ -2643,12 +3517,12 @@ function getHeadSha(directory) {
2643
3517
  }
2644
3518
  }
2645
3519
  function findPlans(directory) {
2646
- const plansDir = join6(directory, PLANS_DIR);
3520
+ const plansDir = join5(directory, PLANS_DIR);
2647
3521
  try {
2648
- if (!existsSync7(plansDir))
3522
+ if (!existsSync6(plansDir))
2649
3523
  return [];
2650
3524
  const files = readdirSync2(plansDir).filter((f) => f.endsWith(".md")).map((f) => {
2651
- const fullPath = join6(plansDir, f);
3525
+ const fullPath = join5(plansDir, f);
2652
3526
  const stat = statSync(fullPath);
2653
3527
  return { path: fullPath, mtime: stat.mtimeMs };
2654
3528
  }).sort((a, b) => b.mtime - a.mtime).map((f) => f.path);
@@ -2658,7 +3532,7 @@ function findPlans(directory) {
2658
3532
  }
2659
3533
  }
2660
3534
  function getPlanProgress(planPath) {
2661
- if (!existsSync7(planPath)) {
3535
+ if (!existsSync6(planPath)) {
2662
3536
  return { total: 0, completed: 0, isComplete: true };
2663
3537
  }
2664
3538
  try {
@@ -2694,7 +3568,7 @@ function resumeWork(directory) {
2694
3568
  return writeWorkState(directory, state);
2695
3569
  }
2696
3570
  // src/features/work-state/validation.ts
2697
- import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
3571
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
2698
3572
  import { resolve as resolve4, sep as sep3 } from "path";
2699
3573
  function validatePlan(planPath, projectDir) {
2700
3574
  const errors = [];
@@ -2709,7 +3583,7 @@ function validatePlan(planPath, projectDir) {
2709
3583
  });
2710
3584
  return { valid: false, errors, warnings };
2711
3585
  }
2712
- if (!existsSync8(resolvedPlanPath)) {
3586
+ if (!existsSync7(resolvedPlanPath)) {
2713
3587
  errors.push({
2714
3588
  severity: "error",
2715
3589
  category: "structure",
@@ -2899,7 +3773,7 @@ function validateFileReferences(content, projectDir, warnings) {
2899
3773
  });
2900
3774
  continue;
2901
3775
  }
2902
- if (!existsSync8(absolutePath)) {
3776
+ if (!existsSync7(absolutePath)) {
2903
3777
  warnings.push({
2904
3778
  severity: "warning",
2905
3779
  category: "file-references",
@@ -2990,8 +3864,8 @@ var ACTIVE_INSTANCE_FILE = "active-instance.json";
2990
3864
  var WORKFLOWS_DIR_PROJECT = ".opencode/workflows";
2991
3865
  var WORKFLOWS_DIR_USER = "workflows";
2992
3866
  // src/features/workflow/storage.ts
2993
- import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync3 } from "fs";
2994
- import { join as join7 } from "path";
3867
+ import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync3 } from "fs";
3868
+ import { join as join6 } from "path";
2995
3869
  import { randomBytes } from "node:crypto";
2996
3870
  function generateInstanceId() {
2997
3871
  return `wf_${randomBytes(4).toString("hex")}`;
@@ -3027,9 +3901,9 @@ function createWorkflowInstance(definition, definitionPath, goal, sessionId) {
3027
3901
  };
3028
3902
  }
3029
3903
  function readWorkflowInstance(directory, instanceId) {
3030
- const filePath = join7(directory, WORKFLOWS_STATE_DIR, instanceId, INSTANCE_STATE_FILE);
3904
+ const filePath = join6(directory, WORKFLOWS_STATE_DIR, instanceId, INSTANCE_STATE_FILE);
3031
3905
  try {
3032
- if (!existsSync9(filePath))
3906
+ if (!existsSync8(filePath))
3033
3907
  return null;
3034
3908
  const raw = readFileSync7(filePath, "utf-8");
3035
3909
  const parsed = JSON.parse(raw);
@@ -3044,20 +3918,20 @@ function readWorkflowInstance(directory, instanceId) {
3044
3918
  }
3045
3919
  function writeWorkflowInstance(directory, instance) {
3046
3920
  try {
3047
- const dir = join7(directory, WORKFLOWS_STATE_DIR, instance.instance_id);
3048
- if (!existsSync9(dir)) {
3049
- mkdirSync3(dir, { recursive: true });
3921
+ const dir = join6(directory, WORKFLOWS_STATE_DIR, instance.instance_id);
3922
+ if (!existsSync8(dir)) {
3923
+ mkdirSync2(dir, { recursive: true });
3050
3924
  }
3051
- writeFileSync2(join7(dir, INSTANCE_STATE_FILE), JSON.stringify(instance, null, 2), "utf-8");
3925
+ writeFileSync2(join6(dir, INSTANCE_STATE_FILE), JSON.stringify(instance, null, 2), "utf-8");
3052
3926
  return true;
3053
3927
  } catch {
3054
3928
  return false;
3055
3929
  }
3056
3930
  }
3057
3931
  function readActiveInstance(directory) {
3058
- const filePath = join7(directory, WORKFLOWS_STATE_DIR, ACTIVE_INSTANCE_FILE);
3932
+ const filePath = join6(directory, WORKFLOWS_STATE_DIR, ACTIVE_INSTANCE_FILE);
3059
3933
  try {
3060
- if (!existsSync9(filePath))
3934
+ if (!existsSync8(filePath))
3061
3935
  return null;
3062
3936
  const raw = readFileSync7(filePath, "utf-8");
3063
3937
  const parsed = JSON.parse(raw);
@@ -3070,21 +3944,21 @@ function readActiveInstance(directory) {
3070
3944
  }
3071
3945
  function setActiveInstance(directory, instanceId) {
3072
3946
  try {
3073
- const dir = join7(directory, WORKFLOWS_STATE_DIR);
3074
- if (!existsSync9(dir)) {
3075
- mkdirSync3(dir, { recursive: true });
3947
+ const dir = join6(directory, WORKFLOWS_STATE_DIR);
3948
+ if (!existsSync8(dir)) {
3949
+ mkdirSync2(dir, { recursive: true });
3076
3950
  }
3077
3951
  const pointer = { instance_id: instanceId };
3078
- writeFileSync2(join7(dir, ACTIVE_INSTANCE_FILE), JSON.stringify(pointer, null, 2), "utf-8");
3952
+ writeFileSync2(join6(dir, ACTIVE_INSTANCE_FILE), JSON.stringify(pointer, null, 2), "utf-8");
3079
3953
  return true;
3080
3954
  } catch {
3081
3955
  return false;
3082
3956
  }
3083
3957
  }
3084
3958
  function clearActiveInstance(directory) {
3085
- const filePath = join7(directory, WORKFLOWS_STATE_DIR, ACTIVE_INSTANCE_FILE);
3959
+ const filePath = join6(directory, WORKFLOWS_STATE_DIR, ACTIVE_INSTANCE_FILE);
3086
3960
  try {
3087
- if (existsSync9(filePath)) {
3961
+ if (existsSync8(filePath)) {
3088
3962
  unlinkSync2(filePath);
3089
3963
  }
3090
3964
  return true;
@@ -3099,10 +3973,9 @@ function getActiveWorkflowInstance(directory) {
3099
3973
  return readWorkflowInstance(directory, pointer.instance_id);
3100
3974
  }
3101
3975
  // src/features/workflow/discovery.ts
3102
- import * as fs5 from "fs";
3103
- import * as path5 from "path";
3104
- import * as os3 from "os";
3105
- import { parse as parseJsonc } from "jsonc-parser";
3976
+ import * as fs4 from "fs";
3977
+ import * as path4 from "path";
3978
+ import * as os2 from "os";
3106
3979
 
3107
3980
  // src/features/workflow/schema.ts
3108
3981
  import { z as z2 } from "zod";
@@ -3140,21 +4013,21 @@ var WorkflowDefinitionSchema = z2.object({
3140
4013
  function loadWorkflowDefinition(filePath) {
3141
4014
  let raw;
3142
4015
  try {
3143
- raw = fs5.readFileSync(filePath, "utf-8");
4016
+ raw = fs4.readFileSync(filePath, "utf-8");
3144
4017
  } catch (err) {
3145
- log("Failed to read workflow definition file", { filePath, error: String(err) });
4018
+ error("Failed to read workflow definition file", { filePath, error: String(err) });
3146
4019
  return null;
3147
4020
  }
3148
4021
  let parsed;
3149
4022
  try {
3150
- parsed = parseJsonc(raw);
4023
+ parsed = parse2(raw);
3151
4024
  } catch (err) {
3152
- log("Failed to parse workflow definition JSONC", { filePath, error: String(err) });
4025
+ error("Failed to parse workflow definition JSONC", { filePath, error: String(err) });
3153
4026
  return null;
3154
4027
  }
3155
4028
  const result = WorkflowDefinitionSchema.safeParse(parsed);
3156
4029
  if (!result.success) {
3157
- log("Workflow definition failed validation", {
4030
+ warn("Workflow definition failed validation", {
3158
4031
  filePath,
3159
4032
  errors: result.error.issues.map((i) => i.message)
3160
4033
  });
@@ -3163,13 +4036,13 @@ function loadWorkflowDefinition(filePath) {
3163
4036
  return result.data;
3164
4037
  }
3165
4038
  function scanWorkflowDirectory(directory, scope) {
3166
- if (!fs5.existsSync(directory))
4039
+ if (!fs4.existsSync(directory))
3167
4040
  return [];
3168
4041
  let entries;
3169
4042
  try {
3170
- entries = fs5.readdirSync(directory, { withFileTypes: true });
4043
+ entries = fs4.readdirSync(directory, { withFileTypes: true });
3171
4044
  } catch (err) {
3172
- log("Failed to read workflows directory", { directory, error: String(err) });
4045
+ warn("Failed to read workflows directory", { directory, error: String(err) });
3173
4046
  return [];
3174
4047
  }
3175
4048
  const workflows = [];
@@ -3178,7 +4051,7 @@ function scanWorkflowDirectory(directory, scope) {
3178
4051
  continue;
3179
4052
  if (!entry.name.endsWith(".jsonc") && !entry.name.endsWith(".json"))
3180
4053
  continue;
3181
- const filePath = path5.join(directory, entry.name);
4054
+ const filePath = path4.join(directory, entry.name);
3182
4055
  const definition = loadWorkflowDefinition(filePath);
3183
4056
  if (definition) {
3184
4057
  workflows.push({ definition, path: filePath, scope });
@@ -3187,8 +4060,8 @@ function scanWorkflowDirectory(directory, scope) {
3187
4060
  return workflows;
3188
4061
  }
3189
4062
  function discoverWorkflows(directory, customDirs) {
3190
- const projectDir = path5.join(directory, WORKFLOWS_DIR_PROJECT);
3191
- const userDir = path5.join(os3.homedir(), ".config", "opencode", WORKFLOWS_DIR_USER);
4063
+ const projectDir = path4.join(directory, WORKFLOWS_DIR_PROJECT);
4064
+ const userDir = path4.join(os2.homedir(), ".config", "opencode", WORKFLOWS_DIR_USER);
3192
4065
  const userWorkflows = scanWorkflowDirectory(userDir, "user");
3193
4066
  const projectWorkflows = scanWorkflowDirectory(projectDir, "project");
3194
4067
  const customWorkflows = [];
@@ -3310,8 +4183,8 @@ function truncateSummary(text) {
3310
4183
  return text.slice(0, maxLength - 3) + "...";
3311
4184
  }
3312
4185
  // src/features/workflow/completion.ts
3313
- import { existsSync as existsSync11 } from "fs";
3314
- import { join as join9 } from "path";
4186
+ import { existsSync as existsSync10 } from "fs";
4187
+ import { join as join8 } from "path";
3315
4188
  var DEFAULT_CONFIRM_KEYWORDS = ["confirmed", "approved", "continue", "done", "let's proceed", "looks good", "lgtm"];
3316
4189
  var VERDICT_APPROVE_RE = /\[\s*APPROVE\s*\]/i;
3317
4190
  var VERDICT_REJECT_RE = /\[\s*REJECT\s*\]/i;
@@ -3363,8 +4236,8 @@ function checkPlanCreated(context) {
3363
4236
  summary: `Plan created at ${matchingPlan}`
3364
4237
  };
3365
4238
  }
3366
- const directPath = join9(directory, ".weave", "plans", `${planName}.md`);
3367
- if (existsSync11(directPath)) {
4239
+ const directPath = join8(directory, ".weave", "plans", `${planName}.md`);
4240
+ if (existsSync10(directPath)) {
3368
4241
  return {
3369
4242
  complete: true,
3370
4243
  artifacts: { plan_path: directPath },
@@ -3379,8 +4252,8 @@ function checkPlanComplete(context) {
3379
4252
  if (!planName) {
3380
4253
  return { complete: false, reason: "plan_complete requires plan_name in completion config" };
3381
4254
  }
3382
- const planPath = join9(directory, ".weave", "plans", `${planName}.md`);
3383
- if (!existsSync11(planPath)) {
4255
+ const planPath = join8(directory, ".weave", "plans", `${planName}.md`);
4256
+ if (!existsSync10(planPath)) {
3384
4257
  return { complete: false, reason: `Plan file not found: ${planPath}` };
3385
4258
  }
3386
4259
  const progress = getPlanProgress(planPath);
@@ -3811,7 +4684,7 @@ ${available ? `Available workflows: ${available}` : "No workflow definitions ava
3811
4684
  sessionId,
3812
4685
  directory
3813
4686
  });
3814
- log("Workflow started", {
4687
+ info("Workflow started", {
3815
4688
  workflowName: match.definition.name,
3816
4689
  goal,
3817
4690
  agent: action.agent
@@ -4176,8 +5049,8 @@ function formatValidationResults(result) {
4176
5049
  if (result.errors.length > 0)
4177
5050
  lines.push("");
4178
5051
  lines.push("**Warnings:**");
4179
- for (const warn of result.warnings) {
4180
- lines.push(`- [${warn.category}] ${warn.message}`);
5052
+ for (const warn2 of result.warnings) {
5053
+ lines.push(`- [${warn2.category}] ${warn2.message}`);
4181
5054
  }
4182
5055
  }
4183
5056
  return lines.join(`
@@ -4376,21 +5249,21 @@ async function resolveTodoWriter() {
4376
5249
  }
4377
5250
 
4378
5251
  // src/hooks/compaction-todo-preserver.ts
4379
- function createCompactionTodoPreserver(client) {
5252
+ function createCompactionTodoPreserver(client2) {
4380
5253
  const snapshots = new Map;
4381
5254
  async function capture(sessionID) {
4382
5255
  try {
4383
- const response = await client.session.todo({ path: { id: sessionID } });
5256
+ const response = await client2.session.todo({ path: { id: sessionID } });
4384
5257
  const todos = response.data ?? [];
4385
5258
  if (todos.length > 0) {
4386
5259
  snapshots.set(sessionID, todos);
4387
- log("[compaction-todo-preserver] Captured snapshot", {
5260
+ debug("[compaction-todo-preserver] Captured snapshot", {
4388
5261
  sessionID,
4389
5262
  count: todos.length
4390
5263
  });
4391
5264
  }
4392
5265
  } catch (err) {
4393
- log("[compaction-todo-preserver] Failed to capture snapshot (non-fatal)", {
5266
+ warn("[compaction-todo-preserver] Failed to capture snapshot (non-fatal)", {
4394
5267
  sessionID,
4395
5268
  error: String(err)
4396
5269
  });
@@ -4402,10 +5275,10 @@ function createCompactionTodoPreserver(client) {
4402
5275
  return;
4403
5276
  }
4404
5277
  try {
4405
- const response = await client.session.todo({ path: { id: sessionID } });
5278
+ const response = await client2.session.todo({ path: { id: sessionID } });
4406
5279
  const currentTodos = response.data ?? [];
4407
5280
  if (currentTodos.length > 0) {
4408
- log("[compaction-todo-preserver] Todos survived compaction, skipping restore", {
5281
+ debug("[compaction-todo-preserver] Todos survived compaction, skipping restore", {
4409
5282
  sessionID,
4410
5283
  currentCount: currentTodos.length
4411
5284
  });
@@ -4415,18 +5288,18 @@ function createCompactionTodoPreserver(client) {
4415
5288
  const todoWriter = await resolveTodoWriter();
4416
5289
  if (todoWriter) {
4417
5290
  todoWriter({ sessionID, todos: snapshot });
4418
- log("[compaction-todo-preserver] Restored todos via direct write", {
5291
+ debug("[compaction-todo-preserver] Restored todos via direct write", {
4419
5292
  sessionID,
4420
5293
  count: snapshot.length
4421
5294
  });
4422
5295
  } else {
4423
- log("[compaction-todo-preserver] Direct write unavailable — todos cannot be restored", {
5296
+ warn("[compaction-todo-preserver] Direct write unavailable — todos cannot be restored", {
4424
5297
  sessionID,
4425
5298
  count: snapshot.length
4426
5299
  });
4427
5300
  }
4428
5301
  } catch (err) {
4429
- log("[compaction-todo-preserver] Failed to restore todos (non-fatal)", {
5302
+ warn("[compaction-todo-preserver] Failed to restore todos (non-fatal)", {
4430
5303
  sessionID,
4431
5304
  error: String(err)
4432
5305
  });
@@ -4447,7 +5320,7 @@ function createCompactionTodoPreserver(client) {
4447
5320
  const sessionID = props?.sessionID ?? props?.info?.id ?? "";
4448
5321
  if (sessionID) {
4449
5322
  snapshots.delete(sessionID);
4450
- log("[compaction-todo-preserver] Cleaned up snapshot on session delete", { sessionID });
5323
+ debug("[compaction-todo-preserver] Cleaned up snapshot on session delete", { sessionID });
4451
5324
  }
4452
5325
  return;
4453
5326
  }
@@ -4459,7 +5332,7 @@ function createCompactionTodoPreserver(client) {
4459
5332
  }
4460
5333
  // src/hooks/todo-continuation-enforcer.ts
4461
5334
  var FINALIZE_TODOS_MARKER = "<!-- weave:finalize-todos -->";
4462
- function createTodoContinuationEnforcer(client, options) {
5335
+ function createTodoContinuationEnforcer(client2, options) {
4463
5336
  const todoFinalizedSessions = new Set;
4464
5337
  let todoWriterPromise;
4465
5338
  if (options !== undefined && "todoWriterOverride" in options) {
@@ -4469,9 +5342,9 @@ function createTodoContinuationEnforcer(client, options) {
4469
5342
  }
4470
5343
  todoWriterPromise.then((writer) => {
4471
5344
  if (writer) {
4472
- log("[todo-continuation-enforcer] Direct write: available");
5345
+ debug("[todo-continuation-enforcer] Direct write: available");
4473
5346
  } else {
4474
- log("[todo-continuation-enforcer] Direct write: unavailable, will fall back to LLM prompt");
5347
+ debug("[todo-continuation-enforcer] Direct write: unavailable, will fall back to LLM prompt");
4475
5348
  }
4476
5349
  }).catch(() => {});
4477
5350
  async function checkAndFinalize(sessionID) {
@@ -4479,7 +5352,7 @@ function createTodoContinuationEnforcer(client, options) {
4479
5352
  return;
4480
5353
  }
4481
5354
  try {
4482
- const todosResponse = await client.session.todo({ path: { id: sessionID } });
5355
+ const todosResponse = await client2.session.todo({ path: { id: sessionID } });
4483
5356
  const todos = todosResponse.data ?? [];
4484
5357
  const inProgressTodos = todos.filter((t) => t.status === "in_progress");
4485
5358
  if (inProgressTodos.length === 0) {
@@ -4490,14 +5363,14 @@ function createTodoContinuationEnforcer(client, options) {
4490
5363
  if (todoWriter) {
4491
5364
  const updatedTodos = todos.map((t) => t.status === "in_progress" ? { ...t, status: "completed" } : t);
4492
5365
  todoWriter({ sessionID, todos: updatedTodos });
4493
- log("[todo-continuation-enforcer] Finalized via direct write (0 tokens)", {
5366
+ debug("[todo-continuation-enforcer] Finalized via direct write (0 tokens)", {
4494
5367
  sessionID,
4495
5368
  count: inProgressTodos.length
4496
5369
  });
4497
5370
  } else {
4498
5371
  const inProgressItems = inProgressTodos.map((t) => ` - "${t.content}"`).join(`
4499
5372
  `);
4500
- await client.session.promptAsync({
5373
+ await client2.session.promptAsync({
4501
5374
  path: { id: sessionID },
4502
5375
  body: {
4503
5376
  parts: [
@@ -4512,14 +5385,14 @@ Use todowrite NOW to mark all of them as "completed" (or "cancelled" if abandone
4512
5385
  ]
4513
5386
  }
4514
5387
  });
4515
- log("[todo-continuation-enforcer] Finalized via LLM prompt (fallback)", {
5388
+ debug("[todo-continuation-enforcer] Finalized via LLM prompt (fallback)", {
4516
5389
  sessionID,
4517
5390
  count: inProgressTodos.length
4518
5391
  });
4519
5392
  }
4520
5393
  } catch (err) {
4521
5394
  todoFinalizedSessions.delete(sessionID);
4522
- log("[todo-continuation-enforcer] Failed to check/finalize todos (non-fatal, will retry)", {
5395
+ warn("[todo-continuation-enforcer] Failed to check/finalize todos (non-fatal, will retry)", {
4523
5396
  sessionID,
4524
5397
  error: String(err)
4525
5398
  });
@@ -4546,8 +5419,8 @@ Use todowrite NOW to mark all of them as "completed" (or "cancelled" if abandone
4546
5419
  };
4547
5420
  }
4548
5421
  // src/features/analytics/storage.ts
4549
- import { existsSync as existsSync12, mkdirSync as mkdirSync4, appendFileSync as appendFileSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3, statSync as statSync2 } from "fs";
4550
- import { join as join10 } from "path";
5422
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, appendFileSync, readFileSync as readFileSync9, writeFileSync as writeFileSync3, statSync as statSync2 } from "fs";
5423
+ import { join as join9 } from "path";
4551
5424
 
4552
5425
  // src/features/analytics/types.ts
4553
5426
  var ANALYTICS_DIR = ".weave/analytics";
@@ -4562,17 +5435,17 @@ function zeroTokenUsage() {
4562
5435
  // src/features/analytics/storage.ts
4563
5436
  var MAX_SESSION_ENTRIES = 1000;
4564
5437
  function ensureAnalyticsDir(directory) {
4565
- const dir = join10(directory, ANALYTICS_DIR);
4566
- mkdirSync4(dir, { recursive: true, mode: 448 });
5438
+ const dir = join9(directory, ANALYTICS_DIR);
5439
+ mkdirSync3(dir, { recursive: true, mode: 448 });
4567
5440
  return dir;
4568
5441
  }
4569
5442
  function appendSessionSummary(directory, summary) {
4570
5443
  try {
4571
5444
  const dir = ensureAnalyticsDir(directory);
4572
- const filePath = join10(dir, SESSION_SUMMARIES_FILE);
5445
+ const filePath = join9(dir, SESSION_SUMMARIES_FILE);
4573
5446
  const line = JSON.stringify(summary) + `
4574
5447
  `;
4575
- appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
5448
+ appendFileSync(filePath, line, { encoding: "utf-8", mode: 384 });
4576
5449
  try {
4577
5450
  const TYPICAL_ENTRY_BYTES = 200;
4578
5451
  const rotationSizeThreshold = MAX_SESSION_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
@@ -4595,9 +5468,9 @@ function appendSessionSummary(directory, summary) {
4595
5468
  }
4596
5469
  }
4597
5470
  function readSessionSummaries(directory) {
4598
- const filePath = join10(directory, ANALYTICS_DIR, SESSION_SUMMARIES_FILE);
5471
+ const filePath = join9(directory, ANALYTICS_DIR, SESSION_SUMMARIES_FILE);
4599
5472
  try {
4600
- if (!existsSync12(filePath))
5473
+ if (!existsSync11(filePath))
4601
5474
  return [];
4602
5475
  const content = readFileSync9(filePath, "utf-8");
4603
5476
  const lines = content.split(`
@@ -4616,7 +5489,7 @@ function readSessionSummaries(directory) {
4616
5489
  function writeFingerprint(directory, fingerprint) {
4617
5490
  try {
4618
5491
  const dir = ensureAnalyticsDir(directory);
4619
- const filePath = join10(dir, FINGERPRINT_FILE);
5492
+ const filePath = join9(dir, FINGERPRINT_FILE);
4620
5493
  writeFileSync3(filePath, JSON.stringify(fingerprint, null, 2), { encoding: "utf-8", mode: 384 });
4621
5494
  return true;
4622
5495
  } catch {
@@ -4624,9 +5497,9 @@ function writeFingerprint(directory, fingerprint) {
4624
5497
  }
4625
5498
  }
4626
5499
  function readFingerprint(directory) {
4627
- const filePath = join10(directory, ANALYTICS_DIR, FINGERPRINT_FILE);
5500
+ const filePath = join9(directory, ANALYTICS_DIR, FINGERPRINT_FILE);
4628
5501
  try {
4629
- if (!existsSync12(filePath))
5502
+ if (!existsSync11(filePath))
4630
5503
  return null;
4631
5504
  const content = readFileSync9(filePath, "utf-8");
4632
5505
  const parsed = JSON.parse(content);
@@ -4640,10 +5513,10 @@ function readFingerprint(directory) {
4640
5513
  function writeMetricsReport(directory, report) {
4641
5514
  try {
4642
5515
  const dir = ensureAnalyticsDir(directory);
4643
- const filePath = join10(dir, METRICS_REPORTS_FILE);
5516
+ const filePath = join9(dir, METRICS_REPORTS_FILE);
4644
5517
  const line = JSON.stringify(report) + `
4645
5518
  `;
4646
- appendFileSync2(filePath, line, { encoding: "utf-8", mode: 384 });
5519
+ appendFileSync(filePath, line, { encoding: "utf-8", mode: 384 });
4647
5520
  try {
4648
5521
  const TYPICAL_ENTRY_BYTES = 200;
4649
5522
  const rotationSizeThreshold = MAX_METRICS_ENTRIES * TYPICAL_ENTRY_BYTES * 0.9;
@@ -4666,9 +5539,9 @@ function writeMetricsReport(directory, report) {
4666
5539
  }
4667
5540
  }
4668
5541
  function readMetricsReports(directory) {
4669
- const filePath = join10(directory, ANALYTICS_DIR, METRICS_REPORTS_FILE);
5542
+ const filePath = join9(directory, ANALYTICS_DIR, METRICS_REPORTS_FILE);
4670
5543
  try {
4671
- if (!existsSync12(filePath))
5544
+ if (!existsSync11(filePath))
4672
5545
  return [];
4673
5546
  const content = readFileSync9(filePath, "utf-8");
4674
5547
  const lines = content.split(`
@@ -5198,7 +6071,7 @@ function generateMetricsReport(directory, state) {
5198
6071
  totalTokens
5199
6072
  });
5200
6073
  } catch (qualityErr) {
5201
- log("[analytics] Failed to calculate quality score (non-fatal)", {
6074
+ warn("[analytics] Failed to calculate quality score (non-fatal)", {
5202
6075
  error: String(qualityErr)
5203
6076
  });
5204
6077
  }
@@ -5219,10 +6092,10 @@ function generateMetricsReport(directory, state) {
5219
6092
  };
5220
6093
  const written = writeMetricsReport(directory, report);
5221
6094
  if (!written) {
5222
- log("[analytics] Failed to write metrics report (non-fatal)");
6095
+ warn("[analytics] Failed to write metrics report (non-fatal)");
5223
6096
  return null;
5224
6097
  }
5225
- log("[analytics] Metrics report generated", {
6098
+ debug("[analytics] Metrics report generated", {
5226
6099
  plan: report.planName,
5227
6100
  coverage: adherence.coverage,
5228
6101
  precision: adherence.precision,
@@ -5230,20 +6103,90 @@ function generateMetricsReport(directory, state) {
5230
6103
  });
5231
6104
  return report;
5232
6105
  } catch (err) {
5233
- log("[analytics] Failed to generate metrics report (non-fatal)", {
6106
+ warn("[analytics] Failed to generate metrics report (non-fatal)", {
5234
6107
  error: String(err)
5235
6108
  });
5236
6109
  return null;
5237
6110
  }
5238
6111
  }
5239
6112
 
6113
+ // src/features/health-report.ts
6114
+ function generateHealthReport(loadResult, agents) {
6115
+ if (!loadResult) {
6116
+ return "⚠ No config load result available — Weave may not have initialized properly.";
6117
+ }
6118
+ const lines = [];
6119
+ const { config, loadedFiles, diagnostics } = loadResult;
6120
+ const hasIssues = diagnostics.length > 0;
6121
+ lines.push(hasIssues ? "## ⚠ Weave Config Health: Issues Found" : "## ✅ Weave Config Health: OK");
6122
+ lines.push("");
6123
+ lines.push("### Config Files");
6124
+ if (loadedFiles.length === 0) {
6125
+ lines.push("No config files found (using defaults)");
6126
+ } else {
6127
+ for (const f of loadedFiles) {
6128
+ lines.push(`- \`${f}\``);
6129
+ }
6130
+ }
6131
+ lines.push("");
6132
+ if (diagnostics.length > 0) {
6133
+ lines.push("### Validation Issues");
6134
+ lines.push("");
6135
+ for (const d of diagnostics) {
6136
+ const icon = d.level === "error" ? "\uD83D\uDD34" : "\uD83D\uDFE1";
6137
+ lines.push(`${icon} **${d.section}**: ${d.message}`);
6138
+ if (d.fields?.length) {
6139
+ for (const f of d.fields) {
6140
+ const fieldLabel = f.path || "(root)";
6141
+ lines.push(` - \`${fieldLabel}\`: ${f.message}`);
6142
+ }
6143
+ }
6144
+ lines.push("");
6145
+ }
6146
+ lines.push("Fix the issues above in your config file and restart opencode.");
6147
+ lines.push("");
6148
+ }
6149
+ lines.push("### Loaded Agents");
6150
+ const builtinKeys = ["loom", "tapestry", "shuttle", "pattern", "thread", "spindle", "warp", "weft"];
6151
+ const builtinDisplayNames = new Set(builtinKeys.map((k) => getAgentDisplayName(k)));
6152
+ const agentNames = Object.keys(agents);
6153
+ const builtinAgents = agentNames.filter((n) => builtinDisplayNames.has(n));
6154
+ const customAgents = agentNames.filter((n) => !builtinDisplayNames.has(n));
6155
+ lines.push(`- Builtin: ${builtinAgents.length}/8 (${builtinAgents.join(", ")})`);
6156
+ if (customAgents.length > 0) {
6157
+ lines.push(`- Custom: ${customAgents.length} (${customAgents.join(", ")})`);
6158
+ } else {
6159
+ lines.push("- Custom: 0");
6160
+ }
6161
+ lines.push("");
6162
+ if (config.custom_agents && Object.keys(config.custom_agents).length > 0) {
6163
+ lines.push("### Custom Agent Config");
6164
+ for (const [name, agentConfig] of Object.entries(config.custom_agents)) {
6165
+ const mode = agentConfig.mode ?? "subagent";
6166
+ const model = agentConfig.model ?? "(default)";
6167
+ lines.push(`- **${agentConfig.display_name ?? name}** — mode: ${mode}, model: ${model}`);
6168
+ }
6169
+ lines.push("");
6170
+ }
6171
+ const disabled = config.disabled_agents ?? [];
6172
+ if (disabled.length > 0) {
6173
+ lines.push(`### Disabled Agents: ${disabled.join(", ")}`);
6174
+ lines.push("");
6175
+ }
6176
+ lines.push("### Logs");
6177
+ lines.push("Detailed logs: `~/.local/share/opencode/log/` (grep for `service=weave`)");
6178
+ lines.push("Real-time: `opencode --print-logs --log-level WARN`");
6179
+ return lines.join(`
6180
+ `);
6181
+ }
6182
+
5240
6183
  // src/plugin/plugin-interface.ts
5241
6184
  function createPluginInterface(args) {
5242
- const { pluginConfig, hooks, tools, configHandler, agents, client, directory = "", tracker } = args;
6185
+ const { pluginConfig, hooks, tools, configHandler, agents, client: client2, directory = "", tracker } = args;
5243
6186
  const lastAssistantMessageText = new Map;
5244
6187
  const lastUserMessageText = new Map;
5245
- const compactionPreserver = hooks.compactionTodoPreserverEnabled && client ? createCompactionTodoPreserver(client) : null;
5246
- const todoContinuationEnforcer = hooks.todoContinuationEnforcerEnabled && client ? createTodoContinuationEnforcer(client) : null;
6188
+ const compactionPreserver = hooks.compactionTodoPreserverEnabled && client2 ? createCompactionTodoPreserver(client2) : null;
6189
+ const todoContinuationEnforcer = hooks.todoContinuationEnforcerEnabled && client2 ? createTodoContinuationEnforcer(client2) : null;
5247
6190
  return {
5248
6191
  tool: tools,
5249
6192
  config: async (config) => {
@@ -5254,14 +6197,14 @@ function createPluginInterface(args) {
5254
6197
  });
5255
6198
  const existingAgents = config.agent ?? {};
5256
6199
  if (Object.keys(existingAgents).length > 0) {
5257
- log("[config] Merging Weave agents over existing agents", {
6200
+ debug("[config] Merging Weave agents over existing agents", {
5258
6201
  existingCount: Object.keys(existingAgents).length,
5259
6202
  weaveCount: Object.keys(result.agents).length,
5260
6203
  existingKeys: Object.keys(existingAgents)
5261
6204
  });
5262
6205
  const collisions = Object.keys(result.agents).filter((key) => (key in existingAgents));
5263
6206
  if (collisions.length > 0) {
5264
- log("[config] Weave agents overriding user-defined agents with same name", {
6207
+ info("[config] Weave agents overriding user-defined agents with same name", {
5265
6208
  overriddenKeys: collisions
5266
6209
  });
5267
6210
  }
@@ -5300,6 +6243,12 @@ function createPluginInterface(args) {
5300
6243
  const result = isWorkflowCommand ? { contextInjection: null, switchAgent: null } : hooks.startWork(promptText, sessionID);
5301
6244
  if (result.switchAgent && message) {
5302
6245
  message.agent = getAgentDisplayName(result.switchAgent);
6246
+ debug("[start-work] Switching agent for plan execution", {
6247
+ sessionId: sessionID,
6248
+ agent: result.switchAgent,
6249
+ displayName: message.agent,
6250
+ hasContextInjection: !!result.contextInjection
6251
+ });
5303
6252
  }
5304
6253
  if (result.contextInjection && parts) {
5305
6254
  const idx = parts.findIndex((p) => p.type === "text" && p.text);
@@ -5322,6 +6271,11 @@ ${result.contextInjection}`;
5322
6271
  const result = hooks.workflowStart(promptText, sessionID);
5323
6272
  if (result.switchAgent && message) {
5324
6273
  message.agent = getAgentDisplayName(result.switchAgent);
6274
+ debug("[workflow] Switching agent for workflow execution", {
6275
+ sessionId: sessionID,
6276
+ agent: result.switchAgent,
6277
+ displayName: message.agent
6278
+ });
5325
6279
  }
5326
6280
  if (result.contextInjection && parts) {
5327
6281
  const idx = parts.findIndex((p) => p.type === "text" && p.text);
@@ -5371,6 +6325,10 @@ ${cmdResult.contextInjection}`;
5371
6325
  }
5372
6326
  if (cmdResult.switchAgent && message) {
5373
6327
  message.agent = getAgentDisplayName(cmdResult.switchAgent);
6328
+ debug("[workflow] Switching agent via workflow command", {
6329
+ agent: cmdResult.switchAgent,
6330
+ displayName: message.agent
6331
+ });
5374
6332
  }
5375
6333
  }
5376
6334
  }
@@ -5391,7 +6349,7 @@ ${cmdResult.contextInjection}`;
5391
6349
  const state = readWorkState(directory);
5392
6350
  if (state && !state.paused) {
5393
6351
  pauseWork(directory);
5394
- log("[work-continuation] Auto-paused: user message received during active plan", { sessionId: sessionID });
6352
+ info("[work-continuation] Auto-paused: user message received during active plan", { sessionId: sessionID });
5395
6353
  }
5396
6354
  }
5397
6355
  }
@@ -5402,7 +6360,7 @@ ${cmdResult.contextInjection}`;
5402
6360
  const maxTokens = input.model?.limit?.context ?? 0;
5403
6361
  if (sessionId && maxTokens > 0) {
5404
6362
  setContextLimit(sessionId, maxTokens);
5405
- log("[context-window] Captured context limit", { sessionId, maxTokens });
6363
+ debug("[context-window] Captured context limit", { sessionId, maxTokens });
5406
6364
  }
5407
6365
  if (tracker && hooks.analyticsEnabled && sessionId && input.agent) {
5408
6366
  tracker.setAgentName(sessionId, input.agent);
@@ -5437,7 +6395,7 @@ ${cmdResult.contextInjection}`;
5437
6395
  try {
5438
6396
  tracker.endSession(evt.properties.info.id);
5439
6397
  } catch (err) {
5440
- log("[analytics] Failed to end session (non-fatal)", { error: String(err) });
6398
+ warn("[analytics] Failed to end session (non-fatal)", { error: String(err) });
5441
6399
  }
5442
6400
  if (directory) {
5443
6401
  try {
@@ -5449,29 +6407,29 @@ ${cmdResult.contextInjection}`;
5449
6407
  }
5450
6408
  }
5451
6409
  } catch (err) {
5452
- log("[analytics] Failed to generate metrics report on session end (non-fatal)", { error: String(err) });
6410
+ warn("[analytics] Failed to generate metrics report on session end (non-fatal)", { error: String(err) });
5453
6411
  }
5454
6412
  }
5455
6413
  }
5456
6414
  }
5457
6415
  if (event.type === "message.updated") {
5458
6416
  const evt = event;
5459
- const info = evt.properties?.info;
5460
- if (info?.role === "assistant" && info.sessionID) {
6417
+ const info2 = evt.properties?.info;
6418
+ if (info2?.role === "assistant" && info2.sessionID) {
5461
6419
  if (hooks.checkContextWindow) {
5462
- const inputTokens = info.tokens?.input ?? 0;
6420
+ const inputTokens = info2.tokens?.input ?? 0;
5463
6421
  if (inputTokens > 0) {
5464
- updateUsage(info.sessionID, inputTokens);
5465
- const tokenState = getState(info.sessionID);
6422
+ updateUsage(info2.sessionID, inputTokens);
6423
+ const tokenState = getState(info2.sessionID);
5466
6424
  if (tokenState && tokenState.maxTokens > 0) {
5467
6425
  const result = hooks.checkContextWindow({
5468
6426
  usedTokens: tokenState.usedTokens,
5469
6427
  maxTokens: tokenState.maxTokens,
5470
- sessionId: info.sessionID
6428
+ sessionId: info2.sessionID
5471
6429
  });
5472
6430
  if (result.action !== "none") {
5473
- log("[context-window] Threshold crossed", {
5474
- sessionId: info.sessionID,
6431
+ warn("[context-window] Threshold crossed", {
6432
+ sessionId: info2.sessionID,
5475
6433
  action: result.action,
5476
6434
  usagePct: result.usagePct
5477
6435
  });
@@ -5483,18 +6441,18 @@ ${cmdResult.contextInjection}`;
5483
6441
  }
5484
6442
  if (event.type === "message.updated" && tracker && hooks.analyticsEnabled) {
5485
6443
  const evt = event;
5486
- const info = evt.properties?.info;
5487
- if (info?.role === "assistant" && info.sessionID) {
5488
- if (typeof info.cost === "number" && info.cost > 0) {
5489
- tracker.trackCost(info.sessionID, info.cost);
6444
+ const info2 = evt.properties?.info;
6445
+ if (info2?.role === "assistant" && info2.sessionID) {
6446
+ if (typeof info2.cost === "number" && info2.cost > 0) {
6447
+ tracker.trackCost(info2.sessionID, info2.cost);
5490
6448
  }
5491
- if (info.tokens) {
5492
- tracker.trackTokenUsage(info.sessionID, {
5493
- input: info.tokens.input ?? 0,
5494
- output: info.tokens.output ?? 0,
5495
- reasoning: info.tokens.reasoning ?? 0,
5496
- cacheRead: info.tokens.cache?.read ?? 0,
5497
- cacheWrite: info.tokens.cache?.write ?? 0
6449
+ if (info2.tokens) {
6450
+ tracker.trackTokenUsage(info2.sessionID, {
6451
+ input: info2.tokens.input ?? 0,
6452
+ output: info2.tokens.output ?? 0,
6453
+ reasoning: info2.tokens.reasoning ?? 0,
6454
+ cacheRead: info2.tokens.cache?.read ?? 0,
6455
+ cacheWrite: info2.tokens.cache?.write ?? 0
5498
6456
  });
5499
6457
  }
5500
6458
  }
@@ -5503,12 +6461,12 @@ ${cmdResult.contextInjection}`;
5503
6461
  const evt = event;
5504
6462
  if (evt.properties?.command === "session.interrupt") {
5505
6463
  pauseWork(directory);
5506
- log("[work-continuation] User interrupt detected — work paused");
6464
+ info("[work-continuation] User interrupt detected — work paused");
5507
6465
  if (directory) {
5508
6466
  const activeWorkflow = getActiveWorkflowInstance(directory);
5509
6467
  if (activeWorkflow && activeWorkflow.status === "running") {
5510
6468
  pauseWorkflow(directory, "User interrupt");
5511
- log("[workflow] User interrupt detected — workflow paused");
6469
+ info("[workflow] User interrupt detected — workflow paused");
5512
6470
  }
5513
6471
  }
5514
6472
  }
@@ -5530,21 +6488,21 @@ ${cmdResult.contextInjection}`;
5530
6488
  const lastMsg = lastAssistantMessageText.get(sessionId) ?? undefined;
5531
6489
  const lastUserMsg = lastUserMessageText.get(sessionId) ?? undefined;
5532
6490
  const result = hooks.workflowContinuation(sessionId, lastMsg, lastUserMsg);
5533
- if (result.continuationPrompt && client) {
6491
+ if (result.continuationPrompt && client2) {
5534
6492
  try {
5535
- await client.session.promptAsync({
6493
+ await client2.session.promptAsync({
5536
6494
  path: { id: sessionId },
5537
6495
  body: {
5538
6496
  parts: [{ type: "text", text: result.continuationPrompt }],
5539
6497
  ...result.switchAgent ? { agent: getAgentDisplayName(result.switchAgent) } : {}
5540
6498
  }
5541
6499
  });
5542
- log("[workflow] Injected workflow continuation prompt", {
6500
+ debug("[workflow] Injected workflow continuation prompt", {
5543
6501
  sessionId,
5544
6502
  agent: result.switchAgent
5545
6503
  });
5546
6504
  } catch (err) {
5547
- log("[workflow] Failed to inject workflow continuation", { sessionId, error: String(err) });
6505
+ error("[workflow] Failed to inject workflow continuation", { sessionId, error: String(err) });
5548
6506
  }
5549
6507
  return;
5550
6508
  }
@@ -5556,21 +6514,21 @@ ${cmdResult.contextInjection}`;
5556
6514
  const sessionId = evt.properties?.sessionID ?? "";
5557
6515
  if (sessionId) {
5558
6516
  const result = hooks.workContinuation(sessionId);
5559
- if (result.continuationPrompt && client) {
6517
+ if (result.continuationPrompt && client2) {
5560
6518
  try {
5561
- await client.session.promptAsync({
6519
+ await client2.session.promptAsync({
5562
6520
  path: { id: sessionId },
5563
6521
  body: {
5564
6522
  parts: [{ type: "text", text: result.continuationPrompt }]
5565
6523
  }
5566
6524
  });
5567
- log("[work-continuation] Injected continuation prompt", { sessionId });
6525
+ debug("[work-continuation] Injected continuation prompt", { sessionId });
5568
6526
  continuationFired = true;
5569
6527
  } catch (err) {
5570
- log("[work-continuation] Failed to inject continuation", { sessionId, error: String(err) });
6528
+ error("[work-continuation] Failed to inject continuation", { sessionId, error: String(err) });
5571
6529
  }
5572
6530
  } else if (result.continuationPrompt) {
5573
- log("[work-continuation] continuationPrompt available but no client", { sessionId });
6531
+ debug("[work-continuation] continuationPrompt available but no client", { sessionId });
5574
6532
  }
5575
6533
  }
5576
6534
  }
@@ -5656,6 +6614,11 @@ ${cmdResult.contextInjection}`;
5656
6614
  const metricsMarkdown = formatMetricsMarkdown(reports, summaries, args2);
5657
6615
  parts.push({ type: "text", text: metricsMarkdown });
5658
6616
  }
6617
+ if (command === "weave-health") {
6618
+ const loadResult = getLastConfigLoadResult();
6619
+ const reportText = generateHealthReport(loadResult, agents);
6620
+ parts.push({ type: "text", text: reportText });
6621
+ }
5659
6622
  },
5660
6623
  "tool.definition": async (input, output) => {
5661
6624
  if (hooks.todoDescriptionOverride) {
@@ -5674,14 +6637,14 @@ ${cmdResult.contextInjection}`;
5674
6637
  };
5675
6638
  }
5676
6639
  // src/features/analytics/fingerprint.ts
5677
- import { existsSync as existsSync13, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
5678
- import { join as join12 } from "path";
6640
+ import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
6641
+ import { join as join11 } from "path";
5679
6642
  import { arch } from "os";
5680
6643
 
5681
6644
  // src/shared/version.ts
5682
6645
  import { readFileSync as readFileSync11 } from "fs";
5683
6646
  import { fileURLToPath } from "url";
5684
- import { dirname as dirname2, join as join11 } from "path";
6647
+ import { dirname as dirname2, join as join10 } from "path";
5685
6648
  var cachedVersion;
5686
6649
  function getWeaveVersion() {
5687
6650
  if (cachedVersion !== undefined)
@@ -5690,7 +6653,7 @@ function getWeaveVersion() {
5690
6653
  const thisDir = dirname2(fileURLToPath(import.meta.url));
5691
6654
  for (const rel of ["../../package.json", "../package.json"]) {
5692
6655
  try {
5693
- const pkg = JSON.parse(readFileSync11(join11(thisDir, rel), "utf-8"));
6656
+ const pkg = JSON.parse(readFileSync11(join10(thisDir, rel), "utf-8"));
5694
6657
  if (pkg.name === "@opencode_weave/weave" && typeof pkg.version === "string") {
5695
6658
  const version = pkg.version;
5696
6659
  cachedVersion = version;
@@ -5795,7 +6758,7 @@ function detectStack(directory) {
5795
6758
  const detected = [];
5796
6759
  for (const marker of STACK_MARKERS) {
5797
6760
  for (const file of marker.files) {
5798
- if (existsSync13(join12(directory, file))) {
6761
+ if (existsSync12(join11(directory, file))) {
5799
6762
  detected.push({
5800
6763
  name: marker.name,
5801
6764
  confidence: marker.confidence,
@@ -5806,8 +6769,8 @@ function detectStack(directory) {
5806
6769
  }
5807
6770
  }
5808
6771
  try {
5809
- const pkgPath = join12(directory, "package.json");
5810
- if (existsSync13(pkgPath)) {
6772
+ const pkgPath = join11(directory, "package.json");
6773
+ if (existsSync12(pkgPath)) {
5811
6774
  const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
5812
6775
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
5813
6776
  if (deps.react) {
@@ -5841,26 +6804,26 @@ function detectStack(directory) {
5841
6804
  });
5842
6805
  }
5843
6806
  function detectPackageManager(directory) {
5844
- if (existsSync13(join12(directory, "bun.lockb")))
6807
+ if (existsSync12(join11(directory, "bun.lockb")))
5845
6808
  return "bun";
5846
- if (existsSync13(join12(directory, "pnpm-lock.yaml")))
6809
+ if (existsSync12(join11(directory, "pnpm-lock.yaml")))
5847
6810
  return "pnpm";
5848
- if (existsSync13(join12(directory, "yarn.lock")))
6811
+ if (existsSync12(join11(directory, "yarn.lock")))
5849
6812
  return "yarn";
5850
- if (existsSync13(join12(directory, "package-lock.json")))
6813
+ if (existsSync12(join11(directory, "package-lock.json")))
5851
6814
  return "npm";
5852
- if (existsSync13(join12(directory, "package.json")))
6815
+ if (existsSync12(join11(directory, "package.json")))
5853
6816
  return "npm";
5854
6817
  return;
5855
6818
  }
5856
6819
  function detectMonorepo(directory) {
5857
6820
  for (const marker of MONOREPO_MARKERS) {
5858
- if (existsSync13(join12(directory, marker)))
6821
+ if (existsSync12(join11(directory, marker)))
5859
6822
  return true;
5860
6823
  }
5861
6824
  try {
5862
- const pkgPath = join12(directory, "package.json");
5863
- if (existsSync13(pkgPath)) {
6825
+ const pkgPath = join11(directory, "package.json");
6826
+ if (existsSync12(pkgPath)) {
5864
6827
  const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
5865
6828
  if (pkg.workspaces)
5866
6829
  return true;
@@ -5895,14 +6858,14 @@ function fingerprintProject(directory) {
5895
6858
  try {
5896
6859
  const fingerprint = generateFingerprint(directory);
5897
6860
  writeFingerprint(directory, fingerprint);
5898
- log("[analytics] Project fingerprinted", {
6861
+ debug("[analytics] Project fingerprinted", {
5899
6862
  stack: fingerprint.stack.map((s) => s.name),
5900
6863
  primaryLanguage: fingerprint.primaryLanguage,
5901
6864
  packageManager: fingerprint.packageManager
5902
6865
  });
5903
6866
  return fingerprint;
5904
6867
  } catch (err) {
5905
- log("[analytics] Fingerprinting failed (non-fatal)", { error: String(err) });
6868
+ warn("[analytics] Fingerprinting failed (non-fatal)", { error: String(err) });
5906
6869
  return null;
5907
6870
  }
5908
6871
  }
@@ -5914,14 +6877,14 @@ function getOrCreateFingerprint(directory) {
5914
6877
  if (existing.weaveVersion === currentVersion) {
5915
6878
  return existing;
5916
6879
  }
5917
- log("[analytics] Fingerprint version mismatch — regenerating", {
6880
+ debug("[analytics] Fingerprint version mismatch — regenerating", {
5918
6881
  cached: existing.weaveVersion ?? "none",
5919
6882
  current: currentVersion
5920
6883
  });
5921
6884
  }
5922
6885
  return fingerprintProject(directory);
5923
6886
  } catch (err) {
5924
- log("[analytics] getOrCreateFingerprint failed (non-fatal)", { error: String(err) });
6887
+ warn("[analytics] getOrCreateFingerprint failed (non-fatal)", { error: String(err) });
5925
6888
  return null;
5926
6889
  }
5927
6890
  }
@@ -6045,7 +7008,7 @@ class SessionTracker {
6045
7008
  };
6046
7009
  try {
6047
7010
  appendSessionSummary(this.directory, summary);
6048
- log("[analytics] Session summary persisted", {
7011
+ debug("[analytics] Session summary persisted", {
6049
7012
  sessionId,
6050
7013
  totalToolCalls,
6051
7014
  totalDelegations: session.delegations.length,
@@ -6055,7 +7018,7 @@ class SessionTracker {
6055
7018
  } : {}
6056
7019
  });
6057
7020
  } catch (err) {
6058
- log("[analytics] Failed to persist session summary (non-fatal)", {
7021
+ warn("[analytics] Failed to persist session summary (non-fatal)", {
6059
7022
  sessionId,
6060
7023
  error: String(err)
6061
7024
  });
@@ -6084,13 +7047,17 @@ function createAnalytics(directory, fingerprint) {
6084
7047
 
6085
7048
  // src/index.ts
6086
7049
  var WeavePlugin = async (ctx) => {
7050
+ setClient(ctx.client);
6087
7051
  const pluginConfig = loadWeaveConfig(ctx.directory, ctx);
7052
+ if (pluginConfig.log_level) {
7053
+ setLogLevel(pluginConfig.log_level);
7054
+ }
6088
7055
  const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
6089
7056
  const isHookEnabled = (name) => !disabledHooks.has(name);
6090
7057
  const analyticsEnabled = pluginConfig.analytics?.enabled === true;
6091
7058
  const fingerprintEnabled = analyticsEnabled && pluginConfig.analytics?.use_fingerprint === true;
6092
7059
  const fingerprint = fingerprintEnabled ? getOrCreateFingerprint(ctx.directory) : null;
6093
- const configDir = join13(ctx.directory, ".opencode");
7060
+ const configDir = join12(ctx.directory, ".opencode");
6094
7061
  const toolsResult = await createTools({ ctx, pluginConfig });
6095
7062
  const managers = createManagers({ ctx, pluginConfig, resolveSkills: toolsResult.resolveSkillsFn, fingerprint, configDir });
6096
7063
  const hooks = createHooks({ pluginConfig, isHookEnabled, directory: ctx.directory, analyticsEnabled });