@osfactory/har 0.8.0 → 0.10.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 path37 = url.path;
769
- if (path37.length === 0) {
768
+ const path40 = url.path;
769
+ if (path40.length === 0) {
770
770
  return;
771
771
  }
772
- if (url.scheme === "file" && path37.length === 1 && isNormalizedWindowsDriveLetter(path37[0])) {
772
+ if (url.scheme === "file" && path40.length === 1 && isNormalizedWindowsDriveLetter(path40[0])) {
773
773
  return;
774
774
  }
775
- path37.pop();
775
+ path40.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(resolve26, reject) {
2095
+ return new Body.Promise(function(resolve27, 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
- resolve26(Buffer.concat(accum, accumBytes));
2129
+ resolve27(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(resolve26, reject) {
2804
+ return new fetch3.Promise(function(resolve27, 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
- resolve26(fetch3(new Request3(locationURL, requestOpts)));
2937
+ resolve27(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
- resolve26(response);
2958
+ resolve27(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
- resolve26(response);
2968
+ resolve27(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
- resolve26(response);
2980
+ resolve27(response);
2981
2981
  });
2982
2982
  raw.on("end", function() {
2983
2983
  if (!response) {
2984
2984
  response = new Response3(body, response_options);
2985
- resolve26(response);
2985
+ resolve27(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
- resolve26(response);
2993
+ resolve27(response);
2994
2994
  return;
2995
2995
  }
2996
2996
  response = new Response3(body, response_options);
2997
- resolve26(response);
2997
+ resolve27(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(path37, { mtimeMs, size }, filenameOrOptions, options = {}) {
6354
+ function createFileFromPath(path40, { 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: path37, size, lastModified: mtimeMs });
6361
+ const file = new FileFromPath({ path: path40, size, lastModified: mtimeMs });
6362
6362
  if (!filename) {
6363
6363
  filename = file.name;
6364
6364
  }
@@ -6367,13 +6367,13 @@ function createFileFromPath(path37, { mtimeMs, size }, filenameOrOptions, option
6367
6367
  lastModified: file.lastModified
6368
6368
  });
6369
6369
  }
6370
- function fileFromPathSync(path37, filenameOrOptions, options = {}) {
6371
- const stats = (0, import_fs5.statSync)(path37);
6372
- return createFileFromPath(path37, stats, filenameOrOptions, options);
6370
+ function fileFromPathSync(path40, filenameOrOptions, options = {}) {
6371
+ const stats = (0, import_fs5.statSync)(path40);
6372
+ return createFileFromPath(path40, stats, filenameOrOptions, options);
6373
6373
  }
6374
- async function fileFromPath2(path37, filenameOrOptions, options) {
6375
- const stats = await import_fs5.promises.stat(path37);
6376
- return createFileFromPath(path37, stats, filenameOrOptions, options);
6374
+ async function fileFromPath2(path40, filenameOrOptions, options) {
6375
+ const stats = await import_fs5.promises.stat(path40);
6376
+ return createFileFromPath(path40, 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 = resolve26.call(this, root, ref);
9387
+ let _sch = resolve27.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 resolve26(root, ref) {
9414
+ function resolve27(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(path37) {
9633
- let input = path37;
9632
+ function removeDotSegments(path40) {
9633
+ let input = path40;
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 [path37, query] = wsComponent.resourceName.split("?");
9886
- wsComponent.path = path37 && path37 !== "/" ? path37 : void 0;
9885
+ const [path40, query] = wsComponent.resourceName.split("?");
9886
+ wsComponent.path = path40 && path40 !== "/" ? path40 : 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 resolve26(baseURI, relativeURI, options) {
10045
+ function resolve27(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: resolve26,
10303
+ resolve: resolve27,
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, fs32, exportName) {
13279
+ function addFormats(ajv, list, fs34, 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, fs32[f2]);
13284
+ ajv.addFormat(f2, fs34[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: (path37) => {
14568
+ require: (path40) => {
14569
14569
  if (typeof require !== "undefined") {
14570
- return require(path37);
14571
- } else if (path37.match(/\.json$/)) {
14572
- return JSON.parse((0, import_fs2.readFileSync)(path37, "utf8"));
14570
+ return require(path40);
14571
+ } else if (path40.match(/\.json$/)) {
14572
+ return JSON.parse((0, import_fs2.readFileSync)(path40, "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((resolve26, reject) => {
17098
+ return new Promise((resolve27, reject) => {
17099
17099
  __classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
17100
17100
  if (err)
17101
17101
  reject(err);
17102
17102
  else
17103
- resolve26(completions);
17103
+ resolve27(completions);
17104
17104
  });
17105
17105
  });
17106
17106
  } else {
@@ -18158,17 +18158,15 @@ function getHarPackageVersion() {
18158
18158
  throw new Error("Could not resolve @osfactory/har package.json for version lookup");
18159
18159
  }
18160
18160
 
18161
- // src/cli/commands/env.ts
18162
- var path31 = __toESM(require("path"));
18163
-
18164
- // src/core/harness.ts
18165
- var fs18 = __toESM(require("fs"));
18166
- var path16 = __toESM(require("path"));
18167
-
18168
- // src/harness/generator.ts
18169
- var fs6 = __toESM(require("fs"));
18161
+ // src/cli/commands/agents.ts
18170
18162
  var path6 = __toESM(require("path"));
18171
18163
 
18164
+ // src/harness/agent-skills.ts
18165
+ var fs5 = __toESM(require("fs"));
18166
+ var os2 = __toESM(require("os"));
18167
+ var path5 = __toESM(require("path"));
18168
+ var readline = __toESM(require("readline"));
18169
+
18172
18170
  // src/utils/file-ops.ts
18173
18171
  var fs2 = __toESM(require("fs"));
18174
18172
  var path2 = __toESM(require("path"));
@@ -18769,31 +18767,10 @@ function resolveTemplateFile(name) {
18769
18767
  return fs3.existsSync(filePath) ? filePath : null;
18770
18768
  }
18771
18769
 
18772
- // src/core/gitignore.ts
18773
- var fs4 = __toESM(require("fs"));
18774
- var path4 = __toESM(require("path"));
18775
- var HARNESS_ROOT_GITIGNORE_PATTERNS = [
18776
- ".env.agent.*",
18777
- "ecosystem.agent.*.config.cjs"
18778
- ];
18779
- function ensureRootGitignorePatterns(checkoutDir) {
18780
- const gitignorePath = path4.join(checkoutDir, ".gitignore");
18781
- const content = fs4.existsSync(gitignorePath) ? fs4.readFileSync(gitignorePath, "utf8") : "";
18782
- const lines = content.split("\n").map((line) => line.trim());
18783
- const missing = HARNESS_ROOT_GITIGNORE_PATTERNS.filter((pattern) => !lines.includes(pattern));
18784
- if (missing.length === 0) return;
18785
- const header2 = lines.length === 0 || content.trim().length === 0 ? "# HAR agent slot artifacts (generated by .har/launch.sh)\n" : "";
18786
- const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
18787
- const block = lines.some((line) => line.includes("HAR agent slot artifacts")) || header2.length === 0 ? `${missing.join("\n")}
18788
- ` : `${header2}${missing.join("\n")}
18789
- `;
18790
- fs4.writeFileSync(gitignorePath, `${content}${suffix}${block}`);
18791
- }
18792
-
18793
18770
  // src/harness/manifest.ts
18794
18771
  var crypto = __toESM(require("crypto"));
18795
- var fs5 = __toESM(require("fs"));
18796
- var path5 = __toESM(require("path"));
18772
+ var fs4 = __toESM(require("fs"));
18773
+ var path4 = __toESM(require("path"));
18797
18774
 
18798
18775
  // node_modules/zod/v3/external.js
18799
18776
  var external_exports = {};
@@ -19273,8 +19250,8 @@ function getErrorMap() {
19273
19250
 
19274
19251
  // node_modules/zod/v3/helpers/parseUtil.js
19275
19252
  var makeIssue = (params) => {
19276
- const { data, path: path37, errorMaps, issueData } = params;
19277
- const fullPath = [...path37, ...issueData.path || []];
19253
+ const { data, path: path40, errorMaps, issueData } = params;
19254
+ const fullPath = [...path40, ...issueData.path || []];
19278
19255
  const fullIssue = {
19279
19256
  ...issueData,
19280
19257
  path: fullPath
@@ -19390,11 +19367,11 @@ var errorUtil;
19390
19367
 
19391
19368
  // node_modules/zod/v3/types.js
19392
19369
  var ParseInputLazyPath = class {
19393
- constructor(parent, value, path37, key) {
19370
+ constructor(parent, value, path40, key) {
19394
19371
  this._cachedPath = [];
19395
19372
  this.parent = parent;
19396
19373
  this.data = value;
19397
- this._path = path37;
19374
+ this._path = path40;
19398
19375
  this._key = key;
19399
19376
  }
19400
19377
  get path() {
@@ -22848,6 +22825,15 @@ var HAR_STAGE_KINDS = [
22848
22825
  "custom"
22849
22826
  ];
22850
22827
  var HAR_AGENT_SLOT_MIN = 1;
22828
+ var AgentSkillTargetSchema = external_exports.enum(["claude", "cursor", "codex"]);
22829
+ var ScaffoldedAgentFileSchema = external_exports.object({
22830
+ /** Repo-relative path, or '~/…' when scope is 'global'. */
22831
+ path: external_exports.string(),
22832
+ agent: AgentSkillTargetSchema,
22833
+ skill: external_exports.string(),
22834
+ checksum: external_exports.string(),
22835
+ scope: external_exports.enum(["repo", "global"]).default("repo")
22836
+ });
22851
22837
  var HarnessManifestSchema = external_exports.object({
22852
22838
  version: external_exports.string(),
22853
22839
  generatorVersion: external_exports.string(),
@@ -22861,7 +22847,8 @@ var HarnessManifestSchema = external_exports.object({
22861
22847
  }).optional(),
22862
22848
  adaptationSummary: external_exports.string().optional(),
22863
22849
  profile: external_exports.enum(["default", "cli", "ios"]).optional(),
22864
- fileChecksums: external_exports.record(external_exports.string()).optional()
22850
+ fileChecksums: external_exports.record(external_exports.string()).optional(),
22851
+ scaffoldedAgentFiles: external_exports.array(ScaffoldedAgentFileSchema).optional()
22865
22852
  });
22866
22853
  var HarnessStageKindSchema = external_exports.enum(HAR_STAGE_KINDS);
22867
22854
  var HarnessArtifactKindSchema = external_exports.enum([
@@ -23131,15 +23118,15 @@ var CHECKSUM_SKIP = /* @__PURE__ */ new Set([
23131
23118
  "ADAPT-PROMPT.md"
23132
23119
  ]);
23133
23120
  function getHarnessDir(repoPath) {
23134
- return path5.join(repoPath, DEFAULT_HAR_DIR);
23121
+ return path4.join(repoPath, DEFAULT_HAR_DIR);
23135
23122
  }
23136
23123
  function getManifestPath(repoPath) {
23137
- return path5.join(getHarnessDir(repoPath), "manifest.json");
23124
+ return path4.join(getHarnessDir(repoPath), "manifest.json");
23138
23125
  }
23139
23126
  function readManifest(repoPath) {
23140
23127
  const manifestPath = getManifestPath(repoPath);
23141
- if (!fs5.existsSync(manifestPath)) return null;
23142
- const raw = JSON.parse(fs5.readFileSync(manifestPath, "utf8"));
23128
+ if (!fs4.existsSync(manifestPath)) return null;
23129
+ const raw = JSON.parse(fs4.readFileSync(manifestPath, "utf8"));
23143
23130
  const result = HarnessManifestSchema.safeParse(raw);
23144
23131
  return result.success ? result.data : null;
23145
23132
  }
@@ -23152,11 +23139,11 @@ function computeFileChecksum(content) {
23152
23139
  }
23153
23140
  function computeHarnessChecksums(harnessDir) {
23154
23141
  const checksums = {};
23155
- if (!fs5.existsSync(harnessDir)) return checksums;
23156
- for (const entry of fs5.readdirSync(harnessDir, { withFileTypes: true })) {
23142
+ if (!fs4.existsSync(harnessDir)) return checksums;
23143
+ for (const entry of fs4.readdirSync(harnessDir, { withFileTypes: true })) {
23157
23144
  if (!entry.isFile() || CHECKSUM_SKIP.has(entry.name)) continue;
23158
- const full = path5.join(harnessDir, entry.name);
23159
- checksums[entry.name] = computeFileChecksum(fs5.readFileSync(full, "utf8"));
23145
+ const full = path4.join(harnessDir, entry.name);
23146
+ checksums[entry.name] = computeFileChecksum(fs4.readFileSync(full, "utf8"));
23160
23147
  }
23161
23148
  return checksums;
23162
23149
  }
@@ -23176,17 +23163,331 @@ function createManifest(repoPath, adaptationSummary, stack, profile) {
23176
23163
  };
23177
23164
  }
23178
23165
  function resolveHarnessRoot(inputPath) {
23179
- let current = path5.resolve(inputPath);
23180
- const { root } = path5.parse(current);
23166
+ let current = path4.resolve(inputPath);
23167
+ const { root } = path4.parse(current);
23181
23168
  for (; ; ) {
23182
- const manifestPath = path5.join(current, DEFAULT_HAR_DIR, "manifest.json");
23183
- if (fs5.existsSync(manifestPath)) {
23169
+ const manifestPath = path4.join(current, DEFAULT_HAR_DIR, "manifest.json");
23170
+ if (fs4.existsSync(manifestPath)) {
23184
23171
  return current;
23185
23172
  }
23186
23173
  if (current === root) break;
23187
- current = path5.dirname(current);
23174
+ current = path4.dirname(current);
23175
+ }
23176
+ return path4.resolve(inputPath);
23177
+ }
23178
+
23179
+ // src/harness/agent-skills.ts
23180
+ var AGENT_SKILL_TARGETS = ["claude", "cursor", "codex"];
23181
+ var MANAGED_HEADER = "<!-- managed by har \u2014 regenerated by `har agents install` / `har env maintain`; edit at your own risk -->";
23182
+ function agentSkillsTemplateDir() {
23183
+ const dir = path5.join(resolveTemplatesDir(), "agent-skills");
23184
+ if (!fs5.existsSync(dir)) {
23185
+ throw new Error("Agent skills templates not found (templates/agent-skills). Run npm run build.");
23186
+ }
23187
+ return dir;
23188
+ }
23189
+ function readSkillsManifest() {
23190
+ const manifestPath = path5.join(agentSkillsTemplateDir(), "skills.manifest.json");
23191
+ return JSON.parse(fs5.readFileSync(manifestPath, "utf8"));
23192
+ }
23193
+ function readSkillBody(entry) {
23194
+ return fs5.readFileSync(path5.join(agentSkillsTemplateDir(), entry.body), "utf8");
23195
+ }
23196
+ function renderYamlValue(value) {
23197
+ if (typeof value === "string") return JSON.stringify(value);
23198
+ return String(value);
23199
+ }
23200
+ function renderClaudeSkill(entry, body) {
23201
+ const frontmatter = entry.claude?.frontmatter ?? { name: entry.id, description: entry.title };
23202
+ const lines = Object.entries(frontmatter).map(([key, value]) => `${key}: ${renderYamlValue(value)}`);
23203
+ return `---
23204
+ ${lines.join("\n")}
23205
+ ---
23206
+
23207
+ ${MANAGED_HEADER}
23208
+
23209
+ ${body}`;
23210
+ }
23211
+ function renderPlainSkill(body) {
23212
+ return `${MANAGED_HEADER}
23213
+
23214
+ ${body}`;
23215
+ }
23216
+ function renderSkillFiles(agents) {
23217
+ const manifest = readSkillsManifest();
23218
+ const files = [];
23219
+ for (const entry of manifest.skills) {
23220
+ const body = readSkillBody(entry);
23221
+ if (agents.includes("claude")) {
23222
+ files.push({
23223
+ agent: "claude",
23224
+ skill: entry.id,
23225
+ scope: "repo",
23226
+ relPath: path5.join(".claude", "skills", entry.id, "SKILL.md"),
23227
+ content: renderClaudeSkill(entry, body)
23228
+ });
23229
+ }
23230
+ if (agents.includes("cursor") && entry.cursor?.command) {
23231
+ files.push({
23232
+ agent: "cursor",
23233
+ skill: entry.id,
23234
+ scope: "repo",
23235
+ relPath: path5.join(".cursor", "commands", `${entry.id}.md`),
23236
+ content: renderPlainSkill(body)
23237
+ });
23238
+ }
23239
+ if (agents.includes("codex") && entry.codex?.promptName) {
23240
+ files.push({
23241
+ agent: "codex",
23242
+ skill: entry.id,
23243
+ scope: "global",
23244
+ relPath: path5.posix.join("~", ".codex", "prompts", `${entry.codex.promptName}.md`),
23245
+ content: renderPlainSkill(body)
23246
+ });
23247
+ }
23248
+ }
23249
+ return files;
23250
+ }
23251
+ function resolveScaffoldPath(repoPath, file) {
23252
+ if (file.scope === "global") {
23253
+ return path5.join(os2.homedir(), file.relPath.replace(/^~\//, ""));
23254
+ }
23255
+ return path5.join(repoPath, file.relPath);
23256
+ }
23257
+ function isHarManaged(filePath) {
23258
+ return fs5.readFileSync(filePath, "utf8").includes(MANAGED_HEADER);
23259
+ }
23260
+ function scaffoldAgentSkills(repoPath, agents, options = {}) {
23261
+ const written = [];
23262
+ const skipped = [];
23263
+ const records = [];
23264
+ for (const file of renderSkillFiles(agents)) {
23265
+ const destPath = resolveScaffoldPath(repoPath, file);
23266
+ if (fs5.existsSync(destPath) && !options.force && !isHarManaged(destPath)) {
23267
+ warn(`Skipped ${file.relPath} \u2014 exists with user modifications (use --force to overwrite)`);
23268
+ skipped.push(file.relPath);
23269
+ continue;
23270
+ }
23271
+ writeFileSafe(destPath, file.content);
23272
+ written.push(file.relPath);
23273
+ records.push({
23274
+ path: file.relPath,
23275
+ agent: file.agent,
23276
+ skill: file.skill,
23277
+ checksum: computeFileChecksum(file.content),
23278
+ scope: file.scope
23279
+ });
23280
+ }
23281
+ recordScaffoldedFiles(repoPath, agents, records);
23282
+ return { written, skipped };
23283
+ }
23284
+ function recordScaffoldedFiles(repoPath, agents, records) {
23285
+ const manifest = readManifest(repoPath);
23286
+ if (!manifest) return;
23287
+ const kept = (manifest.scaffoldedAgentFiles ?? []).filter(
23288
+ (entry) => !agents.includes(entry.agent)
23289
+ );
23290
+ writeManifest(repoPath, {
23291
+ ...manifest,
23292
+ scaffoldedAgentFiles: [...kept, ...records],
23293
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
23294
+ });
23295
+ }
23296
+ function removeAgentSkills(repoPath, agents) {
23297
+ const removed = [];
23298
+ for (const file of renderSkillFiles(agents)) {
23299
+ const destPath = resolveScaffoldPath(repoPath, file);
23300
+ if (!fs5.existsSync(destPath)) continue;
23301
+ if (!isHarManaged(destPath)) {
23302
+ warn(`Kept ${file.relPath} \u2014 has user modifications`);
23303
+ continue;
23304
+ }
23305
+ fs5.rmSync(destPath);
23306
+ removed.push(file.relPath);
23307
+ const parent = path5.dirname(destPath);
23308
+ if (fs5.existsSync(parent) && fs5.readdirSync(parent).length === 0) {
23309
+ fs5.rmdirSync(parent);
23310
+ }
23311
+ }
23312
+ const manifest = readManifest(repoPath);
23313
+ if (manifest?.scaffoldedAgentFiles) {
23314
+ writeManifest(repoPath, {
23315
+ ...manifest,
23316
+ scaffoldedAgentFiles: manifest.scaffoldedAgentFiles.filter(
23317
+ (entry) => !agents.includes(entry.agent)
23318
+ ),
23319
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
23320
+ });
23321
+ }
23322
+ return removed;
23323
+ }
23324
+ function detectAgentTargets(repoPath) {
23325
+ const manifest = readManifest(repoPath);
23326
+ const scaffolded = new Set((manifest?.scaffoldedAgentFiles ?? []).map((entry) => entry.agent));
23327
+ const detected = new Set(scaffolded);
23328
+ if (fs5.existsSync(path5.join(repoPath, ".claude"))) detected.add("claude");
23329
+ if (fs5.existsSync(path5.join(repoPath, ".cursor"))) detected.add("cursor");
23330
+ if (fs5.existsSync(path5.join(os2.homedir(), ".codex"))) detected.add("codex");
23331
+ return AGENT_SKILL_TARGETS.filter((agent) => detected.has(agent));
23332
+ }
23333
+ function parseAgentTargets(raw) {
23334
+ const parts = raw.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
23335
+ const invalid = parts.filter((part) => !AGENT_SKILL_TARGETS.includes(part));
23336
+ if (invalid.length > 0) {
23337
+ throw new Error(
23338
+ `Unknown agent target(s): ${invalid.join(", ")}. Valid: ${AGENT_SKILL_TARGETS.join(", ")}`
23339
+ );
23340
+ }
23341
+ return AGENT_SKILL_TARGETS.filter((agent) => parts.includes(agent));
23342
+ }
23343
+ async function handleAgentSkills(options) {
23344
+ const { repoPath, mode } = options;
23345
+ if (options.enabled === false) return false;
23346
+ let targets;
23347
+ if (options.agents !== void 0) {
23348
+ targets = parseAgentTargets(options.agents);
23349
+ } else {
23350
+ targets = detectAgentTargets(repoPath);
23351
+ if (targets.length === 0) return false;
23352
+ const manifest = readManifest(repoPath);
23353
+ const alreadyScaffolded = (manifest?.scaffoldedAgentFiles ?? []).length > 0;
23354
+ const shouldPrompt = !options.autoYes && !(mode === "maintain" && alreadyScaffolded);
23355
+ if (shouldPrompt) {
23356
+ const accepted = await askYesNo(
23357
+ `Scaffold agent skills (/setup-har, /har-wt, /har-maintain) for: ${targets.join(", ")}? [Y/n]`
23358
+ );
23359
+ if (!accepted) {
23360
+ info("Skipped agent skills");
23361
+ return false;
23362
+ }
23363
+ }
23364
+ }
23365
+ if (targets.length === 0) return false;
23366
+ const result = scaffoldAgentSkills(repoPath, targets, { force: options.force });
23367
+ for (const file of result.written) {
23368
+ info(`${mode === "maintain" ? "Refreshed" : "Wrote"} ${file}`);
23369
+ }
23370
+ return result.written.length > 0;
23371
+ }
23372
+ async function askYesNo(question) {
23373
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
23374
+ return new Promise((resolve27) => {
23375
+ process.stderr.write(`${question} `);
23376
+ rl.once("line", (answer) => {
23377
+ rl.close();
23378
+ const trimmed = answer.trim();
23379
+ if (trimmed === "") {
23380
+ resolve27(true);
23381
+ return;
23382
+ }
23383
+ resolve27(/^y(es)?$/i.test(trimmed));
23384
+ });
23385
+ });
23386
+ }
23387
+
23388
+ // src/cli/commands/agents.ts
23389
+ var targetsOption = (y2) => y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("claude", { type: "boolean", default: false, describe: "Target Claude Code (.claude/skills/)" }).option("cursor", { type: "boolean", default: false, describe: "Target Cursor (.cursor/commands/)" }).option("codex", { type: "boolean", default: false, describe: "Target Codex CLI (~/.codex/prompts/, global)" }).option("agents", {
23390
+ type: "string",
23391
+ describe: `Comma-separated targets (${AGENT_SKILL_TARGETS.join(",")}); alternative to the per-agent flags`
23392
+ });
23393
+ function resolveTargets(argv, repoPath) {
23394
+ if (argv.agents) return parseAgentTargets(argv.agents);
23395
+ const flagged = AGENT_SKILL_TARGETS.filter((agent) => argv[agent]);
23396
+ if (flagged.length > 0) return flagged;
23397
+ return detectAgentTargets(repoPath);
23398
+ }
23399
+ var agentsCommand = {
23400
+ command: "agents <subcommand>",
23401
+ describe: "Manage scaffolded agent skills (/setup-har, /har-wt, /har-maintain)",
23402
+ builder: (yargs) => yargs.command(
23403
+ "install",
23404
+ "Scaffold agent skills into this repo (Claude/Cursor) and globally (Codex)",
23405
+ (y2) => targetsOption(y2).option("force", {
23406
+ type: "boolean",
23407
+ default: false,
23408
+ describe: "Overwrite files that have user modifications"
23409
+ }),
23410
+ handleInstall
23411
+ ).command("remove", "Remove har-managed agent skill files", targetsOption, handleRemove).demandCommand(1, "Specify a subcommand: install | remove"),
23412
+ handler: () => {
23413
+ }
23414
+ };
23415
+ function handleInstall(argv) {
23416
+ const repoPath = path6.resolve(argv.repo);
23417
+ header("har agents install");
23418
+ try {
23419
+ const targets = resolveTargets(argv, repoPath);
23420
+ if (targets.length === 0) {
23421
+ warn("No agent targets detected. Pass --claude, --cursor, --codex, or --agents=\u2026");
23422
+ process.exit(1);
23423
+ }
23424
+ const result = scaffoldAgentSkills(repoPath, targets, { force: argv.force });
23425
+ for (const file of result.written) {
23426
+ info(` + ${file}`);
23427
+ }
23428
+ if (result.written.length === 0 && result.skipped.length > 0) {
23429
+ warn("Nothing written \u2014 all target files have user modifications (use --force).");
23430
+ process.exit(1);
23431
+ }
23432
+ success(`Agent skills installed for: ${targets.join(", ")}`);
23433
+ if (targets.includes("codex")) {
23434
+ info("Codex prompts are global (~/.codex/prompts/) \u2014 Codex has no per-repo prompt support.");
23435
+ }
23436
+ } catch (err) {
23437
+ error(err.message);
23438
+ process.exit(1);
23439
+ }
23440
+ }
23441
+ function handleRemove(argv) {
23442
+ const repoPath = path6.resolve(argv.repo);
23443
+ header("har agents remove");
23444
+ try {
23445
+ const targets = resolveTargets(argv, repoPath);
23446
+ if (targets.length === 0) {
23447
+ warn("No agent targets detected or specified.");
23448
+ return;
23449
+ }
23450
+ const removed = removeAgentSkills(repoPath, targets);
23451
+ for (const file of removed) {
23452
+ info(` - ${file}`);
23453
+ }
23454
+ success(removed.length > 0 ? "Agent skills removed." : "Nothing to remove.");
23455
+ } catch (err) {
23456
+ error(err.message);
23457
+ process.exit(1);
23188
23458
  }
23189
- return path5.resolve(inputPath);
23459
+ }
23460
+
23461
+ // src/cli/commands/env.ts
23462
+ var path33 = __toESM(require("path"));
23463
+
23464
+ // src/core/harness.ts
23465
+ var fs19 = __toESM(require("fs"));
23466
+ var path18 = __toESM(require("path"));
23467
+
23468
+ // src/harness/generator.ts
23469
+ var fs7 = __toESM(require("fs"));
23470
+ var path8 = __toESM(require("path"));
23471
+
23472
+ // src/core/gitignore.ts
23473
+ var fs6 = __toESM(require("fs"));
23474
+ var path7 = __toESM(require("path"));
23475
+ var HARNESS_ROOT_GITIGNORE_PATTERNS = [
23476
+ ".env.agent.*",
23477
+ "ecosystem.agent.*.config.cjs"
23478
+ ];
23479
+ function ensureRootGitignorePatterns(checkoutDir) {
23480
+ const gitignorePath = path7.join(checkoutDir, ".gitignore");
23481
+ const content = fs6.existsSync(gitignorePath) ? fs6.readFileSync(gitignorePath, "utf8") : "";
23482
+ const lines = content.split("\n").map((line) => line.trim());
23483
+ const missing = HARNESS_ROOT_GITIGNORE_PATTERNS.filter((pattern) => !lines.includes(pattern));
23484
+ if (missing.length === 0) return;
23485
+ const header2 = lines.length === 0 || content.trim().length === 0 ? "# HAR agent slot artifacts (generated by .har/launch.sh)\n" : "";
23486
+ const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
23487
+ const block = lines.some((line) => line.includes("HAR agent slot artifacts")) || header2.length === 0 ? `${missing.join("\n")}
23488
+ ` : `${header2}${missing.join("\n")}
23489
+ `;
23490
+ fs6.writeFileSync(gitignorePath, `${content}${suffix}${block}`);
23190
23491
  }
23191
23492
 
23192
23493
  // src/harness/generator.ts
@@ -23202,46 +23503,46 @@ var CLI_PRUNE_FILES = [
23202
23503
  ];
23203
23504
  function pruneCliProfile(harnessDir) {
23204
23505
  for (const file of CLI_PRUNE_FILES) {
23205
- const filePath = path6.join(harnessDir, file);
23206
- if (fs6.existsSync(filePath)) {
23207
- fs6.unlinkSync(filePath);
23506
+ const filePath = path8.join(harnessDir, file);
23507
+ if (fs7.existsSync(filePath)) {
23508
+ fs7.unlinkSync(filePath);
23208
23509
  }
23209
23510
  }
23210
23511
  }
23211
23512
  function scaffoldClaudeMd(repoPath, projectName, force) {
23212
23513
  const templatePath = resolveTemplateFile("CLAUDE.md.template");
23213
23514
  if (!templatePath) return;
23214
- const dest = path6.join(repoPath, "CLAUDE.md");
23215
- if (fs6.existsSync(dest) && !force) return;
23515
+ const dest = path8.join(repoPath, "CLAUDE.md");
23516
+ if (fs7.existsSync(dest) && !force) return;
23216
23517
  const displayName = projectName.replace(/_/g, " ");
23217
- const content = fs6.readFileSync(templatePath, "utf8").replace(/__PROJECT_DISPLAY_NAME__/g, displayName);
23218
- fs6.writeFileSync(dest, content);
23518
+ const content = fs7.readFileSync(templatePath, "utf8").replace(/__PROJECT_DISPLAY_NAME__/g, displayName);
23519
+ fs7.writeFileSync(dest, content);
23219
23520
  }
23220
23521
  function scaffoldHarnessBoilerplate(repoPath, options = {}) {
23221
- const harnessDir = path6.join(repoPath, DEFAULT_HAR_DIR);
23222
- const projectName = path6.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
23522
+ const harnessDir = path8.join(repoPath, DEFAULT_HAR_DIR);
23523
+ const projectName = path8.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
23223
23524
  const profile = options.profile ?? "default";
23224
- const boilerplateDir = path6.join(resolveTemplatesDir(), PROFILE_DIRS[profile]);
23225
- if (fs6.existsSync(harnessDir) && !options.force) {
23525
+ const boilerplateDir = path8.join(resolveTemplatesDir(), PROFILE_DIRS[profile]);
23526
+ if (fs7.existsSync(harnessDir) && !options.force) {
23226
23527
  throw new Error(
23227
23528
  '.har/ already exists. Use --force to overwrite or run "har env maintain" to update in place.'
23228
23529
  );
23229
23530
  }
23230
- if (!fs6.existsSync(boilerplateDir)) {
23531
+ if (!fs7.existsSync(boilerplateDir)) {
23231
23532
  throw new Error(`Boilerplate template not found at ${boilerplateDir}`);
23232
23533
  }
23233
- if (options.force && fs6.existsSync(harnessDir)) {
23234
- fs6.rmSync(harnessDir, { recursive: true, force: true });
23534
+ if (options.force && fs7.existsSync(harnessDir)) {
23535
+ fs7.rmSync(harnessDir, { recursive: true, force: true });
23235
23536
  }
23236
23537
  copyDirRecursive(boilerplateDir, harnessDir);
23237
23538
  if (profile === "cli") {
23238
23539
  pruneCliProfile(harnessDir);
23239
23540
  }
23240
- const harnessEnvPath = path6.join(harnessDir, "harness.env");
23241
- if (fs6.existsSync(harnessEnvPath)) {
23242
- let content = fs6.readFileSync(harnessEnvPath, "utf8");
23541
+ const harnessEnvPath = path8.join(harnessDir, "harness.env");
23542
+ if (fs7.existsSync(harnessEnvPath)) {
23543
+ let content = fs7.readFileSync(harnessEnvPath, "utf8");
23243
23544
  content = content.replace(/__PROJECT_NAME__/g, projectName).replace(/template___PROJECT_NAME__/g, `template_${projectName}`);
23244
- fs6.writeFileSync(harnessEnvPath, content);
23545
+ fs7.writeFileSync(harnessEnvPath, content);
23245
23546
  }
23246
23547
  const manifest = createManifest(
23247
23548
  repoPath,
@@ -23264,8 +23565,8 @@ function finalizeHarness(repoPath, adaptationSummary, stack) {
23264
23565
  }
23265
23566
 
23266
23567
  // src/llm/authoring-agent.ts
23267
- var fs10 = __toESM(require("fs"));
23268
- var path9 = __toESM(require("path"));
23568
+ var fs11 = __toESM(require("fs"));
23569
+ var path11 = __toESM(require("path"));
23269
23570
 
23270
23571
  // node_modules/@anthropic-ai/sdk/error.mjs
23271
23572
  var error_exports = {};
@@ -23664,13 +23965,13 @@ var MultipartBody = class {
23664
23965
  // node_modules/@anthropic-ai/sdk/_shims/node-runtime.mjs
23665
23966
  var import_web = require("node:stream/web");
23666
23967
  var fileFromPathWarned = false;
23667
- async function fileFromPath3(path37, ...args) {
23968
+ async function fileFromPath3(path40, ...args) {
23668
23969
  const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
23669
23970
  if (!fileFromPathWarned) {
23670
- console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path37)}) instead`);
23971
+ console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path40)}) instead`);
23671
23972
  fileFromPathWarned = true;
23672
23973
  }
23673
- return await _fileFromPath(path37, ...args);
23974
+ return await _fileFromPath(path40, ...args);
23674
23975
  }
23675
23976
  var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
23676
23977
  var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
@@ -24166,8 +24467,8 @@ async function defaultParseResponse(props) {
24166
24467
  }
24167
24468
  var APIPromise = class _APIPromise extends Promise {
24168
24469
  constructor(responsePromise, parseResponse = defaultParseResponse) {
24169
- super((resolve26) => {
24170
- resolve26(null);
24470
+ super((resolve27) => {
24471
+ resolve27(null);
24171
24472
  });
24172
24473
  this.responsePromise = responsePromise;
24173
24474
  this.parseResponse = parseResponse;
@@ -24267,29 +24568,29 @@ var APIClient = class {
24267
24568
  defaultIdempotencyKey() {
24268
24569
  return `stainless-node-retry-${uuid4()}`;
24269
24570
  }
24270
- get(path37, opts) {
24271
- return this.methodRequest("get", path37, opts);
24571
+ get(path40, opts) {
24572
+ return this.methodRequest("get", path40, opts);
24272
24573
  }
24273
- post(path37, opts) {
24274
- return this.methodRequest("post", path37, opts);
24574
+ post(path40, opts) {
24575
+ return this.methodRequest("post", path40, opts);
24275
24576
  }
24276
- patch(path37, opts) {
24277
- return this.methodRequest("patch", path37, opts);
24577
+ patch(path40, opts) {
24578
+ return this.methodRequest("patch", path40, opts);
24278
24579
  }
24279
- put(path37, opts) {
24280
- return this.methodRequest("put", path37, opts);
24580
+ put(path40, opts) {
24581
+ return this.methodRequest("put", path40, opts);
24281
24582
  }
24282
- delete(path37, opts) {
24283
- return this.methodRequest("delete", path37, opts);
24583
+ delete(path40, opts) {
24584
+ return this.methodRequest("delete", path40, opts);
24284
24585
  }
24285
- methodRequest(method, path37, opts) {
24586
+ methodRequest(method, path40, opts) {
24286
24587
  return this.request(Promise.resolve(opts).then(async (opts2) => {
24287
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;
24288
- return { method, path: path37, ...opts2, body };
24589
+ return { method, path: path40, ...opts2, body };
24289
24590
  }));
24290
24591
  }
24291
- getAPIList(path37, Page, opts) {
24292
- return this.requestAPIList(Page, { method: "get", path: path37, ...opts });
24592
+ getAPIList(path40, Page, opts) {
24593
+ return this.requestAPIList(Page, { method: "get", path: path40, ...opts });
24293
24594
  }
24294
24595
  calculateContentLength(body) {
24295
24596
  if (typeof body === "string") {
@@ -24307,10 +24608,10 @@ var APIClient = class {
24307
24608
  return null;
24308
24609
  }
24309
24610
  buildRequest(options) {
24310
- const { method, path: path37, query, headers = {} } = options;
24611
+ const { method, path: path40, query, headers = {} } = options;
24311
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;
24312
24613
  const contentLength = this.calculateContentLength(body);
24313
- const url = this.buildURL(path37, query);
24614
+ const url = this.buildURL(path40, query);
24314
24615
  if ("timeout" in options)
24315
24616
  validatePositiveInteger("timeout", options.timeout);
24316
24617
  const timeout = options.timeout ?? this.timeout;
@@ -24419,8 +24720,8 @@ var APIClient = class {
24419
24720
  const request = this.makeRequest(options, null);
24420
24721
  return new PagePromise(this, request, Page);
24421
24722
  }
24422
- buildURL(path37, query) {
24423
- const url = isAbsoluteURL(path37) ? new URL(path37) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path37.startsWith("/") ? path37.slice(1) : path37));
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));
24424
24725
  const defaultQuery = this.defaultQuery();
24425
24726
  if (!isEmptyObj(defaultQuery)) {
24426
24727
  query = { ...defaultQuery, ...query };
@@ -24705,7 +25006,7 @@ var startsWithSchemeRegexp = new RegExp("^(?:[a-z]+:)?//", "i");
24705
25006
  var isAbsoluteURL = (url) => {
24706
25007
  return startsWithSchemeRegexp.test(url);
24707
25008
  };
24708
- var sleep = (ms) => new Promise((resolve26) => setTimeout(resolve26, ms));
25009
+ var sleep = (ms) => new Promise((resolve27) => setTimeout(resolve27, ms));
24709
25010
  var validatePositiveInteger = (name, n2) => {
24710
25011
  if (typeof n2 !== "number" || !Number.isInteger(n2)) {
24711
25012
  throw new AnthropicError(`${name} must be an integer`);
@@ -25196,12 +25497,12 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25196
25497
  }
25197
25498
  return this._emit("error", new AnthropicError(String(error3)));
25198
25499
  });
25199
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve26, reject) => {
25200
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve26, "f");
25500
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve27, reject) => {
25501
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve27, "f");
25201
25502
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectConnectedPromise, reject, "f");
25202
25503
  }), "f");
25203
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve26, reject) => {
25204
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve26, "f");
25504
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve27, reject) => {
25505
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve27, "f");
25205
25506
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectEndPromise, reject, "f");
25206
25507
  }), "f");
25207
25508
  __classPrivateFieldGet8(this, _PromptCachingBetaMessageStream_connectedPromise, "f").catch(() => {
@@ -25330,11 +25631,11 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25330
25631
  * const message = await stream.emitted('message') // rejects if the stream errors
25331
25632
  */
25332
25633
  emitted(event) {
25333
- return new Promise((resolve26, reject) => {
25634
+ return new Promise((resolve27, reject) => {
25334
25635
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_catchingPromiseCreated, true, "f");
25335
25636
  if (event !== "error")
25336
25637
  this.once("error", reject);
25337
- this.once(event, resolve26);
25638
+ this.once(event, resolve27);
25338
25639
  });
25339
25640
  }
25340
25641
  async done() {
@@ -25561,7 +25862,7 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25561
25862
  if (done) {
25562
25863
  return { value: void 0, done: true };
25563
25864
  }
25564
- return new Promise((resolve26, reject) => readQueue.push({ resolve: resolve26, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
25865
+ return new Promise((resolve27, reject) => readQueue.push({ resolve: resolve27, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
25565
25866
  }
25566
25867
  const chunk = pushQueue.shift();
25567
25868
  return { value: chunk, done: false };
@@ -25709,12 +26010,12 @@ var MessageStream = class _MessageStream {
25709
26010
  }
25710
26011
  return this._emit("error", new AnthropicError(String(error3)));
25711
26012
  });
25712
- __classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve26, reject) => {
25713
- __classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve26, "f");
26013
+ __classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve27, reject) => {
26014
+ __classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve27, "f");
25714
26015
  __classPrivateFieldSet8(this, _MessageStream_rejectConnectedPromise, reject, "f");
25715
26016
  }), "f");
25716
- __classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve26, reject) => {
25717
- __classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve26, "f");
26017
+ __classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve27, reject) => {
26018
+ __classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve27, "f");
25718
26019
  __classPrivateFieldSet8(this, _MessageStream_rejectEndPromise, reject, "f");
25719
26020
  }), "f");
25720
26021
  __classPrivateFieldGet9(this, _MessageStream_connectedPromise, "f").catch(() => {
@@ -25843,11 +26144,11 @@ var MessageStream = class _MessageStream {
25843
26144
  * const message = await stream.emitted('message') // rejects if the stream errors
25844
26145
  */
25845
26146
  emitted(event) {
25846
- return new Promise((resolve26, reject) => {
26147
+ return new Promise((resolve27, reject) => {
25847
26148
  __classPrivateFieldSet8(this, _MessageStream_catchingPromiseCreated, true, "f");
25848
26149
  if (event !== "error")
25849
26150
  this.once("error", reject);
25850
- this.once(event, resolve26);
26151
+ this.once(event, resolve27);
25851
26152
  });
25852
26153
  }
25853
26154
  async done() {
@@ -26074,7 +26375,7 @@ var MessageStream = class _MessageStream {
26074
26375
  if (done) {
26075
26376
  return { value: void 0, done: true };
26076
26377
  }
26077
- return new Promise((resolve26, reject) => readQueue.push({ resolve: resolve26, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26378
+ return new Promise((resolve27, reject) => readQueue.push({ resolve: resolve27, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26078
26379
  }
26079
26380
  const chunk = pushQueue.shift();
26080
26381
  return { value: chunk, done: false };
@@ -26243,34 +26544,34 @@ var { AnthropicError: AnthropicError2, APIError: APIError2, APIConnectionError:
26243
26544
  var sdk_default = Anthropic;
26244
26545
 
26245
26546
  // src/llm/tools.ts
26246
- var fs9 = __toESM(require("fs"));
26247
- var path8 = __toESM(require("path"));
26248
- var readline2 = __toESM(require("readline"));
26547
+ var fs10 = __toESM(require("fs"));
26548
+ var path10 = __toESM(require("path"));
26549
+ var readline3 = __toESM(require("readline"));
26249
26550
 
26250
26551
  // src/harness/agent-md.ts
26251
- var fs8 = __toESM(require("fs"));
26252
- var path7 = __toESM(require("path"));
26253
- var readline = __toESM(require("readline"));
26552
+ var fs9 = __toESM(require("fs"));
26553
+ var path9 = __toESM(require("path"));
26554
+ var readline2 = __toESM(require("readline"));
26254
26555
  var AGENT_MD_PROPOSAL = "AGENT.md.proposed";
26255
26556
  var AGENT_MD_PROPOSAL_META = "AGENT.md.proposed.meta.json";
26256
26557
  function writeAgentMdProposal(repoPath, content, rationale) {
26257
26558
  const harnessDir = getHarnessDir(repoPath);
26258
- writeFileSafe(path7.join(harnessDir, AGENT_MD_PROPOSAL), content);
26559
+ writeFileSafe(path9.join(harnessDir, AGENT_MD_PROPOSAL), content);
26259
26560
  writeFileSafe(
26260
- path7.join(harnessDir, AGENT_MD_PROPOSAL_META),
26561
+ path9.join(harnessDir, AGENT_MD_PROPOSAL_META),
26261
26562
  JSON.stringify({ rationale, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
26262
26563
  );
26263
26564
  }
26264
26565
  function readAgentMdProposal(repoPath) {
26265
26566
  const harnessDir = getHarnessDir(repoPath);
26266
- const proposalPath = path7.join(harnessDir, AGENT_MD_PROPOSAL);
26267
- const metaPath = path7.join(harnessDir, AGENT_MD_PROPOSAL_META);
26268
- if (!fs8.existsSync(proposalPath)) return null;
26269
- const content = fs8.readFileSync(proposalPath, "utf8");
26567
+ const proposalPath = path9.join(harnessDir, AGENT_MD_PROPOSAL);
26568
+ const metaPath = path9.join(harnessDir, AGENT_MD_PROPOSAL_META);
26569
+ if (!fs9.existsSync(proposalPath)) return null;
26570
+ const content = fs9.readFileSync(proposalPath, "utf8");
26270
26571
  let rationale = "";
26271
- if (fs8.existsSync(metaPath)) {
26572
+ if (fs9.existsSync(metaPath)) {
26272
26573
  try {
26273
- rationale = JSON.parse(fs8.readFileSync(metaPath, "utf8")).rationale ?? "";
26574
+ rationale = JSON.parse(fs9.readFileSync(metaPath, "utf8")).rationale ?? "";
26274
26575
  } catch {
26275
26576
  rationale = "";
26276
26577
  }
@@ -26278,21 +26579,21 @@ function readAgentMdProposal(repoPath) {
26278
26579
  return {
26279
26580
  content,
26280
26581
  rationale,
26281
- createdAt: fs8.existsSync(metaPath) ? JSON.parse(fs8.readFileSync(metaPath, "utf8")).createdAt : (/* @__PURE__ */ new Date()).toISOString()
26582
+ createdAt: fs9.existsSync(metaPath) ? JSON.parse(fs9.readFileSync(metaPath, "utf8")).createdAt : (/* @__PURE__ */ new Date()).toISOString()
26282
26583
  };
26283
26584
  }
26284
26585
  function clearAgentMdProposal(repoPath) {
26285
26586
  const harnessDir = getHarnessDir(repoPath);
26286
26587
  for (const file of [AGENT_MD_PROPOSAL, AGENT_MD_PROPOSAL_META]) {
26287
- const p2 = path7.join(harnessDir, file);
26288
- if (fs8.existsSync(p2)) fs8.unlinkSync(p2);
26588
+ const p2 = path9.join(harnessDir, file);
26589
+ if (fs9.existsSync(p2)) fs9.unlinkSync(p2);
26289
26590
  }
26290
26591
  }
26291
26592
  async function promptApplyAgentMdProposal(repoPath) {
26292
26593
  const proposal = readAgentMdProposal(repoPath);
26293
26594
  if (!proposal) return false;
26294
- const agentMdPath = path7.join(repoPath, "AGENT.md");
26295
- const exists = fs8.existsSync(agentMdPath);
26595
+ const agentMdPath = path9.join(repoPath, "AGENT.md");
26596
+ const exists = fs9.existsSync(agentMdPath);
26296
26597
  process.stderr.write("\n");
26297
26598
  process.stderr.write("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
26298
26599
  process.stderr.write("Proposed AGENT.md (repo root)\n");
@@ -26315,13 +26616,13 @@ async function promptApplyAgentMdProposal(repoPath) {
26315
26616
  `);
26316
26617
  if (exists) {
26317
26618
  warn("AGENT.md already exists at repo root.");
26318
- const answer = await askYesNo("Replace AGENT.md with this proposal? (y/n)");
26619
+ const answer = await askYesNo2("Replace AGENT.md with this proposal? (y/n)");
26319
26620
  if (!answer) {
26320
26621
  info("Skipped \u2014 proposal kept at .har/AGENT.md.proposed");
26321
26622
  return false;
26322
26623
  }
26323
26624
  } else {
26324
- const answer = await askYesNo("Create AGENT.md at repo root? (y/n)");
26625
+ const answer = await askYesNo2("Create AGENT.md at repo root? (y/n)");
26325
26626
  if (!answer) {
26326
26627
  info("Skipped \u2014 proposal kept at .har/AGENT.md.proposed");
26327
26628
  return false;
@@ -26332,13 +26633,13 @@ async function promptApplyAgentMdProposal(repoPath) {
26332
26633
  info("Wrote AGENT.md");
26333
26634
  return true;
26334
26635
  }
26335
- async function askYesNo(question) {
26336
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
26337
- return new Promise((resolve26) => {
26636
+ async function askYesNo2(question) {
26637
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
26638
+ return new Promise((resolve27) => {
26338
26639
  process.stderr.write(`${question} `);
26339
26640
  rl.once("line", (answer) => {
26340
26641
  rl.close();
26341
- resolve26(/^y(es)?$/i.test(answer.trim()));
26642
+ resolve27(/^y(es)?$/i.test(answer.trim()));
26342
26643
  });
26343
26644
  });
26344
26645
  }
@@ -26480,13 +26781,13 @@ async function handleAuthoringToolCall(toolName, input, repoPath, harnessDir, ca
26480
26781
  async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, callbacks) {
26481
26782
  switch (toolName) {
26482
26783
  case "readRepoFile": {
26483
- const filePath = path8.join(repoPath, input.path);
26784
+ const filePath = path10.join(repoPath, input.path);
26484
26785
  const content = readFile(filePath, input.maxChars ?? 8e3);
26485
26786
  info(`Read repo: ${input.path}`);
26486
26787
  return content;
26487
26788
  }
26488
26789
  case "listRepoDir": {
26489
- const dirPath = path8.join(repoPath, input.path ?? ".");
26790
+ const dirPath = path10.join(repoPath, input.path ?? ".");
26490
26791
  const entries = listDir(dirPath, input.maxFiles ?? 60);
26491
26792
  info(`List repo: ${input.path} \u2192 ${entries.length} entries`);
26492
26793
  return entries.length > 0 ? entries.join("\n") : "(empty directory)";
@@ -26511,17 +26812,17 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
26511
26812
  const filePath = resolveSafePath(harnessDir, relPath);
26512
26813
  writeFileSafe(filePath, input.content);
26513
26814
  if (relPath.endsWith(".sh")) {
26514
- fs9.chmodSync(filePath, 493);
26815
+ fs10.chmodSync(filePath, 493);
26515
26816
  }
26516
26817
  info(`Wrote harness: ${relPath}`);
26517
26818
  return `Written ${relPath} (${input.content.length} bytes)`;
26518
26819
  }
26519
26820
  case "editHarnessFile": {
26520
26821
  const filePath = resolveSafePath(harnessDir, input.path);
26521
- if (!fs9.existsSync(filePath)) {
26822
+ if (!fs10.existsSync(filePath)) {
26522
26823
  return `Error: file not found: ${input.path}`;
26523
26824
  }
26524
- const content = fs9.readFileSync(filePath, "utf8");
26825
+ const content = fs10.readFileSync(filePath, "utf8");
26525
26826
  const oldStr = input.old_string;
26526
26827
  const newStr = input.new_string;
26527
26828
  const count = content.split(oldStr).length - 1;
@@ -26538,10 +26839,10 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
26538
26839
  }
26539
26840
  case "deleteHarnessFile": {
26540
26841
  const filePath = resolveSafePath(harnessDir, input.path);
26541
- if (!fs9.existsSync(filePath)) {
26842
+ if (!fs10.existsSync(filePath)) {
26542
26843
  return `File not found (already deleted): ${input.path}`;
26543
26844
  }
26544
- fs9.unlinkSync(filePath);
26845
+ fs10.unlinkSync(filePath);
26545
26846
  info(`Deleted harness: ${input.path}`);
26546
26847
  return `Deleted ${input.path}`;
26547
26848
  }
@@ -26567,8 +26868,8 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
26567
26868
  }
26568
26869
  }
26569
26870
  async function askUser(question, options) {
26570
- const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
26571
- return new Promise((resolve26) => {
26871
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
26872
+ return new Promise((resolve27) => {
26572
26873
  process.stderr.write("\n");
26573
26874
  process.stderr.write(`\u2753 ${question}
26574
26875
  `);
@@ -26579,7 +26880,7 @@ async function askUser(question, options) {
26579
26880
  process.stderr.write("> ");
26580
26881
  rl.once("line", (answer) => {
26581
26882
  rl.close();
26582
- resolve26(answer.trim());
26883
+ resolve27(answer.trim());
26583
26884
  });
26584
26885
  });
26585
26886
  }
@@ -26592,29 +26893,29 @@ async function authorHarness(repoPath, apiKey, options = {}) {
26592
26893
  const mode = options.mode ?? "init";
26593
26894
  const systemPromptPath = resolvePromptPath("system-authoring.md");
26594
26895
  const agentMdTemplatePath = resolveTemplateFile("AGENT.md.template");
26595
- let systemPrompt = fs10.existsSync(systemPromptPath) ? fs10.readFileSync(systemPromptPath, "utf8") : "Adapt the .har/ boilerplate to match the repository. Edit files directly. Call finishAuthoring when done.";
26896
+ let systemPrompt = fs11.existsSync(systemPromptPath) ? fs11.readFileSync(systemPromptPath, "utf8") : "Adapt the .har/ boilerplate to match the repository. Edit files directly. Call finishAuthoring when done.";
26596
26897
  if (agentMdTemplatePath) {
26597
26898
  systemPrompt += `
26598
26899
 
26599
26900
  ## AGENT.md template (starting point for proposeAgentMd)
26600
26901
 
26601
- ${fs10.readFileSync(agentMdTemplatePath, "utf8")}`;
26902
+ ${fs11.readFileSync(agentMdTemplatePath, "utf8")}`;
26602
26903
  }
26603
26904
  const tools = buildAuthoringTools();
26604
26905
  const messages = [];
26605
26906
  let rootListing;
26606
26907
  try {
26607
- rootListing = fs10.readdirSync(repoPath).slice(0, 50).join("\n");
26908
+ rootListing = fs11.readdirSync(repoPath).slice(0, 50).join("\n");
26608
26909
  } catch {
26609
26910
  throw new Error(`Cannot read repo at path: ${repoPath}`);
26610
26911
  }
26611
26912
  let harnessListing;
26612
26913
  try {
26613
- harnessListing = fs10.readdirSync(harnessDir).join("\n");
26914
+ harnessListing = fs11.readdirSync(harnessDir).join("\n");
26614
26915
  } catch {
26615
26916
  throw new Error(`Cannot read harness dir at: ${harnessDir}`);
26616
26917
  }
26617
- const existingAgentMd = fs10.existsSync(path9.join(repoPath, "AGENT.md")) ? "AGENT.md exists at repo root \u2014 read it before proposing changes via proposeAgentMd." : "No AGENT.md at repo root yet \u2014 propose one via proposeAgentMd.";
26918
+ const existingAgentMd = fs11.existsSync(path11.join(repoPath, "AGENT.md")) ? "AGENT.md exists at repo root \u2014 read it before proposing changes via proposeAgentMd." : "No AGENT.md at repo root yet \u2014 propose one via proposeAgentMd.";
26618
26919
  const modeInstructions = mode === "maintain" ? `This is a MAINTENANCE run. .har/ already exists and may have been edited by humans.
26619
26920
  Update it to reflect current repo changes. Prefer editHarnessFile over writeHarnessFile.
26620
26921
  Keep README.md accurate \u2014 it is the index of the harness. ${existingAgentMd}` : `This is an INITIAL setup. A fresh boilerplate has been copied to .har/.
@@ -26694,8 +26995,8 @@ Try running with --verbose to see what the agent is doing.`
26694
26995
  }
26695
26996
 
26696
26997
  // src/harness/validator.ts
26697
- var fs13 = __toESM(require("fs"));
26698
- var path12 = __toESM(require("path"));
26998
+ var fs14 = __toESM(require("fs"));
26999
+ var path14 = __toESM(require("path"));
26699
27000
 
26700
27001
  // src/utils/shell.ts
26701
27002
  var import_child_process = require("child_process");
@@ -26723,7 +27024,7 @@ function runScript(scriptPath, args = [], options = {}) {
26723
27024
  function runScriptCapture(scriptPath, args = [], options = {}) {
26724
27025
  const stream = options.stream ?? false;
26725
27026
  const { stream: _stream, ...spawnOptions } = options;
26726
- return new Promise((resolve26) => {
27027
+ return new Promise((resolve27) => {
26727
27028
  const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
26728
27029
  ...spawnOptions,
26729
27030
  stdio: ["inherit", "pipe", "pipe"]
@@ -26738,13 +27039,13 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
26738
27039
  stderr += d2;
26739
27040
  if (stream) process.stderr.write(d2);
26740
27041
  });
26741
- proc.on("close", (code) => resolve26({ stdout, stderr, code: code ?? 0 }));
27042
+ proc.on("close", (code) => resolve27({ stdout, stderr, code: code ?? 0 }));
26742
27043
  });
26743
27044
  }
26744
27045
  function runShellCommand(command2, options = {}) {
26745
27046
  const stream = options.stream ?? false;
26746
27047
  const { stream: _stream, ...spawnOptions } = options;
26747
- return new Promise((resolve26) => {
27048
+ return new Promise((resolve27) => {
26748
27049
  const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
26749
27050
  ...spawnOptions,
26750
27051
  stdio: ["inherit", "pipe", "pipe"]
@@ -26759,22 +27060,22 @@ function runShellCommand(command2, options = {}) {
26759
27060
  stderr += d2;
26760
27061
  if (stream) process.stderr.write(d2);
26761
27062
  });
26762
- proc.on("close", (code) => resolve26({ stdout, stderr, code: code ?? 0 }));
27063
+ proc.on("close", (code) => resolve27({ stdout, stderr, code: code ?? 0 }));
26763
27064
  });
26764
27065
  }
26765
27066
 
26766
27067
  // src/harness/stages.ts
26767
- var fs12 = __toESM(require("fs"));
26768
- var path11 = __toESM(require("path"));
27068
+ var fs13 = __toESM(require("fs"));
27069
+ var path13 = __toESM(require("path"));
26769
27070
 
26770
27071
  // src/harness/env.ts
26771
- var fs11 = __toESM(require("fs"));
26772
- var path10 = __toESM(require("path"));
27072
+ var fs12 = __toESM(require("fs"));
27073
+ var path12 = __toESM(require("path"));
26773
27074
  function readHarnessEnv(repoPath) {
26774
- const envPath = path10.join(getHarnessDir(repoPath), "harness.env");
26775
- if (!fs11.existsSync(envPath)) return {};
27075
+ const envPath = path12.join(getHarnessDir(repoPath), "harness.env");
27076
+ if (!fs12.existsSync(envPath)) return {};
26776
27077
  const values = {};
26777
- for (const line of fs11.readFileSync(envPath, "utf8").split("\n")) {
27078
+ for (const line of fs12.readFileSync(envPath, "utf8").split("\n")) {
26778
27079
  const match = line.match(/^export\s+([A-Z0-9_]+)=(.*)$/);
26779
27080
  if (!match) continue;
26780
27081
  values[match[1]] = match[2].replace(/^"|"$/g, "");
@@ -26801,7 +27102,7 @@ var AGENT_REQUIRED_KINDS = /* @__PURE__ */ new Set([
26801
27102
  "custom"
26802
27103
  ]);
26803
27104
  function getStageRegistryPath(repoPath) {
26804
- return path11.join(getHarnessDir(repoPath), STAGE_REGISTRY_FILE);
27105
+ return path13.join(getHarnessDir(repoPath), STAGE_REGISTRY_FILE);
26805
27106
  }
26806
27107
  function synthesizeStageRegistry(repoPath) {
26807
27108
  const harnessDir = getHarnessDir(repoPath);
@@ -26876,13 +27177,13 @@ function readAgentSlotsFromHarnessEnv(harnessEnv) {
26876
27177
  }
26877
27178
  function stageScriptExists(harnessDir, stage) {
26878
27179
  if (stage.script) {
26879
- return fs12.existsSync(path11.join(harnessDir, stage.script));
27180
+ return fs13.existsSync(path13.join(harnessDir, stage.script));
26880
27181
  }
26881
27182
  if (stage.command) {
26882
27183
  const scriptName = stage.command.split(/\s+/)[0].replace(/^\.\/\.har\//, "").replace(/^\.\//, "");
26883
- return fs12.existsSync(path11.join(harnessDir, scriptName));
27184
+ return fs13.existsSync(path13.join(harnessDir, scriptName));
26884
27185
  }
26885
- return fs12.existsSync(path11.join(harnessDir, "stages", `${stage.id}.sh`));
27186
+ return fs13.existsSync(path13.join(harnessDir, "stages", `${stage.id}.sh`));
26886
27187
  }
26887
27188
  function addStageIfRunnable(stages, harnessDir, stage) {
26888
27189
  const parsed = HarnessStageSchema.parse(stage);
@@ -26892,10 +27193,10 @@ function addStageIfRunnable(stages, harnessDir, stage) {
26892
27193
  }
26893
27194
  function readStageRegistry(repoPath) {
26894
27195
  const registryPath = getStageRegistryPath(repoPath);
26895
- if (!fs12.existsSync(registryPath)) {
27196
+ if (!fs13.existsSync(registryPath)) {
26896
27197
  return synthesizeStageRegistry(repoPath);
26897
27198
  }
26898
- const raw = JSON.parse(fs12.readFileSync(registryPath, "utf8"));
27199
+ const raw = JSON.parse(fs13.readFileSync(registryPath, "utf8"));
26899
27200
  const parsed = HarnessStageRegistrySchema.safeParse(raw);
26900
27201
  if (!parsed.success) {
26901
27202
  throw new Error(`Invalid .har/${STAGE_REGISTRY_FILE}: ${parsed.error.message}`);
@@ -26908,7 +27209,7 @@ function writeStageRegistry(repoPath, registry2) {
26908
27209
  if (!parsed.success) {
26909
27210
  throw new Error(`Invalid stage registry: ${parsed.error.message}`);
26910
27211
  }
26911
- fs12.writeFileSync(registryPath, JSON.stringify(parsed.data, null, 2) + "\n");
27212
+ fs13.writeFileSync(registryPath, JSON.stringify(parsed.data, null, 2) + "\n");
26912
27213
  }
26913
27214
  function listStages(repoPath) {
26914
27215
  return readStageRegistry(repoPath).stages;
@@ -27014,22 +27315,22 @@ var SHELL_SCRIPTS = [
27014
27315
  function validateHarness(repoPath) {
27015
27316
  const harnessDir = getHarnessDir(repoPath);
27016
27317
  const issues = [];
27017
- if (!fs13.existsSync(harnessDir)) {
27318
+ if (!fs14.existsSync(harnessDir)) {
27018
27319
  return {
27019
27320
  pass: false,
27020
27321
  issues: [{ file: ".har", message: "Harness directory not found", severity: "error" }]
27021
27322
  };
27022
27323
  }
27023
27324
  for (const file of getRequiredFiles(repoPath)) {
27024
- const filePath = path12.join(harnessDir, file);
27025
- if (!fs13.existsSync(filePath)) {
27325
+ const filePath = path14.join(harnessDir, file);
27326
+ if (!fs14.existsSync(filePath)) {
27026
27327
  issues.push({ file, message: "Required file missing", severity: "error" });
27027
27328
  }
27028
27329
  }
27029
27330
  for (const script of SHELL_SCRIPTS) {
27030
- const scriptPath = path12.join(harnessDir, script);
27031
- if (!fs13.existsSync(scriptPath)) continue;
27032
- const stat = fs13.statSync(scriptPath);
27331
+ const scriptPath = path14.join(harnessDir, script);
27332
+ if (!fs14.existsSync(scriptPath)) continue;
27333
+ const stat = fs14.statSync(scriptPath);
27033
27334
  if (!(stat.mode & 73)) {
27034
27335
  issues.push({ file: script, message: "Script is not executable", severity: "warning" });
27035
27336
  }
@@ -27044,9 +27345,9 @@ function validateHarness(repoPath) {
27044
27345
  }
27045
27346
  const manifest = readManifest(repoPath);
27046
27347
  const profile = manifest?.profile ?? "default";
27047
- const ecosystemPath = path12.join(harnessDir, "ecosystem.agent.template.cjs");
27048
- if (profile !== "cli" && fs13.existsSync(ecosystemPath)) {
27049
- const content = fs13.readFileSync(ecosystemPath, "utf8");
27348
+ const ecosystemPath = path14.join(harnessDir, "ecosystem.agent.template.cjs");
27349
+ if (profile !== "cli" && fs14.existsSync(ecosystemPath)) {
27350
+ const content = fs14.readFileSync(ecosystemPath, "utf8");
27050
27351
  if (!content.includes("module.exports")) {
27051
27352
  issues.push({
27052
27353
  file: "ecosystem.agent.template.cjs",
@@ -27055,9 +27356,9 @@ function validateHarness(repoPath) {
27055
27356
  });
27056
27357
  }
27057
27358
  }
27058
- const harnessEnvPath = path12.join(harnessDir, "harness.env");
27059
- if (fs13.existsSync(harnessEnvPath)) {
27060
- const content = fs13.readFileSync(harnessEnvPath, "utf8");
27359
+ const harnessEnvPath = path14.join(harnessDir, "harness.env");
27360
+ if (fs14.existsSync(harnessEnvPath)) {
27361
+ const content = fs14.readFileSync(harnessEnvPath, "utf8");
27061
27362
  if (content.includes("TODO: set migrate command")) {
27062
27363
  issues.push({ file: "harness.env", message: "Migrate command still has TODO", severity: "warning" });
27063
27364
  }
@@ -27065,17 +27366,17 @@ function validateHarness(repoPath) {
27065
27366
  issues.push({ file: "harness.env", message: "Seed command still has TODO", severity: "warning" });
27066
27367
  }
27067
27368
  }
27068
- const verifyPath = path12.join(harnessDir, "verify.sh");
27069
- if (fs13.existsSync(verifyPath)) {
27070
- const content = fs13.readFileSync(verifyPath, "utf8");
27369
+ const verifyPath = path14.join(harnessDir, "verify.sh");
27370
+ if (fs14.existsSync(verifyPath)) {
27371
+ const content = fs14.readFileSync(verifyPath, "utf8");
27071
27372
  if (content.includes("echo 'TODO:")) {
27072
27373
  issues.push({ file: "verify.sh", message: "Verification steps still have TODO placeholders", severity: "warning" });
27073
27374
  }
27074
27375
  }
27075
- const stagesPath = path12.join(harnessDir, "stages.json");
27076
- if (fs13.existsSync(stagesPath)) {
27376
+ const stagesPath = path14.join(harnessDir, "stages.json");
27377
+ if (fs14.existsSync(stagesPath)) {
27077
27378
  try {
27078
- JSON.parse(fs13.readFileSync(stagesPath, "utf8"));
27379
+ JSON.parse(fs14.readFileSync(stagesPath, "utf8"));
27079
27380
  const registry2 = readStageRegistry(repoPath);
27080
27381
  if (registry2.stages.length === 0) {
27081
27382
  issues.push({ file: "stages.json", message: "No harness stages declared", severity: "warning" });
@@ -27090,9 +27391,9 @@ function validateHarness(repoPath) {
27090
27391
  } else {
27091
27392
  issues.push({ file: "stages.json", message: "Stage registry missing", severity: "warning" });
27092
27393
  }
27093
- const readmePath = path12.join(harnessDir, "README.md");
27094
- if (fs13.existsSync(readmePath)) {
27095
- const content = fs13.readFileSync(readmePath, "utf8");
27394
+ const readmePath = path14.join(harnessDir, "README.md");
27395
+ if (fs14.existsSync(readmePath)) {
27396
+ const content = fs14.readFileSync(readmePath, "utf8");
27096
27397
  if (!content.includes(".har")) {
27097
27398
  issues.push({ file: "README.md", message: "README should document .har/ paths", severity: "warning" });
27098
27399
  }
@@ -27106,8 +27407,8 @@ function validateHarness(repoPath) {
27106
27407
  async function smokeTestHarness(repoPath) {
27107
27408
  const harnessDir = getHarnessDir(repoPath);
27108
27409
  const issues = [];
27109
- const setupScript = path12.join(harnessDir, "setup-infra.sh");
27110
- if (fs13.existsSync(setupScript)) {
27410
+ const setupScript = path14.join(harnessDir, "setup-infra.sh");
27411
+ if (fs14.existsSync(setupScript)) {
27111
27412
  const result = run(`bash "${setupScript}"`, { cwd: repoPath });
27112
27413
  if (result.code !== 0) {
27113
27414
  issues.push({
@@ -27122,8 +27423,8 @@ async function smokeTestHarness(repoPath) {
27122
27423
  }
27123
27424
 
27124
27425
  // src/harness/drift.ts
27125
- var fs14 = __toESM(require("fs"));
27126
- var path13 = __toESM(require("path"));
27426
+ var fs15 = __toESM(require("fs"));
27427
+ var path15 = __toESM(require("path"));
27127
27428
  var PROFILE_DIRS2 = {
27128
27429
  default: "har-boilerplate",
27129
27430
  cli: "har-boilerplate-cli",
@@ -27191,33 +27492,33 @@ function substituteProjectName(content, projectName) {
27191
27492
  return content.replace(/__PROJECT_NAME__/g, projectName).replace(/template___PROJECT_NAME__/g, `template_${projectName}`);
27192
27493
  }
27193
27494
  function listBoilerplateFiles(boilerplateDir) {
27194
- if (!fs14.existsSync(boilerplateDir)) return [];
27195
- return fs14.readdirSync(boilerplateDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
27495
+ if (!fs15.existsSync(boilerplateDir)) return [];
27496
+ return fs15.readdirSync(boilerplateDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
27196
27497
  }
27197
27498
  function compareHarnessToTemplate(repoPath) {
27198
- const resolved = path13.resolve(repoPath);
27499
+ const resolved = path15.resolve(repoPath);
27199
27500
  const manifest = readManifest(resolved);
27200
27501
  const profile = manifest?.profile ?? "default";
27201
27502
  const harnessDir = getHarnessDir(resolved);
27202
- const projectName = path13.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
27203
- const boilerplateDir = path13.join(resolveTemplatesDir(), PROFILE_DIRS2[profile]);
27503
+ const projectName = path15.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
27504
+ const boilerplateDir = path15.join(resolveTemplatesDir(), PROFILE_DIRS2[profile]);
27204
27505
  const templateFiles = listBoilerplateFiles(boilerplateDir);
27205
27506
  const missing = [];
27206
27507
  const checksumMismatch = [];
27207
27508
  const extra = [];
27208
27509
  const unchanged = [];
27209
27510
  for (const file of templateFiles) {
27210
- const templatePath = path13.join(boilerplateDir, file);
27211
- const harnessPath = path13.join(harnessDir, file);
27212
- let templateContent = fs14.readFileSync(templatePath, "utf8");
27511
+ const templatePath = path15.join(boilerplateDir, file);
27512
+ const harnessPath = path15.join(harnessDir, file);
27513
+ let templateContent = fs15.readFileSync(templatePath, "utf8");
27213
27514
  if (file === "harness.env") {
27214
27515
  templateContent = substituteProjectName(templateContent, projectName);
27215
27516
  }
27216
- if (!fs14.existsSync(harnessPath)) {
27517
+ if (!fs15.existsSync(harnessPath)) {
27217
27518
  missing.push(file);
27218
27519
  continue;
27219
27520
  }
27220
- const harnessChecksum = computeFileChecksum(fs14.readFileSync(harnessPath, "utf8"));
27521
+ const harnessChecksum = computeFileChecksum(fs15.readFileSync(harnessPath, "utf8"));
27221
27522
  const templateChecksum = computeFileChecksum(templateContent);
27222
27523
  if (harnessChecksum === templateChecksum) {
27223
27524
  unchanged.push(file);
@@ -27225,10 +27526,10 @@ function compareHarnessToTemplate(repoPath) {
27225
27526
  checksumMismatch.push(file);
27226
27527
  }
27227
27528
  }
27228
- if (fs14.existsSync(harnessDir)) {
27229
- for (const file of fs14.readdirSync(harnessDir)) {
27230
- const full = path13.join(harnessDir, file);
27231
- if (!fs14.statSync(full).isFile()) continue;
27529
+ if (fs15.existsSync(harnessDir)) {
27530
+ for (const file of fs15.readdirSync(harnessDir)) {
27531
+ const full = path15.join(harnessDir, file);
27532
+ if (!fs15.statSync(full).isFile()) continue;
27232
27533
  if (file === "manifest.json" || file.startsWith("ADAPT-PROMPT")) continue;
27233
27534
  if (profile === "cli" && CLI_EXPECTED_ABSENT.has(file)) {
27234
27535
  extra.push(file);
@@ -27255,9 +27556,9 @@ function compareHarnessToTemplate(repoPath) {
27255
27556
  }
27256
27557
 
27257
27558
  // src/harness/maintain-bundle.ts
27258
- var fs15 = __toESM(require("fs"));
27259
- var os2 = __toESM(require("os"));
27260
- var path14 = __toESM(require("path"));
27559
+ var fs16 = __toESM(require("fs"));
27560
+ var os3 = __toESM(require("os"));
27561
+ var path16 = __toESM(require("path"));
27261
27562
  var MAINTAIN_DIR = "maintain";
27262
27563
  var PROFILE_DIRS3 = {
27263
27564
  default: "har-boilerplate",
@@ -27268,14 +27569,14 @@ function substituteProjectName2(content, projectName) {
27268
27569
  return content.replace(/__PROJECT_NAME__/g, projectName).replace(/template___PROJECT_NAME__/g, `template_${projectName}`);
27269
27570
  }
27270
27571
  function projectNameFromRepo(repoPath) {
27271
- return path14.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
27572
+ return path16.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
27272
27573
  }
27273
27574
  function boilerplateDirForProfile(profile) {
27274
- return path14.join(resolveTemplatesDir(), PROFILE_DIRS3[profile]);
27575
+ return path16.join(resolveTemplatesDir(), PROFILE_DIRS3[profile]);
27275
27576
  }
27276
27577
  function readBundledTemplateContent(repoPath, profile, file) {
27277
- const templatePath = path14.join(boilerplateDirForProfile(profile), file);
27278
- let content = fs15.readFileSync(templatePath, "utf8");
27578
+ const templatePath = path16.join(boilerplateDirForProfile(profile), file);
27579
+ let content = fs16.readFileSync(templatePath, "utf8");
27279
27580
  if (file === "harness.env") {
27280
27581
  content = substituteProjectName2(content, projectNameFromRepo(repoPath));
27281
27582
  }
@@ -27306,12 +27607,12 @@ function actionHint(file, kind2) {
27306
27607
  return `Read maintain/diffs/${file}.diff and merge into .har/${file}.`;
27307
27608
  }
27308
27609
  function createUnifiedDiff(installedContent, templateContent, installedLabel, templateLabel) {
27309
- const tmpDir = fs15.mkdtempSync(path14.join(os2.tmpdir(), "har-maintain-diff-"));
27610
+ const tmpDir = fs16.mkdtempSync(path16.join(os3.tmpdir(), "har-maintain-diff-"));
27310
27611
  try {
27311
- const installedPath = path14.join(tmpDir, "installed");
27312
- const templatePath = path14.join(tmpDir, "template");
27313
- fs15.writeFileSync(installedPath, installedContent);
27314
- fs15.writeFileSync(templatePath, templateContent);
27612
+ const installedPath = path16.join(tmpDir, "installed");
27613
+ const templatePath = path16.join(tmpDir, "template");
27614
+ fs16.writeFileSync(installedPath, installedContent);
27615
+ fs16.writeFileSync(templatePath, templateContent);
27315
27616
  const result = run(`diff -u "${installedPath}" "${templatePath}"`);
27316
27617
  const body = result.stdout.trim();
27317
27618
  if (!body) return "";
@@ -27320,7 +27621,7 @@ function createUnifiedDiff(installedContent, templateContent, installedLabel, te
27320
27621
  ${body.split("\n").slice(2).join("\n")}
27321
27622
  `;
27322
27623
  } finally {
27323
- fs15.rmSync(tmpDir, { recursive: true, force: true });
27624
+ fs16.rmSync(tmpDir, { recursive: true, force: true });
27324
27625
  }
27325
27626
  }
27326
27627
  function buildActions(repoPath, profile, drift) {
@@ -27335,7 +27636,7 @@ function buildActions(repoPath, profile, drift) {
27335
27636
  });
27336
27637
  }
27337
27638
  for (const file of drift.checksumMismatch) {
27338
- const installedPath = path14.join(harnessDir, file);
27639
+ const installedPath = path16.join(harnessDir, file);
27339
27640
  const installedRel = `maintain/installed/${file}`;
27340
27641
  const templateRel = `maintain/templates/${file}`;
27341
27642
  const diffRel = `maintain/diffs/${file}.diff`;
@@ -27343,7 +27644,7 @@ function buildActions(repoPath, profile, drift) {
27343
27644
  file,
27344
27645
  kind: "drift",
27345
27646
  template: templateRel,
27346
- installed: fs15.existsSync(installedPath) ? installedRel : void 0,
27647
+ installed: fs16.existsSync(installedPath) ? installedRel : void 0,
27347
27648
  diff: diffRel,
27348
27649
  hint: actionHint(file, "drift")
27349
27650
  });
@@ -27414,28 +27715,28 @@ function buildReadme(report) {
27414
27715
  }
27415
27716
  function writeBundleArtifacts(repoPath, profile, drift, report) {
27416
27717
  const harnessDir = getHarnessDir(repoPath);
27417
- const bundleDir = path14.join(harnessDir, MAINTAIN_DIR);
27418
- if (fs15.existsSync(bundleDir)) {
27419
- fs15.rmSync(bundleDir, { recursive: true, force: true });
27718
+ const bundleDir = path16.join(harnessDir, MAINTAIN_DIR);
27719
+ if (fs16.existsSync(bundleDir)) {
27720
+ fs16.rmSync(bundleDir, { recursive: true, force: true });
27420
27721
  }
27421
27722
  const dirs = [
27422
27723
  bundleDir,
27423
- path14.join(bundleDir, "templates"),
27424
- path14.join(bundleDir, "installed"),
27425
- path14.join(bundleDir, "diffs"),
27426
- path14.join(bundleDir, "stale")
27724
+ path16.join(bundleDir, "templates"),
27725
+ path16.join(bundleDir, "installed"),
27726
+ path16.join(bundleDir, "diffs"),
27727
+ path16.join(bundleDir, "stale")
27427
27728
  ];
27428
27729
  for (const dir of dirs) {
27429
- fs15.mkdirSync(dir, { recursive: true });
27730
+ fs16.mkdirSync(dir, { recursive: true });
27430
27731
  }
27431
27732
  const affectedFiles = [...drift.missing, ...drift.checksumMismatch];
27432
27733
  for (const file of affectedFiles) {
27433
27734
  const templateContent = readBundledTemplateContent(repoPath, profile, file);
27434
- writeFileSafe(path14.join(bundleDir, "templates", file), templateContent);
27435
- const harnessPath = path14.join(harnessDir, file);
27436
- if (fs15.existsSync(harnessPath)) {
27437
- const installedContent = fs15.readFileSync(harnessPath, "utf8");
27438
- writeFileSafe(path14.join(bundleDir, "installed", file), installedContent);
27735
+ writeFileSafe(path16.join(bundleDir, "templates", file), templateContent);
27736
+ const harnessPath = path16.join(harnessDir, file);
27737
+ if (fs16.existsSync(harnessPath)) {
27738
+ const installedContent = fs16.readFileSync(harnessPath, "utf8");
27739
+ writeFileSafe(path16.join(bundleDir, "installed", file), installedContent);
27439
27740
  const diff = createUnifiedDiff(
27440
27741
  installedContent,
27441
27742
  templateContent,
@@ -27443,14 +27744,14 @@ function writeBundleArtifacts(repoPath, profile, drift, report) {
27443
27744
  `templates/${file}`
27444
27745
  );
27445
27746
  if (diff) {
27446
- writeFileSafe(path14.join(bundleDir, "diffs", `${file}.diff`), diff);
27747
+ writeFileSafe(path16.join(bundleDir, "diffs", `${file}.diff`), diff);
27447
27748
  }
27448
27749
  }
27449
27750
  }
27450
- writeFileSafe(path14.join(bundleDir, "README.md"), buildReadme(report));
27451
- writeFileSafe(path14.join(bundleDir, "drift-report.json"), JSON.stringify(report, null, 2) + "\n");
27751
+ writeFileSafe(path16.join(bundleDir, "README.md"), buildReadme(report));
27752
+ writeFileSafe(path16.join(bundleDir, "drift-report.json"), JSON.stringify(report, null, 2) + "\n");
27452
27753
  writeFileSafe(
27453
- path14.join(bundleDir, "validation.json"),
27754
+ path16.join(bundleDir, "validation.json"),
27454
27755
  JSON.stringify(report.validation, null, 2) + "\n"
27455
27756
  );
27456
27757
  if (report.stale.length > 0) {
@@ -27462,7 +27763,7 @@ function writeBundleArtifacts(repoPath, profile, drift, report) {
27462
27763
  ...report.stale.map((s2) => `- **${s2.file}** \u2014 ${s2.hint}`),
27463
27764
  ""
27464
27765
  ];
27465
- writeFileSafe(path14.join(bundleDir, "stale", "MANIFEST.md"), staleLines.join("\n"));
27766
+ writeFileSafe(path16.join(bundleDir, "stale", "MANIFEST.md"), staleLines.join("\n"));
27466
27767
  }
27467
27768
  return bundleDir;
27468
27769
  }
@@ -27488,9 +27789,9 @@ function buildMaintainBundle(repoPath, validation2, drift) {
27488
27789
  return { bundleDir, report };
27489
27790
  }
27490
27791
  function removeMaintainBundle(repoPath) {
27491
- const bundleDir = path14.join(getHarnessDir(repoPath), MAINTAIN_DIR);
27492
- if (fs15.existsSync(bundleDir)) {
27493
- fs15.rmSync(bundleDir, { recursive: true, force: true });
27792
+ const bundleDir = path16.join(getHarnessDir(repoPath), MAINTAIN_DIR);
27793
+ if (fs16.existsSync(bundleDir)) {
27794
+ fs16.rmSync(bundleDir, { recursive: true, force: true });
27494
27795
  }
27495
27796
  }
27496
27797
  function formatMaintainBundlePromptSection(report) {
@@ -27538,66 +27839,66 @@ function formatMaintainBundlePromptSection(report) {
27538
27839
  }
27539
27840
 
27540
27841
  // src/harness/stage-templates.ts
27541
- var fs17 = __toESM(require("fs"));
27542
- var path15 = __toESM(require("path"));
27842
+ var fs18 = __toESM(require("fs"));
27843
+ var path17 = __toESM(require("path"));
27543
27844
 
27544
27845
  // src/harness/parser.ts
27545
- var fs16 = __toESM(require("fs"));
27846
+ var fs17 = __toESM(require("fs"));
27546
27847
  function harnessExists(repoPath) {
27547
- return fs16.existsSync(`${repoPath}/.har/setup-infra.sh`);
27848
+ return fs17.existsSync(`${repoPath}/.har/setup-infra.sh`);
27548
27849
  }
27549
27850
 
27550
27851
  // src/harness/stage-templates.ts
27551
27852
  function resolveTemplateDir(templateId) {
27552
- const dir = path15.join(resolveTemplatesDir(), "stage-templates", templateId);
27553
- if (!fs17.existsSync(dir)) {
27853
+ const dir = path17.join(resolveTemplatesDir(), "stage-templates", templateId);
27854
+ if (!fs18.existsSync(dir)) {
27554
27855
  throw new Error(`Stage template not found: ${templateId}. Run npm run build.`);
27555
27856
  }
27556
27857
  return dir;
27557
27858
  }
27558
27859
  function readTemplateManifest(templateId) {
27559
- const manifestPath = path15.join(resolveTemplateDir(templateId), "template.manifest.json");
27560
- const raw = JSON.parse(fs17.readFileSync(manifestPath, "utf8"));
27860
+ const manifestPath = path17.join(resolveTemplateDir(templateId), "template.manifest.json");
27861
+ const raw = JSON.parse(fs18.readFileSync(manifestPath, "utf8"));
27561
27862
  if (raw.id !== templateId) {
27562
27863
  throw new Error(`Template manifest id mismatch: expected ${templateId}, got ${raw.id}`);
27563
27864
  }
27564
27865
  return raw;
27565
27866
  }
27566
27867
  function ensureParentDir(filePath) {
27567
- const parent = path15.dirname(filePath);
27568
- if (!fs17.existsSync(parent)) {
27569
- fs17.mkdirSync(parent, { recursive: true });
27868
+ const parent = path17.dirname(filePath);
27869
+ if (!fs18.existsSync(parent)) {
27870
+ fs18.mkdirSync(parent, { recursive: true });
27570
27871
  }
27571
27872
  }
27572
27873
  function copyTemplateFile(templateDir, file, repoPath, force) {
27573
- const srcPath = path15.join(templateDir, file.src);
27574
- const destPath = path15.join(repoPath, file.dest);
27575
- if (!fs17.existsSync(srcPath)) {
27874
+ const srcPath = path17.join(templateDir, file.src);
27875
+ const destPath = path17.join(repoPath, file.dest);
27876
+ if (!fs18.existsSync(srcPath)) {
27576
27877
  throw new Error(`Template file missing: ${file.src}`);
27577
27878
  }
27578
- if (fs17.existsSync(destPath) && !force) {
27879
+ if (fs18.existsSync(destPath) && !force) {
27579
27880
  throw new Error(
27580
27881
  `File already exists: ${file.dest}. Use --force to overwrite or remove it first.`
27581
27882
  );
27582
27883
  }
27583
27884
  ensureParentDir(destPath);
27584
- fs17.copyFileSync(srcPath, destPath);
27885
+ fs18.copyFileSync(srcPath, destPath);
27585
27886
  if (file.executable) {
27586
- fs17.chmodSync(destPath, 493);
27887
+ fs18.chmodSync(destPath, 493);
27587
27888
  }
27588
27889
  return { written: true, path: file.dest };
27589
27890
  }
27590
27891
  function mergePackageJson(repoPath, templateDir, fragmentRelPath, warnings) {
27591
- const packagePath = path15.join(repoPath, "package.json");
27592
- if (!fs17.existsSync(packagePath)) {
27892
+ const packagePath = path17.join(repoPath, "package.json");
27893
+ if (!fs18.existsSync(packagePath)) {
27593
27894
  throw new Error("No package.json in repo root. Add one before applying the Playwright template.");
27594
27895
  }
27595
- const fragmentPath = path15.join(templateDir, fragmentRelPath);
27596
- if (!fs17.existsSync(fragmentPath)) {
27896
+ const fragmentPath = path17.join(templateDir, fragmentRelPath);
27897
+ if (!fs18.existsSync(fragmentPath)) {
27597
27898
  throw new Error(`Package fragment missing: ${fragmentRelPath}`);
27598
27899
  }
27599
- const pkg = JSON.parse(fs17.readFileSync(packagePath, "utf8"));
27600
- const fragment = JSON.parse(fs17.readFileSync(fragmentPath, "utf8"));
27900
+ const pkg = JSON.parse(fs18.readFileSync(packagePath, "utf8"));
27901
+ const fragment = JSON.parse(fs18.readFileSync(fragmentPath, "utf8"));
27601
27902
  for (const section of ["scripts", "devDependencies"]) {
27602
27903
  const existing = pkg[section] ?? {};
27603
27904
  const incoming = fragment[section] ?? {};
@@ -27610,7 +27911,7 @@ function mergePackageJson(repoPath, templateDir, fragmentRelPath, warnings) {
27610
27911
  }
27611
27912
  pkg[section] = existing;
27612
27913
  }
27613
- fs17.writeFileSync(packagePath, JSON.stringify(pkg, null, 2) + "\n");
27914
+ fs18.writeFileSync(packagePath, JSON.stringify(pkg, null, 2) + "\n");
27614
27915
  }
27615
27916
  function patchStageRegistry(repoPath, manifest, force) {
27616
27917
  const registry2 = readStageRegistry(repoPath);
@@ -27651,8 +27952,8 @@ function assertHarnessPresent(repoPath) {
27651
27952
  }
27652
27953
  function assertStageNotPresent(repoPath, stageId, force) {
27653
27954
  if (force) return;
27654
- const scriptPath = path15.join(repoPath, ".har", "stages", `${stageId}.sh`);
27655
- if (fs17.existsSync(scriptPath)) {
27955
+ const scriptPath = path17.join(repoPath, ".har", "stages", `${stageId}.sh`);
27956
+ if (fs18.existsSync(scriptPath)) {
27656
27957
  throw new Error(
27657
27958
  `Stage script already exists: .har/stages/${stageId}.sh. Use --force to overwrite.`
27658
27959
  );
@@ -27665,7 +27966,7 @@ function assertStageNotPresent(repoPath, stageId, force) {
27665
27966
  }
27666
27967
  }
27667
27968
  function applyStageTemplate(repoPath, templateId, options = {}) {
27668
- const resolved = path15.resolve(repoPath);
27969
+ const resolved = path17.resolve(repoPath);
27669
27970
  const force = options.force ?? false;
27670
27971
  const warnings = [];
27671
27972
  const filesWritten = [];
@@ -27684,7 +27985,7 @@ function applyStageTemplate(repoPath, templateId, options = {}) {
27684
27985
  if (file.skipFlag === "skipCi" && options.skipCi) {
27685
27986
  continue;
27686
27987
  }
27687
- if (fs17.existsSync(path15.join(resolved, file.dest)) && !force) {
27988
+ if (fs18.existsSync(path17.join(resolved, file.dest)) && !force) {
27688
27989
  warnings.push(`Skipped optional file (exists): ${file.dest}`);
27689
27990
  continue;
27690
27991
  }
@@ -27747,11 +28048,11 @@ function validateAgentId(id, repoPath) {
27747
28048
  // src/core/harness.ts
27748
28049
  function listHarnessScripts(repoPath) {
27749
28050
  const harnessDir = getHarnessDir(repoPath);
27750
- if (!fs18.existsSync(harnessDir)) return [];
27751
- return fs18.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
28051
+ if (!fs19.existsSync(harnessDir)) return [];
28052
+ return fs19.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
27752
28053
  }
27753
28054
  function describeProject(repoPath) {
27754
- const resolved = path16.resolve(repoPath);
28055
+ const resolved = path18.resolve(repoPath);
27755
28056
  const manifest = readManifest(resolved);
27756
28057
  const present = harnessExists(resolved);
27757
28058
  return {
@@ -27771,8 +28072,8 @@ function describeProject(repoPath) {
27771
28072
  };
27772
28073
  }
27773
28074
  async function initHarness(options) {
27774
- const repoPath = path16.resolve(options.repoPath);
27775
- if (!fs18.existsSync(repoPath)) {
28075
+ const repoPath = path18.resolve(options.repoPath);
28076
+ if (!fs19.existsSync(repoPath)) {
27776
28077
  throw new Error(`Path not found: ${repoPath}`);
27777
28078
  }
27778
28079
  const scaffold = scaffoldHarnessBoilerplate(repoPath, {
@@ -27803,9 +28104,9 @@ async function initHarness(options) {
27803
28104
  };
27804
28105
  }
27805
28106
  async function maintainHarness(options) {
27806
- const repoPath = path16.resolve(options.repoPath);
28107
+ const repoPath = path18.resolve(options.repoPath);
27807
28108
  const harnessDir = getHarnessDir(repoPath);
27808
- if (!fs18.existsSync(harnessDir)) {
28109
+ if (!fs19.existsSync(harnessDir)) {
27809
28110
  throw new Error('No .har/ found. Run "har env init" first.');
27810
28111
  }
27811
28112
  if (options.auto) {
@@ -27856,8 +28157,8 @@ function addStageTemplate(repoPath, templateId, options = {}) {
27856
28157
  }
27857
28158
 
27858
28159
  // src/harness/adaptation-prompt.ts
27859
- var fs19 = __toESM(require("fs"));
27860
- var path17 = __toESM(require("path"));
28160
+ var fs20 = __toESM(require("fs"));
28161
+ var path19 = __toESM(require("path"));
27861
28162
  var ADAPTATION_PROMPT_FILE = "ADAPT-PROMPT.md";
27862
28163
  var PROFILE_HINTS = {
27863
28164
  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.",
@@ -27869,7 +28170,7 @@ function loadTemplate(name) {
27869
28170
  if (!filePath) {
27870
28171
  throw new Error(`Adaptation prompt template not found: ${name}. Run npm run build.`);
27871
28172
  }
27872
- return fs19.readFileSync(filePath, "utf8");
28173
+ return fs20.readFileSync(filePath, "utf8");
27873
28174
  }
27874
28175
  function applyProfilePlaceholders(content, profile) {
27875
28176
  return content.replace(/\{\{PROFILE\}\}/g, profile).replace(/\{\{PROFILE_HINT\}\}/g, PROFILE_HINTS[profile]);
@@ -27891,7 +28192,7 @@ function buildMaintainAdaptationPrompt(_repoPath, bundleReport) {
27891
28192
  }
27892
28193
  function writeAdaptationPrompt(repoPath, content) {
27893
28194
  const harnessDir = getHarnessDir(repoPath);
27894
- const filePath = path17.join(harnessDir, ADAPTATION_PROMPT_FILE);
28195
+ const filePath = path19.join(harnessDir, ADAPTATION_PROMPT_FILE);
27895
28196
  writeFileSafe(filePath, content);
27896
28197
  return filePath;
27897
28198
  }
@@ -27910,25 +28211,25 @@ function printAdaptationPrompt(content) {
27910
28211
  }
27911
28212
 
27912
28213
  // src/harness/cursor-rule.ts
27913
- var fs20 = __toESM(require("fs"));
27914
- var path18 = __toESM(require("path"));
27915
- var readline3 = __toESM(require("readline"));
28214
+ var fs21 = __toESM(require("fs"));
28215
+ var path20 = __toESM(require("path"));
28216
+ var readline4 = __toESM(require("readline"));
27916
28217
  var CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/har-workflow.mdc";
27917
28218
  function getCursorRulePath(repoPath) {
27918
- return path18.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
28219
+ return path20.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
27919
28220
  }
27920
28221
  function isCursorWorkspace(repoPath) {
27921
- return fs20.existsSync(path18.join(repoPath, ".cursor"));
28222
+ return fs21.existsSync(path20.join(repoPath, ".cursor"));
27922
28223
  }
27923
28224
  function cursorRuleExists(repoPath) {
27924
- return fs20.existsSync(getCursorRulePath(repoPath));
28225
+ return fs21.existsSync(getCursorRulePath(repoPath));
27925
28226
  }
27926
28227
  function scaffoldCursorRule(repoPath) {
27927
28228
  const templatePath = resolveTemplateFile("cursor-rule.mdc.template");
27928
28229
  if (!templatePath) {
27929
28230
  throw new Error("Cursor rule template not found (cursor-rule.mdc.template). Run npm run build.");
27930
28231
  }
27931
- const content = fs20.readFileSync(templatePath, "utf8");
28232
+ const content = fs21.readFileSync(templatePath, "utf8");
27932
28233
  writeFileSafe(getCursorRulePath(repoPath), content);
27933
28234
  }
27934
28235
  async function handleCursorRule(options) {
@@ -27962,20 +28263,20 @@ async function handleCursorRule(options) {
27962
28263
  }
27963
28264
  async function promptScaffoldCursorRule(exists) {
27964
28265
  const action = exists ? "Update" : "Create";
27965
- return askYesNo2(`${action} ${CURSOR_RULE_RELATIVE_PATH}? [Y/n]`);
28266
+ return askYesNo3(`${action} ${CURSOR_RULE_RELATIVE_PATH}? [Y/n]`);
27966
28267
  }
27967
- async function askYesNo2(question) {
27968
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
27969
- return new Promise((resolve26) => {
28268
+ async function askYesNo3(question) {
28269
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
28270
+ return new Promise((resolve27) => {
27970
28271
  process.stderr.write(`${question} `);
27971
28272
  rl.once("line", (answer) => {
27972
28273
  rl.close();
27973
28274
  const trimmed = answer.trim();
27974
28275
  if (trimmed === "") {
27975
- resolve26(true);
28276
+ resolve27(true);
27976
28277
  return;
27977
28278
  }
27978
- resolve26(/^y(es)?$/i.test(trimmed));
28279
+ resolve27(/^y(es)?$/i.test(trimmed));
27979
28280
  });
27980
28281
  });
27981
28282
  }
@@ -28054,26 +28355,26 @@ function buildStageResult(input) {
28054
28355
  }
28055
28356
 
28056
28357
  // src/core/local-executor.ts
28057
- var fs22 = __toESM(require("fs"));
28058
- var path20 = __toESM(require("path"));
28358
+ var fs23 = __toESM(require("fs"));
28359
+ var path22 = __toESM(require("path"));
28059
28360
 
28060
28361
  // src/core/slot-registry.ts
28061
- var fs21 = __toESM(require("fs"));
28062
- var path19 = __toESM(require("path"));
28362
+ var fs22 = __toESM(require("fs"));
28363
+ var path21 = __toESM(require("path"));
28063
28364
  function getSlotRegistryDir(repoPath) {
28064
- return path19.join(getHarnessDir(repoPath), "slots");
28365
+ return path21.join(getHarnessDir(repoPath), "slots");
28065
28366
  }
28066
28367
  function getSlotRegistryPath(repoPath, agentId) {
28067
- return path19.join(getSlotRegistryDir(repoPath), `agent-${agentId}.json`);
28368
+ return path21.join(getSlotRegistryDir(repoPath), `agent-${agentId}.json`);
28068
28369
  }
28069
28370
  function isSlotResumable(session) {
28070
28371
  return session?.status === "failed" || session?.status === "starting";
28071
28372
  }
28072
28373
  function readSlotRegistry(repoPath, agentId) {
28073
28374
  const file = getSlotRegistryPath(repoPath, agentId);
28074
- if (!fs21.existsSync(file)) return void 0;
28375
+ if (!fs22.existsSync(file)) return void 0;
28075
28376
  try {
28076
- const raw = JSON.parse(fs21.readFileSync(file, "utf8"));
28377
+ const raw = JSON.parse(fs22.readFileSync(file, "utf8"));
28077
28378
  const result = SlotRegistryEntrySchema.safeParse(raw);
28078
28379
  return result.success ? result.data : void 0;
28079
28380
  } catch {
@@ -28083,7 +28384,7 @@ function readSlotRegistry(repoPath, agentId) {
28083
28384
 
28084
28385
  // src/core/local-executor.ts
28085
28386
  function resolveRepoPath(repoPath) {
28086
- return path20.resolve(repoPath);
28387
+ return path22.resolve(repoPath);
28087
28388
  }
28088
28389
  function buildPreviewUrlsFromPorts(ports, env3) {
28089
28390
  const urls = {};
@@ -28135,14 +28436,14 @@ function substituteAgentId(value, agentId) {
28135
28436
  function resolveStageScriptPath(repoPath, stage) {
28136
28437
  const harnessDir = getHarnessDir(repoPath);
28137
28438
  if (stage.script) {
28138
- return path20.join(harnessDir, stage.script);
28439
+ return path22.join(harnessDir, stage.script);
28139
28440
  }
28140
28441
  if (stage.command) {
28141
28442
  const scriptName = stage.command.split(/\s+/)[0].replace(/^\.\/\.har\//, "").replace(/^\.\//, "");
28142
- return path20.join(harnessDir, scriptName);
28443
+ return path22.join(harnessDir, scriptName);
28143
28444
  }
28144
- const stageScript = path20.join(harnessDir, "stages", `${stage.id}.sh`);
28145
- if (fs22.existsSync(stageScript)) {
28445
+ const stageScript = path22.join(harnessDir, "stages", `${stage.id}.sh`);
28446
+ if (fs23.existsSync(stageScript)) {
28146
28447
  return stageScript;
28147
28448
  }
28148
28449
  throw new Error(
@@ -28157,7 +28458,7 @@ function buildExecutionPlan(repoPath, stage, options) {
28157
28458
  ...harnessEnv,
28158
28459
  ...stage.env ?? {}
28159
28460
  };
28160
- const cwd = stage.cwd ? path20.resolve(resolvedRepo, stage.cwd) : resolvedRepo;
28461
+ const cwd = stage.cwd ? path22.resolve(resolvedRepo, stage.cwd) : resolvedRepo;
28161
28462
  const extraArgs = options.args ?? [];
28162
28463
  const launchFlagArgs = [];
28163
28464
  if (stage.kind === "launch" && options.launchFlags) {
@@ -28234,19 +28535,19 @@ var LocalScriptExecutor = class {
28234
28535
  const repoPath = resolveRepoPath(ctx.repoPath);
28235
28536
  const harnessDir = getHarnessDir(repoPath);
28236
28537
  const artifactsDirName = getArtifactsDir(repoPath);
28237
- const artifactsDir = path20.join(harnessDir, artifactsDirName);
28238
- if (!fs22.existsSync(artifactsDir)) return [];
28538
+ const artifactsDir = path22.join(harnessDir, artifactsDirName);
28539
+ if (!fs23.existsSync(artifactsDir)) return [];
28239
28540
  const entries = [];
28240
28541
  const walk = (dir, prefix) => {
28241
- for (const entry of fs22.readdirSync(dir, { withFileTypes: true })) {
28242
- const full = path20.join(dir, entry.name);
28243
- const relative2 = path20.join(prefix, entry.name);
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);
28244
28545
  if (entry.isDirectory()) {
28245
28546
  walk(full, relative2);
28246
28547
  continue;
28247
28548
  }
28248
28549
  if (filter?.stageId && !relative2.includes(filter.stageId)) continue;
28249
- const stat = fs22.statSync(full);
28550
+ const stat = fs23.statSync(full);
28250
28551
  entries.push({
28251
28552
  path: full,
28252
28553
  relativePath: relative2,
@@ -28262,7 +28563,7 @@ var LocalScriptExecutor = class {
28262
28563
  var localScriptExecutor = new LocalScriptExecutor();
28263
28564
 
28264
28565
  // src/core/control-sync.ts
28265
- var path30 = __toESM(require("path"));
28566
+ var path32 = __toESM(require("path"));
28266
28567
 
28267
28568
  // src/core/control-config.ts
28268
28569
  var DEFAULT_CONTROL_API_URL = "http://localhost:3847";
@@ -28274,32 +28575,32 @@ function isControlEnabled() {
28274
28575
  }
28275
28576
 
28276
28577
  // src/core/control-registry.ts
28277
- var fs23 = __toESM(require("fs"));
28278
- var os3 = __toESM(require("os"));
28279
- var path21 = __toESM(require("path"));
28578
+ var fs24 = __toESM(require("fs"));
28579
+ var os4 = __toESM(require("os"));
28580
+ var path23 = __toESM(require("path"));
28280
28581
  function getRegistryPath() {
28281
28582
  if (process.env.HAR_CONTROL_REGISTRY_PATH) {
28282
- return path21.resolve(process.env.HAR_CONTROL_REGISTRY_PATH);
28583
+ return path23.resolve(process.env.HAR_CONTROL_REGISTRY_PATH);
28283
28584
  }
28284
- return path21.join(os3.homedir(), ".har", "repos.json");
28585
+ return path23.join(os4.homedir(), ".har", "repos.json");
28285
28586
  }
28286
28587
  function readRegistry() {
28287
28588
  const registryPath = getRegistryPath();
28288
28589
  try {
28289
- if (!fs23.existsSync(registryPath)) return { repos: [] };
28290
- return JSON.parse(fs23.readFileSync(registryPath, "utf8"));
28590
+ if (!fs24.existsSync(registryPath)) return { repos: [] };
28591
+ return JSON.parse(fs24.readFileSync(registryPath, "utf8"));
28291
28592
  } catch {
28292
28593
  return { repos: [] };
28293
28594
  }
28294
28595
  }
28295
28596
  function writeRegistry(registry2) {
28296
28597
  const registryPath = getRegistryPath();
28297
- fs23.mkdirSync(path21.dirname(registryPath), { recursive: true });
28298
- fs23.writeFileSync(registryPath, JSON.stringify(registry2, null, 2) + "\n");
28598
+ fs24.mkdirSync(path23.dirname(registryPath), { recursive: true });
28599
+ fs24.writeFileSync(registryPath, JSON.stringify(registry2, null, 2) + "\n");
28299
28600
  }
28300
28601
  function recordRepoForControlSync(repoPath) {
28301
28602
  if (process.env.HAR_CONTROL_DISABLED === "true") return;
28302
- const resolved = path21.resolve(repoPath);
28603
+ const resolved = path23.resolve(repoPath);
28303
28604
  if (!readManifest(resolved)) return;
28304
28605
  const registry2 = readRegistry();
28305
28606
  if (registry2.repos.includes(resolved)) return;
@@ -28308,7 +28609,7 @@ function recordRepoForControlSync(repoPath) {
28308
28609
  }
28309
28610
  function listRegisteredRepos() {
28310
28611
  const registry2 = readRegistry();
28311
- const kept = registry2.repos.filter((repoPath) => fs23.existsSync(repoPath) && readManifest(repoPath));
28612
+ const kept = registry2.repos.filter((repoPath) => fs24.existsSync(repoPath) && readManifest(repoPath));
28312
28613
  if (kept.length !== registry2.repos.length) {
28313
28614
  writeRegistry({ repos: kept });
28314
28615
  }
@@ -28316,17 +28617,17 @@ function listRegisteredRepos() {
28316
28617
  }
28317
28618
 
28318
28619
  // src/core/slot-status.ts
28319
- var fs28 = __toESM(require("fs"));
28320
- var os6 = __toESM(require("os"));
28321
- var path27 = __toESM(require("path"));
28620
+ var fs29 = __toESM(require("fs"));
28621
+ var os7 = __toESM(require("os"));
28622
+ var path29 = __toESM(require("path"));
28322
28623
  var import_child_process7 = require("child_process");
28323
28624
 
28324
28625
  // src/core/runs.ts
28325
28626
  var crypto2 = __toESM(require("crypto"));
28326
28627
  var import_child_process2 = require("child_process");
28327
- var fs24 = __toESM(require("fs"));
28328
- var os4 = __toESM(require("os"));
28329
- var path22 = __toESM(require("path"));
28628
+ var fs25 = __toESM(require("fs"));
28629
+ var os5 = __toESM(require("os"));
28630
+ var path24 = __toESM(require("path"));
28330
28631
  var RUNS_DIR = "runs";
28331
28632
  function gitCommonDir(cwd) {
28332
28633
  try {
@@ -28335,7 +28636,7 @@ function gitCommonDir(cwd) {
28335
28636
  encoding: "utf8",
28336
28637
  stdio: ["pipe", "pipe", "ignore"]
28337
28638
  }).trim();
28338
- return out ? path22.resolve(cwd, out) : void 0;
28639
+ return out ? path24.resolve(cwd, out) : void 0;
28339
28640
  } catch {
28340
28641
  return void 0;
28341
28642
  }
@@ -28357,7 +28658,7 @@ function gitPrefix(cwd) {
28357
28658
  }
28358
28659
  }
28359
28660
  function getRunsDir(harnessRoot) {
28360
- return path22.join(getHarnessDir(harnessRoot), RUNS_DIR);
28661
+ return path24.join(getHarnessDir(harnessRoot), RUNS_DIR);
28361
28662
  }
28362
28663
  function formatLocalDate(d2) {
28363
28664
  const y2 = d2.getFullYear();
@@ -28377,24 +28678,24 @@ function buildRunRelativePath(stageId, agentId, startedAt, runId, runsDir) {
28377
28678
  const timePart = formatLocalTime(started);
28378
28679
  const agentPart = agentId !== void 0 ? `_agent-${agentId}` : "";
28379
28680
  let filename = `${timePart}_${stageId}${agentPart}.json`;
28380
- const fullPath = path22.join(runsDir, dateFolder, filename);
28381
- if (fs24.existsSync(fullPath)) {
28681
+ const fullPath = path24.join(runsDir, dateFolder, filename);
28682
+ if (fs25.existsSync(fullPath)) {
28382
28683
  filename = `${timePart}_${stageId}${agentPart}-${runId.slice(0, 8)}.json`;
28383
28684
  }
28384
- return path22.join(dateFolder, filename);
28685
+ return path24.join(dateFolder, filename);
28385
28686
  }
28386
28687
  function resolveRunFilePath(harnessRoot, run2) {
28387
28688
  if (run2.relativePath) {
28388
- return path22.join(getRunsDir(harnessRoot), run2.relativePath);
28689
+ return path24.join(getRunsDir(harnessRoot), run2.relativePath);
28389
28690
  }
28390
- return path22.join(getRunsDir(harnessRoot), `${run2.runId}.json`);
28691
+ return path24.join(getRunsDir(harnessRoot), `${run2.runId}.json`);
28391
28692
  }
28392
28693
  function collectRunFiles(runsDir) {
28393
- if (!fs24.existsSync(runsDir)) return [];
28694
+ if (!fs25.existsSync(runsDir)) return [];
28394
28695
  const files = [];
28395
28696
  const walk = (dir) => {
28396
- for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
28397
- const full = path22.join(dir, entry.name);
28697
+ for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
28698
+ const full = path24.join(dir, entry.name);
28398
28699
  if (entry.isDirectory()) {
28399
28700
  walk(full);
28400
28701
  } else if (entry.name.endsWith(".json")) {
@@ -28408,30 +28709,30 @@ function collectRunFiles(runsDir) {
28408
28709
  function resolveAgentWorkDir(harnessRoot, agentId) {
28409
28710
  if (agentId === void 0) return void 0;
28410
28711
  const entry = readSlotRegistry(harnessRoot, agentId);
28411
- if (entry?.workDir && fs24.existsSync(entry.workDir)) return entry.workDir;
28712
+ if (entry?.workDir && fs25.existsSync(entry.workDir)) return entry.workDir;
28412
28713
  const env3 = readHarnessEnv(harnessRoot);
28413
- const projectName = env3.HARNESS_PROJECT_NAME ?? path22.basename(harnessRoot);
28414
- const worktreeDir = path22.join(os4.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
28714
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path24.basename(harnessRoot);
28715
+ const worktreeDir = path24.join(os5.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
28415
28716
  const relPrefix = gitPrefix(harnessRoot);
28416
28717
  const sessionEnvFiles = [];
28417
- const worktreesRoot = path22.join(os4.homedir(), "worktrees");
28418
- if (fs24.existsSync(worktreesRoot)) {
28718
+ const worktreesRoot = path24.join(os5.homedir(), "worktrees");
28719
+ if (fs25.existsSync(worktreesRoot)) {
28419
28720
  const suffix = `-har-agent-${agentId}-`;
28420
- for (const entry2 of fs24.readdirSync(worktreesRoot, { withFileTypes: true })) {
28721
+ for (const entry2 of fs25.readdirSync(worktreesRoot, { withFileTypes: true })) {
28421
28722
  if (!entry2.isDirectory() || !entry2.name.includes(suffix)) continue;
28422
- const sessionDir = path22.join(worktreesRoot, entry2.name);
28723
+ const sessionDir = path24.join(worktreesRoot, entry2.name);
28423
28724
  if (!sameGitCheckout(harnessRoot, sessionDir)) continue;
28424
- sessionEnvFiles.push(path22.join(sessionDir, relPrefix, `.env.agent.${agentId}`));
28725
+ sessionEnvFiles.push(path24.join(sessionDir, relPrefix, `.env.agent.${agentId}`));
28425
28726
  }
28426
28727
  }
28427
28728
  const candidates = [
28428
- path22.join(worktreeDir, `.env.agent.${agentId}`),
28429
- path22.join(harnessRoot, `.env.agent.${agentId}`),
28729
+ path24.join(worktreeDir, `.env.agent.${agentId}`),
28730
+ path24.join(harnessRoot, `.env.agent.${agentId}`),
28430
28731
  ...sessionEnvFiles.sort()
28431
28732
  ];
28432
28733
  for (const envFile of candidates) {
28433
- if (!fs24.existsSync(envFile)) continue;
28434
- const content = fs24.readFileSync(envFile, "utf8");
28734
+ if (!fs25.existsSync(envFile)) continue;
28735
+ const content = fs25.readFileSync(envFile, "utf8");
28435
28736
  const match = content.match(/^REPO_ROOT=(.+)$/m);
28436
28737
  if (match) return match[1].trim();
28437
28738
  }
@@ -28443,11 +28744,11 @@ function createRun(ctx, meta) {
28443
28744
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
28444
28745
  const runId = crypto2.randomUUID();
28445
28746
  const relativePath = buildRunRelativePath(meta.stageId, meta.agentId, startedAt, runId, runsDir);
28446
- const runFilePath = path22.join(runsDir, relativePath);
28447
- fs24.mkdirSync(path22.dirname(runFilePath), { recursive: true });
28747
+ const runFilePath = path24.join(runsDir, relativePath);
28748
+ fs25.mkdirSync(path24.dirname(runFilePath), { recursive: true });
28448
28749
  const run2 = RunRecordSchema.parse({
28449
28750
  runId,
28450
- repoPath: path22.resolve(ctx.repoPath),
28751
+ repoPath: path24.resolve(ctx.repoPath),
28451
28752
  harnessRoot,
28452
28753
  stageId: meta.stageId,
28453
28754
  kind: meta.kind,
@@ -28458,7 +28759,7 @@ function createRun(ctx, meta) {
28458
28759
  relativePath,
28459
28760
  trigger: ctx.trigger ?? "cli"
28460
28761
  });
28461
- fs24.writeFileSync(runFilePath, JSON.stringify(run2, null, 2) + "\n");
28762
+ fs25.writeFileSync(runFilePath, JSON.stringify(run2, null, 2) + "\n");
28462
28763
  return run2;
28463
28764
  }
28464
28765
  function finishRun(repoPath, runId, update) {
@@ -28477,13 +28778,13 @@ function finishRun(repoPath, runId, update) {
28477
28778
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
28478
28779
  });
28479
28780
  const runPath = resolveRunFilePath(harnessRoot, finished);
28480
- fs24.writeFileSync(runPath, JSON.stringify(finished, null, 2) + "\n");
28781
+ fs25.writeFileSync(runPath, JSON.stringify(finished, null, 2) + "\n");
28481
28782
  return finished;
28482
28783
  }
28483
28784
  function findRunRecord(harnessRoot, runId) {
28484
28785
  const runsDir = getRunsDir(harnessRoot);
28485
28786
  for (const filePath of collectRunFiles(runsDir)) {
28486
- const parsed = RunRecordSchema.safeParse(JSON.parse(fs24.readFileSync(filePath, "utf8")));
28787
+ const parsed = RunRecordSchema.safeParse(JSON.parse(fs25.readFileSync(filePath, "utf8")));
28487
28788
  if (parsed.success && parsed.data.runId === runId) {
28488
28789
  return parsed.data;
28489
28790
  }
@@ -28495,10 +28796,10 @@ function getRun(repoPath, runId) {
28495
28796
  }
28496
28797
  function listRuns(repoPath, filter = {}) {
28497
28798
  const runsDir = getRunsDir(resolveHarnessRoot(repoPath));
28498
- if (!fs24.existsSync(runsDir)) return [];
28799
+ if (!fs25.existsSync(runsDir)) return [];
28499
28800
  const runs = [];
28500
28801
  for (const filePath of collectRunFiles(runsDir)) {
28501
- const parsed = RunRecordSchema.safeParse(JSON.parse(fs24.readFileSync(filePath, "utf8")));
28802
+ const parsed = RunRecordSchema.safeParse(JSON.parse(fs25.readFileSync(filePath, "utf8")));
28502
28803
  if (!parsed.success) continue;
28503
28804
  if (filter.stageId && parsed.data.stageId !== filter.stageId) continue;
28504
28805
  runs.push(parsed.data);
@@ -28511,17 +28812,17 @@ function listRuns(repoPath, filter = {}) {
28511
28812
  }
28512
28813
 
28513
28814
  // src/core/slot-preflight.ts
28514
- var path26 = __toESM(require("path"));
28815
+ var path28 = __toESM(require("path"));
28515
28816
 
28516
28817
  // src/core/control-port.ts
28517
28818
  var import_child_process4 = require("child_process");
28518
- var fs26 = __toESM(require("fs"));
28519
- var path24 = __toESM(require("path"));
28819
+ var fs27 = __toESM(require("fs"));
28820
+ var path26 = __toESM(require("path"));
28520
28821
 
28521
28822
  // src/core/slot-ports.ts
28522
28823
  var import_child_process3 = require("child_process");
28523
- var fs25 = __toESM(require("fs"));
28524
- var path23 = __toESM(require("path"));
28824
+ var fs26 = __toESM(require("fs"));
28825
+ var path25 = __toESM(require("path"));
28525
28826
  function portStep(env3) {
28526
28827
  return Number(env3.HARNESS_PORT_STEP ?? 10);
28527
28828
  }
@@ -28577,7 +28878,7 @@ function allocateAppPorts(repoPath, agentId) {
28577
28878
  }
28578
28879
  function harnessUsesPm2(repoPath) {
28579
28880
  const harnessRoot = resolveHarnessRoot(repoPath);
28580
- return fs25.existsSync(path23.join(harnessRoot, ".har", "ecosystem.agent.template.cjs"));
28881
+ return fs26.existsSync(path25.join(harnessRoot, ".har", "ecosystem.agent.template.cjs"));
28581
28882
  }
28582
28883
 
28583
28884
  // src/core/control-port.ts
@@ -28632,13 +28933,13 @@ function controlDefaultPortWarnings(containers, defaultFrontendPort, allocatedFr
28632
28933
  return [formatControlDefaultPortWarning(control.name, defaultFrontendPort, allocatedFrontend)];
28633
28934
  }
28634
28935
  function inspectControlUpReadiness(repoPath) {
28635
- const resolved = path24.resolve(repoPath);
28936
+ const resolved = path26.resolve(repoPath);
28636
28937
  const port = parseControlHostPort();
28637
28938
  const containers = listDockerContainers();
28638
28939
  const control = controlContainerOnPort(containers, port);
28639
28940
  const warnings = [];
28640
- const controlHarnessDir = path24.join(resolved, "control", ".har");
28641
- const harnessSlot1Active = fs26.existsSync(controlHarnessDir) && readSlotRegistry(path24.join(resolved, "control"), 1)?.status === "active";
28941
+ const controlHarnessDir = path26.join(resolved, "control", ".har");
28942
+ const harnessSlot1Active = fs27.existsSync(controlHarnessDir) && readSlotRegistry(path26.join(resolved, "control"), 1)?.status === "active";
28642
28943
  if (control) {
28643
28944
  return {
28644
28945
  warnings,
@@ -28669,9 +28970,9 @@ function inspectControlUpReadiness(repoPath) {
28669
28970
  }
28670
28971
 
28671
28972
  // src/core/slot-launch-guard-occupied.ts
28672
- var fs27 = __toESM(require("fs"));
28673
- var os5 = __toESM(require("os"));
28674
- var path25 = __toESM(require("path"));
28973
+ var fs28 = __toESM(require("fs"));
28974
+ var os6 = __toESM(require("os"));
28975
+ var path27 = __toESM(require("path"));
28675
28976
  var import_child_process5 = require("child_process");
28676
28977
  function runGit(cwd, args) {
28677
28978
  try {
@@ -28690,7 +28991,7 @@ function worktreeDirty(worktreePath) {
28690
28991
  }
28691
28992
  function gitCommonDir2(cwd) {
28692
28993
  const out = runGit(cwd, "rev-parse --git-common-dir");
28693
- return out ? path25.resolve(cwd, out) : void 0;
28994
+ return out ? path27.resolve(cwd, out) : void 0;
28694
28995
  }
28695
28996
  function sameGitCheckout2(a2, b2) {
28696
28997
  const left2 = gitCommonDir2(a2);
@@ -28699,20 +29000,20 @@ function sameGitCheckout2(a2, b2) {
28699
29000
  }
28700
29001
  function discoverWorktree(harnessRoot, agentId, projectName) {
28701
29002
  const session = readSlotRegistry(harnessRoot, agentId);
28702
- if (session?.worktreePath && fs27.existsSync(session.worktreePath)) {
29003
+ if (session?.worktreePath && fs28.existsSync(session.worktreePath)) {
28703
29004
  return session.worktreePath;
28704
29005
  }
28705
- const legacy = path25.join(os5.homedir(), "worktrees", `${projectName}-agent-${agentId}`);
28706
- if (fs27.existsSync(legacy) && sameGitCheckout2(harnessRoot, legacy)) return legacy;
28707
- const worktreesRoot = path25.join(os5.homedir(), "worktrees");
28708
- if (!fs27.existsSync(worktreesRoot)) return void 0;
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;
28709
29010
  const suffix = `-har-agent-${agentId}-`;
28710
29011
  const relPrefix = runGit(harnessRoot, "rev-parse --show-prefix") ?? "";
28711
- for (const name of fs27.readdirSync(worktreesRoot)) {
29012
+ for (const name of fs28.readdirSync(worktreesRoot)) {
28712
29013
  if (!name.includes(suffix)) continue;
28713
- const candidate = path25.join(worktreesRoot, name);
29014
+ const candidate = path27.join(worktreesRoot, name);
28714
29015
  if (!sameGitCheckout2(harnessRoot, candidate)) continue;
28715
- if (!fs27.existsSync(path25.join(candidate, relPrefix, `.env.agent.${agentId}`))) continue;
29016
+ if (!fs28.existsSync(path27.join(candidate, relPrefix, `.env.agent.${agentId}`))) continue;
28716
29017
  return candidate;
28717
29018
  }
28718
29019
  return void 0;
@@ -28720,13 +29021,13 @@ function discoverWorktree(harnessRoot, agentId, projectName) {
28720
29021
  function collectOccupiedSlot(repoPath, agentId) {
28721
29022
  const harnessRoot = resolveHarnessRoot(repoPath);
28722
29023
  const env3 = readHarnessEnv(harnessRoot);
28723
- const projectName = env3.HARNESS_PROJECT_NAME ?? path25.basename(harnessRoot);
29024
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path27.basename(harnessRoot);
28724
29025
  const session = readSlotRegistry(harnessRoot, agentId);
28725
29026
  const worktreePath = discoverWorktree(harnessRoot, agentId, projectName);
28726
29027
  const workDir = session?.workDir;
28727
- const envInWorkDir = workDir ? fs27.existsSync(path25.join(workDir, `.env.agent.${agentId}`)) : false;
28728
- const envInRoot = fs27.existsSync(path25.join(harnessRoot, `.env.agent.${agentId}`));
28729
- const envInWorktree = worktreePath ? fs27.existsSync(path25.join(worktreePath, `.env.agent.${agentId}`)) : false;
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;
28730
29031
  const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
28731
29032
  if (!active) return void 0;
28732
29033
  const dirty = worktreePath ? worktreeDirty(worktreePath) : void 0;
@@ -28879,7 +29180,7 @@ function checkInfraPort(env3, varName, defaultPort, scanStart, scanEnd) {
28879
29180
  function inspectSlotReadiness(repoPath, agentId, options = {}) {
28880
29181
  const harnessRoot = resolveHarnessRoot(repoPath);
28881
29182
  const env3 = readHarnessEnv(harnessRoot);
28882
- const projectName = env3.HARNESS_PROJECT_NAME ?? path26.basename(harnessRoot);
29183
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path28.basename(harnessRoot);
28883
29184
  const usesPm2 = harnessUsesPm2(repoPath);
28884
29185
  const blockers = [];
28885
29186
  const remediations = [];
@@ -29081,7 +29382,7 @@ function readWorktreeBranch(worktreePath) {
29081
29382
  }
29082
29383
  function gitCommonDir3(cwd) {
29083
29384
  const out = runGit2(cwd, "rev-parse --git-common-dir");
29084
- return out ? path27.resolve(cwd, out) : void 0;
29385
+ return out ? path29.resolve(cwd, out) : void 0;
29085
29386
  }
29086
29387
  function sameGitCheckout3(a2, b2) {
29087
29388
  const left2 = gitCommonDir3(a2);
@@ -29093,12 +29394,12 @@ function gitPrefix2(cwd) {
29093
29394
  return out ?? "";
29094
29395
  }
29095
29396
  function discoverSessionWorktreePath(harnessRoot, agentId) {
29096
- const worktreesRoot = path27.join(os6.homedir(), "worktrees");
29097
- if (!fs28.existsSync(worktreesRoot)) return void 0;
29397
+ const worktreesRoot = path29.join(os7.homedir(), "worktrees");
29398
+ if (!fs29.existsSync(worktreesRoot)) return void 0;
29098
29399
  const suffix = `-har-agent-${agentId}-`;
29099
29400
  const relPrefix = gitPrefix2(harnessRoot);
29100
- const matches = fs28.readdirSync(worktreesRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.includes(suffix)).map((entry) => path27.join(worktreesRoot, entry.name)).filter(
29101
- (candidate) => sameGitCheckout3(harnessRoot, candidate) && fs28.existsSync(path27.join(candidate, relPrefix, `.env.agent.${agentId}`))
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}`))
29102
29403
  );
29103
29404
  return matches.sort()[0];
29104
29405
  }
@@ -29173,18 +29474,18 @@ function detectPm2Issue(projectName, agentId, session, procs) {
29173
29474
  }
29174
29475
  function collectSlotStatus(harnessRoot, agentId, runs, pm2Procs) {
29175
29476
  const env3 = readHarnessEnv(harnessRoot);
29176
- const projectName = env3.HARNESS_PROJECT_NAME ?? path27.basename(harnessRoot);
29477
+ const projectName = env3.HARNESS_PROJECT_NAME ?? path29.basename(harnessRoot);
29177
29478
  const session = readSlotRegistry(harnessRoot, agentId);
29178
- const legacyWorktreePath = path27.join(
29179
- os6.homedir(),
29479
+ const legacyWorktreePath = path29.join(
29480
+ os7.homedir(),
29180
29481
  "worktrees",
29181
29482
  `${projectName}-agent-${agentId}`
29182
29483
  );
29183
- const worktreePath = session?.worktreePath && fs28.existsSync(session.worktreePath) ? session.worktreePath : fs28.existsSync(legacyWorktreePath) ? legacyWorktreePath : discoverSessionWorktreePath(harnessRoot, agentId);
29484
+ const worktreePath = session?.worktreePath && fs29.existsSync(session.worktreePath) ? session.worktreePath : fs29.existsSync(legacyWorktreePath) ? legacyWorktreePath : discoverSessionWorktreePath(harnessRoot, agentId);
29184
29485
  const workDir = resolveAgentWorkDir(harnessRoot, agentId);
29185
- const envInWorkDir = workDir ? fs28.existsSync(path27.join(workDir, `.env.agent.${agentId}`)) : false;
29186
- const envInRoot = fs28.existsSync(path27.join(harnessRoot, `.env.agent.${agentId}`));
29187
- const envInWorktree = worktreePath ? fs28.existsSync(path27.join(worktreePath, `.env.agent.${agentId}`)) : false;
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;
29188
29489
  const active = session !== void 0 && session.status !== "completed" || envInWorkDir || envInRoot || envInWorktree;
29189
29490
  const latest = latestRunForAgent(runs, agentId);
29190
29491
  const verifyRun = runs.find((r2) => r2.agentId === agentId && r2.stageId === "verify");
@@ -29237,7 +29538,7 @@ function collectEnvironmentStatus(repoPath) {
29237
29538
  const slotIds = getAgentSlotIds(harnessRoot);
29238
29539
  const pm2Procs = listPm2Processes2();
29239
29540
  return {
29240
- repoPath: path27.resolve(repoPath),
29541
+ repoPath: path29.resolve(repoPath),
29241
29542
  harnessRoot,
29242
29543
  gitRemote: readGitRemote(harnessRoot),
29243
29544
  profile: manifest?.profile,
@@ -29248,13 +29549,13 @@ function collectEnvironmentStatus(repoPath) {
29248
29549
 
29249
29550
  // src/core/validations.ts
29250
29551
  var crypto3 = __toESM(require("crypto"));
29251
- var fs30 = __toESM(require("fs"));
29252
- var path29 = __toESM(require("path"));
29552
+ var fs31 = __toESM(require("fs"));
29553
+ var path31 = __toESM(require("path"));
29253
29554
 
29254
29555
  // src/core/change-batch.ts
29255
- var fs29 = __toESM(require("fs"));
29256
- var os7 = __toESM(require("os"));
29257
- var path28 = __toESM(require("path"));
29556
+ var fs30 = __toESM(require("fs"));
29557
+ var os8 = __toESM(require("os"));
29558
+ var path30 = __toESM(require("path"));
29258
29559
  function git(checkoutDir, args, env3) {
29259
29560
  const result = run(`git ${args}`, { cwd: checkoutDir, env: env3 });
29260
29561
  if (result.code !== 0) {
@@ -29270,7 +29571,7 @@ function isCheckoutRoot(checkoutDir) {
29270
29571
  const toplevel = tryGit(checkoutDir, "rev-parse --show-toplevel");
29271
29572
  if (!toplevel) return false;
29272
29573
  try {
29273
- return fs29.realpathSync(toplevel) === fs29.realpathSync(checkoutDir);
29574
+ return fs30.realpathSync(toplevel) === fs30.realpathSync(checkoutDir);
29274
29575
  } catch {
29275
29576
  return false;
29276
29577
  }
@@ -29292,7 +29593,7 @@ function isMergeOrRebaseInProgress(checkoutDir) {
29292
29593
  if (tryGit(checkoutDir, "rev-parse -q --verify MERGE_HEAD") !== void 0) return true;
29293
29594
  for (const dir of ["rebase-merge", "rebase-apply"]) {
29294
29595
  const gitPath = tryGit(checkoutDir, `rev-parse --git-path ${dir}`);
29295
- if (gitPath && fs29.existsSync(path28.resolve(checkoutDir, gitPath))) return true;
29596
+ if (gitPath && fs30.existsSync(path30.resolve(checkoutDir, gitPath))) return true;
29296
29597
  }
29297
29598
  return false;
29298
29599
  }
@@ -29315,8 +29616,8 @@ function parseNameStatus(output) {
29315
29616
  return files;
29316
29617
  }
29317
29618
  function computeWorktreeSnapshot(checkoutDir) {
29318
- const tmpDir = fs29.mkdtempSync(path28.join(os7.tmpdir(), "har-idx-"));
29319
- const tmpIndex = path28.join(tmpDir, "index");
29619
+ const tmpDir = fs30.mkdtempSync(path30.join(os8.tmpdir(), "har-idx-"));
29620
+ const tmpIndex = path30.join(tmpDir, "index");
29320
29621
  const env3 = { GIT_INDEX_FILE: tmpIndex };
29321
29622
  try {
29322
29623
  const headTree = getHeadTree(checkoutDir);
@@ -29339,35 +29640,35 @@ function computeWorktreeSnapshot(checkoutDir) {
29339
29640
  changedFiles: parseNameStatus(diffOutput)
29340
29641
  };
29341
29642
  } finally {
29342
- fs29.rmSync(tmpDir, { recursive: true, force: true });
29643
+ fs30.rmSync(tmpDir, { recursive: true, force: true });
29343
29644
  }
29344
29645
  }
29345
29646
 
29346
29647
  // src/core/validations.ts
29347
29648
  var VALIDATIONS_DIR = "validations";
29348
29649
  function getValidationsDir(checkoutDir) {
29349
- return path29.join(checkoutDir, ".har", VALIDATIONS_DIR);
29650
+ return path31.join(checkoutDir, ".har", VALIDATIONS_DIR);
29350
29651
  }
29351
29652
  function validationPath(checkoutDir, treeHash) {
29352
- return path29.join(getValidationsDir(checkoutDir), `${treeHash}.json`);
29653
+ return path31.join(getValidationsDir(checkoutDir), `${treeHash}.json`);
29353
29654
  }
29354
29655
  function writeRecord(checkoutDir, record2) {
29355
29656
  const dir = getValidationsDir(checkoutDir);
29356
- fs30.mkdirSync(dir, { recursive: true });
29357
- fs30.writeFileSync(
29657
+ fs31.mkdirSync(dir, { recursive: true });
29658
+ fs31.writeFileSync(
29358
29659
  validationPath(checkoutDir, record2.treeHash),
29359
29660
  `${JSON.stringify(record2, null, 2)}
29360
29661
  `
29361
29662
  );
29362
29663
  }
29363
29664
  function ensureValidationsIgnored(checkoutDir) {
29364
- const harDir = path29.join(checkoutDir, ".har");
29365
- if (!fs30.existsSync(harDir)) return;
29366
- const gitignorePath = path29.join(harDir, ".gitignore");
29367
- const content = fs30.existsSync(gitignorePath) ? fs30.readFileSync(gitignorePath, "utf8") : "";
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") : "";
29368
29669
  if (content.split("\n").some((line) => line.trim() === `${VALIDATIONS_DIR}/`)) return;
29369
29670
  const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
29370
- fs30.writeFileSync(gitignorePath, `${content}${suffix}${VALIDATIONS_DIR}/
29671
+ fs31.writeFileSync(gitignorePath, `${content}${suffix}${VALIDATIONS_DIR}/
29371
29672
  `);
29372
29673
  }
29373
29674
  function recordValidation(input) {
@@ -29384,8 +29685,8 @@ function recordValidation(input) {
29384
29685
  treeHash: snapshot.treeHash,
29385
29686
  headSha: snapshot.headSha,
29386
29687
  branch: snapshot.branch,
29387
- workDir: path29.resolve(input.checkoutDir),
29388
- harnessRoot: path29.resolve(input.harnessRoot),
29688
+ workDir: path31.resolve(input.checkoutDir),
29689
+ harnessRoot: path31.resolve(input.harnessRoot),
29389
29690
  agentId: input.agentId,
29390
29691
  status: input.status,
29391
29692
  full: input.full,
@@ -29397,16 +29698,16 @@ function recordValidation(input) {
29397
29698
  committedAt: existing?.committedAt
29398
29699
  });
29399
29700
  writeRecord(input.checkoutDir, record2);
29400
- if (path29.resolve(input.harnessRoot) !== path29.resolve(input.checkoutDir)) {
29701
+ if (path31.resolve(input.harnessRoot) !== path31.resolve(input.checkoutDir)) {
29401
29702
  writeRecord(input.harnessRoot, record2);
29402
29703
  }
29403
29704
  return record2;
29404
29705
  }
29405
29706
  function findValidation(checkoutDir, treeHash) {
29406
29707
  const file = validationPath(checkoutDir, treeHash);
29407
- if (!fs30.existsSync(file)) return void 0;
29708
+ if (!fs31.existsSync(file)) return void 0;
29408
29709
  try {
29409
- return ValidationRecordSchema.parse(JSON.parse(fs30.readFileSync(file, "utf8")));
29710
+ return ValidationRecordSchema.parse(JSON.parse(fs31.readFileSync(file, "utf8")));
29410
29711
  } catch {
29411
29712
  return void 0;
29412
29713
  }
@@ -29421,19 +29722,19 @@ function attachCommit(checkoutDir, treeHash, commitSha) {
29421
29722
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
29422
29723
  };
29423
29724
  writeRecord(checkoutDir, updated);
29424
- if (record2.harnessRoot && path29.resolve(record2.harnessRoot) !== path29.resolve(checkoutDir) && fs30.existsSync(path29.join(record2.harnessRoot, ".har"))) {
29725
+ if (record2.harnessRoot && path31.resolve(record2.harnessRoot) !== path31.resolve(checkoutDir) && fs31.existsSync(path31.join(record2.harnessRoot, ".har"))) {
29425
29726
  writeRecord(record2.harnessRoot, updated);
29426
29727
  }
29427
29728
  return updated;
29428
29729
  }
29429
29730
  function listValidations(harnessRoot) {
29430
29731
  const dir = getValidationsDir(harnessRoot);
29431
- if (!fs30.existsSync(dir)) return [];
29732
+ if (!fs31.existsSync(dir)) return [];
29432
29733
  const records = [];
29433
- for (const entry of fs30.readdirSync(dir)) {
29734
+ for (const entry of fs31.readdirSync(dir)) {
29434
29735
  if (!entry.endsWith(".json")) continue;
29435
29736
  try {
29436
- records.push(ValidationRecordSchema.parse(JSON.parse(fs30.readFileSync(path29.join(dir, entry), "utf8"))));
29737
+ records.push(ValidationRecordSchema.parse(JSON.parse(fs31.readFileSync(path31.join(dir, entry), "utf8"))));
29437
29738
  } catch {
29438
29739
  }
29439
29740
  }
@@ -29491,7 +29792,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
29491
29792
  const deadline = Date.now() + timeoutMs;
29492
29793
  while (Date.now() < deadline) {
29493
29794
  if (await isControlApiReachable(apiUrl)) return true;
29494
- await new Promise((resolve26) => setTimeout(resolve26, intervalMs));
29795
+ await new Promise((resolve27) => setTimeout(resolve27, intervalMs));
29495
29796
  }
29496
29797
  return false;
29497
29798
  }
@@ -29503,7 +29804,7 @@ async function syncAllKnownReposWithControl(options) {
29503
29804
  }
29504
29805
  const repoPaths = new Set(listRegisteredRepos());
29505
29806
  if (options?.cwd) {
29506
- const cwd = path30.resolve(options.cwd);
29807
+ const cwd = path32.resolve(options.cwd);
29507
29808
  if (readManifest(cwd)) repoPaths.add(cwd);
29508
29809
  }
29509
29810
  try {
@@ -29511,7 +29812,7 @@ async function syncAllKnownReposWithControl(options) {
29511
29812
  if (listResponse.ok) {
29512
29813
  const repos = await listResponse.json();
29513
29814
  for (const repo of repos) {
29514
- const resolved = path30.resolve(repo.path);
29815
+ const resolved = path32.resolve(repo.path);
29515
29816
  if (readManifest(resolved)) repoPaths.add(resolved);
29516
29817
  }
29517
29818
  }
@@ -29530,7 +29831,7 @@ async function syncAllKnownReposWithControl(options) {
29530
29831
  return { synced, failed };
29531
29832
  }
29532
29833
  async function registerRepoWithControl(options) {
29533
- const repoPath = path30.resolve(options.repoPath);
29834
+ const repoPath = path32.resolve(options.repoPath);
29534
29835
  const apiUrl = options.apiUrl ?? getControlApiUrl();
29535
29836
  const manifest = readManifest(repoPath);
29536
29837
  const stagesRegistry = readStageRegistry(repoPath);
@@ -29543,7 +29844,7 @@ async function registerRepoWithControl(options) {
29543
29844
  return postJson(`${apiUrl}/api/repos`, body, options.dryRun);
29544
29845
  }
29545
29846
  async function syncRepoWithControl(options) {
29546
- const repoPath = path30.resolve(options.repoPath);
29847
+ const repoPath = path32.resolve(options.repoPath);
29547
29848
  const apiUrl = options.apiUrl ?? getControlApiUrl();
29548
29849
  if (options.cloud) {
29549
29850
  const remote = createRemoteExecutor();
@@ -29571,7 +29872,7 @@ async function syncRepoWithControl(options) {
29571
29872
  const listResponse = await fetch(`${apiUrl}/api/repos`);
29572
29873
  if (!listResponse.ok) throw new Error("Failed to list repos from Control API");
29573
29874
  const repos = await listResponse.json();
29574
- const existing = repos.find((r2) => path30.resolve(r2.path) === repoPath);
29875
+ const existing = repos.find((r2) => path32.resolve(r2.path) === repoPath);
29575
29876
  if (!existing) throw new Error(`Repo not registered: ${repoPath}`);
29576
29877
  await syncRepoRunsAndSlots(apiUrl, existing.id, repoPath, options.dryRun);
29577
29878
  return;
@@ -29978,6 +30279,13 @@ var envCommand = {
29978
30279
  type: "boolean",
29979
30280
  default: false,
29980
30281
  describe: "Skip Cursor rule scaffolding"
30282
+ }).option("agents", {
30283
+ type: "string",
30284
+ describe: "Scaffold agent skills for these targets (comma-separated: claude,cursor,codex); auto-detected when omitted"
30285
+ }).option("no-agents", {
30286
+ type: "boolean",
30287
+ default: false,
30288
+ describe: "Skip agent skills scaffolding"
29981
30289
  }),
29982
30290
  handleInit
29983
30291
  ).command(
@@ -30002,6 +30310,13 @@ var envCommand = {
30002
30310
  type: "boolean",
30003
30311
  default: false,
30004
30312
  describe: "Skip Cursor rule scaffolding"
30313
+ }).option("agents", {
30314
+ type: "string",
30315
+ describe: "Scaffold agent skills for these targets (comma-separated: claude,cursor,codex); auto-detected when omitted"
30316
+ }).option("no-agents", {
30317
+ type: "boolean",
30318
+ default: false,
30319
+ describe: "Skip agent skills scaffolding"
30005
30320
  }),
30006
30321
  handleMaintain
30007
30322
  ).command(
@@ -30112,7 +30427,7 @@ var envCommand = {
30112
30427
  }
30113
30428
  };
30114
30429
  async function handleInit(argv) {
30115
- const repoPath = path31.resolve(argv.repo);
30430
+ const repoPath = path33.resolve(argv.repo);
30116
30431
  header("har env init");
30117
30432
  info(`Repository: ${repoPath}`);
30118
30433
  try {
@@ -30156,6 +30471,14 @@ async function handleInit(argv) {
30156
30471
  autoYes: argv.yes,
30157
30472
  mode: "init"
30158
30473
  });
30474
+ await handleAgentSkills({
30475
+ repoPath,
30476
+ agents: argv.agents,
30477
+ enabled: argv.noAgents ? false : void 0,
30478
+ autoYes: argv.yes,
30479
+ force: argv.force,
30480
+ mode: "init"
30481
+ });
30159
30482
  printNextSteps(argv.auto);
30160
30483
  } catch (err) {
30161
30484
  error(err.message);
@@ -30163,7 +30486,7 @@ async function handleInit(argv) {
30163
30486
  }
30164
30487
  }
30165
30488
  async function handleMaintain(argv) {
30166
- const repoPath = path31.resolve(argv.repo);
30489
+ const repoPath = path33.resolve(argv.repo);
30167
30490
  header("har env maintain");
30168
30491
  info(`Repository: ${repoPath}`);
30169
30492
  try {
@@ -30221,6 +30544,13 @@ async function handleMaintain(argv) {
30221
30544
  autoYes: argv.yes,
30222
30545
  mode: "maintain"
30223
30546
  });
30547
+ await handleAgentSkills({
30548
+ repoPath,
30549
+ agents: argv.agents,
30550
+ enabled: argv.noAgents ? false : void 0,
30551
+ autoYes: argv.yes,
30552
+ mode: "maintain"
30553
+ });
30224
30554
  } catch (err) {
30225
30555
  error(err.message);
30226
30556
  process.exit(1);
@@ -30252,7 +30582,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
30252
30582
  if (autoYes) {
30253
30583
  const proposal = readAgentMdProposal(repoPath);
30254
30584
  if (proposal) {
30255
- writeFileSafe(path31.join(repoPath, "AGENT.md"), proposal.content);
30585
+ writeFileSafe(path33.join(repoPath, "AGENT.md"), proposal.content);
30256
30586
  clearAgentMdProposal(repoPath);
30257
30587
  info("Applied AGENT.md proposal (--yes)");
30258
30588
  }
@@ -30261,7 +30591,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
30261
30591
  await promptApplyAgentMdProposal(repoPath);
30262
30592
  }
30263
30593
  async function handleAddStage(argv) {
30264
- const repoPath = path31.resolve(argv.repo);
30594
+ const repoPath = path33.resolve(argv.repo);
30265
30595
  if (argv.template !== "playwright") {
30266
30596
  error(`Unknown stage template: ${argv.template ?? "(missing)"}. Available: playwright`);
30267
30597
  process.exit(1);
@@ -30290,7 +30620,7 @@ async function handleAddStage(argv) {
30290
30620
  }
30291
30621
  }
30292
30622
  async function handlePreflight(argv) {
30293
- const repo = path31.resolve(argv.repo);
30623
+ const repo = path33.resolve(argv.repo);
30294
30624
  const agentId = validateAgentId(argv.id, repo);
30295
30625
  const result = await preflightEnvironment({
30296
30626
  repoPath: repo,
@@ -30308,7 +30638,7 @@ async function handlePreflight(argv) {
30308
30638
  process.exit(result.code);
30309
30639
  }
30310
30640
  async function handleLaunch(argv) {
30311
- const repo = path31.resolve(argv.repo);
30641
+ const repo = path33.resolve(argv.repo);
30312
30642
  const agentId = validateAgentId(argv.id, repo);
30313
30643
  let confirmReplace = argv.replace;
30314
30644
  let force = argv.force;
@@ -30318,10 +30648,10 @@ async function handleLaunch(argv) {
30318
30648
  if (!confirmReplace && process.stdin.isTTY && process.stdout.isTTY) {
30319
30649
  warn("Occupied slot \u2014 review before replacing:");
30320
30650
  console.error(guard.reason ?? "");
30321
- const readline4 = await import("readline");
30322
- const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
30323
- const answer = await new Promise((resolve26) => {
30324
- rl.question("Replace this slot? [y/N] ", resolve26);
30651
+ const readline5 = await import("readline");
30652
+ 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);
30325
30655
  });
30326
30656
  rl.close();
30327
30657
  if (!/^[Yy]$/.test(answer.trim())) {
@@ -30335,10 +30665,10 @@ async function handleLaunch(argv) {
30335
30665
  if (!guardAfterConfirm.allowed && guardAfterConfirm.blocked && guardAfterConfirm.slot?.dirty) {
30336
30666
  if (!force && process.stdin.isTTY && process.stdout.isTTY) {
30337
30667
  warn("Worktree has uncommitted changes.");
30338
- const readline4 = await import("readline");
30339
- const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
30340
- const answer = await new Promise((resolve26) => {
30341
- rl.question("Discard uncommitted changes? [y/N] ", resolve26);
30668
+ const readline5 = await import("readline");
30669
+ 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);
30342
30672
  });
30343
30673
  rl.close();
30344
30674
  if (!/^[Yy]$/.test(answer.trim())) {
@@ -30385,7 +30715,7 @@ async function handleRecover(argv) {
30385
30715
  });
30386
30716
  }
30387
30717
  async function handleVerify(argv) {
30388
- const repo = path31.resolve(argv.repo);
30718
+ const repo = path33.resolve(argv.repo);
30389
30719
  const agentId = validateAgentId(argv.id, repo);
30390
30720
  const result = await runVerification({
30391
30721
  repoPath: repo,
@@ -30397,7 +30727,7 @@ async function handleVerify(argv) {
30397
30727
  process.exit(result.code);
30398
30728
  }
30399
30729
  async function handleTeardown(argv) {
30400
- const repo = path31.resolve(argv.repo);
30730
+ const repo = path33.resolve(argv.repo);
30401
30731
  const agentId = validateAgentId(argv.id, repo);
30402
30732
  const result = await teardownEnvironment({
30403
30733
  repoPath: repo,
@@ -30408,7 +30738,7 @@ async function handleTeardown(argv) {
30408
30738
  process.exit(result.code);
30409
30739
  }
30410
30740
  async function handleComplete(argv) {
30411
- const repo = path31.resolve(argv.repo);
30741
+ const repo = path33.resolve(argv.repo);
30412
30742
  const agentId = validateAgentId(argv.id, repo);
30413
30743
  const result = await completeEnvironment({
30414
30744
  repoPath: repo,
@@ -30429,7 +30759,7 @@ async function handleComplete(argv) {
30429
30759
  process.exit(result.code);
30430
30760
  }
30431
30761
  async function handleStatus(argv) {
30432
- const repoPath = path31.resolve(argv.repo);
30762
+ const repoPath = path33.resolve(argv.repo);
30433
30763
  if (argv.json) {
30434
30764
  const status = collectEnvironmentStatus(repoPath);
30435
30765
  const output = EnvironmentStatusSchema.parse(status);
@@ -30442,7 +30772,7 @@ async function handleStatus(argv) {
30442
30772
  });
30443
30773
  }
30444
30774
  async function handleRunsList(argv) {
30445
- const repoPath = path31.resolve(argv.repo);
30775
+ const repoPath = path33.resolve(argv.repo);
30446
30776
  const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
30447
30777
  if (argv.json) {
30448
30778
  process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
@@ -30459,7 +30789,7 @@ async function handleRunsList(argv) {
30459
30789
  }
30460
30790
  }
30461
30791
  async function handleRunsGet(argv) {
30462
- const run2 = getRun(path31.resolve(argv.repo), argv.runId);
30792
+ const run2 = getRun(path33.resolve(argv.repo), argv.runId);
30463
30793
  if (!run2) {
30464
30794
  error(`Run not found: ${argv.runId}`);
30465
30795
  process.exit(1);
@@ -30532,11 +30862,11 @@ function printNextSteps(auto2) {
30532
30862
  }
30533
30863
 
30534
30864
  // src/cli/commands/control.ts
30535
- var path33 = __toESM(require("path"));
30865
+ var path35 = __toESM(require("path"));
30536
30866
 
30537
30867
  // src/core/control-lifecycle.ts
30538
30868
  var import_child_process8 = require("child_process");
30539
- var path32 = __toESM(require("path"));
30869
+ var path34 = __toESM(require("path"));
30540
30870
 
30541
30871
  // src/core/control-image.ts
30542
30872
  var DEFAULT_CONTROL_IMAGE = "theosfactory/har-control";
@@ -30555,13 +30885,13 @@ function shouldBuildControlLocally() {
30555
30885
 
30556
30886
  // src/core/control-lifecycle.ts
30557
30887
  function resolveControlDir() {
30558
- return path32.resolve(__dirname, "..", "control");
30888
+ return path34.resolve(__dirname, "..", "control");
30559
30889
  }
30560
30890
  function resolveControlComposeFiles(options) {
30561
30891
  const controlDir = resolveControlDir();
30562
- const files = [path32.join(controlDir, "docker-compose.yml")];
30892
+ const files = [path34.join(controlDir, "docker-compose.yml")];
30563
30893
  if (options?.build ?? shouldBuildControlLocally()) {
30564
- files.push(path32.join(controlDir, "docker-compose.build.yml"));
30894
+ files.push(path34.join(controlDir, "docker-compose.build.yml"));
30565
30895
  }
30566
30896
  return files;
30567
30897
  }
@@ -30730,7 +31060,7 @@ async function handleDown() {
30730
31060
  process.exit(code);
30731
31061
  }
30732
31062
  async function handleRegister(argv) {
30733
- const repoPath = path33.resolve(argv.repo);
31063
+ const repoPath = path35.resolve(argv.repo);
30734
31064
  header("har control register");
30735
31065
  info(`Repository: ${repoPath}`);
30736
31066
  try {
@@ -30750,7 +31080,7 @@ async function handleRegister(argv) {
30750
31080
  }
30751
31081
  }
30752
31082
  async function handleSync(argv) {
30753
- const repoPath = path33.resolve(argv.repo);
31083
+ const repoPath = path35.resolve(argv.repo);
30754
31084
  try {
30755
31085
  await syncRepoWithControl({
30756
31086
  repoPath,
@@ -30789,7 +31119,7 @@ async function handleWatch(argv) {
30789
31119
  };
30790
31120
  const tick = async () => {
30791
31121
  if (argv.repo) {
30792
- await syncOne(path33.resolve(argv.repo));
31122
+ await syncOne(path35.resolve(argv.repo));
30793
31123
  return;
30794
31124
  }
30795
31125
  try {
@@ -30820,12 +31150,12 @@ async function handleLogin(argv) {
30820
31150
  }
30821
31151
 
30822
31152
  // src/cli/commands/hooks.ts
30823
- var path35 = __toESM(require("path"));
31153
+ var path38 = __toESM(require("path"));
30824
31154
 
30825
31155
  // src/core/hooks.ts
30826
- var fs31 = __toESM(require("fs"));
30827
- var os8 = __toESM(require("os"));
30828
- var path34 = __toESM(require("path"));
31156
+ var fs32 = __toESM(require("fs"));
31157
+ var os9 = __toESM(require("os"));
31158
+ var path36 = __toESM(require("path"));
30829
31159
  var MARKER_START = "# >>> har commit gate (managed by `har hooks`) >>>";
30830
31160
  var MARKER_END = "# <<< har commit gate <<<";
30831
31161
  var PRE_COMMIT_BLOCK = [
@@ -30850,7 +31180,7 @@ function resolveCheckoutRoot(cwd) {
30850
31180
  function resolveHooksDir(checkoutDir) {
30851
31181
  const hooksPath = tryGit2(checkoutDir, "rev-parse --git-path hooks");
30852
31182
  if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
30853
- return path34.resolve(checkoutDir, hooksPath);
31183
+ return path36.resolve(checkoutDir, hooksPath);
30854
31184
  }
30855
31185
  function getConfiguredHooksPath(checkoutDir) {
30856
31186
  return tryGit2(checkoutDir, "config core.hooksPath") || void 0;
@@ -30858,9 +31188,9 @@ function getConfiguredHooksPath(checkoutDir) {
30858
31188
  function defaultHarInvocation() {
30859
31189
  const entry = process.argv[1];
30860
31190
  if (entry && entry.endsWith(".js")) {
30861
- return `"${process.execPath}" "${path34.resolve(entry)}"`;
31191
+ return `"${process.execPath}" "${path36.resolve(entry)}"`;
30862
31192
  }
30863
- if (entry) return `"${path34.resolve(entry)}"`;
31193
+ if (entry) return `"${path36.resolve(entry)}"`;
30864
31194
  return "har";
30865
31195
  }
30866
31196
  function buildHookScript(harInvocation, subcommand, failOpenNotice) {
@@ -30881,40 +31211,40 @@ exit 0
30881
31211
  `;
30882
31212
  }
30883
31213
  function upsertMarkedBlock(filePath, block) {
30884
- if (!fs31.existsSync(filePath)) {
30885
- fs31.writeFileSync(filePath, `#!/bin/sh
31214
+ if (!fs32.existsSync(filePath)) {
31215
+ fs32.writeFileSync(filePath, `#!/bin/sh
30886
31216
  ${block}
30887
31217
  `, { mode: 493 });
30888
31218
  return "created";
30889
31219
  }
30890
- const content = fs31.readFileSync(filePath, "utf8");
31220
+ const content = fs32.readFileSync(filePath, "utf8");
30891
31221
  if (content.includes(MARKER_START)) {
30892
31222
  const pattern = new RegExp(
30893
31223
  `${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}`
30894
31224
  );
30895
- fs31.writeFileSync(filePath, content.replace(pattern, block));
30896
- fs31.chmodSync(filePath, 493);
31225
+ fs32.writeFileSync(filePath, content.replace(pattern, block));
31226
+ fs32.chmodSync(filePath, 493);
30897
31227
  return "updated";
30898
31228
  }
30899
31229
  const suffix = content.endsWith("\n") ? "" : "\n";
30900
- fs31.writeFileSync(filePath, `${content}${suffix}
31230
+ fs32.writeFileSync(filePath, `${content}${suffix}
30901
31231
  ${block}
30902
31232
  `);
30903
- fs31.chmodSync(filePath, 493);
31233
+ fs32.chmodSync(filePath, 493);
30904
31234
  return "appended";
30905
31235
  }
30906
31236
  function removeMarkedBlock(filePath) {
30907
- if (!fs31.existsSync(filePath)) return false;
30908
- const content = fs31.readFileSync(filePath, "utf8");
31237
+ if (!fs32.existsSync(filePath)) return false;
31238
+ const content = fs32.readFileSync(filePath, "utf8");
30909
31239
  if (!content.includes(MARKER_START)) return false;
30910
31240
  const pattern = new RegExp(
30911
31241
  `\\n?${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}\\n?`
30912
31242
  );
30913
31243
  const stripped = content.replace(pattern, "\n");
30914
31244
  if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
30915
- fs31.rmSync(filePath);
31245
+ fs32.rmSync(filePath);
30916
31246
  } else {
30917
- fs31.writeFileSync(filePath, stripped);
31247
+ fs32.writeFileSync(filePath, stripped);
30918
31248
  }
30919
31249
  return true;
30920
31250
  }
@@ -30936,20 +31266,20 @@ Or re-run with --force to write into that directory anyway.`
30936
31266
  );
30937
31267
  }
30938
31268
  const hooksDir = resolveHooksDir(checkout);
30939
- fs31.mkdirSync(hooksDir, { recursive: true });
31269
+ fs32.mkdirSync(hooksDir, { recursive: true });
30940
31270
  const invocation = options.harInvocation ?? defaultHarInvocation();
30941
- fs31.writeFileSync(
30942
- path34.join(hooksDir, "har-pre-commit"),
31271
+ fs32.writeFileSync(
31272
+ path36.join(hooksDir, "har-pre-commit"),
30943
31273
  buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
30944
31274
  { mode: 493 }
30945
31275
  );
30946
- fs31.writeFileSync(
30947
- path34.join(hooksDir, "har-post-commit"),
31276
+ fs32.writeFileSync(
31277
+ path36.join(hooksDir, "har-post-commit"),
30948
31278
  buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
30949
31279
  { mode: 493 }
30950
31280
  );
30951
- const preCommit = upsertMarkedBlock(path34.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
30952
- const postCommit = upsertMarkedBlock(path34.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
31281
+ const preCommit = upsertMarkedBlock(path36.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
31282
+ const postCommit = upsertMarkedBlock(path36.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
30953
31283
  ensureValidationsIgnored(checkout);
30954
31284
  return { hooksDir, preCommit, postCommit };
30955
31285
  }
@@ -30958,12 +31288,12 @@ function uninstallHooks(repoPath) {
30958
31288
  if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
30959
31289
  const hooksDir = resolveHooksDir(checkout);
30960
31290
  let removed = false;
30961
- removed = removeMarkedBlock(path34.join(hooksDir, "pre-commit")) || removed;
30962
- removed = removeMarkedBlock(path34.join(hooksDir, "post-commit")) || removed;
31291
+ removed = removeMarkedBlock(path36.join(hooksDir, "pre-commit")) || removed;
31292
+ removed = removeMarkedBlock(path36.join(hooksDir, "post-commit")) || removed;
30963
31293
  for (const file of ["har-pre-commit", "har-post-commit"]) {
30964
- const full = path34.join(hooksDir, file);
30965
- if (fs31.existsSync(full)) {
30966
- fs31.rmSync(full);
31294
+ const full = path36.join(hooksDir, file);
31295
+ if (fs32.existsSync(full)) {
31296
+ fs32.rmSync(full);
30967
31297
  removed = true;
30968
31298
  }
30969
31299
  }
@@ -30980,9 +31310,9 @@ function getCommitGateConfig(checkoutDir) {
30980
31310
  function isAgentWorktree(checkoutDir) {
30981
31311
  const branch = getCurrentBranch(checkoutDir);
30982
31312
  if (branch && /^har-agent-\d+$/.test(branch)) return true;
30983
- const worktreesRoot = path34.join(os8.homedir(), "worktrees");
30984
- const resolved = path34.resolve(checkoutDir);
30985
- return resolved.startsWith(`${worktreesRoot}${path34.sep}`) && /-agent-\d+$/.test(path34.basename(resolved));
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));
30986
31316
  }
30987
31317
  function resolveEffectiveMode(checkoutDir, gate) {
30988
31318
  if (!gate.enabled) return "off";
@@ -30995,15 +31325,15 @@ function getHooksStatus(repoPath) {
30995
31325
  const hooksDir = resolveHooksDir(checkout);
30996
31326
  const gate = getCommitGateConfig(checkout);
30997
31327
  const hasBlock = (name) => {
30998
- const file = path34.join(hooksDir, name);
30999
- return fs31.existsSync(file) && fs31.readFileSync(file, "utf8").includes(MARKER_START);
31328
+ const file = path36.join(hooksDir, name);
31329
+ return fs32.existsSync(file) && fs32.readFileSync(file, "utf8").includes(MARKER_START);
31000
31330
  };
31001
31331
  return {
31002
31332
  checkout,
31003
31333
  hooksDir,
31004
31334
  configuredHooksPath: getConfiguredHooksPath(checkout),
31005
- preCommitInstalled: hasBlock("pre-commit") && fs31.existsSync(path34.join(hooksDir, "har-pre-commit")),
31006
- postCommitInstalled: hasBlock("post-commit") && fs31.existsSync(path34.join(hooksDir, "har-post-commit")),
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")),
31007
31337
  gate,
31008
31338
  effectiveMode: resolveEffectiveMode(checkout, gate)
31009
31339
  };
@@ -31014,7 +31344,7 @@ function checkCommitGate(cwd) {
31014
31344
  }
31015
31345
  const checkout = resolveCheckoutRoot(cwd);
31016
31346
  if (!checkout) return { exitCode: 0, messages: [] };
31017
- if (!fs31.existsSync(path34.join(checkout, ".har", "stages.json"))) {
31347
+ if (!fs32.existsSync(path36.join(checkout, ".har", "stages.json"))) {
31018
31348
  return { exitCode: 0, messages: [] };
31019
31349
  }
31020
31350
  const gate = getCommitGateConfig(checkout);
@@ -31084,13 +31414,114 @@ async function recordCommitAssociation(cwd) {
31084
31414
  }
31085
31415
  }
31086
31416
 
31417
+ // src/core/claude-hooks.ts
31418
+ var fs33 = __toESM(require("fs"));
31419
+ var path37 = __toESM(require("path"));
31420
+ var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
31421
+ var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
31422
+ var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
31423
+ var GUARD_COMMAND = `"$CLAUDE_PROJECT_DIR"/${CLAUDE_GUARD_RELATIVE_PATH}`;
31424
+ function isHarGuardEntry(entry) {
31425
+ return (entry.hooks ?? []).some(
31426
+ (hook) => typeof hook.command === "string" && hook.command.includes("claude-worktree-guard.sh")
31427
+ );
31428
+ }
31429
+ function readSettings(settingsPath) {
31430
+ if (!fs33.existsSync(settingsPath)) return {};
31431
+ const raw = fs33.readFileSync(settingsPath, "utf8").trim();
31432
+ if (raw === "") return {};
31433
+ const parsed = JSON.parse(raw);
31434
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
31435
+ throw new Error(`${settingsPath} is not a JSON object \u2014 fix it before installing the guard.`);
31436
+ }
31437
+ return parsed;
31438
+ }
31439
+ function writeSettings(settingsPath, settings) {
31440
+ fs33.mkdirSync(path37.dirname(settingsPath), { recursive: true });
31441
+ fs33.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
31442
+ }
31443
+ function installClaudeGuard(repoPath) {
31444
+ const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
31445
+ if (!templatePath) {
31446
+ throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
31447
+ }
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);
31453
+ const settings = readSettings(settingsPath);
31454
+ const hooks = settings.hooks ?? {};
31455
+ const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
31456
+ preToolUse.push({
31457
+ matcher: GUARD_MATCHER,
31458
+ hooks: [{ type: "command", command: GUARD_COMMAND }]
31459
+ });
31460
+ settings.hooks = { ...hooks, PreToolUse: preToolUse };
31461
+ writeSettings(settingsPath, settings);
31462
+ return { guardScript, settingsPath };
31463
+ }
31464
+ function uninstallClaudeGuard(repoPath) {
31465
+ let removed = false;
31466
+ const guardScript = path37.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
31467
+ if (fs33.existsSync(guardScript)) {
31468
+ fs33.rmSync(guardScript);
31469
+ removed = true;
31470
+ const parent = path37.dirname(guardScript);
31471
+ if (fs33.existsSync(parent) && fs33.readdirSync(parent).length === 0) fs33.rmdirSync(parent);
31472
+ }
31473
+ const settingsPath = path37.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31474
+ if (fs33.existsSync(settingsPath)) {
31475
+ const settings = readSettings(settingsPath);
31476
+ const preToolUse = settings.hooks?.PreToolUse;
31477
+ if (preToolUse) {
31478
+ const filtered = preToolUse.filter((entry) => !isHarGuardEntry(entry));
31479
+ if (filtered.length !== preToolUse.length) {
31480
+ removed = true;
31481
+ if (filtered.length > 0) {
31482
+ settings.hooks = { ...settings.hooks, PreToolUse: filtered };
31483
+ } else {
31484
+ const rest = { ...settings.hooks };
31485
+ delete rest.PreToolUse;
31486
+ if (Object.keys(rest).length > 0) {
31487
+ settings.hooks = rest;
31488
+ } else {
31489
+ delete settings.hooks;
31490
+ }
31491
+ }
31492
+ writeSettings(settingsPath, settings);
31493
+ }
31494
+ }
31495
+ }
31496
+ return { removed };
31497
+ }
31498
+ function claudeGuardInstalled(repoPath) {
31499
+ const settingsPath = path37.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
31500
+ if (!fs33.existsSync(settingsPath)) return false;
31501
+ try {
31502
+ const settings = readSettings(settingsPath);
31503
+ return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
31504
+ } catch {
31505
+ return false;
31506
+ }
31507
+ }
31508
+
31087
31509
  // src/cli/commands/hooks.ts
31088
31510
  function repoOption(y2) {
31089
31511
  return y2.option("repo", { type: "string", default: ".", describe: "Path to the repository" });
31090
31512
  }
31091
- function handleInstall(argv) {
31513
+ function handleInstall2(argv) {
31092
31514
  try {
31093
- const result = installHooks({ repoPath: path35.resolve(argv.repo), force: argv.force });
31515
+ if (argv.claude) {
31516
+ const result2 = installClaudeGuard(path38.resolve(argv.repo));
31517
+ success("Claude Code worktree guard installed.");
31518
+ info(`Guard script: ${result2.guardScript}`);
31519
+ info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
31520
+ info("File edits in the MAIN checkout are now blocked with a pointer to /har-wt.");
31521
+ info("Commit both files so the guard travels with the repo.");
31522
+ return;
31523
+ }
31524
+ const result = installHooks({ repoPath: path38.resolve(argv.repo), force: argv.force });
31094
31525
  success(`Commit gate installed in ${result.hooksDir}`);
31095
31526
  info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
31096
31527
  info("Commits of unverified change batches will be blocked in agent worktrees.");
@@ -31102,7 +31533,16 @@ function handleInstall(argv) {
31102
31533
  }
31103
31534
  function handleUninstall(argv) {
31104
31535
  try {
31105
- const result = uninstallHooks(path35.resolve(argv.repo));
31536
+ if (argv.claude) {
31537
+ const result2 = uninstallClaudeGuard(path38.resolve(argv.repo));
31538
+ if (result2.removed) {
31539
+ success("Claude Code worktree guard removed.");
31540
+ } else {
31541
+ info("No Claude Code worktree guard was installed.");
31542
+ }
31543
+ return;
31544
+ }
31545
+ const result = uninstallHooks(path38.resolve(argv.repo));
31106
31546
  if (result.removed) {
31107
31547
  success(`Commit gate removed from ${result.hooksDir}`);
31108
31548
  } else {
@@ -31115,7 +31555,7 @@ function handleUninstall(argv) {
31115
31555
  }
31116
31556
  function handleStatus2(argv) {
31117
31557
  try {
31118
- const status = getHooksStatus(path35.resolve(argv.repo));
31558
+ const status = getHooksStatus(path38.resolve(argv.repo));
31119
31559
  if (argv.json) {
31120
31560
  process.stdout.write(`${JSON.stringify(status, null, 2)}
31121
31561
  `);
@@ -31132,6 +31572,9 @@ function handleStatus2(argv) {
31132
31572
  `Gate: enabled=${status.gate.enabled} mode=${status.gate.mode} scope=${status.gate.scope}`
31133
31573
  );
31134
31574
  info(`Effective: ${status.effectiveMode} (in this checkout)`);
31575
+ info(
31576
+ `Claude guard: ${claudeGuardInstalled(path38.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
31577
+ );
31135
31578
  } catch (err) {
31136
31579
  error(err instanceof Error ? err.message : String(err));
31137
31580
  process.exitCode = 1;
@@ -31158,9 +31601,22 @@ var hooksCommand = {
31158
31601
  type: "boolean",
31159
31602
  default: false,
31160
31603
  describe: "Write hooks even when core.hooksPath points at a managed directory"
31604
+ }).option("claude", {
31605
+ type: "boolean",
31606
+ default: false,
31607
+ describe: "Install the Claude Code worktree guard instead (blocks Edit/Write in the main checkout, points to /har-wt)"
31161
31608
  }),
31162
- handleInstall
31163
- ).command("uninstall", "Remove the har commit gate hooks", repoOption, handleUninstall).command(
31609
+ handleInstall2
31610
+ ).command(
31611
+ "uninstall",
31612
+ "Remove the har commit gate hooks",
31613
+ (y2) => repoOption(y2).option("claude", {
31614
+ type: "boolean",
31615
+ default: false,
31616
+ describe: "Remove the Claude Code worktree guard instead"
31617
+ }),
31618
+ handleUninstall
31619
+ ).command(
31164
31620
  "status",
31165
31621
  "Show hook installation and gate configuration",
31166
31622
  (y2) => repoOption(y2).option("json", { type: "boolean", default: false }),
@@ -31378,10 +31834,10 @@ function assignProp(target, prop, value) {
31378
31834
  configurable: true
31379
31835
  });
31380
31836
  }
31381
- function getElementAtPath(obj, path37) {
31382
- if (!path37)
31837
+ function getElementAtPath(obj, path40) {
31838
+ if (!path40)
31383
31839
  return obj;
31384
- return path37.reduce((acc, key) => acc?.[key], obj);
31840
+ return path40.reduce((acc, key) => acc?.[key], obj);
31385
31841
  }
31386
31842
  function promiseAllObject(promisesObj) {
31387
31843
  const keys = Object.keys(promisesObj);
@@ -31701,11 +32157,11 @@ function aborted(x2, startIndex = 0) {
31701
32157
  }
31702
32158
  return false;
31703
32159
  }
31704
- function prefixIssues(path37, issues) {
32160
+ function prefixIssues(path40, issues) {
31705
32161
  return issues.map((iss) => {
31706
32162
  var _a3;
31707
32163
  (_a3 = iss).path ?? (_a3.path = []);
31708
- iss.path.unshift(path37);
32164
+ iss.path.unshift(path40);
31709
32165
  return iss;
31710
32166
  });
31711
32167
  }
@@ -37028,7 +37484,7 @@ var Protocol = class {
37028
37484
  return;
37029
37485
  }
37030
37486
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
37031
- await new Promise((resolve26) => setTimeout(resolve26, pollInterval));
37487
+ await new Promise((resolve27) => setTimeout(resolve27, pollInterval));
37032
37488
  options?.signal?.throwIfAborted();
37033
37489
  }
37034
37490
  } catch (error3) {
@@ -37045,7 +37501,7 @@ var Protocol = class {
37045
37501
  */
37046
37502
  request(request, resultSchema, options) {
37047
37503
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
37048
- return new Promise((resolve26, reject) => {
37504
+ return new Promise((resolve27, reject) => {
37049
37505
  const earlyReject = (error3) => {
37050
37506
  reject(error3);
37051
37507
  };
@@ -37123,7 +37579,7 @@ var Protocol = class {
37123
37579
  if (!parseResult.success) {
37124
37580
  reject(parseResult.error);
37125
37581
  } else {
37126
- resolve26(parseResult.data);
37582
+ resolve27(parseResult.data);
37127
37583
  }
37128
37584
  } catch (error3) {
37129
37585
  reject(error3);
@@ -37384,12 +37840,12 @@ var Protocol = class {
37384
37840
  }
37385
37841
  } catch {
37386
37842
  }
37387
- return new Promise((resolve26, reject) => {
37843
+ return new Promise((resolve27, reject) => {
37388
37844
  if (signal.aborted) {
37389
37845
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
37390
37846
  return;
37391
37847
  }
37392
- const timeoutId = setTimeout(resolve26, interval);
37848
+ const timeoutId = setTimeout(resolve27, interval);
37393
37849
  signal.addEventListener("abort", () => {
37394
37850
  clearTimeout(timeoutId);
37395
37851
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -38179,7 +38635,7 @@ var Server = class extends Protocol {
38179
38635
  };
38180
38636
 
38181
38637
  // src/mcp/server.ts
38182
- var path36 = __toESM(require("path"));
38638
+ var path39 = __toESM(require("path"));
38183
38639
 
38184
38640
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
38185
38641
  var import_node_process2 = __toESM(require("node:process"), 1);
@@ -38262,12 +38718,12 @@ var StdioServerTransport = class {
38262
38718
  this.onclose?.();
38263
38719
  }
38264
38720
  send(message) {
38265
- return new Promise((resolve26) => {
38721
+ return new Promise((resolve27) => {
38266
38722
  const json = serializeMessage(message);
38267
38723
  if (this._stdout.write(json)) {
38268
- resolve26();
38724
+ resolve27();
38269
38725
  } else {
38270
- this._stdout.once("drain", resolve26);
38726
+ this._stdout.once("drain", resolve27);
38271
38727
  }
38272
38728
  });
38273
38729
  }
@@ -38811,7 +39267,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
38811
39267
  const input = ControlUpInputSchema.parse({ ...args, repo });
38812
39268
  const result = await startControlAndSync({
38813
39269
  detach: input.detach,
38814
- cwd: path36.resolve(input.repo)
39270
+ cwd: path39.resolve(input.repo)
38815
39271
  });
38816
39272
  if (result.code !== 0) {
38817
39273
  return {
@@ -38875,7 +39331,7 @@ var mcpCommand = {
38875
39331
 
38876
39332
  // src/cli/index.ts
38877
39333
  async function runCli() {
38878
- await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(envCommand).command(controlCommand).command(hooksCommand).command(mcpCommand).demandCommand(1, "Please specify a command. Try: har env init").strict().help().version(getHarPackageVersion()).parse();
39334
+ await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(envCommand).command(agentsCommand).command(controlCommand).command(hooksCommand).command(mcpCommand).demandCommand(1, "Please specify a command. Try: har env init").strict().help().version(getHarPackageVersion()).parse();
38879
39335
  }
38880
39336
 
38881
39337
  // src/index.ts