@ifrankwang/openspec-agents 0.127.0 → 0.130.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;
3125
- }
3126
- address.push(":");
3127
- continue;
3128
- } else if (cursor === "%") {
3129
- if (!consume(buffer, address, output)) {
3130
- break;
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;
3131
3130
  }
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;
3301
3351
  } else {
3302
- output += escape(input[i]);
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;
3425
+ } else {
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,
@@ -29434,6 +29724,9 @@ function agentToReviewLayer(agent) {
29434
29724
  return "quality";
29435
29725
  return;
29436
29726
  }
29727
+ function isReviewerFamily(agent) {
29728
+ return agent === "openspec-architect" || agentToReviewLayer(agent) !== undefined;
29729
+ }
29437
29730
  function readIssueSource(child) {
29438
29731
  return typeof child.metadata["source"] === "string" ? child.metadata["source"] : undefined;
29439
29732
  }
@@ -29719,6 +30012,39 @@ async function reconcileMainPollution(mainRepo, worktreePath, opts) {
29719
30012
  }
29720
30013
  }
29721
30014
  }
30015
+ async function autoCommitWorktreeChanges(wtPath, agent, stepId) {
30016
+ const statusRes = await runGitChecked(wtPath, ["status", "--porcelain"]);
30017
+ if (!statusRes.success) {
30018
+ return { status: "failed", files: [], untrackedFiles: [], stderr: statusRes.stderr };
30019
+ }
30020
+ const files = [];
30021
+ const untrackedFiles = [];
30022
+ for (const line of statusRes.stdout.split(`
30023
+ `).map((l) => l.trim()).filter(Boolean)) {
30024
+ const path2 = parsePorcelainPaths(line)[0];
30025
+ if (!path2 || path2.startsWith("openspec/states/"))
30026
+ continue;
30027
+ if (line.startsWith("??"))
30028
+ untrackedFiles.push(path2);
30029
+ else
30030
+ files.push(path2);
30031
+ }
30032
+ if (files.length === 0)
30033
+ return { status: "skipped", files: [], untrackedFiles };
30034
+ const addResult = await runGitChecked(wtPath, ["add", "-u", "--", ".", ":(exclude)openspec/states"]);
30035
+ if (!addResult.success) {
30036
+ return { status: "failed", files, untrackedFiles, stderr: addResult.stderr };
30037
+ }
30038
+ const commitResult = await runGitChecked(wtPath, [
30039
+ "commit",
30040
+ "-m",
30041
+ `docs(opx): direct fixes by ${agent} (${stepId})`
30042
+ ]);
30043
+ if (!commitResult.success) {
30044
+ return { status: "failed", files, untrackedFiles, stderr: commitResult.stderr };
30045
+ }
30046
+ return { status: "committed", files, untrackedFiles };
30047
+ }
29722
30048
 
29723
30049
  // src/core/state.ts
29724
30050
  import path2 from "path";
@@ -34624,6 +34950,20 @@ function renderAgentSummaries(agentSummaries, agentName) {
34624
34950
  lines.push("");
34625
34951
  return lines;
34626
34952
  }
34953
+ function renderDevSelfCheckDeclaration(metadata) {
34954
+ const selfCheck = typeof metadata["self_check_results"] === "string" ? metadata["self_check_results"].trim() : "";
34955
+ const testResults = typeof metadata["test_results"] === "string" ? metadata["test_results"].trim() : "";
34956
+ if (selfCheck === "" && testResults === "")
34957
+ return [];
34958
+ const lines = ["## 开发者自检申报", ""];
34959
+ if (selfCheck !== "") {
34960
+ lines.push("**自检申报(self_check_results)**:", "", selfCheck, "");
34961
+ }
34962
+ if (testResults !== "") {
34963
+ lines.push("**接口测试结果(test_results)**:", "", testResults, "");
34964
+ }
34965
+ return lines;
34966
+ }
34627
34967
  var INTERPOLATION_ALLOWED_KEYS = new Set([
34628
34968
  "worktree_path",
34629
34969
  "change_id",
@@ -35156,7 +35496,13 @@ function renderAgentWorking(item, rec, step, common2, ctxAgent, state, tg, toolC
35156
35496
  lines.push(...renderStepSemantics(step, common2, stepCtx));
35157
35497
  lines.push(...renderWorktreeSection(state, tg, { showNamespace: true, showPort: true }));
35158
35498
  if (step?.id === "verify_tool" && agentToReviewLayer(ctxAgent) === "tool" && toolChanges?.hasNonDocChange) {
35159
- lines.push(...renderToolChangesEvidence(item, tg, toolChanges));
35499
+ lines.push(...renderToolChangesEvidence(item, tg, toolChanges, "tool_review"));
35500
+ }
35501
+ if (step?.id === "quality_review" && toolChanges) {
35502
+ lines.push(...renderToolChangesEvidence(item, tg, toolChanges, "merged_review"));
35503
+ }
35504
+ if (step?.id === "quality_review") {
35505
+ lines.push(...renderDevSelfCheckDeclaration(item.metadata));
35160
35506
  }
35161
35507
  lines.push(...renderAgentSummaries(readAgentSummaries(item), ctxAgent));
35162
35508
  lines.push(...renderStepContext(item, step, ctxAgent, state, exemptionCtx));
@@ -35364,15 +35710,20 @@ function renderToolChildren(item, ctxAgent, exemptionCtx) {
35364
35710
  lines.push(...renderChildrenSection("Issue (待裁定是否可豁免)", pending, exemptionCtx));
35365
35711
  return lines;
35366
35712
  }
35367
- function renderToolChangesEvidence(item, tg, toolChanges) {
35713
+ var EVIDENCE_KIND_LABELS = {
35714
+ tool_review: { title: "自上次工具检查", lastCheck: "上次工具检查" },
35715
+ merged_review: { title: "自上次合并审查", lastCheck: "上次合并审查" }
35716
+ };
35717
+ function renderToolChangesEvidence(item, tg, toolChanges, kind) {
35718
+ const label = EVIDENCE_KIND_LABELS[kind];
35368
35719
  const checkpoint = typeof item.metadata["_tool_review_checkpoint"] === "string" ? item.metadata["_tool_review_checkpoint"] : undefined;
35369
35720
  const baseRef = tg.baseRef ?? undefined;
35370
35721
  const rangeRef = checkpoint ?? baseRef;
35371
- const lines = ["## 本次变更证据(自上次工具检查)", ""];
35722
+ const lines = [`## 本次变更证据(${label.title})`, ""];
35372
35723
  if (checkpoint) {
35373
- lines.push(`- **口径**: 本次为「自上次工具检查(${checkpoint}..HEAD)」的增量区间(含已提交与未提交的非 openspec 变更);` + `与上方「变更范围」(${baseRef ?? "(无)"}..HEAD 整个 change 累计口径)不同`, "");
35724
+ lines.push(`- **口径**: 本次为「${label.title}(${checkpoint}..HEAD)」的增量区间(含已提交与未提交的非 openspec 变更);` + `与上方「变更范围」(${baseRef ?? "(无)"}..HEAD 整个 change 累计口径)不同`, "");
35374
35725
  } else if (baseRef) {
35375
- lines.push(`- **口径**: 本次区间以基线(${baseRef}..HEAD)兜底,与上方「变更范围」一致(首次进入,无上次工具检查记录;` + "含已提交与未提交的非 openspec 变更)", "");
35726
+ lines.push(`- **口径**: 本次区间以基线(${baseRef}..HEAD)兜底,与上方「变更范围」一致(首次进入,无${label.lastCheck}记录;` + "含已提交与未提交的非 openspec 变更)", "");
35376
35727
  } else {
35377
35728
  lines.push("- **口径**: 无检查点且无基线基准,无法界定已提交变更区间;本次变更仅按检出文件与未提交变更提示", "");
35378
35729
  }
@@ -36035,7 +36386,8 @@ async function statusExecute(params, ctx) {
36035
36386
  const tg = findTaskGroup(state, state.taskGroupId);
36036
36387
  const mainPollution = ctx.orchestrator ? await detectMainRepoPollution(ctx.worktree) : null;
36037
36388
  let toolChanges;
36038
- if (agentToReviewLayer(agent) === "tool" && rec.status === "recommend" && rec.stepId === "verify_tool" && rec.agents.includes(agent)) {
36389
+ const changesEvidenceStep = rec.status === "recommend" && rec.agents.includes(agent) && (agentToReviewLayer(agent) === "tool" && rec.stepId === "verify_tool" || agent === "openspec-reviewer" && rec.stepId === "quality_review");
36390
+ if (changesEvidenceStep) {
36039
36391
  const wtPath = typeof item.metadata["worktree_path"] === "string" ? item.metadata["worktree_path"] : undefined;
36040
36392
  if (wtPath) {
36041
36393
  const checkpoint = typeof item.metadata["_tool_review_checkpoint"] === "string" ? item.metadata["_tool_review_checkpoint"] : undefined;
@@ -36314,6 +36666,9 @@ function handleImplementParams(item, params) {
36314
36666
  if (params.self_check_results) {
36315
36667
  item.metadata["self_check_results"] = params.self_check_results;
36316
36668
  }
36669
+ if (params.test_results) {
36670
+ item.metadata["test_results"] = params.test_results;
36671
+ }
36317
36672
  }
36318
36673
  function mergeBoundaryInto(item, expansion) {
36319
36674
  const boundary = item.metadata["execution_boundary"];
@@ -36365,7 +36720,7 @@ function handleReviewParams(item, params, newChildren, stepId, opts) {
36365
36720
  }
36366
36721
  mergeBoundaryInto(item, params.boundary_expansion);
36367
36722
  }
36368
- if (params.test_results)
36723
+ if (params.test_results && stepId !== "quality_review")
36369
36724
  item.metadata["test_results"] = params.test_results;
36370
36725
  if (params.validation_steps) {
36371
36726
  assertStructuredSkipReasons(params.validation_steps);
@@ -36375,7 +36730,7 @@ function handleReviewParams(item, params, newChildren, stepId, opts) {
36375
36730
  const uncovered = uncoveredMustDo(opts.capabilityTags, params.validation_steps);
36376
36731
  if (uncovered.length > 0) {
36377
36732
  throw new Error(`提交的验证步骤未覆盖质量门 skill 必做清单,缺少以下必做项:${uncovered.join("、")}。
36378
- ` + `请按已加载质量门类 skill 的必做清单逐项执行并逐项申报结果(completed=true);` + `确实无法执行的必做项须以结构化 skip_reason 申报降级理由` + `(格式:${SKIP_REASON_FORMAT})。`);
36733
+ ` + `请按已加载质量门类 skill 的必做清单逐项执行并逐项申报结果(completed=true);` + `确实无法执行的必做项须以结构化 skip_reason 申报降级理由` + `(格式:${SKIP_REASON_FORMAT})。` + `低成本必做项必须实跑后申报;核验申报仅限 workflow 指令白名单限定的高成本必做项。`);
36379
36734
  }
36380
36735
  }
36381
36736
  if (params.verified_tasks?.length || params.failed_tasks?.length) {
@@ -36581,6 +36936,18 @@ async function agentSubmitExecute(params, ctx) {
36581
36936
  exemptIds: params.exempt_issue_ids,
36582
36937
  newChildren: accepted
36583
36938
  });
36939
+ const autoCommitLines = [];
36940
+ if (isReviewerFamily(ctx.agent)) {
36941
+ const ac = await autoCommitWorktreeChanges(wtPath, ctx.agent, params.step_id);
36942
+ if (ac.status === "committed") {
36943
+ autoCommitLines.push(`- ✅ worktree 直改修正已自动提交(${ac.files.length} 个文件,commit message: \`docs(opx): direct fixes by ${ctx.agent} (${params.step_id})\`)`);
36944
+ } else if (ac.status === "failed") {
36945
+ autoCommitLines.push(`- ⚠️ worktree 直改修正自动提交失败:${ac.stderr || "未知错误"}。请编排者安排补提交(worktree 内 git add -u 后 commit)`);
36946
+ }
36947
+ if (ac.untrackedFiles.length > 0) {
36948
+ autoCommitLines.push(`- ⚠️ 存在未跟踪新建文件未纳入自动提交(须纳入版本控制时由编排者/开发者处理):${ac.untrackedFiles.join("、")}`);
36949
+ }
36950
+ }
36584
36951
  if (stepPhase === "todo" && params.step_id === "analyze" && params.verdict === "passed" && wtPath && typeof item.metadata["branch_name"] === "string") {
36585
36952
  const baseRef = typeof item.metadata["base_ref"] === "string" ? item.metadata["base_ref"] : null;
36586
36953
  await reconcileMainPollution(ctx.worktree, wtPath, {
@@ -36588,7 +36955,7 @@ async function agentSubmitExecute(params, ctx) {
36588
36955
  baseRef
36589
36956
  });
36590
36957
  }
36591
- if (params.step_id === "verify_tool" && agentToReviewLayer(ctx.agent) === "tool") {
36958
+ if (params.step_id === "verify_tool" && agentToReviewLayer(ctx.agent) === "tool" || params.step_id === "quality_review" && ctx.agent === "openspec-reviewer") {
36592
36959
  const head = await getCurrentHead(wtPath);
36593
36960
  if (head)
36594
36961
  item.metadata["_tool_review_checkpoint"] = head;
@@ -36597,7 +36964,17 @@ async function agentSubmitExecute(params, ctx) {
36597
36964
  await writeDismissedExemption(ctx.worktree, child, { changeId: state.changeId, exemptedBy: ctx.agent });
36598
36965
  }
36599
36966
  await writeState(ctx.worktree, state);
36600
- return SUBMIT_OK_MESSAGE;
36967
+ if (autoCommitLines.length === 0)
36968
+ return SUBMIT_OK_MESSAGE;
36969
+ return [
36970
+ `## ✅ ${SUBMIT_OK_MESSAGE}`,
36971
+ "",
36972
+ "### worktree 自动提交",
36973
+ "",
36974
+ ...autoCommitLines,
36975
+ ""
36976
+ ].join(`
36977
+ `);
36601
36978
  } finally {
36602
36979
  releaseLock(lockPath);
36603
36980
  }
@@ -36985,7 +37362,10 @@ var agentSubmitSchema = {
36985
37362
  description: "implement step:已完成的 task id(覆盖门禁:全部 open/rejected task 必须被覆盖)",
36986
37363
  items: { type: "string" }
36987
37364
  },
36988
- test_results: { type: "string", description: "verify_tool step:UT 运行结果摘要" },
37365
+ test_results: {
37366
+ type: "string",
37367
+ description: "implement / verify_tool step:测试运行结果摘要(implement 为接口测试执行结果——附执行顺序与覆盖接口清单;verify_tool 为 UT 运行结果摘要);quality_review(simple 合并审查)下由开发者在 implement 申报,审查者提交时不生效"
37368
+ },
36989
37369
  validation_steps: {
36990
37370
  type: "array",
36991
37371
  description: "review step:验证步骤执行摘要",
@@ -37127,7 +37507,7 @@ async function ensureDefaultUnattended(args, ctx) {
37127
37507
  }
37128
37508
  } catch {}
37129
37509
  }
37130
- var PKG_VERSION = "0.127.0";
37510
+ var PKG_VERSION = "0.130.0";
37131
37511
  function buildMcpServer(worktree, opts = {}) {
37132
37512
  const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
37133
37513
  for (const [name, spec] of Object.entries(TOOL_SPECS)) {