@osfactory/har 0.10.0 → 0.11.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.
package/dist/index.js CHANGED
@@ -765,14 +765,14 @@ var require_url_state_machine = __commonJS({
765
765
  return url.replace(/\u0009|\u000A|\u000D/g, "");
766
766
  }
767
767
  function shortenPath(url) {
768
- const path40 = url.path;
769
- if (path40.length === 0) {
768
+ const path41 = url.path;
769
+ if (path41.length === 0) {
770
770
  return;
771
771
  }
772
- if (url.scheme === "file" && path40.length === 1 && isNormalizedWindowsDriveLetter(path40[0])) {
772
+ if (url.scheme === "file" && path41.length === 1 && isNormalizedWindowsDriveLetter(path41[0])) {
773
773
  return;
774
774
  }
775
- path40.pop();
775
+ path41.pop();
776
776
  }
777
777
  function includesCredentials(url) {
778
778
  return url.username !== "" || url.password !== "";
@@ -2092,7 +2092,7 @@ var require_lib2 = __commonJS({
2092
2092
  let accum = [];
2093
2093
  let accumBytes = 0;
2094
2094
  let abort = false;
2095
- return new Body.Promise(function(resolve27, reject) {
2095
+ return new Body.Promise(function(resolve28, reject) {
2096
2096
  let resTimeout;
2097
2097
  if (_this4.timeout) {
2098
2098
  resTimeout = setTimeout(function() {
@@ -2126,7 +2126,7 @@ var require_lib2 = __commonJS({
2126
2126
  }
2127
2127
  clearTimeout(resTimeout);
2128
2128
  try {
2129
- resolve27(Buffer.concat(accum, accumBytes));
2129
+ resolve28(Buffer.concat(accum, accumBytes));
2130
2130
  } catch (err) {
2131
2131
  reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
2132
2132
  }
@@ -2801,7 +2801,7 @@ var require_lib2 = __commonJS({
2801
2801
  throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
2802
2802
  }
2803
2803
  Body.Promise = fetch3.Promise;
2804
- return new fetch3.Promise(function(resolve27, reject) {
2804
+ return new fetch3.Promise(function(resolve28, reject) {
2805
2805
  const request = new Request3(url, opts);
2806
2806
  const options = getNodeRequestOptions(request);
2807
2807
  const send = (options.protocol === "https:" ? https : http).request;
@@ -2934,7 +2934,7 @@ var require_lib2 = __commonJS({
2934
2934
  requestOpts.body = void 0;
2935
2935
  requestOpts.headers.delete("content-length");
2936
2936
  }
2937
- resolve27(fetch3(new Request3(locationURL, requestOpts)));
2937
+ resolve28(fetch3(new Request3(locationURL, requestOpts)));
2938
2938
  finalize();
2939
2939
  return;
2940
2940
  }
@@ -2955,7 +2955,7 @@ var require_lib2 = __commonJS({
2955
2955
  const codings = headers.get("Content-Encoding");
2956
2956
  if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
2957
2957
  response = new Response3(body, response_options);
2958
- resolve27(response);
2958
+ resolve28(response);
2959
2959
  return;
2960
2960
  }
2961
2961
  const zlibOptions = {
@@ -2965,7 +2965,7 @@ var require_lib2 = __commonJS({
2965
2965
  if (codings == "gzip" || codings == "x-gzip") {
2966
2966
  body = body.pipe(zlib.createGunzip(zlibOptions));
2967
2967
  response = new Response3(body, response_options);
2968
- resolve27(response);
2968
+ resolve28(response);
2969
2969
  return;
2970
2970
  }
2971
2971
  if (codings == "deflate" || codings == "x-deflate") {
@@ -2977,12 +2977,12 @@ var require_lib2 = __commonJS({
2977
2977
  body = body.pipe(zlib.createInflateRaw());
2978
2978
  }
2979
2979
  response = new Response3(body, response_options);
2980
- resolve27(response);
2980
+ resolve28(response);
2981
2981
  });
2982
2982
  raw.on("end", function() {
2983
2983
  if (!response) {
2984
2984
  response = new Response3(body, response_options);
2985
- resolve27(response);
2985
+ resolve28(response);
2986
2986
  }
2987
2987
  });
2988
2988
  return;
@@ -2990,11 +2990,11 @@ var require_lib2 = __commonJS({
2990
2990
  if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
2991
2991
  body = body.pipe(zlib.createBrotliDecompress());
2992
2992
  response = new Response3(body, response_options);
2993
- resolve27(response);
2993
+ resolve28(response);
2994
2994
  return;
2995
2995
  }
2996
2996
  response = new Response3(body, response_options);
2997
- resolve27(response);
2997
+ resolve28(response);
2998
2998
  });
2999
2999
  writeToStream(req, request);
3000
3000
  });
@@ -6351,14 +6351,14 @@ __export(fileFromPath_exports, {
6351
6351
  fileFromPathSync: () => fileFromPathSync,
6352
6352
  isFile: () => isFile
6353
6353
  });
6354
- function createFileFromPath(path40, { mtimeMs, size }, filenameOrOptions, options = {}) {
6354
+ function createFileFromPath(path41, { mtimeMs, size }, filenameOrOptions, options = {}) {
6355
6355
  let filename;
6356
6356
  if (isPlainObject_default2(filenameOrOptions)) {
6357
6357
  [options, filename] = [filenameOrOptions, void 0];
6358
6358
  } else {
6359
6359
  filename = filenameOrOptions;
6360
6360
  }
6361
- const file = new FileFromPath({ path: path40, size, lastModified: mtimeMs });
6361
+ const file = new FileFromPath({ path: path41, size, lastModified: mtimeMs });
6362
6362
  if (!filename) {
6363
6363
  filename = file.name;
6364
6364
  }
@@ -6367,13 +6367,13 @@ function createFileFromPath(path40, { mtimeMs, size }, filenameOrOptions, option
6367
6367
  lastModified: file.lastModified
6368
6368
  });
6369
6369
  }
6370
- function fileFromPathSync(path40, filenameOrOptions, options = {}) {
6371
- const stats = (0, import_fs5.statSync)(path40);
6372
- return createFileFromPath(path40, stats, filenameOrOptions, options);
6370
+ function fileFromPathSync(path41, filenameOrOptions, options = {}) {
6371
+ const stats = (0, import_fs5.statSync)(path41);
6372
+ return createFileFromPath(path41, stats, filenameOrOptions, options);
6373
6373
  }
6374
- async function fileFromPath2(path40, filenameOrOptions, options) {
6375
- const stats = await import_fs5.promises.stat(path40);
6376
- return createFileFromPath(path40, stats, filenameOrOptions, options);
6374
+ async function fileFromPath2(path41, filenameOrOptions, options) {
6375
+ const stats = await import_fs5.promises.stat(path41);
6376
+ return createFileFromPath(path41, stats, filenameOrOptions, options);
6377
6377
  }
6378
6378
  var import_fs5, import_path5, import_node_domexception, __classPrivateFieldSet5, __classPrivateFieldGet6, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
6379
6379
  var init_fileFromPath = __esm({
@@ -9384,7 +9384,7 @@ var require_compile = __commonJS({
9384
9384
  const schOrFunc = root.refs[ref];
9385
9385
  if (schOrFunc)
9386
9386
  return schOrFunc;
9387
- let _sch = resolve27.call(this, root, ref);
9387
+ let _sch = resolve28.call(this, root, ref);
9388
9388
  if (_sch === void 0) {
9389
9389
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
9390
9390
  const { schemaId } = this.opts;
@@ -9411,7 +9411,7 @@ var require_compile = __commonJS({
9411
9411
  function sameSchemaEnv(s1, s2) {
9412
9412
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
9413
9413
  }
9414
- function resolve27(root, ref) {
9414
+ function resolve28(root, ref) {
9415
9415
  let sch;
9416
9416
  while (typeof (sch = this.refs[ref]) == "string")
9417
9417
  ref = sch;
@@ -9629,8 +9629,8 @@ var require_utils2 = __commonJS({
9629
9629
  }
9630
9630
  return ind;
9631
9631
  }
9632
- function removeDotSegments(path40) {
9633
- let input = path40;
9632
+ function removeDotSegments(path41) {
9633
+ let input = path41;
9634
9634
  const output = [];
9635
9635
  let nextSlash = -1;
9636
9636
  let len = 0;
@@ -9882,8 +9882,8 @@ var require_schemes = __commonJS({
9882
9882
  wsComponent.secure = void 0;
9883
9883
  }
9884
9884
  if (wsComponent.resourceName) {
9885
- const [path40, query] = wsComponent.resourceName.split("?");
9886
- wsComponent.path = path40 && path40 !== "/" ? path40 : void 0;
9885
+ const [path41, query] = wsComponent.resourceName.split("?");
9886
+ wsComponent.path = path41 && path41 !== "/" ? path41 : void 0;
9887
9887
  wsComponent.query = query;
9888
9888
  wsComponent.resourceName = void 0;
9889
9889
  }
@@ -10042,7 +10042,7 @@ var require_fast_uri = __commonJS({
10042
10042
  }
10043
10043
  return uri;
10044
10044
  }
10045
- function resolve27(baseURI, relativeURI, options) {
10045
+ function resolve28(baseURI, relativeURI, options) {
10046
10046
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
10047
10047
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
10048
10048
  schemelessOptions.skipEscape = true;
@@ -10300,7 +10300,7 @@ var require_fast_uri = __commonJS({
10300
10300
  var fastUri = {
10301
10301
  SCHEMES,
10302
10302
  normalize: normalize3,
10303
- resolve: resolve27,
10303
+ resolve: resolve28,
10304
10304
  resolveComponent,
10305
10305
  equal,
10306
10306
  serialize,
@@ -13276,12 +13276,12 @@ var require_dist = __commonJS({
13276
13276
  throw new Error(`Unknown format "${name}"`);
13277
13277
  return f2;
13278
13278
  };
13279
- function addFormats(ajv, list, fs34, exportName) {
13279
+ function addFormats(ajv, list, fs35, exportName) {
13280
13280
  var _a3;
13281
13281
  var _b2;
13282
13282
  (_a3 = (_b2 = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
13283
13283
  for (const f2 of list)
13284
- ajv.addFormat(f2, fs34[f2]);
13284
+ ajv.addFormat(f2, fs35[f2]);
13285
13285
  }
13286
13286
  module2.exports = exports2 = formatsPlugin;
13287
13287
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -14565,11 +14565,11 @@ var parser = new YargsParser({
14565
14565
  resolve: import_path2.resolve,
14566
14566
  // TODO: figure out a way to combine ESM and CJS coverage, such that
14567
14567
  // we can exercise all the lines below:
14568
- require: (path40) => {
14568
+ require: (path41) => {
14569
14569
  if (typeof require !== "undefined") {
14570
- return require(path40);
14571
- } else if (path40.match(/\.json$/)) {
14572
- return JSON.parse((0, import_fs2.readFileSync)(path40, "utf8"));
14570
+ return require(path41);
14571
+ } else if (path41.match(/\.json$/)) {
14572
+ return JSON.parse((0, import_fs2.readFileSync)(path41, "utf8"));
14573
14573
  } else {
14574
14574
  throw Error("only .json config files are supported in ESM");
14575
14575
  }
@@ -17095,12 +17095,12 @@ var YargsInstance = class {
17095
17095
  async getCompletion(args, done) {
17096
17096
  argsert("<array> [function]", [args, done], arguments.length);
17097
17097
  if (!done) {
17098
- return new Promise((resolve27, reject) => {
17098
+ return new Promise((resolve28, reject) => {
17099
17099
  __classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
17100
17100
  if (err)
17101
17101
  reject(err);
17102
17102
  else
17103
- resolve27(completions);
17103
+ resolve28(completions);
17104
17104
  });
17105
17105
  });
17106
17106
  } else {
@@ -19250,8 +19250,8 @@ function getErrorMap() {
19250
19250
 
19251
19251
  // node_modules/zod/v3/helpers/parseUtil.js
19252
19252
  var makeIssue = (params) => {
19253
- const { data, path: path40, errorMaps, issueData } = params;
19254
- const fullPath = [...path40, ...issueData.path || []];
19253
+ const { data, path: path41, errorMaps, issueData } = params;
19254
+ const fullPath = [...path41, ...issueData.path || []];
19255
19255
  const fullIssue = {
19256
19256
  ...issueData,
19257
19257
  path: fullPath
@@ -19367,11 +19367,11 @@ var errorUtil;
19367
19367
 
19368
19368
  // node_modules/zod/v3/types.js
19369
19369
  var ParseInputLazyPath = class {
19370
- constructor(parent, value, path40, key) {
19370
+ constructor(parent, value, path41, key) {
19371
19371
  this._cachedPath = [];
19372
19372
  this.parent = parent;
19373
19373
  this.data = value;
19374
- this._path = path40;
19374
+ this._path = path41;
19375
19375
  this._key = key;
19376
19376
  }
19377
19377
  get path() {
@@ -23371,16 +23371,16 @@ async function handleAgentSkills(options) {
23371
23371
  }
23372
23372
  async function askYesNo(question) {
23373
23373
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
23374
- return new Promise((resolve27) => {
23374
+ return new Promise((resolve28) => {
23375
23375
  process.stderr.write(`${question} `);
23376
23376
  rl.once("line", (answer) => {
23377
23377
  rl.close();
23378
23378
  const trimmed = answer.trim();
23379
23379
  if (trimmed === "") {
23380
- resolve27(true);
23380
+ resolve28(true);
23381
23381
  return;
23382
23382
  }
23383
- resolve27(/^y(es)?$/i.test(trimmed));
23383
+ resolve28(/^y(es)?$/i.test(trimmed));
23384
23384
  });
23385
23385
  });
23386
23386
  }
@@ -23459,7 +23459,7 @@ function handleRemove(argv) {
23459
23459
  }
23460
23460
 
23461
23461
  // src/cli/commands/env.ts
23462
- var path33 = __toESM(require("path"));
23462
+ var path34 = __toESM(require("path"));
23463
23463
 
23464
23464
  // src/core/harness.ts
23465
23465
  var fs19 = __toESM(require("fs"));
@@ -23965,13 +23965,13 @@ var MultipartBody = class {
23965
23965
  // node_modules/@anthropic-ai/sdk/_shims/node-runtime.mjs
23966
23966
  var import_web = require("node:stream/web");
23967
23967
  var fileFromPathWarned = false;
23968
- async function fileFromPath3(path40, ...args) {
23968
+ async function fileFromPath3(path41, ...args) {
23969
23969
  const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
23970
23970
  if (!fileFromPathWarned) {
23971
- console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path40)}) instead`);
23971
+ console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path41)}) instead`);
23972
23972
  fileFromPathWarned = true;
23973
23973
  }
23974
- return await _fileFromPath(path40, ...args);
23974
+ return await _fileFromPath(path41, ...args);
23975
23975
  }
23976
23976
  var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
23977
23977
  var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
@@ -24467,8 +24467,8 @@ async function defaultParseResponse(props) {
24467
24467
  }
24468
24468
  var APIPromise = class _APIPromise extends Promise {
24469
24469
  constructor(responsePromise, parseResponse = defaultParseResponse) {
24470
- super((resolve27) => {
24471
- resolve27(null);
24470
+ super((resolve28) => {
24471
+ resolve28(null);
24472
24472
  });
24473
24473
  this.responsePromise = responsePromise;
24474
24474
  this.parseResponse = parseResponse;
@@ -24568,29 +24568,29 @@ var APIClient = class {
24568
24568
  defaultIdempotencyKey() {
24569
24569
  return `stainless-node-retry-${uuid4()}`;
24570
24570
  }
24571
- get(path40, opts) {
24572
- return this.methodRequest("get", path40, opts);
24571
+ get(path41, opts) {
24572
+ return this.methodRequest("get", path41, opts);
24573
24573
  }
24574
- post(path40, opts) {
24575
- return this.methodRequest("post", path40, opts);
24574
+ post(path41, opts) {
24575
+ return this.methodRequest("post", path41, opts);
24576
24576
  }
24577
- patch(path40, opts) {
24578
- return this.methodRequest("patch", path40, opts);
24577
+ patch(path41, opts) {
24578
+ return this.methodRequest("patch", path41, opts);
24579
24579
  }
24580
- put(path40, opts) {
24581
- return this.methodRequest("put", path40, opts);
24580
+ put(path41, opts) {
24581
+ return this.methodRequest("put", path41, opts);
24582
24582
  }
24583
- delete(path40, opts) {
24584
- return this.methodRequest("delete", path40, opts);
24583
+ delete(path41, opts) {
24584
+ return this.methodRequest("delete", path41, opts);
24585
24585
  }
24586
- methodRequest(method, path40, opts) {
24586
+ methodRequest(method, path41, opts) {
24587
24587
  return this.request(Promise.resolve(opts).then(async (opts2) => {
24588
24588
  const body = opts2 && isBlobLike(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
24589
- return { method, path: path40, ...opts2, body };
24589
+ return { method, path: path41, ...opts2, body };
24590
24590
  }));
24591
24591
  }
24592
- getAPIList(path40, Page, opts) {
24593
- return this.requestAPIList(Page, { method: "get", path: path40, ...opts });
24592
+ getAPIList(path41, Page, opts) {
24593
+ return this.requestAPIList(Page, { method: "get", path: path41, ...opts });
24594
24594
  }
24595
24595
  calculateContentLength(body) {
24596
24596
  if (typeof body === "string") {
@@ -24608,10 +24608,10 @@ var APIClient = class {
24608
24608
  return null;
24609
24609
  }
24610
24610
  buildRequest(options) {
24611
- const { method, path: path40, query, headers = {} } = options;
24611
+ const { method, path: path41, query, headers = {} } = options;
24612
24612
  const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : isMultipartBody(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
24613
24613
  const contentLength = this.calculateContentLength(body);
24614
- const url = this.buildURL(path40, query);
24614
+ const url = this.buildURL(path41, query);
24615
24615
  if ("timeout" in options)
24616
24616
  validatePositiveInteger("timeout", options.timeout);
24617
24617
  const timeout = options.timeout ?? this.timeout;
@@ -24720,8 +24720,8 @@ var APIClient = class {
24720
24720
  const request = this.makeRequest(options, null);
24721
24721
  return new PagePromise(this, request, Page);
24722
24722
  }
24723
- buildURL(path40, query) {
24724
- const url = isAbsoluteURL(path40) ? new URL(path40) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path40.startsWith("/") ? path40.slice(1) : path40));
24723
+ buildURL(path41, query) {
24724
+ const url = isAbsoluteURL(path41) ? new URL(path41) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path41.startsWith("/") ? path41.slice(1) : path41));
24725
24725
  const defaultQuery = this.defaultQuery();
24726
24726
  if (!isEmptyObj(defaultQuery)) {
24727
24727
  query = { ...defaultQuery, ...query };
@@ -25006,7 +25006,7 @@ var startsWithSchemeRegexp = new RegExp("^(?:[a-z]+:)?//", "i");
25006
25006
  var isAbsoluteURL = (url) => {
25007
25007
  return startsWithSchemeRegexp.test(url);
25008
25008
  };
25009
- var sleep = (ms) => new Promise((resolve27) => setTimeout(resolve27, ms));
25009
+ var sleep = (ms) => new Promise((resolve28) => setTimeout(resolve28, ms));
25010
25010
  var validatePositiveInteger = (name, n2) => {
25011
25011
  if (typeof n2 !== "number" || !Number.isInteger(n2)) {
25012
25012
  throw new AnthropicError(`${name} must be an integer`);
@@ -25497,12 +25497,12 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25497
25497
  }
25498
25498
  return this._emit("error", new AnthropicError(String(error3)));
25499
25499
  });
25500
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve27, reject) => {
25501
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve27, "f");
25500
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve28, reject) => {
25501
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve28, "f");
25502
25502
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectConnectedPromise, reject, "f");
25503
25503
  }), "f");
25504
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve27, reject) => {
25505
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve27, "f");
25504
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve28, reject) => {
25505
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve28, "f");
25506
25506
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectEndPromise, reject, "f");
25507
25507
  }), "f");
25508
25508
  __classPrivateFieldGet8(this, _PromptCachingBetaMessageStream_connectedPromise, "f").catch(() => {
@@ -25631,11 +25631,11 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25631
25631
  * const message = await stream.emitted('message') // rejects if the stream errors
25632
25632
  */
25633
25633
  emitted(event) {
25634
- return new Promise((resolve27, reject) => {
25634
+ return new Promise((resolve28, reject) => {
25635
25635
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_catchingPromiseCreated, true, "f");
25636
25636
  if (event !== "error")
25637
25637
  this.once("error", reject);
25638
- this.once(event, resolve27);
25638
+ this.once(event, resolve28);
25639
25639
  });
25640
25640
  }
25641
25641
  async done() {
@@ -25862,7 +25862,7 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25862
25862
  if (done) {
25863
25863
  return { value: void 0, done: true };
25864
25864
  }
25865
- return new Promise((resolve27, reject) => readQueue.push({ resolve: resolve27, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
25865
+ return new Promise((resolve28, reject) => readQueue.push({ resolve: resolve28, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
25866
25866
  }
25867
25867
  const chunk = pushQueue.shift();
25868
25868
  return { value: chunk, done: false };
@@ -26010,12 +26010,12 @@ var MessageStream = class _MessageStream {
26010
26010
  }
26011
26011
  return this._emit("error", new AnthropicError(String(error3)));
26012
26012
  });
26013
- __classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve27, reject) => {
26014
- __classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve27, "f");
26013
+ __classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve28, reject) => {
26014
+ __classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve28, "f");
26015
26015
  __classPrivateFieldSet8(this, _MessageStream_rejectConnectedPromise, reject, "f");
26016
26016
  }), "f");
26017
- __classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve27, reject) => {
26018
- __classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve27, "f");
26017
+ __classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve28, reject) => {
26018
+ __classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve28, "f");
26019
26019
  __classPrivateFieldSet8(this, _MessageStream_rejectEndPromise, reject, "f");
26020
26020
  }), "f");
26021
26021
  __classPrivateFieldGet9(this, _MessageStream_connectedPromise, "f").catch(() => {
@@ -26144,11 +26144,11 @@ var MessageStream = class _MessageStream {
26144
26144
  * const message = await stream.emitted('message') // rejects if the stream errors
26145
26145
  */
26146
26146
  emitted(event) {
26147
- return new Promise((resolve27, reject) => {
26147
+ return new Promise((resolve28, reject) => {
26148
26148
  __classPrivateFieldSet8(this, _MessageStream_catchingPromiseCreated, true, "f");
26149
26149
  if (event !== "error")
26150
26150
  this.once("error", reject);
26151
- this.once(event, resolve27);
26151
+ this.once(event, resolve28);
26152
26152
  });
26153
26153
  }
26154
26154
  async done() {
@@ -26375,7 +26375,7 @@ var MessageStream = class _MessageStream {
26375
26375
  if (done) {
26376
26376
  return { value: void 0, done: true };
26377
26377
  }
26378
- return new Promise((resolve27, reject) => readQueue.push({ resolve: resolve27, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26378
+ return new Promise((resolve28, reject) => readQueue.push({ resolve: resolve28, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26379
26379
  }
26380
26380
  const chunk = pushQueue.shift();
26381
26381
  return { value: chunk, done: false };
@@ -26635,11 +26635,11 @@ async function promptApplyAgentMdProposal(repoPath) {
26635
26635
  }
26636
26636
  async function askYesNo2(question) {
26637
26637
  const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
26638
- return new Promise((resolve27) => {
26638
+ return new Promise((resolve28) => {
26639
26639
  process.stderr.write(`${question} `);
26640
26640
  rl.once("line", (answer) => {
26641
26641
  rl.close();
26642
- resolve27(/^y(es)?$/i.test(answer.trim()));
26642
+ resolve28(/^y(es)?$/i.test(answer.trim()));
26643
26643
  });
26644
26644
  });
26645
26645
  }
@@ -26869,7 +26869,7 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
26869
26869
  }
26870
26870
  async function askUser(question, options) {
26871
26871
  const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
26872
- return new Promise((resolve27) => {
26872
+ return new Promise((resolve28) => {
26873
26873
  process.stderr.write("\n");
26874
26874
  process.stderr.write(`\u2753 ${question}
26875
26875
  `);
@@ -26880,7 +26880,7 @@ async function askUser(question, options) {
26880
26880
  process.stderr.write("> ");
26881
26881
  rl.once("line", (answer) => {
26882
26882
  rl.close();
26883
- resolve27(answer.trim());
26883
+ resolve28(answer.trim());
26884
26884
  });
26885
26885
  });
26886
26886
  }
@@ -27024,7 +27024,7 @@ function runScript(scriptPath, args = [], options = {}) {
27024
27024
  function runScriptCapture(scriptPath, args = [], options = {}) {
27025
27025
  const stream = options.stream ?? false;
27026
27026
  const { stream: _stream, ...spawnOptions } = options;
27027
- return new Promise((resolve27) => {
27027
+ return new Promise((resolve28) => {
27028
27028
  const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
27029
27029
  ...spawnOptions,
27030
27030
  stdio: ["inherit", "pipe", "pipe"]
@@ -27039,13 +27039,13 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
27039
27039
  stderr += d2;
27040
27040
  if (stream) process.stderr.write(d2);
27041
27041
  });
27042
- proc.on("close", (code) => resolve27({ stdout, stderr, code: code ?? 0 }));
27042
+ proc.on("close", (code) => resolve28({ stdout, stderr, code: code ?? 0 }));
27043
27043
  });
27044
27044
  }
27045
27045
  function runShellCommand(command2, options = {}) {
27046
27046
  const stream = options.stream ?? false;
27047
27047
  const { stream: _stream, ...spawnOptions } = options;
27048
- return new Promise((resolve27) => {
27048
+ return new Promise((resolve28) => {
27049
27049
  const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
27050
27050
  ...spawnOptions,
27051
27051
  stdio: ["inherit", "pipe", "pipe"]
@@ -27060,7 +27060,7 @@ function runShellCommand(command2, options = {}) {
27060
27060
  stderr += d2;
27061
27061
  if (stream) process.stderr.write(d2);
27062
27062
  });
27063
- proc.on("close", (code) => resolve27({ stdout, stderr, code: code ?? 0 }));
27063
+ proc.on("close", (code) => resolve28({ stdout, stderr, code: code ?? 0 }));
27064
27064
  });
27065
27065
  }
27066
27066
 
@@ -27849,6 +27849,24 @@ function harnessExists(repoPath) {
27849
27849
  }
27850
27850
 
27851
27851
  // src/harness/stage-templates.ts
27852
+ var STAGE_TEMPLATE_IDS = ["playwright", "rocketsim"];
27853
+ var TemplateManifestFileSchema = external_exports.object({
27854
+ src: external_exports.string().min(1),
27855
+ dest: external_exports.string().min(1),
27856
+ executable: external_exports.boolean().optional(),
27857
+ skipFlag: external_exports.string().optional()
27858
+ });
27859
+ var StageTemplateManifestSchema = external_exports.object({
27860
+ id: external_exports.enum(STAGE_TEMPLATE_IDS),
27861
+ stageId: external_exports.string().min(1),
27862
+ verificationStages: external_exports.array(external_exports.string().min(1)).min(1),
27863
+ stage: external_exports.record(external_exports.unknown()),
27864
+ files: external_exports.array(TemplateManifestFileSchema).min(1),
27865
+ optionalFiles: external_exports.array(TemplateManifestFileSchema).optional(),
27866
+ merge: external_exports.record(external_exports.string()).optional(),
27867
+ nextSteps: external_exports.array(external_exports.string().min(1)).min(1),
27868
+ docsPath: external_exports.string().min(1)
27869
+ });
27852
27870
  function resolveTemplateDir(templateId) {
27853
27871
  const dir = path17.join(resolveTemplatesDir(), "stage-templates", templateId);
27854
27872
  if (!fs18.existsSync(dir)) {
@@ -27858,11 +27876,16 @@ function resolveTemplateDir(templateId) {
27858
27876
  }
27859
27877
  function readTemplateManifest(templateId) {
27860
27878
  const manifestPath = path17.join(resolveTemplateDir(templateId), "template.manifest.json");
27861
- const raw = JSON.parse(fs18.readFileSync(manifestPath, "utf8"));
27862
- if (raw.id !== templateId) {
27863
- throw new Error(`Template manifest id mismatch: expected ${templateId}, got ${raw.id}`);
27879
+ const parsed = StageTemplateManifestSchema.safeParse(
27880
+ JSON.parse(fs18.readFileSync(manifestPath, "utf8"))
27881
+ );
27882
+ if (!parsed.success) {
27883
+ throw new Error(`Invalid template manifest for ${templateId}: ${parsed.error.message}`);
27864
27884
  }
27865
- return raw;
27885
+ if (parsed.data.id !== templateId) {
27886
+ throw new Error(`Template manifest id mismatch: expected ${templateId}, got ${parsed.data.id}`);
27887
+ }
27888
+ return parsed.data;
27866
27889
  }
27867
27890
  function ensureParentDir(filePath) {
27868
27891
  const parent = path17.dirname(filePath);
@@ -27891,7 +27914,7 @@ function copyTemplateFile(templateDir, file, repoPath, force) {
27891
27914
  function mergePackageJson(repoPath, templateDir, fragmentRelPath, warnings) {
27892
27915
  const packagePath = path17.join(repoPath, "package.json");
27893
27916
  if (!fs18.existsSync(packagePath)) {
27894
- throw new Error("No package.json in repo root. Add one before applying the Playwright template.");
27917
+ throw new Error("No package.json in repo root. Add one before applying this stage template.");
27895
27918
  }
27896
27919
  const fragmentPath = path17.join(templateDir, fragmentRelPath);
27897
27920
  if (!fs18.existsSync(fragmentPath)) {
@@ -27924,8 +27947,7 @@ function patchStageRegistry(repoPath, manifest, force) {
27924
27947
  }
27925
27948
  const stages = existing ? registry2.stages.map((s2) => s2.id === stage.id ? stage : s2) : [...registry2.stages, stage];
27926
27949
  const verificationStages = [...registry2.verificationStages ?? []];
27927
- const toAdd = manifest.verificationStages ?? (manifest.verificationStageId ? [manifest.verificationStageId] : [manifest.stageId]);
27928
- for (const id of toAdd) {
27950
+ for (const id of manifest.verificationStages) {
27929
27951
  if (!verificationStages.includes(id)) {
27930
27952
  verificationStages.push(id);
27931
27953
  }
@@ -27934,7 +27956,7 @@ function patchStageRegistry(repoPath, manifest, force) {
27934
27956
  if (verifyIdx >= 0) {
27935
27957
  stages[verifyIdx] = {
27936
27958
  ...stages[verifyIdx],
27937
- description: "Verification pipeline (quick smoke by default; --full adds tests, lint, and browser-e2e when installed)",
27959
+ description: `Verification pipeline (quick smoke by default; --full runs the registry's verificationStages: ${verificationStages.join(", ")})`,
27938
27960
  acceptsArgs: ["--full"]
27939
27961
  };
27940
27962
  }
@@ -28010,21 +28032,22 @@ function applyStageTemplate(repoPath, templateId, options = {}) {
28010
28032
  for (const warning of warnings) {
28011
28033
  warn(` \u26A0 ${warning}`);
28012
28034
  }
28013
- const nextSteps = manifest.nextSteps ?? [
28014
- "npm install",
28015
- "npx playwright install",
28016
- "./.har/launch.sh 1",
28017
- `./.har/stages/${manifest.stageId}.sh 1`,
28018
- "npx playwright show-report .har/artifacts/browser-e2e/playwright-report"
28019
- ];
28020
28035
  return {
28021
28036
  templateId,
28022
28037
  stageId: manifest.stageId,
28023
28038
  filesWritten,
28024
28039
  warnings,
28025
- nextSteps
28040
+ nextSteps: manifest.nextSteps,
28041
+ docsPath: manifest.docsPath
28026
28042
  };
28027
28043
  }
28044
+ function listStageTemplateIds() {
28045
+ const root = path17.join(resolveTemplatesDir(), "stage-templates");
28046
+ if (!fs18.existsSync(root)) return [...STAGE_TEMPLATE_IDS];
28047
+ return fs18.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter(
28048
+ (name) => STAGE_TEMPLATE_IDS.includes(name)
28049
+ );
28050
+ }
28028
28051
 
28029
28052
  // src/utils/validation.ts
28030
28053
  function requireApiKey() {
@@ -28156,9 +28179,111 @@ function addStageTemplate(repoPath, templateId, options = {}) {
28156
28179
  return applyStageTemplate(repoPath, templateId, options);
28157
28180
  }
28158
28181
 
28159
- // src/harness/adaptation-prompt.ts
28182
+ // src/harness/custom-stage.ts
28160
28183
  var fs20 = __toESM(require("fs"));
28161
28184
  var path19 = __toESM(require("path"));
28185
+ var STAGE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
28186
+ function addCustomStage(repoPath, options) {
28187
+ const resolved = path19.resolve(repoPath);
28188
+ if (!harnessExists(resolved)) {
28189
+ throw new Error('No .har/ harness found. Run "har env init" first.');
28190
+ }
28191
+ const id = options.id.trim();
28192
+ if (!STAGE_ID_PATTERN.test(id)) {
28193
+ throw new Error(
28194
+ `Invalid stage id "${id}". Use lowercase letters, digits, dots, dashes (e.g. "unit-tests").`
28195
+ );
28196
+ }
28197
+ const kind2 = options.kind ?? "custom";
28198
+ if (!HarnessStageKindSchema.options.includes(kind2)) {
28199
+ throw new Error(
28200
+ `Invalid stage kind "${kind2}". Available: ${HarnessStageKindSchema.options.join(", ")}`
28201
+ );
28202
+ }
28203
+ if (Boolean(options.command) === Boolean(options.script)) {
28204
+ throw new Error(
28205
+ 'Choose exactly one execution mode: --command "<shell command>" for a one-liner, or --script to scaffold .har/stages/<id>.sh.'
28206
+ );
28207
+ }
28208
+ const registry2 = readStageRegistry(resolved);
28209
+ const existing = registry2.stages.find((s2) => s2.id === id);
28210
+ if (existing && !options.force) {
28211
+ throw new Error(`Stage "${id}" already registered in .har/stages.json. Use --force to replace.`);
28212
+ }
28213
+ const filesWritten = [];
28214
+ const stage = {
28215
+ id,
28216
+ kind: kind2,
28217
+ description: options.description ?? `Custom ${kind2} stage`,
28218
+ requiresAgentId: true,
28219
+ artifacts: []
28220
+ };
28221
+ if (options.command) {
28222
+ stage.command = options.command;
28223
+ } else {
28224
+ const scriptRel = path19.join("stages", `${id}.sh`);
28225
+ const scriptAbs = path19.join(resolved, ".har", scriptRel);
28226
+ if (fs20.existsSync(scriptAbs) && !options.force) {
28227
+ throw new Error(`Stage script already exists: .har/${scriptRel}. Use --force to overwrite.`);
28228
+ }
28229
+ const skeletonPath = path19.join(
28230
+ resolveTemplatesDir(),
28231
+ "stage-templates",
28232
+ "custom-stage-skeleton.sh"
28233
+ );
28234
+ const skeleton = fs20.readFileSync(skeletonPath, "utf8").replace(/__STAGE_ID__/g, id).replace(/__STAGE_KIND__/g, kind2).replace(/__STAGE_DESCRIPTION__/g, stage.description);
28235
+ fs20.mkdirSync(path19.dirname(scriptAbs), { recursive: true });
28236
+ fs20.writeFileSync(scriptAbs, skeleton);
28237
+ fs20.chmodSync(scriptAbs, 493);
28238
+ filesWritten.push(`.har/${scriptRel}`);
28239
+ stage.script = scriptRel;
28240
+ stage.artifacts = [
28241
+ {
28242
+ path: `.har/artifacts/${id}`,
28243
+ kind: "directory",
28244
+ description: `Artifacts for the ${id} stage`
28245
+ }
28246
+ ];
28247
+ }
28248
+ const parsedStage = HarnessStageSchema.parse(stage);
28249
+ const stages = existing ? registry2.stages.map((s2) => s2.id === id ? parsedStage : s2) : [...registry2.stages, parsedStage];
28250
+ const verification = options.verification ?? false;
28251
+ const verificationStages = [...registry2.verificationStages ?? []];
28252
+ if (verification && !verificationStages.includes(id)) {
28253
+ verificationStages.push(id);
28254
+ const verifyIdx = stages.findIndex((s2) => s2.id === "verify");
28255
+ if (verifyIdx >= 0) {
28256
+ stages[verifyIdx] = {
28257
+ ...stages[verifyIdx],
28258
+ description: `Verification pipeline (quick smoke by default; --full runs the registry's verificationStages: ${verificationStages.join(", ")})`
28259
+ };
28260
+ }
28261
+ }
28262
+ writeStageRegistry(resolved, { ...registry2, stages, verificationStages });
28263
+ filesWritten.push(".har/stages.json");
28264
+ const nextSteps = options.command ? [
28265
+ `Try it: ${options.command.replace(/\{agentId\}/g, "1")}`,
28266
+ `Agents run it via the MCP tool har_run_stage (stageId: "${id}")`,
28267
+ ...verification ? ["./.har/verify.sh 1 --full # runs it as part of full verification"] : []
28268
+ ] : [
28269
+ `Edit .har/stages/${id}.sh \u2014 replace the TODO block with the real check`,
28270
+ "./.har/launch.sh 1",
28271
+ `./.har/stages/${id}.sh 1`,
28272
+ ...verification ? ["./.har/verify.sh 1 --full # runs it as part of full verification"] : []
28273
+ ];
28274
+ return {
28275
+ stageId: id,
28276
+ kind: kind2,
28277
+ mode: options.command ? "command" : "script",
28278
+ filesWritten,
28279
+ verification,
28280
+ nextSteps
28281
+ };
28282
+ }
28283
+
28284
+ // src/harness/adaptation-prompt.ts
28285
+ var fs21 = __toESM(require("fs"));
28286
+ var path20 = __toESM(require("path"));
28162
28287
  var ADAPTATION_PROMPT_FILE = "ADAPT-PROMPT.md";
28163
28288
  var PROFILE_HINTS = {
28164
28289
  default: "Web app profile (SaaS/full-stack) \u2014 Docker Compose for shared infra (HARNESS_INFRA_SERVICES), PM2 for the primary application only, git worktree per agent slot by default. Launch provisions toolchain via harness.env (HARNESS_ECOSYSTEM, HARNESS_INSTALL_CMD) and writes paths to .env.agent.<id>. Identify the primary app agents modify; run supporting services shared.",
@@ -28170,7 +28295,7 @@ function loadTemplate(name) {
28170
28295
  if (!filePath) {
28171
28296
  throw new Error(`Adaptation prompt template not found: ${name}. Run npm run build.`);
28172
28297
  }
28173
- return fs20.readFileSync(filePath, "utf8");
28298
+ return fs21.readFileSync(filePath, "utf8");
28174
28299
  }
28175
28300
  function applyProfilePlaceholders(content, profile) {
28176
28301
  return content.replace(/\{\{PROFILE\}\}/g, profile).replace(/\{\{PROFILE_HINT\}\}/g, PROFILE_HINTS[profile]);
@@ -28192,7 +28317,7 @@ function buildMaintainAdaptationPrompt(_repoPath, bundleReport) {
28192
28317
  }
28193
28318
  function writeAdaptationPrompt(repoPath, content) {
28194
28319
  const harnessDir = getHarnessDir(repoPath);
28195
- const filePath = path19.join(harnessDir, ADAPTATION_PROMPT_FILE);
28320
+ const filePath = path20.join(harnessDir, ADAPTATION_PROMPT_FILE);
28196
28321
  writeFileSafe(filePath, content);
28197
28322
  return filePath;
28198
28323
  }
@@ -28211,25 +28336,25 @@ function printAdaptationPrompt(content) {
28211
28336
  }
28212
28337
 
28213
28338
  // src/harness/cursor-rule.ts
28214
- var fs21 = __toESM(require("fs"));
28215
- var path20 = __toESM(require("path"));
28339
+ var fs22 = __toESM(require("fs"));
28340
+ var path21 = __toESM(require("path"));
28216
28341
  var readline4 = __toESM(require("readline"));
28217
28342
  var CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/har-workflow.mdc";
28218
28343
  function getCursorRulePath(repoPath) {
28219
- return path20.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
28344
+ return path21.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
28220
28345
  }
28221
28346
  function isCursorWorkspace(repoPath) {
28222
- return fs21.existsSync(path20.join(repoPath, ".cursor"));
28347
+ return fs22.existsSync(path21.join(repoPath, ".cursor"));
28223
28348
  }
28224
28349
  function cursorRuleExists(repoPath) {
28225
- return fs21.existsSync(getCursorRulePath(repoPath));
28350
+ return fs22.existsSync(getCursorRulePath(repoPath));
28226
28351
  }
28227
28352
  function scaffoldCursorRule(repoPath) {
28228
28353
  const templatePath = resolveTemplateFile("cursor-rule.mdc.template");
28229
28354
  if (!templatePath) {
28230
28355
  throw new Error("Cursor rule template not found (cursor-rule.mdc.template). Run npm run build.");
28231
28356
  }
28232
- const content = fs21.readFileSync(templatePath, "utf8");
28357
+ const content = fs22.readFileSync(templatePath, "utf8");
28233
28358
  writeFileSafe(getCursorRulePath(repoPath), content);
28234
28359
  }
28235
28360
  async function handleCursorRule(options) {
@@ -28267,16 +28392,16 @@ async function promptScaffoldCursorRule(exists) {
28267
28392
  }
28268
28393
  async function askYesNo3(question) {
28269
28394
  const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
28270
- return new Promise((resolve27) => {
28395
+ return new Promise((resolve28) => {
28271
28396
  process.stderr.write(`${question} `);
28272
28397
  rl.once("line", (answer) => {
28273
28398
  rl.close();
28274
28399
  const trimmed = answer.trim();
28275
28400
  if (trimmed === "") {
28276
- resolve27(true);
28401
+ resolve28(true);
28277
28402
  return;
28278
28403
  }
28279
- resolve27(/^y(es)?$/i.test(trimmed));
28404
+ resolve28(/^y(es)?$/i.test(trimmed));
28280
28405
  });
28281
28406
  });
28282
28407
  }
@@ -28355,26 +28480,26 @@ function buildStageResult(input) {
28355
28480
  }
28356
28481
 
28357
28482
  // src/core/local-executor.ts
28358
- var fs23 = __toESM(require("fs"));
28359
- var path22 = __toESM(require("path"));
28483
+ var fs24 = __toESM(require("fs"));
28484
+ var path23 = __toESM(require("path"));
28360
28485
 
28361
28486
  // src/core/slot-registry.ts
28362
- var fs22 = __toESM(require("fs"));
28363
- var path21 = __toESM(require("path"));
28487
+ var fs23 = __toESM(require("fs"));
28488
+ var path22 = __toESM(require("path"));
28364
28489
  function getSlotRegistryDir(repoPath) {
28365
- return path21.join(getHarnessDir(repoPath), "slots");
28490
+ return path22.join(getHarnessDir(repoPath), "slots");
28366
28491
  }
28367
28492
  function getSlotRegistryPath(repoPath, agentId) {
28368
- return path21.join(getSlotRegistryDir(repoPath), `agent-${agentId}.json`);
28493
+ return path22.join(getSlotRegistryDir(repoPath), `agent-${agentId}.json`);
28369
28494
  }
28370
28495
  function isSlotResumable(session) {
28371
28496
  return session?.status === "failed" || session?.status === "starting";
28372
28497
  }
28373
28498
  function readSlotRegistry(repoPath, agentId) {
28374
28499
  const file = getSlotRegistryPath(repoPath, agentId);
28375
- if (!fs22.existsSync(file)) return void 0;
28500
+ if (!fs23.existsSync(file)) return void 0;
28376
28501
  try {
28377
- const raw = JSON.parse(fs22.readFileSync(file, "utf8"));
28502
+ const raw = JSON.parse(fs23.readFileSync(file, "utf8"));
28378
28503
  const result = SlotRegistryEntrySchema.safeParse(raw);
28379
28504
  return result.success ? result.data : void 0;
28380
28505
  } catch {
@@ -28384,7 +28509,7 @@ function readSlotRegistry(repoPath, agentId) {
28384
28509
 
28385
28510
  // src/core/local-executor.ts
28386
28511
  function resolveRepoPath(repoPath) {
28387
- return path22.resolve(repoPath);
28512
+ return path23.resolve(repoPath);
28388
28513
  }
28389
28514
  function buildPreviewUrlsFromPorts(ports, env3) {
28390
28515
  const urls = {};
@@ -28436,14 +28561,14 @@ function substituteAgentId(value, agentId) {
28436
28561
  function resolveStageScriptPath(repoPath, stage) {
28437
28562
  const harnessDir = getHarnessDir(repoPath);
28438
28563
  if (stage.script) {
28439
- return path22.join(harnessDir, stage.script);
28564
+ return path23.join(harnessDir, stage.script);
28440
28565
  }
28441
28566
  if (stage.command) {
28442
28567
  const scriptName = stage.command.split(/\s+/)[0].replace(/^\.\/\.har\//, "").replace(/^\.\//, "");
28443
- return path22.join(harnessDir, scriptName);
28568
+ return path23.join(harnessDir, scriptName);
28444
28569
  }
28445
- const stageScript = path22.join(harnessDir, "stages", `${stage.id}.sh`);
28446
- if (fs23.existsSync(stageScript)) {
28570
+ const stageScript = path23.join(harnessDir, "stages", `${stage.id}.sh`);
28571
+ if (fs24.existsSync(stageScript)) {
28447
28572
  return stageScript;
28448
28573
  }
28449
28574
  throw new Error(
@@ -28458,7 +28583,7 @@ function buildExecutionPlan(repoPath, stage, options) {
28458
28583
  ...harnessEnv,
28459
28584
  ...stage.env ?? {}
28460
28585
  };
28461
- const cwd = stage.cwd ? path22.resolve(resolvedRepo, stage.cwd) : resolvedRepo;
28586
+ const cwd = stage.cwd ? path23.resolve(resolvedRepo, stage.cwd) : resolvedRepo;
28462
28587
  const extraArgs = options.args ?? [];
28463
28588
  const launchFlagArgs = [];
28464
28589
  if (stage.kind === "launch" && options.launchFlags) {
@@ -28535,19 +28660,19 @@ var LocalScriptExecutor = class {
28535
28660
  const repoPath = resolveRepoPath(ctx.repoPath);
28536
28661
  const harnessDir = getHarnessDir(repoPath);
28537
28662
  const artifactsDirName = getArtifactsDir(repoPath);
28538
- const artifactsDir = path22.join(harnessDir, artifactsDirName);
28539
- if (!fs23.existsSync(artifactsDir)) return [];
28663
+ const artifactsDir = path23.join(harnessDir, artifactsDirName);
28664
+ if (!fs24.existsSync(artifactsDir)) return [];
28540
28665
  const entries = [];
28541
28666
  const walk = (dir, prefix) => {
28542
- for (const entry of fs23.readdirSync(dir, { withFileTypes: true })) {
28543
- const full = path22.join(dir, entry.name);
28544
- const relative2 = path22.join(prefix, entry.name);
28667
+ for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
28668
+ const full = path23.join(dir, entry.name);
28669
+ const relative2 = path23.join(prefix, entry.name);
28545
28670
  if (entry.isDirectory()) {
28546
28671
  walk(full, relative2);
28547
28672
  continue;
28548
28673
  }
28549
28674
  if (filter?.stageId && !relative2.includes(filter.stageId)) continue;
28550
- const stat = fs23.statSync(full);
28675
+ const stat = fs24.statSync(full);
28551
28676
  entries.push({
28552
28677
  path: full,
28553
28678
  relativePath: relative2,
@@ -28563,7 +28688,7 @@ var LocalScriptExecutor = class {
28563
28688
  var localScriptExecutor = new LocalScriptExecutor();
28564
28689
 
28565
28690
  // src/core/control-sync.ts
28566
- var path32 = __toESM(require("path"));
28691
+ var path33 = __toESM(require("path"));
28567
28692
 
28568
28693
  // src/core/control-config.ts
28569
28694
  var DEFAULT_CONTROL_API_URL = "http://localhost:3847";
@@ -28575,32 +28700,32 @@ function isControlEnabled() {
28575
28700
  }
28576
28701
 
28577
28702
  // src/core/control-registry.ts
28578
- var fs24 = __toESM(require("fs"));
28703
+ var fs25 = __toESM(require("fs"));
28579
28704
  var os4 = __toESM(require("os"));
28580
- var path23 = __toESM(require("path"));
28705
+ var path24 = __toESM(require("path"));
28581
28706
  function getRegistryPath() {
28582
28707
  if (process.env.HAR_CONTROL_REGISTRY_PATH) {
28583
- return path23.resolve(process.env.HAR_CONTROL_REGISTRY_PATH);
28708
+ return path24.resolve(process.env.HAR_CONTROL_REGISTRY_PATH);
28584
28709
  }
28585
- return path23.join(os4.homedir(), ".har", "repos.json");
28710
+ return path24.join(os4.homedir(), ".har", "repos.json");
28586
28711
  }
28587
28712
  function readRegistry() {
28588
28713
  const registryPath = getRegistryPath();
28589
28714
  try {
28590
- if (!fs24.existsSync(registryPath)) return { repos: [] };
28591
- return JSON.parse(fs24.readFileSync(registryPath, "utf8"));
28715
+ if (!fs25.existsSync(registryPath)) return { repos: [] };
28716
+ return JSON.parse(fs25.readFileSync(registryPath, "utf8"));
28592
28717
  } catch {
28593
28718
  return { repos: [] };
28594
28719
  }
28595
28720
  }
28596
28721
  function writeRegistry(registry2) {
28597
28722
  const registryPath = getRegistryPath();
28598
- fs24.mkdirSync(path23.dirname(registryPath), { recursive: true });
28599
- fs24.writeFileSync(registryPath, JSON.stringify(registry2, null, 2) + "\n");
28723
+ fs25.mkdirSync(path24.dirname(registryPath), { recursive: true });
28724
+ fs25.writeFileSync(registryPath, JSON.stringify(registry2, null, 2) + "\n");
28600
28725
  }
28601
28726
  function recordRepoForControlSync(repoPath) {
28602
28727
  if (process.env.HAR_CONTROL_DISABLED === "true") return;
28603
- const resolved = path23.resolve(repoPath);
28728
+ const resolved = path24.resolve(repoPath);
28604
28729
  if (!readManifest(resolved)) return;
28605
28730
  const registry2 = readRegistry();
28606
28731
  if (registry2.repos.includes(resolved)) return;
@@ -28609,7 +28734,7 @@ function recordRepoForControlSync(repoPath) {
28609
28734
  }
28610
28735
  function listRegisteredRepos() {
28611
28736
  const registry2 = readRegistry();
28612
- const kept = registry2.repos.filter((repoPath) => fs24.existsSync(repoPath) && readManifest(repoPath));
28737
+ const kept = registry2.repos.filter((repoPath) => fs25.existsSync(repoPath) && readManifest(repoPath));
28613
28738
  if (kept.length !== registry2.repos.length) {
28614
28739
  writeRegistry({ repos: kept });
28615
28740
  }
@@ -28617,17 +28742,17 @@ function listRegisteredRepos() {
28617
28742
  }
28618
28743
 
28619
28744
  // src/core/slot-status.ts
28620
- var fs29 = __toESM(require("fs"));
28745
+ var fs30 = __toESM(require("fs"));
28621
28746
  var os7 = __toESM(require("os"));
28622
- var path29 = __toESM(require("path"));
28747
+ var path30 = __toESM(require("path"));
28623
28748
  var import_child_process7 = require("child_process");
28624
28749
 
28625
28750
  // src/core/runs.ts
28626
28751
  var crypto2 = __toESM(require("crypto"));
28627
28752
  var import_child_process2 = require("child_process");
28628
- var fs25 = __toESM(require("fs"));
28753
+ var fs26 = __toESM(require("fs"));
28629
28754
  var os5 = __toESM(require("os"));
28630
- var path24 = __toESM(require("path"));
28755
+ var path25 = __toESM(require("path"));
28631
28756
  var RUNS_DIR = "runs";
28632
28757
  function gitCommonDir(cwd) {
28633
28758
  try {
@@ -28636,7 +28761,7 @@ function gitCommonDir(cwd) {
28636
28761
  encoding: "utf8",
28637
28762
  stdio: ["pipe", "pipe", "ignore"]
28638
28763
  }).trim();
28639
- return out ? path24.resolve(cwd, out) : void 0;
28764
+ return out ? path25.resolve(cwd, out) : void 0;
28640
28765
  } catch {
28641
28766
  return void 0;
28642
28767
  }
@@ -28658,7 +28783,7 @@ function gitPrefix(cwd) {
28658
28783
  }
28659
28784
  }
28660
28785
  function getRunsDir(harnessRoot) {
28661
- return path24.join(getHarnessDir(harnessRoot), RUNS_DIR);
28786
+ return path25.join(getHarnessDir(harnessRoot), RUNS_DIR);
28662
28787
  }
28663
28788
  function formatLocalDate(d2) {
28664
28789
  const y2 = d2.getFullYear();
@@ -28678,24 +28803,24 @@ function buildRunRelativePath(stageId, agentId, startedAt, runId, runsDir) {
28678
28803
  const timePart = formatLocalTime(started);
28679
28804
  const agentPart = agentId !== void 0 ? `_agent-${agentId}` : "";
28680
28805
  let filename = `${timePart}_${stageId}${agentPart}.json`;
28681
- const fullPath = path24.join(runsDir, dateFolder, filename);
28682
- if (fs25.existsSync(fullPath)) {
28806
+ const fullPath = path25.join(runsDir, dateFolder, filename);
28807
+ if (fs26.existsSync(fullPath)) {
28683
28808
  filename = `${timePart}_${stageId}${agentPart}-${runId.slice(0, 8)}.json`;
28684
28809
  }
28685
- return path24.join(dateFolder, filename);
28810
+ return path25.join(dateFolder, filename);
28686
28811
  }
28687
28812
  function resolveRunFilePath(harnessRoot, run2) {
28688
28813
  if (run2.relativePath) {
28689
- return path24.join(getRunsDir(harnessRoot), run2.relativePath);
28814
+ return path25.join(getRunsDir(harnessRoot), run2.relativePath);
28690
28815
  }
28691
- return path24.join(getRunsDir(harnessRoot), `${run2.runId}.json`);
28816
+ return path25.join(getRunsDir(harnessRoot), `${run2.runId}.json`);
28692
28817
  }
28693
28818
  function collectRunFiles(runsDir) {
28694
- if (!fs25.existsSync(runsDir)) return [];
28819
+ if (!fs26.existsSync(runsDir)) return [];
28695
28820
  const files = [];
28696
28821
  const walk = (dir) => {
28697
- for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
28698
- const full = path24.join(dir, entry.name);
28822
+ for (const entry of fs26.readdirSync(dir, { withFileTypes: true })) {
28823
+ const full = path25.join(dir, entry.name);
28699
28824
  if (entry.isDirectory()) {
28700
28825
  walk(full);
28701
28826
  } else if (entry.name.endsWith(".json")) {
@@ -28709,30 +28834,30 @@ function collectRunFiles(runsDir) {
28709
28834
  function resolveAgentWorkDir(harnessRoot, agentId) {
28710
28835
  if (agentId === void 0) return void 0;
28711
28836
  const entry = readSlotRegistry(harnessRoot, agentId);
28712
- if (entry?.workDir && fs25.existsSync(entry.workDir)) return entry.workDir;
28837
+ if (entry?.workDir && fs26.existsSync(entry.workDir)) return entry.workDir;
28713
28838
  const env3 = readHarnessEnv(harnessRoot);
28714
- const projectName = env3.HARNESS_PROJECT_NAME ?? path24.basename(harnessRoot);
28715
- const worktreeDir = path24.join(os5.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
28839
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path25.basename(harnessRoot);
28840
+ const worktreeDir = path25.join(os5.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
28716
28841
  const relPrefix = gitPrefix(harnessRoot);
28717
28842
  const sessionEnvFiles = [];
28718
- const worktreesRoot = path24.join(os5.homedir(), "worktrees");
28719
- if (fs25.existsSync(worktreesRoot)) {
28843
+ const worktreesRoot = path25.join(os5.homedir(), "worktrees");
28844
+ if (fs26.existsSync(worktreesRoot)) {
28720
28845
  const suffix = `-har-agent-${agentId}-`;
28721
- for (const entry2 of fs25.readdirSync(worktreesRoot, { withFileTypes: true })) {
28846
+ for (const entry2 of fs26.readdirSync(worktreesRoot, { withFileTypes: true })) {
28722
28847
  if (!entry2.isDirectory() || !entry2.name.includes(suffix)) continue;
28723
- const sessionDir = path24.join(worktreesRoot, entry2.name);
28848
+ const sessionDir = path25.join(worktreesRoot, entry2.name);
28724
28849
  if (!sameGitCheckout(harnessRoot, sessionDir)) continue;
28725
- sessionEnvFiles.push(path24.join(sessionDir, relPrefix, `.env.agent.${agentId}`));
28850
+ sessionEnvFiles.push(path25.join(sessionDir, relPrefix, `.env.agent.${agentId}`));
28726
28851
  }
28727
28852
  }
28728
28853
  const candidates = [
28729
- path24.join(worktreeDir, `.env.agent.${agentId}`),
28730
- path24.join(harnessRoot, `.env.agent.${agentId}`),
28854
+ path25.join(worktreeDir, `.env.agent.${agentId}`),
28855
+ path25.join(harnessRoot, `.env.agent.${agentId}`),
28731
28856
  ...sessionEnvFiles.sort()
28732
28857
  ];
28733
28858
  for (const envFile of candidates) {
28734
- if (!fs25.existsSync(envFile)) continue;
28735
- const content = fs25.readFileSync(envFile, "utf8");
28859
+ if (!fs26.existsSync(envFile)) continue;
28860
+ const content = fs26.readFileSync(envFile, "utf8");
28736
28861
  const match = content.match(/^REPO_ROOT=(.+)$/m);
28737
28862
  if (match) return match[1].trim();
28738
28863
  }
@@ -28744,11 +28869,11 @@ function createRun(ctx, meta) {
28744
28869
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
28745
28870
  const runId = crypto2.randomUUID();
28746
28871
  const relativePath = buildRunRelativePath(meta.stageId, meta.agentId, startedAt, runId, runsDir);
28747
- const runFilePath = path24.join(runsDir, relativePath);
28748
- fs25.mkdirSync(path24.dirname(runFilePath), { recursive: true });
28872
+ const runFilePath = path25.join(runsDir, relativePath);
28873
+ fs26.mkdirSync(path25.dirname(runFilePath), { recursive: true });
28749
28874
  const run2 = RunRecordSchema.parse({
28750
28875
  runId,
28751
- repoPath: path24.resolve(ctx.repoPath),
28876
+ repoPath: path25.resolve(ctx.repoPath),
28752
28877
  harnessRoot,
28753
28878
  stageId: meta.stageId,
28754
28879
  kind: meta.kind,
@@ -28759,7 +28884,7 @@ function createRun(ctx, meta) {
28759
28884
  relativePath,
28760
28885
  trigger: ctx.trigger ?? "cli"
28761
28886
  });
28762
- fs25.writeFileSync(runFilePath, JSON.stringify(run2, null, 2) + "\n");
28887
+ fs26.writeFileSync(runFilePath, JSON.stringify(run2, null, 2) + "\n");
28763
28888
  return run2;
28764
28889
  }
28765
28890
  function finishRun(repoPath, runId, update) {
@@ -28778,13 +28903,13 @@ function finishRun(repoPath, runId, update) {
28778
28903
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
28779
28904
  });
28780
28905
  const runPath = resolveRunFilePath(harnessRoot, finished);
28781
- fs25.writeFileSync(runPath, JSON.stringify(finished, null, 2) + "\n");
28906
+ fs26.writeFileSync(runPath, JSON.stringify(finished, null, 2) + "\n");
28782
28907
  return finished;
28783
28908
  }
28784
28909
  function findRunRecord(harnessRoot, runId) {
28785
28910
  const runsDir = getRunsDir(harnessRoot);
28786
28911
  for (const filePath of collectRunFiles(runsDir)) {
28787
- const parsed = RunRecordSchema.safeParse(JSON.parse(fs25.readFileSync(filePath, "utf8")));
28912
+ const parsed = RunRecordSchema.safeParse(JSON.parse(fs26.readFileSync(filePath, "utf8")));
28788
28913
  if (parsed.success && parsed.data.runId === runId) {
28789
28914
  return parsed.data;
28790
28915
  }
@@ -28796,10 +28921,10 @@ function getRun(repoPath, runId) {
28796
28921
  }
28797
28922
  function listRuns(repoPath, filter = {}) {
28798
28923
  const runsDir = getRunsDir(resolveHarnessRoot(repoPath));
28799
- if (!fs25.existsSync(runsDir)) return [];
28924
+ if (!fs26.existsSync(runsDir)) return [];
28800
28925
  const runs = [];
28801
28926
  for (const filePath of collectRunFiles(runsDir)) {
28802
- const parsed = RunRecordSchema.safeParse(JSON.parse(fs25.readFileSync(filePath, "utf8")));
28927
+ const parsed = RunRecordSchema.safeParse(JSON.parse(fs26.readFileSync(filePath, "utf8")));
28803
28928
  if (!parsed.success) continue;
28804
28929
  if (filter.stageId && parsed.data.stageId !== filter.stageId) continue;
28805
28930
  runs.push(parsed.data);
@@ -28812,17 +28937,17 @@ function listRuns(repoPath, filter = {}) {
28812
28937
  }
28813
28938
 
28814
28939
  // src/core/slot-preflight.ts
28815
- var path28 = __toESM(require("path"));
28940
+ var path29 = __toESM(require("path"));
28816
28941
 
28817
28942
  // src/core/control-port.ts
28818
28943
  var import_child_process4 = require("child_process");
28819
- var fs27 = __toESM(require("fs"));
28820
- var path26 = __toESM(require("path"));
28944
+ var fs28 = __toESM(require("fs"));
28945
+ var path27 = __toESM(require("path"));
28821
28946
 
28822
28947
  // src/core/slot-ports.ts
28823
28948
  var import_child_process3 = require("child_process");
28824
- var fs26 = __toESM(require("fs"));
28825
- var path25 = __toESM(require("path"));
28949
+ var fs27 = __toESM(require("fs"));
28950
+ var path26 = __toESM(require("path"));
28826
28951
  function portStep(env3) {
28827
28952
  return Number(env3.HARNESS_PORT_STEP ?? 10);
28828
28953
  }
@@ -28878,7 +29003,7 @@ function allocateAppPorts(repoPath, agentId) {
28878
29003
  }
28879
29004
  function harnessUsesPm2(repoPath) {
28880
29005
  const harnessRoot = resolveHarnessRoot(repoPath);
28881
- return fs26.existsSync(path25.join(harnessRoot, ".har", "ecosystem.agent.template.cjs"));
29006
+ return fs27.existsSync(path26.join(harnessRoot, ".har", "ecosystem.agent.template.cjs"));
28882
29007
  }
28883
29008
 
28884
29009
  // src/core/control-port.ts
@@ -28933,13 +29058,13 @@ function controlDefaultPortWarnings(containers, defaultFrontendPort, allocatedFr
28933
29058
  return [formatControlDefaultPortWarning(control.name, defaultFrontendPort, allocatedFrontend)];
28934
29059
  }
28935
29060
  function inspectControlUpReadiness(repoPath) {
28936
- const resolved = path26.resolve(repoPath);
29061
+ const resolved = path27.resolve(repoPath);
28937
29062
  const port = parseControlHostPort();
28938
29063
  const containers = listDockerContainers();
28939
29064
  const control = controlContainerOnPort(containers, port);
28940
29065
  const warnings = [];
28941
- const controlHarnessDir = path26.join(resolved, "control", ".har");
28942
- const harnessSlot1Active = fs27.existsSync(controlHarnessDir) && readSlotRegistry(path26.join(resolved, "control"), 1)?.status === "active";
29066
+ const controlHarnessDir = path27.join(resolved, "control", ".har");
29067
+ const harnessSlot1Active = fs28.existsSync(controlHarnessDir) && readSlotRegistry(path27.join(resolved, "control"), 1)?.status === "active";
28943
29068
  if (control) {
28944
29069
  return {
28945
29070
  warnings,
@@ -28970,9 +29095,9 @@ function inspectControlUpReadiness(repoPath) {
28970
29095
  }
28971
29096
 
28972
29097
  // src/core/slot-launch-guard-occupied.ts
28973
- var fs28 = __toESM(require("fs"));
29098
+ var fs29 = __toESM(require("fs"));
28974
29099
  var os6 = __toESM(require("os"));
28975
- var path27 = __toESM(require("path"));
29100
+ var path28 = __toESM(require("path"));
28976
29101
  var import_child_process5 = require("child_process");
28977
29102
  function runGit(cwd, args) {
28978
29103
  try {
@@ -28991,7 +29116,7 @@ function worktreeDirty(worktreePath) {
28991
29116
  }
28992
29117
  function gitCommonDir2(cwd) {
28993
29118
  const out = runGit(cwd, "rev-parse --git-common-dir");
28994
- return out ? path27.resolve(cwd, out) : void 0;
29119
+ return out ? path28.resolve(cwd, out) : void 0;
28995
29120
  }
28996
29121
  function sameGitCheckout2(a2, b2) {
28997
29122
  const left2 = gitCommonDir2(a2);
@@ -29000,20 +29125,20 @@ function sameGitCheckout2(a2, b2) {
29000
29125
  }
29001
29126
  function discoverWorktree(harnessRoot, agentId, projectName) {
29002
29127
  const session = readSlotRegistry(harnessRoot, agentId);
29003
- if (session?.worktreePath && fs28.existsSync(session.worktreePath)) {
29128
+ if (session?.worktreePath && fs29.existsSync(session.worktreePath)) {
29004
29129
  return session.worktreePath;
29005
29130
  }
29006
- const legacy = path27.join(os6.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
29007
- if (fs28.existsSync(legacy) && sameGitCheckout2(harnessRoot, legacy)) return legacy;
29008
- const worktreesRoot = path27.join(os6.homedir(), "worktrees");
29009
- if (!fs28.existsSync(worktreesRoot)) return void 0;
29131
+ const legacy = path28.join(os6.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
29132
+ if (fs29.existsSync(legacy) && sameGitCheckout2(harnessRoot, legacy)) return legacy;
29133
+ const worktreesRoot = path28.join(os6.homedir(), "worktrees");
29134
+ if (!fs29.existsSync(worktreesRoot)) return void 0;
29010
29135
  const suffix = `-har-agent-${agentId}-`;
29011
29136
  const relPrefix = runGit(harnessRoot, "rev-parse --show-prefix") ?? "";
29012
- for (const name of fs28.readdirSync(worktreesRoot)) {
29137
+ for (const name of fs29.readdirSync(worktreesRoot)) {
29013
29138
  if (!name.includes(suffix)) continue;
29014
- const candidate = path27.join(worktreesRoot, name);
29139
+ const candidate = path28.join(worktreesRoot, name);
29015
29140
  if (!sameGitCheckout2(harnessRoot, candidate)) continue;
29016
- if (!fs28.existsSync(path27.join(candidate, relPrefix, `.env.agent.${agentId}`))) continue;
29141
+ if (!fs29.existsSync(path28.join(candidate, relPrefix, `.env.agent.${agentId}`))) continue;
29017
29142
  return candidate;
29018
29143
  }
29019
29144
  return void 0;
@@ -29021,13 +29146,13 @@ function discoverWorktree(harnessRoot, agentId, projectName) {
29021
29146
  function collectOccupiedSlot(repoPath, agentId) {
29022
29147
  const harnessRoot = resolveHarnessRoot(repoPath);
29023
29148
  const env3 = readHarnessEnv(harnessRoot);
29024
- const projectName = env3.HARNESS_PROJECT_NAME ?? path27.basename(harnessRoot);
29149
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path28.basename(harnessRoot);
29025
29150
  const session = readSlotRegistry(harnessRoot, agentId);
29026
29151
  const worktreePath = discoverWorktree(harnessRoot, agentId, projectName);
29027
29152
  const workDir = session?.workDir;
29028
- const envInWorkDir = workDir ? fs28.existsSync(path27.join(workDir, `.env.agent.${agentId}`)) : false;
29029
- const envInRoot = fs28.existsSync(path27.join(harnessRoot, `.env.agent.${agentId}`));
29030
- const envInWorktree = worktreePath ? fs28.existsSync(path27.join(worktreePath, `.env.agent.${agentId}`)) : false;
29153
+ const envInWorkDir = workDir ? fs29.existsSync(path28.join(workDir, `.env.agent.${agentId}`)) : false;
29154
+ const envInRoot = fs29.existsSync(path28.join(harnessRoot, `.env.agent.${agentId}`));
29155
+ const envInWorktree = worktreePath ? fs29.existsSync(path28.join(worktreePath, `.env.agent.${agentId}`)) : false;
29031
29156
  const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
29032
29157
  if (!active) return void 0;
29033
29158
  const dirty = worktreePath ? worktreeDirty(worktreePath) : void 0;
@@ -29180,7 +29305,7 @@ function checkInfraPort(env3, varName, defaultPort, scanStart, scanEnd) {
29180
29305
  function inspectSlotReadiness(repoPath, agentId, options = {}) {
29181
29306
  const harnessRoot = resolveHarnessRoot(repoPath);
29182
29307
  const env3 = readHarnessEnv(harnessRoot);
29183
- const projectName = env3.HARNESS_PROJECT_NAME ?? path28.basename(harnessRoot);
29308
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path29.basename(harnessRoot);
29184
29309
  const usesPm2 = harnessUsesPm2(repoPath);
29185
29310
  const blockers = [];
29186
29311
  const remediations = [];
@@ -29382,7 +29507,7 @@ function readWorktreeBranch(worktreePath) {
29382
29507
  }
29383
29508
  function gitCommonDir3(cwd) {
29384
29509
  const out = runGit2(cwd, "rev-parse --git-common-dir");
29385
- return out ? path29.resolve(cwd, out) : void 0;
29510
+ return out ? path30.resolve(cwd, out) : void 0;
29386
29511
  }
29387
29512
  function sameGitCheckout3(a2, b2) {
29388
29513
  const left2 = gitCommonDir3(a2);
@@ -29394,12 +29519,12 @@ function gitPrefix2(cwd) {
29394
29519
  return out ?? "";
29395
29520
  }
29396
29521
  function discoverSessionWorktreePath(harnessRoot, agentId) {
29397
- const worktreesRoot = path29.join(os7.homedir(), "worktrees");
29398
- if (!fs29.existsSync(worktreesRoot)) return void 0;
29522
+ const worktreesRoot = path30.join(os7.homedir(), "worktrees");
29523
+ if (!fs30.existsSync(worktreesRoot)) return void 0;
29399
29524
  const suffix = `-har-agent-${agentId}-`;
29400
29525
  const relPrefix = gitPrefix2(harnessRoot);
29401
- const matches = fs29.readdirSync(worktreesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.includes(suffix)).map((entry) => path29.join(worktreesRoot, entry.name)).filter(
29402
- (candidate) => sameGitCheckout3(harnessRoot, candidate) && fs29.existsSync(path29.join(candidate, relPrefix, `.env.agent.${agentId}`))
29526
+ const matches = fs30.readdirSync(worktreesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.includes(suffix)).map((entry) => path30.join(worktreesRoot, entry.name)).filter(
29527
+ (candidate) => sameGitCheckout3(harnessRoot, candidate) && fs30.existsSync(path30.join(candidate, relPrefix, `.env.agent.${agentId}`))
29403
29528
  );
29404
29529
  return matches.sort()[0];
29405
29530
  }
@@ -29474,18 +29599,18 @@ function detectPm2Issue(projectName, agentId, session, procs) {
29474
29599
  }
29475
29600
  function collectSlotStatus(harnessRoot, agentId, runs, pm2Procs) {
29476
29601
  const env3 = readHarnessEnv(harnessRoot);
29477
- const projectName = env3.HARNESS_PROJECT_NAME ?? path29.basename(harnessRoot);
29602
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path30.basename(harnessRoot);
29478
29603
  const session = readSlotRegistry(harnessRoot, agentId);
29479
- const legacyWorktreePath = path29.join(
29604
+ const legacyWorktreePath = path30.join(
29480
29605
  os7.homedir(),
29481
29606
  "worktrees",
29482
29607
  `${projectName}-agent-${agentId}`
29483
29608
  );
29484
- const worktreePath = session?.worktreePath && fs29.existsSync(session.worktreePath) ? session.worktreePath : fs29.existsSync(legacyWorktreePath) ? legacyWorktreePath : discoverSessionWorktreePath(harnessRoot, agentId);
29609
+ const worktreePath = session?.worktreePath && fs30.existsSync(session.worktreePath) ? session.worktreePath : fs30.existsSync(legacyWorktreePath) ? legacyWorktreePath : discoverSessionWorktreePath(harnessRoot, agentId);
29485
29610
  const workDir = resolveAgentWorkDir(harnessRoot, agentId);
29486
- const envInWorkDir = workDir ? fs29.existsSync(path29.join(workDir, `.env.agent.${agentId}`)) : false;
29487
- const envInRoot = fs29.existsSync(path29.join(harnessRoot, `.env.agent.${agentId}`));
29488
- const envInWorktree = worktreePath ? fs29.existsSync(path29.join(worktreePath, `.env.agent.${agentId}`)) : false;
29611
+ const envInWorkDir = workDir ? fs30.existsSync(path30.join(workDir, `.env.agent.${agentId}`)) : false;
29612
+ const envInRoot = fs30.existsSync(path30.join(harnessRoot, `.env.agent.${agentId}`));
29613
+ const envInWorktree = worktreePath ? fs30.existsSync(path30.join(worktreePath, `.env.agent.${agentId}`)) : false;
29489
29614
  const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
29490
29615
  const latest = latestRunForAgent(runs, agentId);
29491
29616
  const verifyRun = runs.find((r2) => r2.agentId === agentId && r2.stageId === "verify");
@@ -29538,7 +29663,7 @@ function collectEnvironmentStatus(repoPath) {
29538
29663
  const slotIds = getAgentSlotIds(harnessRoot);
29539
29664
  const pm2Procs = listPm2Processes2();
29540
29665
  return {
29541
- repoPath: path29.resolve(repoPath),
29666
+ repoPath: path30.resolve(repoPath),
29542
29667
  harnessRoot,
29543
29668
  gitRemote: readGitRemote(harnessRoot),
29544
29669
  profile: manifest?.profile,
@@ -29549,13 +29674,13 @@ function collectEnvironmentStatus(repoPath) {
29549
29674
 
29550
29675
  // src/core/validations.ts
29551
29676
  var crypto3 = __toESM(require("crypto"));
29552
- var fs31 = __toESM(require("fs"));
29553
- var path31 = __toESM(require("path"));
29677
+ var fs32 = __toESM(require("fs"));
29678
+ var path32 = __toESM(require("path"));
29554
29679
 
29555
29680
  // src/core/change-batch.ts
29556
- var fs30 = __toESM(require("fs"));
29681
+ var fs31 = __toESM(require("fs"));
29557
29682
  var os8 = __toESM(require("os"));
29558
- var path30 = __toESM(require("path"));
29683
+ var path31 = __toESM(require("path"));
29559
29684
  function git(checkoutDir, args, env3) {
29560
29685
  const result = run(`git ${args}`, { cwd: checkoutDir, env: env3 });
29561
29686
  if (result.code !== 0) {
@@ -29571,7 +29696,7 @@ function isCheckoutRoot(checkoutDir) {
29571
29696
  const toplevel = tryGit(checkoutDir, "rev-parse --show-toplevel");
29572
29697
  if (!toplevel) return false;
29573
29698
  try {
29574
- return fs30.realpathSync(toplevel) === fs30.realpathSync(checkoutDir);
29699
+ return fs31.realpathSync(toplevel) === fs31.realpathSync(checkoutDir);
29575
29700
  } catch {
29576
29701
  return false;
29577
29702
  }
@@ -29593,7 +29718,7 @@ function isMergeOrRebaseInProgress(checkoutDir) {
29593
29718
  if (tryGit(checkoutDir, "rev-parse -q --verify MERGE_HEAD") !== void 0) return true;
29594
29719
  for (const dir of ["rebase-merge", "rebase-apply"]) {
29595
29720
  const gitPath = tryGit(checkoutDir, `rev-parse --git-path ${dir}`);
29596
- if (gitPath && fs30.existsSync(path30.resolve(checkoutDir, gitPath))) return true;
29721
+ if (gitPath && fs31.existsSync(path31.resolve(checkoutDir, gitPath))) return true;
29597
29722
  }
29598
29723
  return false;
29599
29724
  }
@@ -29616,8 +29741,8 @@ function parseNameStatus(output) {
29616
29741
  return files;
29617
29742
  }
29618
29743
  function computeWorktreeSnapshot(checkoutDir) {
29619
- const tmpDir = fs30.mkdtempSync(path30.join(os8.tmpdir(), "har-idx-"));
29620
- const tmpIndex = path30.join(tmpDir, "index");
29744
+ const tmpDir = fs31.mkdtempSync(path31.join(os8.tmpdir(), "har-idx-"));
29745
+ const tmpIndex = path31.join(tmpDir, "index");
29621
29746
  const env3 = { GIT_INDEX_FILE: tmpIndex };
29622
29747
  try {
29623
29748
  const headTree = getHeadTree(checkoutDir);
@@ -29640,35 +29765,35 @@ function computeWorktreeSnapshot(checkoutDir) {
29640
29765
  changedFiles: parseNameStatus(diffOutput)
29641
29766
  };
29642
29767
  } finally {
29643
- fs30.rmSync(tmpDir, { recursive: true, force: true });
29768
+ fs31.rmSync(tmpDir, { recursive: true, force: true });
29644
29769
  }
29645
29770
  }
29646
29771
 
29647
29772
  // src/core/validations.ts
29648
29773
  var VALIDATIONS_DIR = "validations";
29649
29774
  function getValidationsDir(checkoutDir) {
29650
- return path31.join(checkoutDir, ".har", VALIDATIONS_DIR);
29775
+ return path32.join(checkoutDir, ".har", VALIDATIONS_DIR);
29651
29776
  }
29652
29777
  function validationPath(checkoutDir, treeHash) {
29653
- return path31.join(getValidationsDir(checkoutDir), `${treeHash}.json`);
29778
+ return path32.join(getValidationsDir(checkoutDir), `${treeHash}.json`);
29654
29779
  }
29655
29780
  function writeRecord(checkoutDir, record2) {
29656
29781
  const dir = getValidationsDir(checkoutDir);
29657
- fs31.mkdirSync(dir, { recursive: true });
29658
- fs31.writeFileSync(
29782
+ fs32.mkdirSync(dir, { recursive: true });
29783
+ fs32.writeFileSync(
29659
29784
  validationPath(checkoutDir, record2.treeHash),
29660
29785
  `${JSON.stringify(record2, null, 2)}
29661
29786
  `
29662
29787
  );
29663
29788
  }
29664
29789
  function ensureValidationsIgnored(checkoutDir) {
29665
- const harDir = path31.join(checkoutDir, ".har");
29666
- if (!fs31.existsSync(harDir)) return;
29667
- const gitignorePath = path31.join(harDir, ".gitignore");
29668
- const content = fs31.existsSync(gitignorePath) ? fs31.readFileSync(gitignorePath, "utf8") : "";
29790
+ const harDir = path32.join(checkoutDir, ".har");
29791
+ if (!fs32.existsSync(harDir)) return;
29792
+ const gitignorePath = path32.join(harDir, ".gitignore");
29793
+ const content = fs32.existsSync(gitignorePath) ? fs32.readFileSync(gitignorePath, "utf8") : "";
29669
29794
  if (content.split("\n").some((line) => line.trim() === `${VALIDATIONS_DIR}/`)) return;
29670
29795
  const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
29671
- fs31.writeFileSync(gitignorePath, `${content}${suffix}${VALIDATIONS_DIR}/
29796
+ fs32.writeFileSync(gitignorePath, `${content}${suffix}${VALIDATIONS_DIR}/
29672
29797
  `);
29673
29798
  }
29674
29799
  function recordValidation(input) {
@@ -29685,8 +29810,8 @@ function recordValidation(input) {
29685
29810
  treeHash: snapshot.treeHash,
29686
29811
  headSha: snapshot.headSha,
29687
29812
  branch: snapshot.branch,
29688
- workDir: path31.resolve(input.checkoutDir),
29689
- harnessRoot: path31.resolve(input.harnessRoot),
29813
+ workDir: path32.resolve(input.checkoutDir),
29814
+ harnessRoot: path32.resolve(input.harnessRoot),
29690
29815
  agentId: input.agentId,
29691
29816
  status: input.status,
29692
29817
  full: input.full,
@@ -29698,16 +29823,16 @@ function recordValidation(input) {
29698
29823
  committedAt: existing?.committedAt
29699
29824
  });
29700
29825
  writeRecord(input.checkoutDir, record2);
29701
- if (path31.resolve(input.harnessRoot) !== path31.resolve(input.checkoutDir)) {
29826
+ if (path32.resolve(input.harnessRoot) !== path32.resolve(input.checkoutDir)) {
29702
29827
  writeRecord(input.harnessRoot, record2);
29703
29828
  }
29704
29829
  return record2;
29705
29830
  }
29706
29831
  function findValidation(checkoutDir, treeHash) {
29707
29832
  const file = validationPath(checkoutDir, treeHash);
29708
- if (!fs31.existsSync(file)) return void 0;
29833
+ if (!fs32.existsSync(file)) return void 0;
29709
29834
  try {
29710
- return ValidationRecordSchema.parse(JSON.parse(fs31.readFileSync(file, "utf8")));
29835
+ return ValidationRecordSchema.parse(JSON.parse(fs32.readFileSync(file, "utf8")));
29711
29836
  } catch {
29712
29837
  return void 0;
29713
29838
  }
@@ -29722,19 +29847,19 @@ function attachCommit(checkoutDir, treeHash, commitSha) {
29722
29847
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
29723
29848
  };
29724
29849
  writeRecord(checkoutDir, updated);
29725
- if (record2.harnessRoot && path31.resolve(record2.harnessRoot) !== path31.resolve(checkoutDir) && fs31.existsSync(path31.join(record2.harnessRoot, ".har"))) {
29850
+ if (record2.harnessRoot && path32.resolve(record2.harnessRoot) !== path32.resolve(checkoutDir) && fs32.existsSync(path32.join(record2.harnessRoot, ".har"))) {
29726
29851
  writeRecord(record2.harnessRoot, updated);
29727
29852
  }
29728
29853
  return updated;
29729
29854
  }
29730
29855
  function listValidations(harnessRoot) {
29731
29856
  const dir = getValidationsDir(harnessRoot);
29732
- if (!fs31.existsSync(dir)) return [];
29857
+ if (!fs32.existsSync(dir)) return [];
29733
29858
  const records = [];
29734
- for (const entry of fs31.readdirSync(dir)) {
29859
+ for (const entry of fs32.readdirSync(dir)) {
29735
29860
  if (!entry.endsWith(".json")) continue;
29736
29861
  try {
29737
- records.push(ValidationRecordSchema.parse(JSON.parse(fs31.readFileSync(path31.join(dir, entry), "utf8"))));
29862
+ records.push(ValidationRecordSchema.parse(JSON.parse(fs32.readFileSync(path32.join(dir, entry), "utf8"))));
29738
29863
  } catch {
29739
29864
  }
29740
29865
  }
@@ -29792,7 +29917,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
29792
29917
  const deadline = Date.now() + timeoutMs;
29793
29918
  while (Date.now() < deadline) {
29794
29919
  if (await isControlApiReachable(apiUrl)) return true;
29795
- await new Promise((resolve27) => setTimeout(resolve27, intervalMs));
29920
+ await new Promise((resolve28) => setTimeout(resolve28, intervalMs));
29796
29921
  }
29797
29922
  return false;
29798
29923
  }
@@ -29804,7 +29929,7 @@ async function syncAllKnownReposWithControl(options) {
29804
29929
  }
29805
29930
  const repoPaths = new Set(listRegisteredRepos());
29806
29931
  if (options?.cwd) {
29807
- const cwd = path32.resolve(options.cwd);
29932
+ const cwd = path33.resolve(options.cwd);
29808
29933
  if (readManifest(cwd)) repoPaths.add(cwd);
29809
29934
  }
29810
29935
  try {
@@ -29812,7 +29937,7 @@ async function syncAllKnownReposWithControl(options) {
29812
29937
  if (listResponse.ok) {
29813
29938
  const repos = await listResponse.json();
29814
29939
  for (const repo of repos) {
29815
- const resolved = path32.resolve(repo.path);
29940
+ const resolved = path33.resolve(repo.path);
29816
29941
  if (readManifest(resolved)) repoPaths.add(resolved);
29817
29942
  }
29818
29943
  }
@@ -29831,7 +29956,7 @@ async function syncAllKnownReposWithControl(options) {
29831
29956
  return { synced, failed };
29832
29957
  }
29833
29958
  async function registerRepoWithControl(options) {
29834
- const repoPath = path32.resolve(options.repoPath);
29959
+ const repoPath = path33.resolve(options.repoPath);
29835
29960
  const apiUrl = options.apiUrl ?? getControlApiUrl();
29836
29961
  const manifest = readManifest(repoPath);
29837
29962
  const stagesRegistry = readStageRegistry(repoPath);
@@ -29844,7 +29969,7 @@ async function registerRepoWithControl(options) {
29844
29969
  return postJson(`${apiUrl}/api/repos`, body, options.dryRun);
29845
29970
  }
29846
29971
  async function syncRepoWithControl(options) {
29847
- const repoPath = path32.resolve(options.repoPath);
29972
+ const repoPath = path33.resolve(options.repoPath);
29848
29973
  const apiUrl = options.apiUrl ?? getControlApiUrl();
29849
29974
  if (options.cloud) {
29850
29975
  const remote = createRemoteExecutor();
@@ -29872,7 +29997,7 @@ async function syncRepoWithControl(options) {
29872
29997
  const listResponse = await fetch(`${apiUrl}/api/repos`);
29873
29998
  if (!listResponse.ok) throw new Error("Failed to list repos from Control API");
29874
29999
  const repos = await listResponse.json();
29875
- const existing = repos.find((r2) => path32.resolve(r2.path) === repoPath);
30000
+ const existing = repos.find((r2) => path33.resolve(r2.path) === repoPath);
29876
30001
  if (!existing) throw new Error(`Repo not registered: ${repoPath}`);
29877
30002
  await syncRepoRunsAndSlots(apiUrl, existing.id, repoPath, options.dryRun);
29878
30003
  return;
@@ -30320,11 +30445,36 @@ var envCommand = {
30320
30445
  }),
30321
30446
  handleMaintain
30322
30447
  ).command(
30323
- "add-stage <template>",
30324
- "Add an optional stage template (e.g. playwright)",
30448
+ "add-stage [template]",
30449
+ `Add a stage template (${STAGE_TEMPLATE_IDS.join(", ")}) or a custom stage (--custom)`,
30325
30450
  (y2) => y2.positional("template", {
30326
30451
  type: "string",
30327
- describe: "Stage template id (playwright)"
30452
+ describe: `Stage template id (${STAGE_TEMPLATE_IDS.join(", ")}), or the new stage id with --custom`
30453
+ }).option("list", {
30454
+ type: "boolean",
30455
+ default: false,
30456
+ describe: "List available stage templates and exit"
30457
+ }).option("custom", {
30458
+ type: "boolean",
30459
+ default: false,
30460
+ describe: "Register a custom stage instead of a shipped template"
30461
+ }).option("kind", {
30462
+ type: "string",
30463
+ describe: "Custom stage kind (setup, launch, verify, test, inspect, reset, teardown, custom)"
30464
+ }).option("command", {
30465
+ type: "string",
30466
+ describe: 'Custom stage shell command ({agentId} is substituted), e.g. "npm test"'
30467
+ }).option("script", {
30468
+ type: "boolean",
30469
+ default: false,
30470
+ describe: "Scaffold .har/stages/<id>.sh from the contract skeleton (see .har/STAGES.md)"
30471
+ }).option("description", {
30472
+ type: "string",
30473
+ describe: "Custom stage description shown in the registry and Mission Control"
30474
+ }).option("verification", {
30475
+ type: "boolean",
30476
+ default: false,
30477
+ describe: "Include the custom stage in verify --full (stages.json verificationStages)"
30328
30478
  }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("force", {
30329
30479
  type: "boolean",
30330
30480
  default: false,
@@ -30332,7 +30482,7 @@ var envCommand = {
30332
30482
  }).option("skip-ci", {
30333
30483
  type: "boolean",
30334
30484
  default: false,
30335
- describe: "Do not copy .github/workflows/playwright.yml"
30485
+ describe: "Do not copy optional CI workflow files (e.g. .github/workflows/playwright.yml)"
30336
30486
  }),
30337
30487
  handleAddStage
30338
30488
  ).command(
@@ -30427,7 +30577,7 @@ var envCommand = {
30427
30577
  }
30428
30578
  };
30429
30579
  async function handleInit(argv) {
30430
- const repoPath = path33.resolve(argv.repo);
30580
+ const repoPath = path34.resolve(argv.repo);
30431
30581
  header("har env init");
30432
30582
  info(`Repository: ${repoPath}`);
30433
30583
  try {
@@ -30486,7 +30636,7 @@ async function handleInit(argv) {
30486
30636
  }
30487
30637
  }
30488
30638
  async function handleMaintain(argv) {
30489
- const repoPath = path33.resolve(argv.repo);
30639
+ const repoPath = path34.resolve(argv.repo);
30490
30640
  header("har env maintain");
30491
30641
  info(`Repository: ${repoPath}`);
30492
30642
  try {
@@ -30582,7 +30732,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
30582
30732
  if (autoYes) {
30583
30733
  const proposal = readAgentMdProposal(repoPath);
30584
30734
  if (proposal) {
30585
- writeFileSafe(path33.join(repoPath, "AGENT.md"), proposal.content);
30735
+ writeFileSafe(path34.join(repoPath, "AGENT.md"), proposal.content);
30586
30736
  clearAgentMdProposal(repoPath);
30587
30737
  info("Applied AGENT.md proposal (--yes)");
30588
30738
  }
@@ -30591,16 +30741,64 @@ async function handleAgentMdProposal(repoPath, autoYes) {
30591
30741
  await promptApplyAgentMdProposal(repoPath);
30592
30742
  }
30593
30743
  async function handleAddStage(argv) {
30594
- const repoPath = path33.resolve(argv.repo);
30595
- if (argv.template !== "playwright") {
30596
- error(`Unknown stage template: ${argv.template ?? "(missing)"}. Available: playwright`);
30744
+ const repoPath = path34.resolve(argv.repo);
30745
+ const available = listStageTemplateIds();
30746
+ if (argv.list) {
30747
+ for (const id of available) {
30748
+ console.log(id);
30749
+ }
30750
+ return;
30751
+ }
30752
+ if (argv.custom) {
30753
+ if (!argv.template) {
30754
+ error(
30755
+ 'Missing stage id. Usage: har env add-stage <id> --custom (--command "npm test" | --script) [--kind test] [--verification]'
30756
+ );
30757
+ process.exit(1);
30758
+ }
30759
+ header("har env add-stage --custom");
30760
+ info(`Repository: ${repoPath}`);
30761
+ info(`Stage: ${argv.template}`);
30762
+ try {
30763
+ const result = addCustomStage(repoPath, {
30764
+ id: argv.template,
30765
+ kind: argv.kind,
30766
+ command: argv.command,
30767
+ script: argv.script,
30768
+ description: argv.description,
30769
+ verification: argv.verification,
30770
+ force: argv.force
30771
+ });
30772
+ divider();
30773
+ success(`Custom stage registered: ${result.stageId} (kind: ${result.kind}, ${result.mode})`);
30774
+ for (const file of result.filesWritten) {
30775
+ info(` + ${file}`);
30776
+ }
30777
+ console.error("");
30778
+ console.error(" Next steps:");
30779
+ for (const step of result.nextSteps) {
30780
+ console.error(` ${step}`);
30781
+ }
30782
+ console.error("");
30783
+ console.error(" Docs: .har/STAGES.md");
30784
+ console.error("");
30785
+ } catch (err) {
30786
+ error(err.message);
30787
+ process.exit(1);
30788
+ }
30789
+ return;
30790
+ }
30791
+ if (!argv.template || !available.includes(argv.template)) {
30792
+ error(
30793
+ `Unknown stage template: ${argv.template ?? "(missing)"}. Available: ${available.join(", ")}. For a project-specific stage, use: har env add-stage <id> --custom`
30794
+ );
30597
30795
  process.exit(1);
30598
30796
  }
30599
30797
  header("har env add-stage");
30600
30798
  info(`Repository: ${repoPath}`);
30601
30799
  info(`Template: ${argv.template}`);
30602
30800
  try {
30603
- const result = addStageTemplate(repoPath, "playwright", {
30801
+ const result = addStageTemplate(repoPath, argv.template, {
30604
30802
  force: argv.force,
30605
30803
  skipCi: argv.skipCi
30606
30804
  });
@@ -30612,7 +30810,7 @@ async function handleAddStage(argv) {
30612
30810
  console.error(` ${step}`);
30613
30811
  }
30614
30812
  console.error("");
30615
- console.error(" Docs: .har/stages/PLAYWRIGHT.md");
30813
+ console.error(` Docs: ${result.docsPath}`);
30616
30814
  console.error("");
30617
30815
  } catch (err) {
30618
30816
  error(err.message);
@@ -30620,7 +30818,7 @@ async function handleAddStage(argv) {
30620
30818
  }
30621
30819
  }
30622
30820
  async function handlePreflight(argv) {
30623
- const repo = path33.resolve(argv.repo);
30821
+ const repo = path34.resolve(argv.repo);
30624
30822
  const agentId = validateAgentId(argv.id, repo);
30625
30823
  const result = await preflightEnvironment({
30626
30824
  repoPath: repo,
@@ -30638,7 +30836,7 @@ async function handlePreflight(argv) {
30638
30836
  process.exit(result.code);
30639
30837
  }
30640
30838
  async function handleLaunch(argv) {
30641
- const repo = path33.resolve(argv.repo);
30839
+ const repo = path34.resolve(argv.repo);
30642
30840
  const agentId = validateAgentId(argv.id, repo);
30643
30841
  let confirmReplace = argv.replace;
30644
30842
  let force = argv.force;
@@ -30650,8 +30848,8 @@ async function handleLaunch(argv) {
30650
30848
  console.error(guard.reason ?? "");
30651
30849
  const readline5 = await import("readline");
30652
30850
  const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
30653
- const answer = await new Promise((resolve27) => {
30654
- rl.question("Replace this slot? [y/N] ", resolve27);
30851
+ const answer = await new Promise((resolve28) => {
30852
+ rl.question("Replace this slot? [y/N] ", resolve28);
30655
30853
  });
30656
30854
  rl.close();
30657
30855
  if (!/^[Yy]$/.test(answer.trim())) {
@@ -30667,8 +30865,8 @@ async function handleLaunch(argv) {
30667
30865
  warn("Worktree has uncommitted changes.");
30668
30866
  const readline5 = await import("readline");
30669
30867
  const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
30670
- const answer = await new Promise((resolve27) => {
30671
- rl.question("Discard uncommitted changes? [y/N] ", resolve27);
30868
+ const answer = await new Promise((resolve28) => {
30869
+ rl.question("Discard uncommitted changes? [y/N] ", resolve28);
30672
30870
  });
30673
30871
  rl.close();
30674
30872
  if (!/^[Yy]$/.test(answer.trim())) {
@@ -30715,7 +30913,7 @@ async function handleRecover(argv) {
30715
30913
  });
30716
30914
  }
30717
30915
  async function handleVerify(argv) {
30718
- const repo = path33.resolve(argv.repo);
30916
+ const repo = path34.resolve(argv.repo);
30719
30917
  const agentId = validateAgentId(argv.id, repo);
30720
30918
  const result = await runVerification({
30721
30919
  repoPath: repo,
@@ -30727,7 +30925,7 @@ async function handleVerify(argv) {
30727
30925
  process.exit(result.code);
30728
30926
  }
30729
30927
  async function handleTeardown(argv) {
30730
- const repo = path33.resolve(argv.repo);
30928
+ const repo = path34.resolve(argv.repo);
30731
30929
  const agentId = validateAgentId(argv.id, repo);
30732
30930
  const result = await teardownEnvironment({
30733
30931
  repoPath: repo,
@@ -30738,7 +30936,7 @@ async function handleTeardown(argv) {
30738
30936
  process.exit(result.code);
30739
30937
  }
30740
30938
  async function handleComplete(argv) {
30741
- const repo = path33.resolve(argv.repo);
30939
+ const repo = path34.resolve(argv.repo);
30742
30940
  const agentId = validateAgentId(argv.id, repo);
30743
30941
  const result = await completeEnvironment({
30744
30942
  repoPath: repo,
@@ -30759,7 +30957,7 @@ async function handleComplete(argv) {
30759
30957
  process.exit(result.code);
30760
30958
  }
30761
30959
  async function handleStatus(argv) {
30762
- const repoPath = path33.resolve(argv.repo);
30960
+ const repoPath = path34.resolve(argv.repo);
30763
30961
  if (argv.json) {
30764
30962
  const status = collectEnvironmentStatus(repoPath);
30765
30963
  const output = EnvironmentStatusSchema.parse(status);
@@ -30772,7 +30970,7 @@ async function handleStatus(argv) {
30772
30970
  });
30773
30971
  }
30774
30972
  async function handleRunsList(argv) {
30775
- const repoPath = path33.resolve(argv.repo);
30973
+ const repoPath = path34.resolve(argv.repo);
30776
30974
  const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
30777
30975
  if (argv.json) {
30778
30976
  process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
@@ -30789,7 +30987,7 @@ async function handleRunsList(argv) {
30789
30987
  }
30790
30988
  }
30791
30989
  async function handleRunsGet(argv) {
30792
- const run2 = getRun(path33.resolve(argv.repo), argv.runId);
30990
+ const run2 = getRun(path34.resolve(argv.repo), argv.runId);
30793
30991
  if (!run2) {
30794
30992
  error(`Run not found: ${argv.runId}`);
30795
30993
  process.exit(1);
@@ -30862,11 +31060,11 @@ function printNextSteps(auto2) {
30862
31060
  }
30863
31061
 
30864
31062
  // src/cli/commands/control.ts
30865
- var path35 = __toESM(require("path"));
31063
+ var path36 = __toESM(require("path"));
30866
31064
 
30867
31065
  // src/core/control-lifecycle.ts
30868
31066
  var import_child_process8 = require("child_process");
30869
- var path34 = __toESM(require("path"));
31067
+ var path35 = __toESM(require("path"));
30870
31068
 
30871
31069
  // src/core/control-image.ts
30872
31070
  var DEFAULT_CONTROL_IMAGE = "theosfactory/har-control";
@@ -30885,13 +31083,13 @@ function shouldBuildControlLocally() {
30885
31083
 
30886
31084
  // src/core/control-lifecycle.ts
30887
31085
  function resolveControlDir() {
30888
- return path34.resolve(__dirname, "..", "control");
31086
+ return path35.resolve(__dirname, "..", "control");
30889
31087
  }
30890
31088
  function resolveControlComposeFiles(options) {
30891
31089
  const controlDir = resolveControlDir();
30892
- const files = [path34.join(controlDir, "docker-compose.yml")];
31090
+ const files = [path35.join(controlDir, "docker-compose.yml")];
30893
31091
  if (options?.build ?? shouldBuildControlLocally()) {
30894
- files.push(path34.join(controlDir, "docker-compose.build.yml"));
31092
+ files.push(path35.join(controlDir, "docker-compose.build.yml"));
30895
31093
  }
30896
31094
  return files;
30897
31095
  }
@@ -31060,7 +31258,7 @@ async function handleDown() {
31060
31258
  process.exit(code);
31061
31259
  }
31062
31260
  async function handleRegister(argv) {
31063
- const repoPath = path35.resolve(argv.repo);
31261
+ const repoPath = path36.resolve(argv.repo);
31064
31262
  header("har control register");
31065
31263
  info(`Repository: ${repoPath}`);
31066
31264
  try {
@@ -31080,7 +31278,7 @@ async function handleRegister(argv) {
31080
31278
  }
31081
31279
  }
31082
31280
  async function handleSync(argv) {
31083
- const repoPath = path35.resolve(argv.repo);
31281
+ const repoPath = path36.resolve(argv.repo);
31084
31282
  try {
31085
31283
  await syncRepoWithControl({
31086
31284
  repoPath,
@@ -31119,7 +31317,7 @@ async function handleWatch(argv) {
31119
31317
  };
31120
31318
  const tick = async () => {
31121
31319
  if (argv.repo) {
31122
- await syncOne(path35.resolve(argv.repo));
31320
+ await syncOne(path36.resolve(argv.repo));
31123
31321
  return;
31124
31322
  }
31125
31323
  try {
@@ -31150,12 +31348,12 @@ async function handleLogin(argv) {
31150
31348
  }
31151
31349
 
31152
31350
  // src/cli/commands/hooks.ts
31153
- var path38 = __toESM(require("path"));
31351
+ var path39 = __toESM(require("path"));
31154
31352
 
31155
31353
  // src/core/hooks.ts
31156
- var fs32 = __toESM(require("fs"));
31354
+ var fs33 = __toESM(require("fs"));
31157
31355
  var os9 = __toESM(require("os"));
31158
- var path36 = __toESM(require("path"));
31356
+ var path37 = __toESM(require("path"));
31159
31357
  var MARKER_START = "# >>> har commit gate (managed by `har hooks`) >>>";
31160
31358
  var MARKER_END = "# <<< har commit gate <<<";
31161
31359
  var PRE_COMMIT_BLOCK = [
@@ -31180,7 +31378,7 @@ function resolveCheckoutRoot(cwd) {
31180
31378
  function resolveHooksDir(checkoutDir) {
31181
31379
  const hooksPath = tryGit2(checkoutDir, "rev-parse --git-path hooks");
31182
31380
  if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
31183
- return path36.resolve(checkoutDir, hooksPath);
31381
+ return path37.resolve(checkoutDir, hooksPath);
31184
31382
  }
31185
31383
  function getConfiguredHooksPath(checkoutDir) {
31186
31384
  return tryGit2(checkoutDir, "config core.hooksPath") || void 0;
@@ -31188,9 +31386,9 @@ function getConfiguredHooksPath(checkoutDir) {
31188
31386
  function defaultHarInvocation() {
31189
31387
  const entry = process.argv[1];
31190
31388
  if (entry && entry.endsWith(".js")) {
31191
- return `"${process.execPath}" "${path36.resolve(entry)}"`;
31389
+ return `"${process.execPath}" "${path37.resolve(entry)}"`;
31192
31390
  }
31193
- if (entry) return `"${path36.resolve(entry)}"`;
31391
+ if (entry) return `"${path37.resolve(entry)}"`;
31194
31392
  return "har";
31195
31393
  }
31196
31394
  function buildHookScript(harInvocation, subcommand, failOpenNotice) {
@@ -31211,40 +31409,40 @@ exit 0
31211
31409
  `;
31212
31410
  }
31213
31411
  function upsertMarkedBlock(filePath, block) {
31214
- if (!fs32.existsSync(filePath)) {
31215
- fs32.writeFileSync(filePath, `#!/bin/sh
31412
+ if (!fs33.existsSync(filePath)) {
31413
+ fs33.writeFileSync(filePath, `#!/bin/sh
31216
31414
  ${block}
31217
31415
  `, { mode: 493 });
31218
31416
  return "created";
31219
31417
  }
31220
- const content = fs32.readFileSync(filePath, "utf8");
31418
+ const content = fs33.readFileSync(filePath, "utf8");
31221
31419
  if (content.includes(MARKER_START)) {
31222
31420
  const pattern = new RegExp(
31223
31421
  `${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}`
31224
31422
  );
31225
- fs32.writeFileSync(filePath, content.replace(pattern, block));
31226
- fs32.chmodSync(filePath, 493);
31423
+ fs33.writeFileSync(filePath, content.replace(pattern, block));
31424
+ fs33.chmodSync(filePath, 493);
31227
31425
  return "updated";
31228
31426
  }
31229
31427
  const suffix = content.endsWith("\n") ? "" : "\n";
31230
- fs32.writeFileSync(filePath, `${content}${suffix}
31428
+ fs33.writeFileSync(filePath, `${content}${suffix}
31231
31429
  ${block}
31232
31430
  `);
31233
- fs32.chmodSync(filePath, 493);
31431
+ fs33.chmodSync(filePath, 493);
31234
31432
  return "appended";
31235
31433
  }
31236
31434
  function removeMarkedBlock(filePath) {
31237
- if (!fs32.existsSync(filePath)) return false;
31238
- const content = fs32.readFileSync(filePath, "utf8");
31435
+ if (!fs33.existsSync(filePath)) return false;
31436
+ const content = fs33.readFileSync(filePath, "utf8");
31239
31437
  if (!content.includes(MARKER_START)) return false;
31240
31438
  const pattern = new RegExp(
31241
31439
  `\\n?${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}\\n?`
31242
31440
  );
31243
31441
  const stripped = content.replace(pattern, "\n");
31244
31442
  if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
31245
- fs32.rmSync(filePath);
31443
+ fs33.rmSync(filePath);
31246
31444
  } else {
31247
- fs32.writeFileSync(filePath, stripped);
31445
+ fs33.writeFileSync(filePath, stripped);
31248
31446
  }
31249
31447
  return true;
31250
31448
  }
@@ -31266,20 +31464,20 @@ Or re-run with --force to write into that directory anyway.`
31266
31464
  );
31267
31465
  }
31268
31466
  const hooksDir = resolveHooksDir(checkout);
31269
- fs32.mkdirSync(hooksDir, { recursive: true });
31467
+ fs33.mkdirSync(hooksDir, { recursive: true });
31270
31468
  const invocation = options.harInvocation ?? defaultHarInvocation();
31271
- fs32.writeFileSync(
31272
- path36.join(hooksDir, "har-pre-commit"),
31469
+ fs33.writeFileSync(
31470
+ path37.join(hooksDir, "har-pre-commit"),
31273
31471
  buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
31274
31472
  { mode: 493 }
31275
31473
  );
31276
- fs32.writeFileSync(
31277
- path36.join(hooksDir, "har-post-commit"),
31474
+ fs33.writeFileSync(
31475
+ path37.join(hooksDir, "har-post-commit"),
31278
31476
  buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
31279
31477
  { mode: 493 }
31280
31478
  );
31281
- const preCommit = upsertMarkedBlock(path36.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
31282
- const postCommit = upsertMarkedBlock(path36.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
31479
+ const preCommit = upsertMarkedBlock(path37.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
31480
+ const postCommit = upsertMarkedBlock(path37.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
31283
31481
  ensureValidationsIgnored(checkout);
31284
31482
  return { hooksDir, preCommit, postCommit };
31285
31483
  }
@@ -31288,12 +31486,12 @@ function uninstallHooks(repoPath) {
31288
31486
  if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
31289
31487
  const hooksDir = resolveHooksDir(checkout);
31290
31488
  let removed = false;
31291
- removed = removeMarkedBlock(path36.join(hooksDir, "pre-commit")) || removed;
31292
- removed = removeMarkedBlock(path36.join(hooksDir, "post-commit")) || removed;
31489
+ removed = removeMarkedBlock(path37.join(hooksDir, "pre-commit")) || removed;
31490
+ removed = removeMarkedBlock(path37.join(hooksDir, "post-commit")) || removed;
31293
31491
  for (const file of ["har-pre-commit", "har-post-commit"]) {
31294
- const full = path36.join(hooksDir, file);
31295
- if (fs32.existsSync(full)) {
31296
- fs32.rmSync(full);
31492
+ const full = path37.join(hooksDir, file);
31493
+ if (fs33.existsSync(full)) {
31494
+ fs33.rmSync(full);
31297
31495
  removed = true;
31298
31496
  }
31299
31497
  }
@@ -31310,9 +31508,9 @@ function getCommitGateConfig(checkoutDir) {
31310
31508
  function isAgentWorktree(checkoutDir) {
31311
31509
  const branch = getCurrentBranch(checkoutDir);
31312
31510
  if (branch && /^har-agent-\d+$/.test(branch)) return true;
31313
- const worktreesRoot = path36.join(os9.homedir(), "worktrees");
31314
- const resolved = path36.resolve(checkoutDir);
31315
- return resolved.startsWith(`${worktreesRoot}${path36.sep}`) && /-agent-\d+$/.test(path36.basename(resolved));
31511
+ const worktreesRoot = path37.join(os9.homedir(), "worktrees");
31512
+ const resolved = path37.resolve(checkoutDir);
31513
+ return resolved.startsWith(`${worktreesRoot}${path37.sep}`) && /-agent-\d+$/.test(path37.basename(resolved));
31316
31514
  }
31317
31515
  function resolveEffectiveMode(checkoutDir, gate) {
31318
31516
  if (!gate.enabled) return "off";
@@ -31325,15 +31523,15 @@ function getHooksStatus(repoPath) {
31325
31523
  const hooksDir = resolveHooksDir(checkout);
31326
31524
  const gate = getCommitGateConfig(checkout);
31327
31525
  const hasBlock = (name) => {
31328
- const file = path36.join(hooksDir, name);
31329
- return fs32.existsSync(file) && fs32.readFileSync(file, "utf8").includes(MARKER_START);
31526
+ const file = path37.join(hooksDir, name);
31527
+ return fs33.existsSync(file) && fs33.readFileSync(file, "utf8").includes(MARKER_START);
31330
31528
  };
31331
31529
  return {
31332
31530
  checkout,
31333
31531
  hooksDir,
31334
31532
  configuredHooksPath: getConfiguredHooksPath(checkout),
31335
- preCommitInstalled: hasBlock("pre-commit") && fs32.existsSync(path36.join(hooksDir, "har-pre-commit")),
31336
- postCommitInstalled: hasBlock("post-commit") && fs32.existsSync(path36.join(hooksDir, "har-post-commit")),
31533
+ preCommitInstalled: hasBlock("pre-commit") && fs33.existsSync(path37.join(hooksDir, "har-pre-commit")),
31534
+ postCommitInstalled: hasBlock("post-commit") && fs33.existsSync(path37.join(hooksDir, "har-post-commit")),
31337
31535
  gate,
31338
31536
  effectiveMode: resolveEffectiveMode(checkout, gate)
31339
31537
  };
@@ -31344,7 +31542,7 @@ function checkCommitGate(cwd) {
31344
31542
  }
31345
31543
  const checkout = resolveCheckoutRoot(cwd);
31346
31544
  if (!checkout) return { exitCode: 0, messages: [] };
31347
- if (!fs32.existsSync(path36.join(checkout, ".har", "stages.json"))) {
31545
+ if (!fs33.existsSync(path37.join(checkout, ".har", "stages.json"))) {
31348
31546
  return { exitCode: 0, messages: [] };
31349
31547
  }
31350
31548
  const gate = getCommitGateConfig(checkout);
@@ -31415,8 +31613,8 @@ async function recordCommitAssociation(cwd) {
31415
31613
  }
31416
31614
 
31417
31615
  // src/core/claude-hooks.ts
31418
- var fs33 = __toESM(require("fs"));
31419
- var path37 = __toESM(require("path"));
31616
+ var fs34 = __toESM(require("fs"));
31617
+ var path38 = __toESM(require("path"));
31420
31618
  var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
31421
31619
  var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
31422
31620
  var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
@@ -31427,8 +31625,8 @@ function isHarGuardEntry(entry) {
31427
31625
  );
31428
31626
  }
31429
31627
  function readSettings(settingsPath) {
31430
- if (!fs33.existsSync(settingsPath)) return {};
31431
- const raw = fs33.readFileSync(settingsPath, "utf8").trim();
31628
+ if (!fs34.existsSync(settingsPath)) return {};
31629
+ const raw = fs34.readFileSync(settingsPath, "utf8").trim();
31432
31630
  if (raw === "") return {};
31433
31631
  const parsed = JSON.parse(raw);
31434
31632
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -31437,19 +31635,19 @@ function readSettings(settingsPath) {
31437
31635
  return parsed;
31438
31636
  }
31439
31637
  function writeSettings(settingsPath, settings) {
31440
- fs33.mkdirSync(path37.dirname(settingsPath), { recursive: true });
31441
- fs33.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
31638
+ fs34.mkdirSync(path38.dirname(settingsPath), { recursive: true });
31639
+ fs34.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
31442
31640
  }
31443
31641
  function installClaudeGuard(repoPath) {
31444
31642
  const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
31445
31643
  if (!templatePath) {
31446
31644
  throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
31447
31645
  }
31448
- const guardScript = path37.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
31449
- fs33.mkdirSync(path37.dirname(guardScript), { recursive: true });
31450
- fs33.copyFileSync(templatePath, guardScript);
31451
- fs33.chmodSync(guardScript, 493);
31452
- const settingsPath = path37.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31646
+ const guardScript = path38.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
31647
+ fs34.mkdirSync(path38.dirname(guardScript), { recursive: true });
31648
+ fs34.copyFileSync(templatePath, guardScript);
31649
+ fs34.chmodSync(guardScript, 493);
31650
+ const settingsPath = path38.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31453
31651
  const settings = readSettings(settingsPath);
31454
31652
  const hooks = settings.hooks ?? {};
31455
31653
  const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
@@ -31463,15 +31661,15 @@ function installClaudeGuard(repoPath) {
31463
31661
  }
31464
31662
  function uninstallClaudeGuard(repoPath) {
31465
31663
  let removed = false;
31466
- const guardScript = path37.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
31467
- if (fs33.existsSync(guardScript)) {
31468
- fs33.rmSync(guardScript);
31664
+ const guardScript = path38.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
31665
+ if (fs34.existsSync(guardScript)) {
31666
+ fs34.rmSync(guardScript);
31469
31667
  removed = true;
31470
- const parent = path37.dirname(guardScript);
31471
- if (fs33.existsSync(parent) && fs33.readdirSync(parent).length === 0) fs33.rmdirSync(parent);
31668
+ const parent = path38.dirname(guardScript);
31669
+ if (fs34.existsSync(parent) && fs34.readdirSync(parent).length === 0) fs34.rmdirSync(parent);
31472
31670
  }
31473
- const settingsPath = path37.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31474
- if (fs33.existsSync(settingsPath)) {
31671
+ const settingsPath = path38.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31672
+ if (fs34.existsSync(settingsPath)) {
31475
31673
  const settings = readSettings(settingsPath);
31476
31674
  const preToolUse = settings.hooks?.PreToolUse;
31477
31675
  if (preToolUse) {
@@ -31496,8 +31694,8 @@ function uninstallClaudeGuard(repoPath) {
31496
31694
  return { removed };
31497
31695
  }
31498
31696
  function claudeGuardInstalled(repoPath) {
31499
- const settingsPath = path37.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31500
- if (!fs33.existsSync(settingsPath)) return false;
31697
+ const settingsPath = path38.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31698
+ if (!fs34.existsSync(settingsPath)) return false;
31501
31699
  try {
31502
31700
  const settings = readSettings(settingsPath);
31503
31701
  return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
@@ -31513,7 +31711,7 @@ function repoOption(y2) {
31513
31711
  function handleInstall2(argv) {
31514
31712
  try {
31515
31713
  if (argv.claude) {
31516
- const result2 = installClaudeGuard(path38.resolve(argv.repo));
31714
+ const result2 = installClaudeGuard(path39.resolve(argv.repo));
31517
31715
  success("Claude Code worktree guard installed.");
31518
31716
  info(`Guard script: ${result2.guardScript}`);
31519
31717
  info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
@@ -31521,7 +31719,7 @@ function handleInstall2(argv) {
31521
31719
  info("Commit both files so the guard travels with the repo.");
31522
31720
  return;
31523
31721
  }
31524
- const result = installHooks({ repoPath: path38.resolve(argv.repo), force: argv.force });
31722
+ const result = installHooks({ repoPath: path39.resolve(argv.repo), force: argv.force });
31525
31723
  success(`Commit gate installed in ${result.hooksDir}`);
31526
31724
  info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
31527
31725
  info("Commits of unverified change batches will be blocked in agent worktrees.");
@@ -31534,7 +31732,7 @@ function handleInstall2(argv) {
31534
31732
  function handleUninstall(argv) {
31535
31733
  try {
31536
31734
  if (argv.claude) {
31537
- const result2 = uninstallClaudeGuard(path38.resolve(argv.repo));
31735
+ const result2 = uninstallClaudeGuard(path39.resolve(argv.repo));
31538
31736
  if (result2.removed) {
31539
31737
  success("Claude Code worktree guard removed.");
31540
31738
  } else {
@@ -31542,7 +31740,7 @@ function handleUninstall(argv) {
31542
31740
  }
31543
31741
  return;
31544
31742
  }
31545
- const result = uninstallHooks(path38.resolve(argv.repo));
31743
+ const result = uninstallHooks(path39.resolve(argv.repo));
31546
31744
  if (result.removed) {
31547
31745
  success(`Commit gate removed from ${result.hooksDir}`);
31548
31746
  } else {
@@ -31555,7 +31753,7 @@ function handleUninstall(argv) {
31555
31753
  }
31556
31754
  function handleStatus2(argv) {
31557
31755
  try {
31558
- const status = getHooksStatus(path38.resolve(argv.repo));
31756
+ const status = getHooksStatus(path39.resolve(argv.repo));
31559
31757
  if (argv.json) {
31560
31758
  process.stdout.write(`${JSON.stringify(status, null, 2)}
31561
31759
  `);
@@ -31573,7 +31771,7 @@ function handleStatus2(argv) {
31573
31771
  );
31574
31772
  info(`Effective: ${status.effectiveMode} (in this checkout)`);
31575
31773
  info(
31576
- `Claude guard: ${claudeGuardInstalled(path38.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
31774
+ `Claude guard: ${claudeGuardInstalled(path39.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
31577
31775
  );
31578
31776
  } catch (err) {
31579
31777
  error(err instanceof Error ? err.message : String(err));
@@ -31834,10 +32032,10 @@ function assignProp(target, prop, value) {
31834
32032
  configurable: true
31835
32033
  });
31836
32034
  }
31837
- function getElementAtPath(obj, path40) {
31838
- if (!path40)
32035
+ function getElementAtPath(obj, path41) {
32036
+ if (!path41)
31839
32037
  return obj;
31840
- return path40.reduce((acc, key) => acc?.[key], obj);
32038
+ return path41.reduce((acc, key) => acc?.[key], obj);
31841
32039
  }
31842
32040
  function promiseAllObject(promisesObj) {
31843
32041
  const keys = Object.keys(promisesObj);
@@ -32157,11 +32355,11 @@ function aborted(x2, startIndex = 0) {
32157
32355
  }
32158
32356
  return false;
32159
32357
  }
32160
- function prefixIssues(path40, issues) {
32358
+ function prefixIssues(path41, issues) {
32161
32359
  return issues.map((iss) => {
32162
32360
  var _a3;
32163
32361
  (_a3 = iss).path ?? (_a3.path = []);
32164
- iss.path.unshift(path40);
32362
+ iss.path.unshift(path41);
32165
32363
  return iss;
32166
32364
  });
32167
32365
  }
@@ -37484,7 +37682,7 @@ var Protocol = class {
37484
37682
  return;
37485
37683
  }
37486
37684
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
37487
- await new Promise((resolve27) => setTimeout(resolve27, pollInterval));
37685
+ await new Promise((resolve28) => setTimeout(resolve28, pollInterval));
37488
37686
  options?.signal?.throwIfAborted();
37489
37687
  }
37490
37688
  } catch (error3) {
@@ -37501,7 +37699,7 @@ var Protocol = class {
37501
37699
  */
37502
37700
  request(request, resultSchema, options) {
37503
37701
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
37504
- return new Promise((resolve27, reject) => {
37702
+ return new Promise((resolve28, reject) => {
37505
37703
  const earlyReject = (error3) => {
37506
37704
  reject(error3);
37507
37705
  };
@@ -37579,7 +37777,7 @@ var Protocol = class {
37579
37777
  if (!parseResult.success) {
37580
37778
  reject(parseResult.error);
37581
37779
  } else {
37582
- resolve27(parseResult.data);
37780
+ resolve28(parseResult.data);
37583
37781
  }
37584
37782
  } catch (error3) {
37585
37783
  reject(error3);
@@ -37840,12 +38038,12 @@ var Protocol = class {
37840
38038
  }
37841
38039
  } catch {
37842
38040
  }
37843
- return new Promise((resolve27, reject) => {
38041
+ return new Promise((resolve28, reject) => {
37844
38042
  if (signal.aborted) {
37845
38043
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
37846
38044
  return;
37847
38045
  }
37848
- const timeoutId = setTimeout(resolve27, interval);
38046
+ const timeoutId = setTimeout(resolve28, interval);
37849
38047
  signal.addEventListener("abort", () => {
37850
38048
  clearTimeout(timeoutId);
37851
38049
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -38635,7 +38833,7 @@ var Server = class extends Protocol {
38635
38833
  };
38636
38834
 
38637
38835
  // src/mcp/server.ts
38638
- var path39 = __toESM(require("path"));
38836
+ var path40 = __toESM(require("path"));
38639
38837
 
38640
38838
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
38641
38839
  var import_node_process2 = __toESM(require("node:process"), 1);
@@ -38718,12 +38916,12 @@ var StdioServerTransport = class {
38718
38916
  this.onclose?.();
38719
38917
  }
38720
38918
  send(message) {
38721
- return new Promise((resolve27) => {
38919
+ return new Promise((resolve28) => {
38722
38920
  const json = serializeMessage(message);
38723
38921
  if (this._stdout.write(json)) {
38724
- resolve27();
38922
+ resolve28();
38725
38923
  } else {
38726
- this._stdout.once("drain", resolve27);
38924
+ this._stdout.once("drain", resolve28);
38727
38925
  }
38728
38926
  });
38729
38927
  }
@@ -39267,7 +39465,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
39267
39465
  const input = ControlUpInputSchema.parse({ ...args, repo });
39268
39466
  const result = await startControlAndSync({
39269
39467
  detach: input.detach,
39270
- cwd: path39.resolve(input.repo)
39468
+ cwd: path40.resolve(input.repo)
39271
39469
  });
39272
39470
  if (result.code !== 0) {
39273
39471
  return {