@ifrankwang/openspec-agents 0.128.0 → 0.131.0

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.
@@ -3053,7 +3053,25 @@ var require_utils = __commonJS(function(exports, module) {
3053
3053
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3054
3054
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3055
3055
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3056
- var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3056
+ var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
3057
+ var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
3058
+ var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
3059
+ var BYTE_HEX = new Array(256);
3060
+ {
3061
+ const HEX_DIGITS = "0123456789ABCDEF";
3062
+ for (let i = 0;i < 256; i++) {
3063
+ BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
3064
+ }
3065
+ }
3066
+ function percentEncodeNonAscii(cp) {
3067
+ if (cp < 2048) {
3068
+ return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
3069
+ }
3070
+ if (cp < 65536) {
3071
+ return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3072
+ }
3073
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3074
+ }
3057
3075
  function stringArrayToHexStripped(input) {
3058
3076
  let acc = "";
3059
3077
  let code = 0;
@@ -3078,91 +3096,122 @@ var require_utils = __commonJS(function(exports, module) {
3078
3096
  }
3079
3097
  return acc;
3080
3098
  }
3099
+ var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
3100
+ var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
3101
+ var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
3081
3102
  var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
3082
- function consumeIsZone(buffer) {
3083
- buffer.length = 0;
3084
- return true;
3085
- }
3086
- function consumeHextets(buffer, address, output) {
3087
- if (buffer.length) {
3088
- const hex3 = stringArrayToHexStripped(buffer);
3089
- if (hex3 !== "") {
3090
- address.push(hex3);
3091
- } else {
3092
- output.error = true;
3093
- return false;
3103
+ function isZoneIdentifier(zone) {
3104
+ if (zone.length === 0)
3105
+ return false;
3106
+ for (let i = 0;i < zone.length; i++) {
3107
+ if (isZoneCharacter(zone[i]))
3108
+ continue;
3109
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
3110
+ i += 2;
3111
+ continue;
3094
3112
  }
3095
- buffer.length = 0;
3113
+ return false;
3096
3114
  }
3097
3115
  return true;
3098
3116
  }
3099
- function getIPV6(input) {
3100
- let tokenCount = 0;
3101
- const output = { error: false, address: "", zone: "" };
3102
- const address = [];
3103
- const buffer = [];
3104
- let endipv6Encountered = false;
3105
- let endIpv6 = false;
3106
- let consume = consumeHextets;
3107
- for (let i = 0;i < input.length; i++) {
3108
- const cursor = input[i];
3109
- if (cursor === "[" || cursor === "]") {
3110
- continue;
3111
- }
3112
- if (cursor === ":") {
3113
- if (endipv6Encountered === true) {
3114
- endIpv6 = true;
3115
- }
3116
- if (!consume(buffer, address, output)) {
3117
- break;
3118
- }
3119
- if (++tokenCount > 7) {
3120
- output.error = true;
3121
- break;
3122
- }
3123
- if (i > 0 && input[i - 1] === ":") {
3124
- endipv6Encountered = true;
3117
+ function compressIPv6ZeroRun(hextets) {
3118
+ let bestStart = -1;
3119
+ let bestLength = 0;
3120
+ let runStart = -1;
3121
+ let runLength = 0;
3122
+ for (let i = 0;i < hextets.length; i++) {
3123
+ if (hextets[i] === "0") {
3124
+ if (runStart === -1)
3125
+ runStart = i;
3126
+ runLength++;
3127
+ if (runLength > bestLength) {
3128
+ bestLength = runLength;
3129
+ bestStart = runStart;
3125
3130
  }
3126
- address.push(":");
3127
- continue;
3128
- } else if (cursor === "%") {
3129
- if (!consume(buffer, address, output)) {
3130
- break;
3131
- }
3132
- consume = consumeIsZone;
3133
3131
  } else {
3134
- buffer.push(cursor);
3135
- continue;
3132
+ runStart = -1;
3133
+ runLength = 0;
3136
3134
  }
3137
3135
  }
3138
- if (buffer.length) {
3139
- if (consume === consumeIsZone) {
3140
- output.zone = buffer.join("");
3141
- } else if (endIpv6) {
3142
- address.push(buffer.join(""));
3143
- } else {
3144
- address.push(stringArrayToHexStripped(buffer));
3136
+ if (bestLength < 2)
3137
+ return hextets.join(":");
3138
+ const head = hextets.slice(0, bestStart).join(":");
3139
+ const tail = hextets.slice(bestStart + bestLength).join(":");
3140
+ return head + "::" + tail;
3141
+ }
3142
+ function normalizeIPv6Address(input) {
3143
+ const compression = input.indexOf("::");
3144
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1)
3145
+ return;
3146
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
3147
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
3148
+ if (compression !== -1) {
3149
+ if (left.length === 1 && left[0] === "")
3150
+ left.length = 0;
3151
+ if (right.length === 1 && right[0] === "")
3152
+ right.length = 0;
3153
+ }
3154
+ const parts = left.concat(right);
3155
+ let hextetCount = 0;
3156
+ for (let i = 0;i < parts.length; i++) {
3157
+ const part = parts[i];
3158
+ if (part === "")
3159
+ return;
3160
+ if (part.indexOf(".") !== -1) {
3161
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part))
3162
+ return;
3163
+ hextetCount += 2;
3164
+ continue;
3145
3165
  }
3166
+ if (!isHextet(part))
3167
+ return;
3168
+ parts[i] = parseInt(part, 16).toString(16);
3169
+ hextetCount++;
3146
3170
  }
3147
- output.address = address.join("");
3148
- return output;
3171
+ if (compression === -1) {
3172
+ if (hextetCount !== 8)
3173
+ return;
3174
+ return compressIPv6ZeroRun(parts);
3175
+ }
3176
+ if (hextetCount >= 8)
3177
+ return;
3178
+ const expanded = parts.slice(0, left.length);
3179
+ for (let i = hextetCount;i < 8; i++)
3180
+ expanded.push("0");
3181
+ for (let i = left.length;i < parts.length; i++)
3182
+ expanded.push(parts[i]);
3183
+ return compressIPv6ZeroRun(expanded);
3149
3184
  }
3150
3185
  function normalizeIPv6(host) {
3151
- if (findToken(host, ":") < 2) {
3152
- return { host, isIPV6: false };
3153
- }
3154
- const ipv63 = getIPV6(host);
3155
- if (!ipv63.error) {
3156
- let newHost = ipv63.address;
3157
- let escapedHost = ipv63.address;
3158
- if (ipv63.zone) {
3159
- newHost += "%" + ipv63.zone;
3160
- escapedHost += "%25" + ipv63.zone;
3161
- }
3162
- return { host: newHost, isIPV6: true, escapedHost };
3163
- } else {
3164
- return { host, isIPV6: false };
3165
- }
3186
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
3187
+ const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
3188
+ if (hasBracket && !bracketed)
3189
+ return { host, isIPV6: false, error: true };
3190
+ let input = bracketed ? host.slice(1, -1) : host;
3191
+ if (bracketed && isIPvFuture(input)) {
3192
+ input = input.toLowerCase();
3193
+ return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
3194
+ }
3195
+ if (findToken(input, ":") < 2) {
3196
+ return { host, isIPV6: false, error: bracketed };
3197
+ }
3198
+ let zoneIdentifier = "";
3199
+ const zoneSeparator = input.indexOf("%");
3200
+ if (zoneSeparator !== -1) {
3201
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
3202
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
3203
+ if (!isZoneIdentifier(zoneIdentifier))
3204
+ return { host, isIPV6: false, error: true };
3205
+ input = input.slice(0, zoneSeparator);
3206
+ }
3207
+ const address = normalizeIPv6Address(input);
3208
+ if (address === undefined)
3209
+ return { host, isIPV6: false, error: true };
3210
+ return {
3211
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
3212
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
3213
+ isIPV6: true
3214
+ };
3166
3215
  }
3167
3216
  function findToken(str, token) {
3168
3217
  let ind = 0;
@@ -3282,7 +3331,8 @@ var require_utils = __commonJS(function(exports, module) {
3282
3331
  function normalizePathEncoding(input) {
3283
3332
  let output = "";
3284
3333
  for (let i = 0;i < input.length; i++) {
3285
- if (input[i] === "%" && i + 2 < input.length) {
3334
+ const ch = input[i];
3335
+ if (ch === "%" && i + 2 < input.length) {
3286
3336
  const hex3 = input.slice(i + 1, i + 3);
3287
3337
  if (isHexPair(hex3)) {
3288
3338
  const normalizedHex = hex3.toUpperCase();
@@ -3296,10 +3346,152 @@ var require_utils = __commonJS(function(exports, module) {
3296
3346
  continue;
3297
3347
  }
3298
3348
  }
3299
- if (isPathCharacter(input[i])) {
3300
- output += input[i];
3349
+ if (isPathCharacter(ch)) {
3350
+ output += ch;
3351
+ } else {
3352
+ const code = input.charCodeAt(i);
3353
+ if (code < 128) {
3354
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3355
+ } else if (code < 55296 || code > 57343) {
3356
+ output += percentEncodeNonAscii(code);
3357
+ } else if (code <= 56319 && i + 1 < input.length) {
3358
+ const low = input.charCodeAt(i + 1);
3359
+ if (low >= 56320 && low <= 57343) {
3360
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3361
+ i++;
3362
+ } else {
3363
+ output += percentEncodeNonAscii(65533);
3364
+ }
3365
+ } else {
3366
+ output += percentEncodeNonAscii(65533);
3367
+ }
3368
+ }
3369
+ }
3370
+ return output;
3371
+ }
3372
+ function serializePathEncoding(input, pathNoScheme = false) {
3373
+ let output = "";
3374
+ let firstSegment = pathNoScheme && input[0] !== "/";
3375
+ for (let i = 0;i < input.length; i++) {
3376
+ const ch = input[i];
3377
+ if (ch === "%" && i + 2 < input.length) {
3378
+ const hex3 = input.slice(i + 1, i + 3);
3379
+ if (isHexPair(hex3)) {
3380
+ output += "%" + hex3.toUpperCase();
3381
+ i += 2;
3382
+ continue;
3383
+ }
3384
+ }
3385
+ if (ch === "/") {
3386
+ firstSegment = false;
3387
+ }
3388
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
3389
+ output += ch;
3390
+ } else {
3391
+ const code = input.charCodeAt(i);
3392
+ if (code < 128) {
3393
+ output += BYTE_HEX[code];
3394
+ } else if (code < 55296 || code > 57343) {
3395
+ output += percentEncodeNonAscii(code);
3396
+ } else if (code <= 56319 && i + 1 < input.length) {
3397
+ const low = input.charCodeAt(i + 1);
3398
+ if (low >= 56320 && low <= 57343) {
3399
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3400
+ i++;
3401
+ } else {
3402
+ output += percentEncodeNonAscii(65533);
3403
+ }
3404
+ } else {
3405
+ output += percentEncodeNonAscii(65533);
3406
+ }
3407
+ }
3408
+ }
3409
+ return output;
3410
+ }
3411
+ function encodeComponent(input, isAllowed) {
3412
+ let output = "";
3413
+ for (let i = 0;i < input.length; i++) {
3414
+ const ch = input[i];
3415
+ if (ch === "%" && i + 2 < input.length) {
3416
+ const hex3 = input.slice(i + 1, i + 3);
3417
+ if (isHexPair(hex3)) {
3418
+ output += "%" + hex3.toUpperCase();
3419
+ i += 2;
3420
+ continue;
3421
+ }
3422
+ }
3423
+ if (isAllowed(ch)) {
3424
+ output += ch;
3301
3425
  } else {
3302
- output += escape(input[i]);
3426
+ const code = input.charCodeAt(i);
3427
+ if (code < 128) {
3428
+ output += BYTE_HEX[code];
3429
+ } else if (code < 55296 || code > 57343) {
3430
+ output += percentEncodeNonAscii(code);
3431
+ } else if (code <= 56319 && i + 1 < input.length) {
3432
+ const low = input.charCodeAt(i + 1);
3433
+ if (low >= 56320 && low <= 57343) {
3434
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3435
+ i++;
3436
+ } else {
3437
+ output += percentEncodeNonAscii(65533);
3438
+ }
3439
+ } else {
3440
+ output += percentEncodeNonAscii(65533);
3441
+ }
3442
+ }
3443
+ }
3444
+ return output;
3445
+ }
3446
+ function encodeUserinfo(input) {
3447
+ return encodeComponent(input, isUserinfoCharacter);
3448
+ }
3449
+ function encodeQuery(input) {
3450
+ return encodeComponent(input, isQueryFragmentCharacter);
3451
+ }
3452
+ function encodeFragment(input) {
3453
+ return encodeComponent(input, isQueryFragmentCharacter);
3454
+ }
3455
+ function isEscapeSafe(cp) {
3456
+ return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
3457
+ }
3458
+ function normalizeQueryFragmentEncoding(input) {
3459
+ let output = "";
3460
+ for (let i = 0;i < input.length; i++) {
3461
+ const ch = input[i];
3462
+ if (ch === "%" && i + 2 < input.length) {
3463
+ const hex3 = input.slice(i + 1, i + 3);
3464
+ if (isHexPair(hex3)) {
3465
+ const normalizedHex = hex3.toUpperCase();
3466
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3467
+ if (isUnreserved(decoded)) {
3468
+ output += decoded;
3469
+ } else {
3470
+ output += "%" + normalizedHex;
3471
+ }
3472
+ i += 2;
3473
+ continue;
3474
+ }
3475
+ }
3476
+ if (isQueryFragmentCharacter(ch)) {
3477
+ output += ch;
3478
+ } else {
3479
+ const code = input.charCodeAt(i);
3480
+ if (code < 128) {
3481
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3482
+ } else if (code < 55296 || code > 57343) {
3483
+ output += percentEncodeNonAscii(code);
3484
+ } else if (code <= 56319 && i + 1 < input.length) {
3485
+ const low = input.charCodeAt(i + 1);
3486
+ if (low >= 56320 && low <= 57343) {
3487
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3488
+ i++;
3489
+ } else {
3490
+ output += percentEncodeNonAscii(65533);
3491
+ }
3492
+ } else {
3493
+ output += percentEncodeNonAscii(65533);
3494
+ }
3303
3495
  }
3304
3496
  }
3305
3497
  return output;
@@ -3322,14 +3514,18 @@ var require_utils = __commonJS(function(exports, module) {
3322
3514
  function recomposeAuthority(component) {
3323
3515
  const uriTokens = [];
3324
3516
  if (component.userinfo !== undefined) {
3325
- uriTokens.push(component.userinfo);
3517
+ uriTokens.push(encodeUserinfo(component.userinfo));
3326
3518
  uriTokens.push("@");
3327
3519
  }
3328
3520
  if (component.host !== undefined) {
3329
- let host = unescape(component.host);
3521
+ let host = component.host;
3330
3522
  if (!isIPv4(host)) {
3331
- const ipV6res = normalizeIPv6(host);
3332
- if (ipV6res.isIPV6 === true) {
3523
+ let ipV6res = normalizeIPv6(host);
3524
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
3525
+ host = normalizePercentEncoding(host, true);
3526
+ ipV6res = normalizeIPv6(host);
3527
+ }
3528
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
3333
3529
  host = `[${ipV6res.escapedHost}]`;
3334
3530
  } else {
3335
3531
  host = reescapeHostDelimiters(host, false);
@@ -3349,6 +3545,11 @@ var require_utils = __commonJS(function(exports, module) {
3349
3545
  reescapeHostDelimiters,
3350
3546
  normalizePercentEncoding,
3351
3547
  normalizePathEncoding,
3548
+ serializePathEncoding,
3549
+ normalizeQueryFragmentEncoding,
3550
+ encodeUserinfo,
3551
+ encodeQuery,
3552
+ encodeFragment,
3352
3553
  escapePreservingEscapes,
3353
3554
  removeDotSegments,
3354
3555
  isIPv4,
@@ -3361,7 +3562,7 @@ var require_utils = __commonJS(function(exports, module) {
3361
3562
  // node_modules/fast-uri/lib/schemes.js
3362
3563
  var require_schemes = __commonJS(function(exports, module) {
3363
3564
  var { isUUID } = require_utils();
3364
- var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
3565
+ var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
3365
3566
  var supportedSchemeNames = [
3366
3567
  "http",
3367
3568
  "https",
@@ -3416,9 +3617,10 @@ var require_schemes = __commonJS(function(exports, module) {
3416
3617
  wsComponent.secure = undefined;
3417
3618
  }
3418
3619
  if (wsComponent.resourceName) {
3419
- const [path, query] = wsComponent.resourceName.split("?");
3620
+ const queryIndex = wsComponent.resourceName.indexOf("?");
3621
+ const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3420
3622
  wsComponent.path = path && path !== "/" ? path : undefined;
3421
- wsComponent.query = query;
3623
+ wsComponent.query = queryIndex === -1 ? undefined : wsComponent.resourceName.slice(queryIndex + 1);
3422
3624
  wsComponent.resourceName = undefined;
3423
3625
  }
3424
3626
  wsComponent.fragment = undefined;
@@ -3430,7 +3632,7 @@ var require_schemes = __commonJS(function(exports, module) {
3430
3632
  return urnComponent;
3431
3633
  }
3432
3634
  const matches = urnComponent.path.match(URN_REG);
3433
- if (matches) {
3635
+ if (matches && matches[0] === urnComponent.path) {
3434
3636
  const scheme = options.scheme || urnComponent.scheme || "urn";
3435
3637
  urnComponent.nid = matches[1].toLowerCase();
3436
3638
  urnComponent.nss = matches[2];
@@ -3534,8 +3736,17 @@ var require_schemes = __commonJS(function(exports, module) {
3534
3736
 
3535
3737
  // node_modules/fast-uri/index.js
3536
3738
  var require_fast_uri = __commonJS(function(exports, module) {
3537
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3739
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3538
3740
  var { SCHEMES, getSchemeHandler } = require_schemes();
3741
+ var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
3742
+ var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
3743
+ function decodeValidScheme(scheme) {
3744
+ const decodedScheme = unescape(String(scheme));
3745
+ if (!VALID_SCHEME.test(decodedScheme)) {
3746
+ throw new TypeError(MALFORMED_SCHEME_ERROR);
3747
+ }
3748
+ return decodedScheme;
3749
+ }
3539
3750
  function normalize(uri, options) {
3540
3751
  if (typeof uri === "string") {
3541
3752
  uri = normalizeString(uri, options);
@@ -3546,12 +3757,34 @@ var require_fast_uri = __commonJS(function(exports, module) {
3546
3757
  }
3547
3758
  function resolve(baseURI, relativeURI, options) {
3548
3759
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3549
- const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3550
- const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3551
- if (baseMalformed || relativeMalformed) {
3760
+ const {
3761
+ parsed: baseParsed,
3762
+ malformedAuthorityOrPort: baseMalformed,
3763
+ malformedPercentEncoding: baseMalformedPercentEncoding,
3764
+ malformedSchemeSpecific: baseMalformedSchemeSpecific,
3765
+ malformedHost: baseMalformedHost,
3766
+ malformedScheme: baseMalformedScheme
3767
+ } = parseWithStatus(baseURI, schemelessOptions);
3768
+ const {
3769
+ parsed: relativeParsed,
3770
+ malformedAuthorityOrPort: relativeMalformed,
3771
+ malformedPercentEncoding: relativeMalformedPercentEncoding,
3772
+ malformedSchemeSpecific: relativeMalformedSchemeSpecific,
3773
+ malformedHost: relativeMalformedHost,
3774
+ malformedScheme: relativeMalformedScheme
3775
+ } = parseWithStatus(relativeURI, schemelessOptions);
3776
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
3552
3777
  throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3553
3778
  }
3554
3779
  const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3780
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
3781
+ const resolvedHost = resolved.host;
3782
+ const resolvedHostIsIP = resolvedHost !== undefined && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
3783
+ canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
3784
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
3785
+ if (resolved.error && !encodedASCIIHost) {
3786
+ throw new Error(resolved.error);
3787
+ }
3555
3788
  schemelessOptions.skipEscape = true;
3556
3789
  return serialize(resolved, schemelessOptions);
3557
3790
  }
@@ -3611,7 +3844,7 @@ var require_fast_uri = __commonJS(function(exports, module) {
3611
3844
  function equal(uriA, uriB, options) {
3612
3845
  const normalizedA = normalizeComparableURI(uriA, options);
3613
3846
  const normalizedB = normalizeComparableURI(uriB, options);
3614
- return normalizedA !== undefined && normalizedB !== undefined && normalizedA.toLowerCase() === normalizedB.toLowerCase();
3847
+ return normalizedA !== undefined && normalizedB !== undefined && normalizedA === normalizedB;
3615
3848
  }
3616
3849
  function serialize(cmpts, opts) {
3617
3850
  const component = {
@@ -3632,20 +3865,23 @@ var require_fast_uri = __commonJS(function(exports, module) {
3632
3865
  };
3633
3866
  const options = Object.assign({}, opts);
3634
3867
  const uriTokens = [];
3868
+ if (component.scheme) {
3869
+ component.scheme = decodeValidScheme(component.scheme);
3870
+ }
3635
3871
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
3636
3872
  if (schemeHandler && schemeHandler.serialize)
3637
3873
  schemeHandler.serialize(component, options);
3874
+ const hasAuthority = component.userinfo !== undefined || component.host !== undefined || component.port !== undefined;
3875
+ const pathNoScheme = !options.skipEscape && component.scheme === undefined && !hasAuthority;
3638
3876
  if (component.path !== undefined) {
3639
3877
  if (!options.skipEscape) {
3640
- component.path = escapePreservingEscapes(component.path);
3641
- if (component.scheme !== undefined) {
3642
- component.path = component.path.split("%3A").join(":");
3643
- }
3878
+ component.path = serializePathEncoding(component.path, pathNoScheme);
3644
3879
  } else {
3645
3880
  component.path = normalizePercentEncoding(component.path);
3646
3881
  }
3647
3882
  }
3648
3883
  if (options.reference !== "suffix" && component.scheme) {
3884
+ component.scheme = decodeValidScheme(component.scheme);
3649
3885
  uriTokens.push(component.scheme, ":");
3650
3886
  }
3651
3887
  const authority = recomposeAuthority(component);
@@ -3663,16 +3899,19 @@ var require_fast_uri = __commonJS(function(exports, module) {
3663
3899
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
3664
3900
  s = removeDotSegments(s);
3665
3901
  }
3902
+ if (pathNoScheme) {
3903
+ s = serializePathEncoding(s, true);
3904
+ }
3666
3905
  if (authority === undefined && s[0] === "/" && s[1] === "/") {
3667
3906
  s = "/%2F" + s.slice(2);
3668
3907
  }
3669
3908
  uriTokens.push(s);
3670
3909
  }
3671
3910
  if (component.query !== undefined) {
3672
- uriTokens.push("?", component.query);
3911
+ uriTokens.push("?", encodeQuery(component.query));
3673
3912
  }
3674
3913
  if (component.fragment !== undefined) {
3675
- uriTokens.push("#", component.fragment);
3914
+ uriTokens.push("#", encodeFragment(component.fragment));
3676
3915
  }
3677
3916
  return uriTokens.join("");
3678
3917
  }
@@ -3688,6 +3927,33 @@ var require_fast_uri = __commonJS(function(exports, module) {
3688
3927
  }
3689
3928
  return;
3690
3929
  }
3930
+ function hasMalformedPercentEncoding(component) {
3931
+ if (component === undefined)
3932
+ return false;
3933
+ let percent = component.indexOf("%");
3934
+ while (percent !== -1) {
3935
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
3936
+ return true;
3937
+ }
3938
+ percent = component.indexOf("%", percent + 3);
3939
+ }
3940
+ return false;
3941
+ }
3942
+ function hasMalformedComponentPercentEncoding(matches) {
3943
+ const host = matches[4];
3944
+ return hasMalformedPercentEncoding(matches[3]) || host !== undefined && !(host[0] === "[" && host[host.length - 1] === "]") && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
3945
+ }
3946
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
3947
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && parsed.host[0] !== "[" && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3948
+ try {
3949
+ parsed.host = new URL("http://" + parsed.host).hostname;
3950
+ } catch (e) {
3951
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3952
+ return true;
3953
+ }
3954
+ }
3955
+ return false;
3956
+ }
3691
3957
  function parseWithStatus(uri, opts) {
3692
3958
  const options = Object.assign({}, opts);
3693
3959
  const parsed = {
@@ -3700,6 +3966,11 @@ var require_fast_uri = __commonJS(function(exports, module) {
3700
3966
  fragment: undefined
3701
3967
  };
3702
3968
  let malformedAuthorityOrPort = false;
3969
+ let malformedPercentEncoding = false;
3970
+ let malformedSchemeSpecific = false;
3971
+ let malformedHost = false;
3972
+ let malformedIPLiteral = false;
3973
+ let malformedScheme = false;
3703
3974
  let isIP = false;
3704
3975
  if (options.reference === "suffix") {
3705
3976
  if (options.scheme) {
@@ -3736,6 +4007,19 @@ var require_fast_uri = __commonJS(function(exports, module) {
3736
4007
  parsed.path = matches[6] || "";
3737
4008
  parsed.query = matches[7];
3738
4009
  parsed.fragment = matches[8];
4010
+ if (parsed.scheme !== undefined) {
4011
+ const decodedScheme = unescape(parsed.scheme);
4012
+ if (VALID_SCHEME.test(decodedScheme)) {
4013
+ parsed.scheme = decodedScheme.toLowerCase();
4014
+ } else {
4015
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
4016
+ malformedScheme = true;
4017
+ }
4018
+ }
4019
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
4020
+ if (malformedPercentEncoding) {
4021
+ parsed.error = parsed.error || "URI contains malformed percent-encoding.";
4022
+ }
3739
4023
  if (isNaN(parsed.port)) {
3740
4024
  parsed.port = matches[5];
3741
4025
  }
@@ -3747,9 +4031,15 @@ var require_fast_uri = __commonJS(function(exports, module) {
3747
4031
  if (parsed.host) {
3748
4032
  const ipv4result = isIPv4(parsed.host);
3749
4033
  if (ipv4result === false) {
4034
+ const bracketedIPLiteral = parsed.host[0] === "[" && parsed.host[parsed.host.length - 1] === "]";
3750
4035
  const ipv6result = normalizeIPv6(parsed.host);
3751
- parsed.host = ipv6result.host.toLowerCase();
3752
- isIP = ipv6result.isIPV6;
4036
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4037
+ malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true;
4038
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4039
+ if (malformedIPLiteral) {
4040
+ parsed.error = parsed.error || "URI host is malformed.";
4041
+ malformedAuthorityOrPort = true;
4042
+ }
3753
4043
  } else {
3754
4044
  isIP = true;
3755
4045
  }
@@ -3767,42 +4057,34 @@ var require_fast_uri = __commonJS(function(exports, module) {
3767
4057
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
3768
4058
  }
3769
4059
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
3770
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3771
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3772
- try {
3773
- parsed.host = new URL("http://" + parsed.host).hostname;
3774
- } catch (e) {
3775
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3776
- }
3777
- }
3778
- }
4060
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
3779
4061
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
3780
4062
  if (uri.indexOf("%") !== -1) {
3781
- if (parsed.scheme !== undefined) {
3782
- parsed.scheme = unescape(parsed.scheme);
3783
- }
3784
- if (parsed.host !== undefined) {
3785
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
4063
+ if (parsed.host !== undefined && !malformedIPLiteral) {
4064
+ const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4065
+ parsed.host = reescapeHostDelimiters(host, isIP);
3786
4066
  }
3787
4067
  }
3788
4068
  if (parsed.path) {
3789
4069
  parsed.path = normalizePathEncoding(parsed.path);
3790
4070
  }
4071
+ if (parsed.query) {
4072
+ parsed.query = normalizeQueryFragmentEncoding(parsed.query);
4073
+ }
3791
4074
  if (parsed.fragment) {
3792
- try {
3793
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3794
- } catch {
3795
- parsed.error = parsed.error || "URI malformed";
3796
- }
4075
+ parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
3797
4076
  }
3798
4077
  }
3799
4078
  if (schemeHandler && schemeHandler.parse) {
3800
4079
  schemeHandler.parse(parsed, options);
4080
+ if (schemeHandler === SCHEMES.urn && parsed.nid === undefined) {
4081
+ malformedSchemeSpecific = true;
4082
+ }
3801
4083
  }
3802
4084
  } else {
3803
4085
  parsed.error = parsed.error || "URI can not be parsed.";
3804
4086
  }
3805
- return { parsed, malformedAuthorityOrPort };
4087
+ return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
3806
4088
  }
3807
4089
  function parse6(uri, opts) {
3808
4090
  return parseWithStatus(uri, opts).parsed;
@@ -3811,20 +4093,28 @@ var require_fast_uri = __commonJS(function(exports, module) {
3811
4093
  return normalizeStringWithStatus(uri, opts).normalized;
3812
4094
  }
3813
4095
  function normalizeStringWithStatus(uri, opts) {
3814
- const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
4096
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
3815
4097
  return {
3816
- normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
3817
- malformedAuthorityOrPort
4098
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
4099
+ malformedAuthorityOrPort,
4100
+ malformedPercentEncoding,
4101
+ malformedSchemeSpecific,
4102
+ malformedHost,
4103
+ malformedScheme
3818
4104
  };
3819
4105
  }
3820
4106
  function normalizeComparableURI(uri, opts) {
3821
- if (typeof uri === "string") {
3822
- const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3823
- return malformedAuthorityOrPort ? undefined : normalized;
4107
+ if (typeof uri !== "string" && typeof uri !== "object") {
4108
+ return;
3824
4109
  }
3825
- if (typeof uri === "object") {
3826
- return serialize(uri, opts);
4110
+ let value;
4111
+ try {
4112
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
4113
+ } catch {
4114
+ return;
3827
4115
  }
4116
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
4117
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? undefined : normalized;
3828
4118
  }
3829
4119
  var fastUri = {
3830
4120
  SCHEMES,
@@ -29396,6 +29686,7 @@ var EMPTY_COMPLETION_RESULT = {
29396
29686
 
29397
29687
  // src/core/tools/lifecycle.ts
29398
29688
  import path6 from "path";
29689
+ import { rmdir } from "node:fs/promises";
29399
29690
 
29400
29691
  // src/core/types.ts
29401
29692
  var CODE_DIMENSIONS = ["style", "architecture", "performance", "security", "maintainability"];
@@ -29452,7 +29743,15 @@ function reviewLayerFromMetadata(child) {
29452
29743
  // src/core/git.ts
29453
29744
  import path from "path";
29454
29745
  import { execFile } from "node:child_process";
29455
- import { readFile, writeFile, stat } from "node:fs/promises";
29746
+ import { readFile, writeFile, stat, rm } from "node:fs/promises";
29747
+ async function pathExists(p) {
29748
+ try {
29749
+ await stat(p);
29750
+ return true;
29751
+ } catch {
29752
+ return false;
29753
+ }
29754
+ }
29456
29755
  function execGit(args) {
29457
29756
  return new Promise((resolve) => {
29458
29757
  const child = execFile("git", args, { encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
@@ -29485,6 +29784,55 @@ async function runGit(worktree, args) {
29485
29784
  async function runGitChecked(worktree, args) {
29486
29785
  return gitRunner.runChecked(worktree, args);
29487
29786
  }
29787
+ async function removeTaskGroupWorktree(repoRoot, wtPath, opts = {}) {
29788
+ const result = { dirResolved: false, dirResolvedByFallback: false, pruned: false, branchDeleted: null, errors: [] };
29789
+ if (!await pathExists(wtPath)) {
29790
+ const pruneRes = await runGitChecked(repoRoot, ["worktree", "prune"]);
29791
+ if (pruneRes.success)
29792
+ result.pruned = true;
29793
+ else
29794
+ result.errors.push(`worktree prune 失败:${pruneRes.stderr}`);
29795
+ result.dirResolved = true;
29796
+ } else {
29797
+ const rmRes = await runGitChecked(repoRoot, ["worktree", "remove", wtPath, "--force"]);
29798
+ if (!rmRes.success)
29799
+ result.errors.push(`git worktree remove 失败:${rmRes.stderr}`);
29800
+ if (await pathExists(wtPath)) {
29801
+ try {
29802
+ await rm(wtPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
29803
+ } catch (e) {
29804
+ result.errors.push(`文件系统兜底删除 "${wtPath}" 失败:${e instanceof Error ? e.message : String(e)}`);
29805
+ }
29806
+ const pruneRes = await runGitChecked(repoRoot, ["worktree", "prune"]);
29807
+ if (pruneRes.success)
29808
+ result.pruned = true;
29809
+ else
29810
+ result.errors.push(`worktree prune 失败:${pruneRes.stderr}`);
29811
+ if (!await pathExists(wtPath)) {
29812
+ result.dirResolved = true;
29813
+ result.dirResolvedByFallback = true;
29814
+ }
29815
+ } else {
29816
+ result.dirResolved = true;
29817
+ }
29818
+ }
29819
+ if (!result.dirResolved) {
29820
+ if (opts.branchName) {
29821
+ result.errors.push(`目录 "${wtPath}" 未能删除,跳过分支 "${opts.branchName}" 的删除以保留排查线索`);
29822
+ }
29823
+ return result;
29824
+ }
29825
+ if (opts.branchName) {
29826
+ const branchRes = await runGitChecked(repoRoot, ["branch", "-D", opts.branchName]);
29827
+ if (branchRes.success)
29828
+ result.branchDeleted = true;
29829
+ else {
29830
+ result.branchDeleted = false;
29831
+ result.errors.push(`删除分支 "${opts.branchName}" 失败:${branchRes.stderr}`);
29832
+ }
29833
+ }
29834
+ return result;
29835
+ }
29488
29836
  async function getCurrentHead(worktree) {
29489
29837
  return runGit(worktree, ["rev-parse", "HEAD"]);
29490
29838
  }
@@ -32405,16 +32753,21 @@ function requireLoader() {
32405
32753
  state.result += _result;
32406
32754
  }
32407
32755
  }
32756
+ function chargeMergeWork(state) {
32757
+ state.totalMergeKeys++;
32758
+ if (state.maxTotalMergeKeys !== -1 && state.totalMergeKeys > state.maxTotalMergeKeys) {
32759
+ throwError(state, "merge keys exceeded maxTotalMergeKeys (" + state.maxTotalMergeKeys + ")");
32760
+ }
32761
+ }
32408
32762
  function mergeMappings(state, destination, source, overridableKeys) {
32409
32763
  if (!common2.isObject(source)) {
32410
32764
  throwError(state, "cannot merge mappings; the provided source object is unacceptable");
32411
32765
  }
32766
+ chargeMergeWork(state);
32412
32767
  const sourceKeys = Object.keys(source);
32413
32768
  for (let index = 0, quantity = sourceKeys.length;index < quantity; index += 1) {
32414
32769
  const key = sourceKeys[index];
32415
- if (state.maxTotalMergeKeys !== -1 && ++state.totalMergeKeys > state.maxTotalMergeKeys) {
32416
- throwError(state, "merge keys exceeded maxTotalMergeKeys (" + state.maxTotalMergeKeys + ")");
32417
- }
32770
+ chargeMergeWork(state);
32418
32771
  if (!_hasOwnProperty.call(destination, key)) {
32419
32772
  setProperty(destination, key, source[key]);
32420
32773
  overridableKeys[key] = true;
@@ -32442,6 +32795,9 @@ function requireLoader() {
32442
32795
  }
32443
32796
  if (keyTag === "tag:yaml.org,2002:merge") {
32444
32797
  if (Array.isArray(valueNode)) {
32798
+ if (valueNode.length > 100) {
32799
+ throwError(state, "abnormal merge sequence size");
32800
+ }
32445
32801
  for (let index = 0, quantity = valueNode.length;index < quantity; index += 1) {
32446
32802
  mergeMappings(state, _result, valueNode[index], overridableKeys);
32447
32803
  }
@@ -34660,6 +35016,20 @@ function renderAgentSummaries(agentSummaries, agentName) {
34660
35016
  lines.push("");
34661
35017
  return lines;
34662
35018
  }
35019
+ function renderDevSelfCheckDeclaration(metadata) {
35020
+ const selfCheck = typeof metadata["self_check_results"] === "string" ? metadata["self_check_results"].trim() : "";
35021
+ const testResults = typeof metadata["test_results"] === "string" ? metadata["test_results"].trim() : "";
35022
+ if (selfCheck === "" && testResults === "")
35023
+ return [];
35024
+ const lines = ["## 开发者自检申报", ""];
35025
+ if (selfCheck !== "") {
35026
+ lines.push("**自检申报(self_check_results)**:", "", selfCheck, "");
35027
+ }
35028
+ if (testResults !== "") {
35029
+ lines.push("**接口测试结果(test_results)**:", "", testResults, "");
35030
+ }
35031
+ return lines;
35032
+ }
34663
35033
  var INTERPOLATION_ALLOWED_KEYS = new Set([
34664
35034
  "worktree_path",
34665
35035
  "change_id",
@@ -35192,7 +35562,13 @@ function renderAgentWorking(item, rec, step, common2, ctxAgent, state, tg, toolC
35192
35562
  lines.push(...renderStepSemantics(step, common2, stepCtx));
35193
35563
  lines.push(...renderWorktreeSection(state, tg, { showNamespace: true, showPort: true }));
35194
35564
  if (step?.id === "verify_tool" && agentToReviewLayer(ctxAgent) === "tool" && toolChanges?.hasNonDocChange) {
35195
- lines.push(...renderToolChangesEvidence(item, tg, toolChanges));
35565
+ lines.push(...renderToolChangesEvidence(item, tg, toolChanges, "tool_review"));
35566
+ }
35567
+ if (step?.id === "quality_review" && toolChanges) {
35568
+ lines.push(...renderToolChangesEvidence(item, tg, toolChanges, "merged_review"));
35569
+ }
35570
+ if (step?.id === "quality_review") {
35571
+ lines.push(...renderDevSelfCheckDeclaration(item.metadata));
35196
35572
  }
35197
35573
  lines.push(...renderAgentSummaries(readAgentSummaries(item), ctxAgent));
35198
35574
  lines.push(...renderStepContext(item, step, ctxAgent, state, exemptionCtx));
@@ -35400,15 +35776,20 @@ function renderToolChildren(item, ctxAgent, exemptionCtx) {
35400
35776
  lines.push(...renderChildrenSection("Issue (待裁定是否可豁免)", pending, exemptionCtx));
35401
35777
  return lines;
35402
35778
  }
35403
- function renderToolChangesEvidence(item, tg, toolChanges) {
35779
+ var EVIDENCE_KIND_LABELS = {
35780
+ tool_review: { title: "自上次工具检查", lastCheck: "上次工具检查" },
35781
+ merged_review: { title: "自上次合并审查", lastCheck: "上次合并审查" }
35782
+ };
35783
+ function renderToolChangesEvidence(item, tg, toolChanges, kind) {
35784
+ const label = EVIDENCE_KIND_LABELS[kind];
35404
35785
  const checkpoint = typeof item.metadata["_tool_review_checkpoint"] === "string" ? item.metadata["_tool_review_checkpoint"] : undefined;
35405
35786
  const baseRef = tg.baseRef ?? undefined;
35406
35787
  const rangeRef = checkpoint ?? baseRef;
35407
- const lines = ["## 本次变更证据(自上次工具检查)", ""];
35788
+ const lines = [`## 本次变更证据(${label.title})`, ""];
35408
35789
  if (checkpoint) {
35409
- lines.push(`- **口径**: 本次为「自上次工具检查(${checkpoint}..HEAD)」的增量区间(含已提交与未提交的非 openspec 变更);` + `与上方「变更范围」(${baseRef ?? "(无)"}..HEAD 整个 change 累计口径)不同`, "");
35790
+ lines.push(`- **口径**: 本次为「${label.title}(${checkpoint}..HEAD)」的增量区间(含已提交与未提交的非 openspec 变更);` + `与上方「变更范围」(${baseRef ?? "(无)"}..HEAD 整个 change 累计口径)不同`, "");
35410
35791
  } else if (baseRef) {
35411
- lines.push(`- **口径**: 本次区间以基线(${baseRef}..HEAD)兜底,与上方「变更范围」一致(首次进入,无上次工具检查记录;` + "含已提交与未提交的非 openspec 变更)", "");
35792
+ lines.push(`- **口径**: 本次区间以基线(${baseRef}..HEAD)兜底,与上方「变更范围」一致(首次进入,无${label.lastCheck}记录;` + "含已提交与未提交的非 openspec 变更)", "");
35412
35793
  } else {
35413
35794
  lines.push("- **口径**: 无检查点且无基线基准,无法界定已提交变更区间;本次变更仅按检出文件与未提交变更提示", "");
35414
35795
  }
@@ -36014,13 +36395,15 @@ async function setWorktreeExecute(params, ctx) {
36014
36395
  await bindWorktreeRefs(item, existingPath, branch, state.baseBranch);
36015
36396
  reused = true;
36016
36397
  } else {
36017
- const rmResult = await runGitChecked(repoRoot, ["worktree", "remove", existingPath, "--force"]);
36018
- if (!rmResult.success) {
36019
- throw new Error(`无法清理已有 worktree "${existingPath}":${rmResult.stderr}`);
36020
- }
36021
- const branchRmResult = await runGitChecked(repoRoot, ["branch", "-D", branch]);
36022
- if (!branchRmResult.success) {
36023
- throw new Error(`无法清理已有分支 "${branch}":${branchRmResult.stderr}`);
36398
+ const cleanup = await removeTaskGroupWorktree(repoRoot, existingPath, { branchName: branch });
36399
+ if (!cleanup.dirResolved || cleanup.branchDeleted === false) {
36400
+ const problems = [];
36401
+ if (!cleanup.dirResolved)
36402
+ problems.push(`无法清理已有 worktree "${existingPath}"`);
36403
+ if (cleanup.branchDeleted === false)
36404
+ problems.push(`无法清理已有分支 "${branch}"`);
36405
+ throw new Error(`${problems.join(";")}:${cleanup.errors.join(";") || "原因未知"}
36406
+ 请手动处理后重试。`);
36024
36407
  }
36025
36408
  }
36026
36409
  }
@@ -36071,7 +36454,8 @@ async function statusExecute(params, ctx) {
36071
36454
  const tg = findTaskGroup(state, state.taskGroupId);
36072
36455
  const mainPollution = ctx.orchestrator ? await detectMainRepoPollution(ctx.worktree) : null;
36073
36456
  let toolChanges;
36074
- if (agentToReviewLayer(agent) === "tool" && rec.status === "recommend" && rec.stepId === "verify_tool" && rec.agents.includes(agent)) {
36457
+ const changesEvidenceStep = rec.status === "recommend" && rec.agents.includes(agent) && (agentToReviewLayer(agent) === "tool" && rec.stepId === "verify_tool" || agent === "openspec-reviewer" && rec.stepId === "quality_review");
36458
+ if (changesEvidenceStep) {
36075
36459
  const wtPath = typeof item.metadata["worktree_path"] === "string" ? item.metadata["worktree_path"] : undefined;
36076
36460
  if (wtPath) {
36077
36461
  const checkpoint = typeof item.metadata["_tool_review_checkpoint"] === "string" ? item.metadata["_tool_review_checkpoint"] : undefined;
@@ -36083,6 +36467,19 @@ async function statusExecute(params, ctx) {
36083
36467
  const exemptionItems = (await readExemptions(ctx.worktree)).items;
36084
36468
  return renderWorkflowStatusView(item, workflow, rec, { agent, orchestrator: ctx.orchestrator, identityDeclared: ctx.identityDeclared }, { state, tg, mainPollution, toolChanges, exemptedHits, exemptionItems });
36085
36469
  }
36470
+ async function sweepEmptyWorktreeParents(repoRoot, removedDir, changeId) {
36471
+ const repoRootAbs = path6.resolve(repoRoot);
36472
+ const groupLevelDir = path6.join(repoRootAbs, ".worktree", changeId);
36473
+ const worktreeLevelDir = path6.join(repoRootAbs, ".worktree");
36474
+ if (path6.dirname(path6.resolve(removedDir)) === groupLevelDir) {
36475
+ try {
36476
+ await rmdir(groupLevelDir);
36477
+ } catch {}
36478
+ }
36479
+ try {
36480
+ await rmdir(worktreeLevelDir);
36481
+ } catch {}
36482
+ }
36086
36483
  async function completeTaskGroupExecute(params, ctx) {
36087
36484
  assertOrchestrator(ctx, "opx_orch_complete_task_group");
36088
36485
  const state = await readStateByWorktree(ctx.worktree, params.change_id);
@@ -36137,17 +36534,42 @@ async function completeTaskGroupExecute(params, ctx) {
36137
36534
  `);
36138
36535
  }
36139
36536
  }
36140
- if (worktreePath && branchName) {
36141
- try {
36142
- await runGit(ctx.worktree, ["worktree", "remove", worktreePath, "--force"]);
36143
- await runGit(ctx.worktree, ["branch", "-D", branchName]);
36144
- } catch {}
36537
+ let cleanupNote = "";
36538
+ let cleanupResidualWarning = "";
36539
+ if (worktreePath) {
36540
+ const cleanup = await removeTaskGroupWorktree(ctx.worktree, worktreePath, { branchName });
36541
+ if (cleanup.dirResolved) {
36542
+ await sweepEmptyWorktreeParents(ctx.worktree, worktreePath, params.change_id);
36543
+ const successNotes = [];
36544
+ if (cleanup.dirResolvedByFallback) {
36545
+ successNotes.push("- **cleanup**: git worktree remove 未直接移除目录,已按文件系统兜底删除并 prune 管理记录。");
36546
+ }
36547
+ if (cleanup.errors.length > 0) {
36548
+ successNotes.push(`- **cleanup 部分告警**: ${cleanup.errors.join(";")}`);
36549
+ }
36550
+ cleanupNote = successNotes.join(`
36551
+ `);
36552
+ } else {
36553
+ item.metadata["cleanup_residual"] = { worktree_path: worktreePath, errors: cleanup.errors };
36554
+ cleanupResidualWarning = [
36555
+ "",
36556
+ "## ⚠️ worktree 清理残留(不影响收尾)",
36557
+ `- **残留路径**: \`${worktreePath}\``,
36558
+ "- **原因**:",
36559
+ ...cleanup.errors.map((e) => ` - ${e}`),
36560
+ `- **处理**: 请人工执行 \`rm -rf '${worktreePath}' && git worktree prune\` 清理残留目录与 git 管理记录。`,
36561
+ branchName ? `- **分支**: 分支 "${branchName}" 未删除。` : ""
36562
+ ].filter(Boolean).join(`
36563
+ `);
36564
+ }
36145
36565
  }
36146
36566
  item.metadata["completed_at"] = new Date().toISOString();
36147
36567
  await writeState(ctx.worktree, state);
36148
36568
  const doneMessage = `任务组已完成并合并到 "${mergeTarget}"。`;
36149
- return checkboxWarning ? `${doneMessage}
36150
- ${checkboxWarning}` : doneMessage;
36569
+ const notes = [cleanupNote, checkboxWarning, cleanupResidualWarning].filter(Boolean);
36570
+ return notes.length > 0 ? `${doneMessage}
36571
+ ${notes.join(`
36572
+ `)}` : doneMessage;
36151
36573
  }
36152
36574
  async function setUnattendedExecute(params, ctx) {
36153
36575
  assertOrchestrator(ctx, "opx_orch_set_unattended");
@@ -36350,6 +36772,9 @@ function handleImplementParams(item, params) {
36350
36772
  if (params.self_check_results) {
36351
36773
  item.metadata["self_check_results"] = params.self_check_results;
36352
36774
  }
36775
+ if (params.test_results) {
36776
+ item.metadata["test_results"] = params.test_results;
36777
+ }
36353
36778
  }
36354
36779
  function mergeBoundaryInto(item, expansion) {
36355
36780
  const boundary = item.metadata["execution_boundary"];
@@ -36401,7 +36826,7 @@ function handleReviewParams(item, params, newChildren, stepId, opts) {
36401
36826
  }
36402
36827
  mergeBoundaryInto(item, params.boundary_expansion);
36403
36828
  }
36404
- if (params.test_results)
36829
+ if (params.test_results && stepId !== "quality_review")
36405
36830
  item.metadata["test_results"] = params.test_results;
36406
36831
  if (params.validation_steps) {
36407
36832
  assertStructuredSkipReasons(params.validation_steps);
@@ -36411,7 +36836,7 @@ function handleReviewParams(item, params, newChildren, stepId, opts) {
36411
36836
  const uncovered = uncoveredMustDo(opts.capabilityTags, params.validation_steps);
36412
36837
  if (uncovered.length > 0) {
36413
36838
  throw new Error(`提交的验证步骤未覆盖质量门 skill 必做清单,缺少以下必做项:${uncovered.join("、")}。
36414
- ` + `请按已加载质量门类 skill 的必做清单逐项执行并逐项申报结果(completed=true);` + `确实无法执行的必做项须以结构化 skip_reason 申报降级理由` + `(格式:${SKIP_REASON_FORMAT})。`);
36839
+ ` + `请按已加载质量门类 skill 的必做清单逐项执行并逐项申报结果(completed=true);` + `确实无法执行的必做项须以结构化 skip_reason 申报降级理由` + `(格式:${SKIP_REASON_FORMAT})。` + `低成本必做项必须实跑后申报;核验申报仅限 workflow 指令白名单限定的高成本必做项。`);
36415
36840
  }
36416
36841
  }
36417
36842
  if (params.verified_tasks?.length || params.failed_tasks?.length) {
@@ -36636,7 +37061,7 @@ async function agentSubmitExecute(params, ctx) {
36636
37061
  baseRef
36637
37062
  });
36638
37063
  }
36639
- if (params.step_id === "verify_tool" && agentToReviewLayer(ctx.agent) === "tool") {
37064
+ if (params.step_id === "verify_tool" && agentToReviewLayer(ctx.agent) === "tool" || params.step_id === "quality_review" && ctx.agent === "openspec-reviewer") {
36640
37065
  const head = await getCurrentHead(wtPath);
36641
37066
  if (head)
36642
37067
  item.metadata["_tool_review_checkpoint"] = head;
@@ -37043,7 +37468,10 @@ var agentSubmitSchema = {
37043
37468
  description: "implement step:已完成的 task id(覆盖门禁:全部 open/rejected task 必须被覆盖)",
37044
37469
  items: { type: "string" }
37045
37470
  },
37046
- test_results: { type: "string", description: "verify_tool step:UT 运行结果摘要" },
37471
+ test_results: {
37472
+ type: "string",
37473
+ description: "implement / verify_tool step:测试运行结果摘要(implement 为接口测试执行结果——附执行顺序与覆盖接口清单;verify_tool 为 UT 运行结果摘要);quality_review(simple 合并审查)下由开发者在 implement 申报,审查者提交时不生效"
37474
+ },
37047
37475
  validation_steps: {
37048
37476
  type: "array",
37049
37477
  description: "review step:验证步骤执行摘要",
@@ -37185,7 +37613,7 @@ async function ensureDefaultUnattended(args, ctx) {
37185
37613
  }
37186
37614
  } catch {}
37187
37615
  }
37188
- var PKG_VERSION = "0.128.0";
37616
+ var PKG_VERSION = "0.131.0";
37189
37617
  function buildMcpServer(worktree, opts = {}) {
37190
37618
  const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
37191
37619
  for (const [name, spec] of Object.entries(TOOL_SPECS)) {