@osfactory/har 1.2.0 → 1.4.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
@@ -593,8 +593,8 @@ var init_parseUtil = __esm({
593
593
  init_errors();
594
594
  init_en();
595
595
  makeIssue = (params) => {
596
- const { data, path: path91, errorMaps, issueData } = params;
597
- const fullPath = [...path91, ...issueData.path || []];
596
+ const { data, path: path97, errorMaps, issueData } = params;
597
+ const fullPath = [...path97, ...issueData.path || []];
598
598
  const fullIssue = {
599
599
  ...issueData,
600
600
  path: fullPath
@@ -902,11 +902,11 @@ var init_types = __esm({
902
902
  init_parseUtil();
903
903
  init_util();
904
904
  ParseInputLazyPath = class {
905
- constructor(parent, value, path91, key) {
905
+ constructor(parent, value, path97, key) {
906
906
  this._cachedPath = [];
907
907
  this.parent = parent;
908
908
  this.data = value;
909
- this._path = path91;
909
+ this._path = path97;
910
910
  this._key = key;
911
911
  }
912
912
  get path() {
@@ -4796,7 +4796,9 @@ var init_schema = __esm({
4796
4796
  toolCallId: external_exports.string().optional(),
4797
4797
  correlationId: external_exports.string().optional(),
4798
4798
  workUnitId: WorkUnitIdSchema.optional(),
4799
- attemptId: WorkAttemptIdSchema.optional()
4799
+ attemptId: WorkAttemptIdSchema.optional(),
4800
+ /** One occupancy of a slot (#316) — records are scoped to it, not to slot id. */
4801
+ occupancyKey: external_exports.string().min(1).optional()
4800
4802
  }).passthrough();
4801
4803
  AgentTrajectoryRecordSchema = AgentTrajectoryRecordV1Schema;
4802
4804
  USAGE_HARVEST_VERSION = 2;
@@ -4811,6 +4813,8 @@ var init_schema = __esm({
4811
4813
  suffix: external_exports.string().optional(),
4812
4814
  workUnitId: WorkUnitIdSchema.optional(),
4813
4815
  attemptId: WorkAttemptIdSchema.optional(),
4816
+ /** One occupancy of a slot (#316) — usage is scoped to it, not to slot id. */
4817
+ occupancyKey: external_exports.string().min(1).optional(),
4814
4818
  tokensInput: external_exports.number().nonnegative().default(0),
4815
4819
  tokensOutput: external_exports.number().nonnegative().default(0),
4816
4820
  tokensCacheRead: external_exports.number().nonnegative().default(0),
@@ -28632,6 +28636,149 @@ var init_usage_pricing = __esm({
28632
28636
  }
28633
28637
  });
28634
28638
 
28639
+ // packages/schemas/src/line.ts
28640
+ var LINE_CONTRACT_VERSION, LINE_BUNDLE_KIND, LINE_MANIFEST_FILES, LineIdSchema, StationIdSchema, LineSkillSchema, LineMcpSchema, LineWorkSchema, LineWaveCellSchema, LineStationSchema, LineGateStageSchema, LineGateSchema, LineExtraStageSchema, LineHandoffSchema, LineTravelerSchema, LineProgramSchema, LineManifestFileSchema, LineManifestSchema, LineLedgerEntrySchema, LineLedgerSchema;
28641
+ var init_line = __esm({
28642
+ "packages/schemas/src/line.ts"() {
28643
+ "use strict";
28644
+ init_zod();
28645
+ init_schema();
28646
+ LINE_CONTRACT_VERSION = 1;
28647
+ LINE_BUNDLE_KIND = "line";
28648
+ LINE_MANIFEST_FILES = ["line.manifest.json", "template.manifest.json"];
28649
+ LineIdSchema = external_exports.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/, "Line id must be a lowercase slug (letters, digits, ._-)");
28650
+ StationIdSchema = external_exports.string().min(1).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "Station id must be stable and shell-friendly");
28651
+ LineSkillSchema = external_exports.object({
28652
+ id: external_exports.string().min(1),
28653
+ role: external_exports.enum(["orchestrator", "station"]).default("station"),
28654
+ /** Install hint only — HAR never vendors third-party skill packs. */
28655
+ install: external_exports.string().min(1).optional()
28656
+ });
28657
+ LineMcpSchema = external_exports.object({
28658
+ name: external_exports.string().min(1),
28659
+ why: external_exports.string().min(1).optional(),
28660
+ required: external_exports.boolean().default(false)
28661
+ });
28662
+ LineWorkSchema = external_exports.object({
28663
+ /** `github`, `linear`, `none`, or a free string. */
28664
+ source: external_exports.string().min(1).default("none"),
28665
+ ids: external_exports.array(external_exports.string().min(1)).default([]),
28666
+ url: external_exports.string().min(1).optional(),
28667
+ optional: external_exports.boolean().default(false)
28668
+ });
28669
+ LineWaveCellSchema = external_exports.object({
28670
+ workId: external_exports.string().min(1).optional(),
28671
+ title: external_exports.string().min(1).optional()
28672
+ });
28673
+ LineStationSchema = external_exports.object({
28674
+ id: StationIdSchema,
28675
+ title: external_exports.string().min(1),
28676
+ description: external_exports.string().min(1).optional(),
28677
+ work: LineWorkSchema.optional(),
28678
+ /** Each inner array is one wave; cells inside a wave run in parallel slots. */
28679
+ waves: external_exports.array(external_exports.array(LineWaveCellSchema)).optional(),
28680
+ skills: external_exports.array(external_exports.string().min(1)).optional(),
28681
+ mcp: external_exports.array(external_exports.string().min(1)).optional()
28682
+ });
28683
+ LineGateStageSchema = external_exports.object({
28684
+ id: external_exports.string().min(1),
28685
+ /** Required from this station onward — this is the ratchet tag. */
28686
+ fromStation: StationIdSchema,
28687
+ tier: external_exports.enum(["quick", "full"]).default("full")
28688
+ });
28689
+ LineGateSchema = external_exports.object({
28690
+ /** Must be true: a factory line never drops an earlier station's stages. */
28691
+ cumulative: external_exports.literal(true),
28692
+ /** When set, the gate is skipped unless this env var is "1". */
28693
+ optInEnv: external_exports.string().min(1).nullable().default(null),
28694
+ stages: external_exports.array(LineGateStageSchema).default([])
28695
+ });
28696
+ LineExtraStageSchema = external_exports.object({
28697
+ id: external_exports.string().min(1),
28698
+ kind: HarnessStageKindSchema.default("test"),
28699
+ tier: external_exports.enum(["quick", "full"]).default("full"),
28700
+ description: external_exports.string().min(1).optional()
28701
+ });
28702
+ LineHandoffSchema = external_exports.object({
28703
+ /** Must be false: agents hand off, they do not merge or release. */
28704
+ autonomousShip: external_exports.literal(false),
28705
+ waitFor: external_exports.string().min(1).optional()
28706
+ });
28707
+ LineTravelerSchema = external_exports.object({
28708
+ kind: external_exports.string().min(1),
28709
+ ref: external_exports.string().min(1).optional(),
28710
+ note: external_exports.string().min(1).optional()
28711
+ });
28712
+ LineProgramSchema = external_exports.object({
28713
+ contractVersion: external_exports.literal(LINE_CONTRACT_VERSION),
28714
+ id: LineIdSchema,
28715
+ title: external_exports.string().min(1),
28716
+ description: external_exports.string().min(1).optional(),
28717
+ skills: external_exports.array(LineSkillSchema).default([]),
28718
+ mcp: external_exports.array(LineMcpSchema).default([]),
28719
+ plugins: external_exports.array(external_exports.string().min(1)).default([]),
28720
+ stations: external_exports.array(LineStationSchema).min(1),
28721
+ gate: LineGateSchema,
28722
+ extraStages: external_exports.array(LineExtraStageSchema).default([]),
28723
+ handoff: LineHandoffSchema,
28724
+ traveler: LineTravelerSchema.optional(),
28725
+ prototypeNotes: external_exports.array(external_exports.string().min(1)).default([])
28726
+ }).superRefine((data, ctx) => {
28727
+ const stationIds = new Set(data.stations.map((s) => s.id));
28728
+ if (stationIds.size !== data.stations.length) {
28729
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: "station ids must be unique" });
28730
+ }
28731
+ for (const stage of data.gate.stages) {
28732
+ if (!stationIds.has(stage.fromStation)) {
28733
+ ctx.addIssue({
28734
+ code: external_exports.ZodIssueCode.custom,
28735
+ message: `gate.stages[].fromStation "${stage.fromStation}" is not a station id`
28736
+ });
28737
+ }
28738
+ }
28739
+ });
28740
+ LineManifestFileSchema = external_exports.object({
28741
+ src: external_exports.string().min(1),
28742
+ dest: external_exports.string().min(1),
28743
+ executable: external_exports.boolean().optional()
28744
+ });
28745
+ LineManifestSchema = external_exports.object({
28746
+ kind: external_exports.literal(LINE_BUNDLE_KIND),
28747
+ id: LineIdSchema,
28748
+ title: external_exports.string().min(1).optional(),
28749
+ /** Bundle-relative path to the program JSON. */
28750
+ program: external_exports.string().min(1).default("line.json"),
28751
+ files: external_exports.array(LineManifestFileSchema).default([]),
28752
+ /** Stages to REGISTER in .har/stages.json — never added to verificationStages. */
28753
+ stages: external_exports.array(HarnessStageSchema).default([]),
28754
+ nextSteps: external_exports.array(external_exports.string().min(1)).default([]),
28755
+ docsPath: external_exports.string().min(1).optional()
28756
+ }).passthrough().superRefine((data, ctx) => {
28757
+ const raw = data;
28758
+ if (raw.verificationStages !== void 0) {
28759
+ ctx.addIssue({
28760
+ code: external_exports.ZodIssueCode.custom,
28761
+ message: "Line manifests must not declare verificationStages \u2014 line gate stages are opt-in and run via `har line gate`."
28762
+ });
28763
+ }
28764
+ });
28765
+ LineLedgerEntrySchema = external_exports.object({
28766
+ id: LineIdSchema,
28767
+ source: external_exports.enum(["bundled", "local", "path", "npm", "git"]),
28768
+ spec: external_exports.string().min(1),
28769
+ version: external_exports.string().optional(),
28770
+ /** Stages this line registered (registered only — not on verify). */
28771
+ stageIds: external_exports.array(external_exports.string().min(1)).default([]),
28772
+ programPath: external_exports.string().min(1),
28773
+ installedAt: external_exports.string()
28774
+ });
28775
+ LineLedgerSchema = external_exports.object({
28776
+ version: external_exports.literal("1"),
28777
+ lines: external_exports.array(LineLedgerEntrySchema).default([])
28778
+ });
28779
+ }
28780
+ });
28781
+
28635
28782
  // src/harness/schema.ts
28636
28783
  var init_schema2 = __esm({
28637
28784
  "src/harness/schema.ts"() {
@@ -28640,6 +28787,7 @@ var init_schema2 = __esm({
28640
28787
  init_harness_env();
28641
28788
  init_usage_authority();
28642
28789
  init_usage_pricing();
28790
+ init_line();
28643
28791
  }
28644
28792
  });
28645
28793
 
@@ -29043,7 +29191,7 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
29043
29191
  const stream = options.stream ?? false;
29044
29192
  const echoStdout = stream && (options.streamStdout ?? true);
29045
29193
  const { stream: _stream, streamStdout: _streamStdout, ...spawnOptions } = options;
29046
- return new Promise((resolve60) => {
29194
+ return new Promise((resolve66) => {
29047
29195
  const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
29048
29196
  ...spawnOptions,
29049
29197
  stdio: ["inherit", "pipe", "pipe"]
@@ -29058,14 +29206,14 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
29058
29206
  stderr += d;
29059
29207
  if (stream) process.stderr.write(d);
29060
29208
  });
29061
- proc.on("close", (code) => resolve60({ stdout, stderr, code: code ?? 0 }));
29209
+ proc.on("close", (code) => resolve66({ stdout, stderr, code: code ?? 0 }));
29062
29210
  });
29063
29211
  }
29064
29212
  function runShellCommand(command2, options = {}) {
29065
29213
  const stream = options.stream ?? false;
29066
29214
  const echoStdout = stream && (options.streamStdout ?? true);
29067
29215
  const { stream: _stream, streamStdout: _streamStdout, ...spawnOptions } = options;
29068
- return new Promise((resolve60) => {
29216
+ return new Promise((resolve66) => {
29069
29217
  const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
29070
29218
  ...spawnOptions,
29071
29219
  stdio: ["inherit", "pipe", "pipe"]
@@ -29080,7 +29228,7 @@ function runShellCommand(command2, options = {}) {
29080
29228
  stderr += d;
29081
29229
  if (stream) process.stderr.write(d);
29082
29230
  });
29083
- proc.on("close", (code) => resolve60({ stdout, stderr, code: code ?? 0 }));
29231
+ proc.on("close", (code) => resolve66({ stdout, stderr, code: code ?? 0 }));
29084
29232
  });
29085
29233
  }
29086
29234
  var import_child_process;
@@ -30071,7 +30219,7 @@ var init_slot_registry = __esm({
30071
30219
 
30072
30220
  // src/runtime/exec.ts
30073
30221
  function spawnResult(cmd, args, opts, shell) {
30074
- return new Promise((resolve60) => {
30222
+ return new Promise((resolve66) => {
30075
30223
  const stream = opts.stream ?? false;
30076
30224
  const proc = (0, import_child_process3.spawn)(cmd, args, {
30077
30225
  cwd: opts.cwd,
@@ -30083,8 +30231,8 @@ function spawnResult(cmd, args, opts, shell) {
30083
30231
  let stderr = "";
30084
30232
  proc.stdout?.on("data", (c) => stdout += c.toString());
30085
30233
  proc.stderr?.on("data", (c) => stderr += c.toString());
30086
- proc.on("error", (err) => resolve60({ code: 127, stdout, stderr: stderr || String(err) }));
30087
- proc.on("close", (code) => resolve60({ code: code ?? 1, stdout, stderr }));
30234
+ proc.on("error", (err) => resolve66({ code: 127, stdout, stderr: stderr || String(err) }));
30235
+ proc.on("close", (code) => resolve66({ code: code ?? 1, stdout, stderr }));
30088
30236
  });
30089
30237
  }
30090
30238
  var import_child_process3, fs13, path13, realSystemOps, defaultExec, stderrLog, realSleep;
@@ -30147,7 +30295,7 @@ var init_exec = __esm({
30147
30295
  process.stderr.write(`==> ${message}
30148
30296
  `);
30149
30297
  };
30150
- realSleep = (seconds) => new Promise((resolve60) => setTimeout(resolve60, seconds * 1e3));
30298
+ realSleep = (seconds) => new Promise((resolve66) => setTimeout(resolve66, seconds * 1e3));
30151
30299
  }
30152
30300
  });
30153
30301
 
@@ -32608,6 +32756,13 @@ function findValidation(checkoutDir, treeHash) {
32608
32756
  return void 0;
32609
32757
  }
32610
32758
  }
32759
+ function findPassingFullValidation(input) {
32760
+ if (!isCheckoutRoot(input.checkoutDir)) return void 0;
32761
+ const { treeHash } = computeWorktreeSnapshot(input.checkoutDir);
32762
+ const record2 = findValidation(input.checkoutDir, treeHash) ?? (input.harnessRoot ? findValidation(input.harnessRoot, treeHash) : void 0);
32763
+ if (record2?.status === "pass" && record2.full) return record2;
32764
+ return void 0;
32765
+ }
32611
32766
  function attachCommit(checkoutDir, treeHash, commitSha) {
32612
32767
  const record2 = findValidation(checkoutDir, treeHash);
32613
32768
  if (!record2) return void 0;
@@ -34738,16 +34893,16 @@ var init_local_executor = __esm({
34738
34893
  const walk = (dir, prefix) => {
34739
34894
  for (const entry of fs35.readdirSync(dir, { withFileTypes: true })) {
34740
34895
  const full = path36.join(dir, entry.name);
34741
- const relative5 = path36.join(prefix, entry.name);
34896
+ const relative6 = path36.join(prefix, entry.name);
34742
34897
  if (entry.isDirectory()) {
34743
- walk(full, relative5);
34898
+ walk(full, relative6);
34744
34899
  continue;
34745
34900
  }
34746
- if (filter2?.stageId && !relative5.includes(filter2.stageId)) continue;
34901
+ if (filter2?.stageId && !relative6.includes(filter2.stageId)) continue;
34747
34902
  const stat = fs35.statSync(full);
34748
34903
  entries.push({
34749
34904
  path: full,
34750
- relativePath: relative5,
34905
+ relativePath: relative6,
34751
34906
  sizeBytes: stat.size,
34752
34907
  modifiedAt: stat.mtime.toISOString()
34753
34908
  });
@@ -36663,7 +36818,7 @@ async function attempt(url) {
36663
36818
  async function readControlJson(url) {
36664
36819
  const first = await attempt(url);
36665
36820
  if (first.read.ok || !first.retryable) return first.read;
36666
- await new Promise((resolve60) => setTimeout(resolve60, RETRY_DELAY_MS));
36821
+ await new Promise((resolve66) => setTimeout(resolve66, RETRY_DELAY_MS));
36667
36822
  return (await attempt(url)).read;
36668
36823
  }
36669
36824
  async function readControlPages(options) {
@@ -37148,7 +37303,7 @@ async function postForResponse(url, headers, payload) {
37148
37303
  } catch (err) {
37149
37304
  lastErr = err;
37150
37305
  if (attempt2 < MAX_FETCH_ATTEMPTS) {
37151
- await new Promise((resolve60) => setTimeout(resolve60, attempt2 * 100));
37306
+ await new Promise((resolve66) => setTimeout(resolve66, attempt2 * 100));
37152
37307
  }
37153
37308
  }
37154
37309
  }
@@ -37388,7 +37543,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
37388
37543
  const deadline = Date.now() + timeoutMs;
37389
37544
  while (Date.now() < deadline) {
37390
37545
  if (await isControlApiReachable(apiUrl)) return true;
37391
- await new Promise((resolve60) => setTimeout(resolve60, intervalMs));
37546
+ await new Promise((resolve66) => setTimeout(resolve66, intervalMs));
37392
37547
  }
37393
37548
  return false;
37394
37549
  }
@@ -39037,15 +39192,15 @@ var require_route = __commonJS({
39037
39192
  };
39038
39193
  }
39039
39194
  function wrapConversion(toModel, graph) {
39040
- const path91 = [graph[toModel].parent, toModel];
39195
+ const path97 = [graph[toModel].parent, toModel];
39041
39196
  let fn = conversions[graph[toModel].parent][toModel];
39042
39197
  let cur = graph[toModel].parent;
39043
39198
  while (graph[cur].parent) {
39044
- path91.unshift(graph[cur].parent);
39199
+ path97.unshift(graph[cur].parent);
39045
39200
  fn = link(conversions[graph[cur].parent][cur], fn);
39046
39201
  cur = graph[cur].parent;
39047
39202
  }
39048
- fn.conversion = path91;
39203
+ fn.conversion = path97;
39049
39204
  return fn;
39050
39205
  }
39051
39206
  module2.exports = function(fromModel) {
@@ -45539,26 +45694,26 @@ var require_lib2 = __commonJS({
45539
45694
  return matches;
45540
45695
  };
45541
45696
  exports2.analyse = analyse;
45542
- var detectFile = (filepath, opts = {}) => new Promise((resolve60, reject) => {
45697
+ var detectFile = (filepath, opts = {}) => new Promise((resolve66, reject) => {
45543
45698
  let fd;
45544
- const fs76 = (0, node_1.default)();
45699
+ const fs79 = (0, node_1.default)();
45545
45700
  const handler = (err, buffer) => {
45546
45701
  if (fd) {
45547
- fs76.closeSync(fd);
45702
+ fs79.closeSync(fd);
45548
45703
  }
45549
45704
  if (err) {
45550
45705
  reject(err);
45551
45706
  } else if (buffer) {
45552
- resolve60((0, exports2.detect)(buffer));
45707
+ resolve66((0, exports2.detect)(buffer));
45553
45708
  } else {
45554
45709
  reject(new Error("No error and no buffer received"));
45555
45710
  }
45556
45711
  };
45557
45712
  const sampleSize = (opts === null || opts === void 0 ? void 0 : opts.sampleSize) || 0;
45558
45713
  if (sampleSize > 0) {
45559
- fd = fs76.openSync(filepath, "r");
45714
+ fd = fs79.openSync(filepath, "r");
45560
45715
  let sample = Buffer.allocUnsafe(sampleSize);
45561
- fs76.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
45716
+ fs79.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
45562
45717
  if (err) {
45563
45718
  handler(err, null);
45564
45719
  } else {
@@ -45570,22 +45725,22 @@ var require_lib2 = __commonJS({
45570
45725
  });
45571
45726
  return;
45572
45727
  }
45573
- fs76.readFile(filepath, handler);
45728
+ fs79.readFile(filepath, handler);
45574
45729
  });
45575
45730
  exports2.detectFile = detectFile;
45576
45731
  var detectFileSync = (filepath, opts = {}) => {
45577
- const fs76 = (0, node_1.default)();
45732
+ const fs79 = (0, node_1.default)();
45578
45733
  if (opts && opts.sampleSize) {
45579
- const fd = fs76.openSync(filepath, "r");
45734
+ const fd = fs79.openSync(filepath, "r");
45580
45735
  let sample = Buffer.allocUnsafe(opts.sampleSize);
45581
- const bytesRead = fs76.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
45736
+ const bytesRead = fs79.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
45582
45737
  if (bytesRead < opts.sampleSize) {
45583
45738
  sample = sample.subarray(0, bytesRead);
45584
45739
  }
45585
- fs76.closeSync(fd);
45740
+ fs79.closeSync(fd);
45586
45741
  return (0, exports2.detect)(sample);
45587
45742
  }
45588
- return (0, exports2.detect)(fs76.readFileSync(filepath));
45743
+ return (0, exports2.detect)(fs79.readFileSync(filepath));
45589
45744
  };
45590
45745
  exports2.detectFileSync = detectFileSync;
45591
45746
  exports2.default = {
@@ -50098,7 +50253,7 @@ var require_Observable = __commonJS({
50098
50253
  Observable2.prototype.forEach = function(next, promiseCtor) {
50099
50254
  var _this = this;
50100
50255
  promiseCtor = getPromiseCtor(promiseCtor);
50101
- return new promiseCtor(function(resolve60, reject) {
50256
+ return new promiseCtor(function(resolve66, reject) {
50102
50257
  var subscriber = new Subscriber_1.SafeSubscriber({
50103
50258
  next: function(value) {
50104
50259
  try {
@@ -50109,7 +50264,7 @@ var require_Observable = __commonJS({
50109
50264
  }
50110
50265
  },
50111
50266
  error: reject,
50112
- complete: resolve60
50267
+ complete: resolve66
50113
50268
  });
50114
50269
  _this.subscribe(subscriber);
50115
50270
  });
@@ -50131,14 +50286,14 @@ var require_Observable = __commonJS({
50131
50286
  Observable2.prototype.toPromise = function(promiseCtor) {
50132
50287
  var _this = this;
50133
50288
  promiseCtor = getPromiseCtor(promiseCtor);
50134
- return new promiseCtor(function(resolve60, reject) {
50289
+ return new promiseCtor(function(resolve66, reject) {
50135
50290
  var value;
50136
50291
  _this.subscribe(function(x) {
50137
50292
  return value = x;
50138
50293
  }, function(err) {
50139
50294
  return reject(err);
50140
50295
  }, function() {
50141
- return resolve60(value);
50296
+ return resolve66(value);
50142
50297
  });
50143
50298
  });
50144
50299
  };
@@ -52234,11 +52389,11 @@ var require_innerFrom = __commonJS({
52234
52389
  "use strict";
52235
52390
  var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P2, generator) {
52236
52391
  function adopt(value) {
52237
- return value instanceof P2 ? value : new P2(function(resolve60) {
52238
- resolve60(value);
52392
+ return value instanceof P2 ? value : new P2(function(resolve66) {
52393
+ resolve66(value);
52239
52394
  });
52240
52395
  }
52241
- return new (P2 || (P2 = Promise))(function(resolve60, reject) {
52396
+ return new (P2 || (P2 = Promise))(function(resolve66, reject) {
52242
52397
  function fulfilled(value) {
52243
52398
  try {
52244
52399
  step(generator.next(value));
@@ -52254,7 +52409,7 @@ var require_innerFrom = __commonJS({
52254
52409
  }
52255
52410
  }
52256
52411
  function step(result) {
52257
- result.done ? resolve60(result.value) : adopt(result.value).then(fulfilled, rejected);
52412
+ result.done ? resolve66(result.value) : adopt(result.value).then(fulfilled, rejected);
52258
52413
  }
52259
52414
  step((generator = generator.apply(thisArg, _arguments || [])).next());
52260
52415
  });
@@ -52336,14 +52491,14 @@ var require_innerFrom = __commonJS({
52336
52491
  }, i);
52337
52492
  function verb(n) {
52338
52493
  i[n] = o[n] && function(v2) {
52339
- return new Promise(function(resolve60, reject) {
52340
- v2 = o[n](v2), settle(resolve60, reject, v2.done, v2.value);
52494
+ return new Promise(function(resolve66, reject) {
52495
+ v2 = o[n](v2), settle(resolve66, reject, v2.done, v2.value);
52341
52496
  });
52342
52497
  };
52343
52498
  }
52344
- function settle(resolve60, reject, d, v2) {
52499
+ function settle(resolve66, reject, d, v2) {
52345
52500
  Promise.resolve(v2).then(function(v3) {
52346
- resolve60({ value: v3, done: d });
52501
+ resolve66({ value: v3, done: d });
52347
52502
  }, reject);
52348
52503
  }
52349
52504
  };
@@ -52962,7 +53117,7 @@ var require_lastValueFrom = __commonJS({
52962
53117
  var EmptyError_1 = require_EmptyError();
52963
53118
  function lastValueFrom2(source, config2) {
52964
53119
  var hasConfig = typeof config2 === "object";
52965
- return new Promise(function(resolve60, reject) {
53120
+ return new Promise(function(resolve66, reject) {
52966
53121
  var _hasValue = false;
52967
53122
  var _value;
52968
53123
  source.subscribe({
@@ -52973,9 +53128,9 @@ var require_lastValueFrom = __commonJS({
52973
53128
  error: reject,
52974
53129
  complete: function() {
52975
53130
  if (_hasValue) {
52976
- resolve60(_value);
53131
+ resolve66(_value);
52977
53132
  } else if (hasConfig) {
52978
- resolve60(config2.defaultValue);
53133
+ resolve66(config2.defaultValue);
52979
53134
  } else {
52980
53135
  reject(new EmptyError_1.EmptyError());
52981
53136
  }
@@ -52997,16 +53152,16 @@ var require_firstValueFrom = __commonJS({
52997
53152
  var Subscriber_1 = require_Subscriber();
52998
53153
  function firstValueFrom(source, config2) {
52999
53154
  var hasConfig = typeof config2 === "object";
53000
- return new Promise(function(resolve60, reject) {
53155
+ return new Promise(function(resolve66, reject) {
53001
53156
  var subscriber = new Subscriber_1.SafeSubscriber({
53002
53157
  next: function(value) {
53003
- resolve60(value);
53158
+ resolve66(value);
53004
53159
  subscriber.unsubscribe();
53005
53160
  },
53006
53161
  error: reject,
53007
53162
  complete: function() {
53008
53163
  if (hasConfig) {
53009
- resolve60(config2.defaultValue);
53164
+ resolve66(config2.defaultValue);
53010
53165
  } else {
53011
53166
  reject(new EmptyError_1.EmptyError());
53012
53167
  }
@@ -58798,14 +58953,14 @@ var require_run_async = __commonJS({
58798
58953
  return function() {
58799
58954
  var args = arguments;
58800
58955
  var originalThis = this;
58801
- var promise = new Promise(function(resolve60, reject) {
58956
+ var promise = new Promise(function(resolve66, reject) {
58802
58957
  var resolved = false;
58803
58958
  const wrappedResolve = function(value) {
58804
58959
  if (resolved) {
58805
58960
  console.warn("Run-async promise already resolved.");
58806
58961
  }
58807
58962
  resolved = true;
58808
- resolve60(value);
58963
+ resolve66(value);
58809
58964
  };
58810
58965
  var rejected = false;
58811
58966
  const wrappedReject = function(value) {
@@ -58913,32 +59068,32 @@ __export(otel_hooks_exports, {
58913
59068
  });
58914
59069
  function getOtelHooksHome() {
58915
59070
  if (process.env.HAR_OTEL_HOOKS_HOME) {
58916
- return path73.resolve(process.env.HAR_OTEL_HOOKS_HOME);
59071
+ return path76.resolve(process.env.HAR_OTEL_HOOKS_HOME);
58917
59072
  }
58918
- return path73.join(os22.homedir(), ".har", "otel-hooks");
59073
+ return path76.join(os22.homedir(), ".har", "otel-hooks");
58919
59074
  }
58920
59075
  function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
58921
- return path73.join(hooksHome, "otel_config.json");
59076
+ return path76.join(hooksHome, "otel_config.json");
58922
59077
  }
58923
59078
  function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
58924
- return path73.join(hooksHome, "run-otel-hook.sh");
59079
+ return path76.join(hooksHome, "run-otel-hook.sh");
58925
59080
  }
58926
59081
  function getOtelHooksStateDir(hooksHome = getOtelHooksHome()) {
58927
- return path73.join(hooksHome, "state");
59082
+ return path76.join(hooksHome, "state");
58928
59083
  }
58929
59084
  function localOtelHookBin(hooksHome) {
58930
- return path73.join(hooksHome, "node_modules", ".bin", "otel-hook");
59085
+ return path76.join(hooksHome, "node_modules", ".bin", "otel-hook");
58931
59086
  }
58932
59087
  function installedPackageVersion(hooksHome) {
58933
59088
  try {
58934
- const pkgPath = path73.join(
59089
+ const pkgPath = path76.join(
58935
59090
  hooksHome,
58936
59091
  "node_modules",
58937
59092
  "@osfactory",
58938
59093
  "otel-hook",
58939
59094
  "package.json"
58940
59095
  );
58941
- const pkg = JSON.parse(fs66.readFileSync(pkgPath, "utf8"));
59096
+ const pkg = JSON.parse(fs68.readFileSync(pkgPath, "utf8"));
58942
59097
  return typeof pkg.version === "string" ? pkg.version : null;
58943
59098
  } catch {
58944
59099
  return null;
@@ -58989,15 +59144,15 @@ function buildOtelHooksConfig(options) {
58989
59144
  };
58990
59145
  }
58991
59146
  function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
58992
- fs66.mkdirSync(hooksHome, { recursive: true });
59147
+ fs68.mkdirSync(hooksHome, { recursive: true });
58993
59148
  const configPath = getOtelHooksConfigPath(hooksHome);
58994
- fs66.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
59149
+ fs68.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
58995
59150
  `);
58996
59151
  return configPath;
58997
59152
  }
58998
59153
  function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
58999
- fs66.mkdirSync(hooksHome, { recursive: true });
59000
- fs66.mkdirSync(getOtelHooksStateDir(hooksHome), { recursive: true });
59154
+ fs68.mkdirSync(hooksHome, { recursive: true });
59155
+ fs68.mkdirSync(getOtelHooksStateDir(hooksHome), { recursive: true });
59001
59156
  const wrapperPath = getOtelHooksWrapperPath(hooksHome);
59002
59157
  const configPath = getOtelHooksConfigPath(hooksHome);
59003
59158
  const stateDir = getOtelHooksStateDir(hooksHome);
@@ -59009,9 +59164,9 @@ exec "${otelHookCommand}" run \\
59009
59164
  --state-dir "${stateDir}" \\
59010
59165
  "$@"
59011
59166
  `;
59012
- fs66.writeFileSync(wrapperPath, script, { mode: 493 });
59167
+ fs68.writeFileSync(wrapperPath, script, { mode: 493 });
59013
59168
  try {
59014
- fs66.chmodSync(wrapperPath, 493);
59169
+ fs68.chmodSync(wrapperPath, 493);
59015
59170
  } catch {
59016
59171
  }
59017
59172
  return wrapperPath;
@@ -59024,7 +59179,7 @@ function commandExists3(command2) {
59024
59179
  }
59025
59180
  function resolveOtelHookCommand(hooksHome) {
59026
59181
  const local = localOtelHookBin(hooksHome);
59027
- if (fs66.existsSync(local)) return local;
59182
+ if (fs68.existsSync(local)) return local;
59028
59183
  return null;
59029
59184
  }
59030
59185
  function resolveHooksInstaller(hooksHome) {
@@ -59039,7 +59194,7 @@ function resolveHooksInstaller(hooksHome) {
59039
59194
  return candidates.find((candidate) => commandExists3(candidate.command)) ?? null;
59040
59195
  }
59041
59196
  function tryInstallPackage(hooksHome) {
59042
- fs66.mkdirSync(hooksHome, { recursive: true });
59197
+ fs68.mkdirSync(hooksHome, { recursive: true });
59043
59198
  const installer = resolveHooksInstaller(hooksHome);
59044
59199
  if (!installer) {
59045
59200
  return {
@@ -59052,7 +59207,7 @@ function tryInstallPackage(hooksHome) {
59052
59207
  encoding: "utf8",
59053
59208
  timeout: 18e4
59054
59209
  });
59055
- if (result.status === 0 && fs66.existsSync(localOtelHookBin(hooksHome))) {
59210
+ if (result.status === 0 && fs68.existsSync(localOtelHookBin(hooksHome))) {
59056
59211
  return { ok: true, detail: described };
59057
59212
  }
59058
59213
  return {
@@ -59078,14 +59233,14 @@ function uninstallLegacyPythonHooks(hooksHome) {
59078
59233
  }
59079
59234
  function removeLegacyPythonArtifacts(hooksHome) {
59080
59235
  const legacyPaths = [
59081
- path73.join(hooksHome, ".venv"),
59082
- path73.join(hooksHome, "otel_config.yaml"),
59083
- path73.join(hooksHome, "venv")
59236
+ path76.join(hooksHome, ".venv"),
59237
+ path76.join(hooksHome, "otel_config.yaml"),
59238
+ path76.join(hooksHome, "venv")
59084
59239
  ];
59085
59240
  for (const target of legacyPaths) {
59086
59241
  try {
59087
- if (fs66.existsSync(target)) {
59088
- fs66.rmSync(target, { recursive: true, force: true });
59242
+ if (fs68.existsSync(target)) {
59243
+ fs68.rmSync(target, { recursive: true, force: true });
59089
59244
  }
59090
59245
  } catch {
59091
59246
  }
@@ -59126,10 +59281,10 @@ function runSetupProvider(providerId, otelHookCommand, wrapperPath) {
59126
59281
  };
59127
59282
  }
59128
59283
  function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
59129
- if (!fs66.existsSync(filePath)) return false;
59284
+ if (!fs68.existsSync(filePath)) return false;
59130
59285
  let raw;
59131
59286
  try {
59132
- raw = fs66.readFileSync(filePath, "utf8");
59287
+ raw = fs68.readFileSync(filePath, "utf8");
59133
59288
  } catch {
59134
59289
  return false;
59135
59290
  }
@@ -59176,15 +59331,15 @@ function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
59176
59331
  return changed;
59177
59332
  };
59178
59333
  if (!walk(parsed)) return false;
59179
- fs66.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59334
+ fs68.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59180
59335
  `);
59181
59336
  return true;
59182
59337
  }
59183
59338
  function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
59184
- if (!fs66.existsSync(filePath)) return false;
59339
+ if (!fs68.existsSync(filePath)) return false;
59185
59340
  let raw;
59186
59341
  try {
59187
- raw = fs66.readFileSync(filePath, "utf8");
59342
+ raw = fs68.readFileSync(filePath, "utf8");
59188
59343
  } catch {
59189
59344
  return false;
59190
59345
  }
@@ -59219,21 +59374,21 @@ function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
59219
59374
  }
59220
59375
  }
59221
59376
  if (!changed) return false;
59222
- fs66.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59377
+ fs68.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59223
59378
  `);
59224
59379
  return true;
59225
59380
  }
59226
59381
  function rewriteKnownHookRegistrations(wrapperPath) {
59227
59382
  const home = os22.homedir();
59228
59383
  const candidates = [
59229
- path73.join(home, ".cursor", "hooks.json"),
59230
- path73.join(home, ".claude", "settings.json"),
59231
- path73.join(home, ".codex", "hooks.json")
59384
+ path76.join(home, ".cursor", "hooks.json"),
59385
+ path76.join(home, ".claude", "settings.json"),
59386
+ path76.join(home, ".codex", "hooks.json")
59232
59387
  ];
59233
59388
  for (const filePath of candidates) {
59234
59389
  try {
59235
59390
  rewriteHookCommandsToWrapper(filePath, wrapperPath);
59236
- if (filePath.endsWith(`${path73.sep}.cursor${path73.sep}hooks.json`)) {
59391
+ if (filePath.endsWith(`${path76.sep}.cursor${path76.sep}hooks.json`)) {
59237
59392
  pruneLegacyCursorHookEvents(filePath, wrapperPath);
59238
59393
  }
59239
59394
  } catch {
@@ -59249,7 +59404,7 @@ function ensureOtelHooks(options) {
59249
59404
  const hooksHome = getOtelHooksHome();
59250
59405
  const errors = [];
59251
59406
  const providersSetup = [];
59252
- fs66.mkdirSync(hooksHome, { recursive: true });
59407
+ fs68.mkdirSync(hooksHome, { recursive: true });
59253
59408
  let otelHookCommand = resolveOtelHookCommand(hooksHome);
59254
59409
  const needsInstall = !otelHookCommand || installedPackageVersion(hooksHome) !== pinnedVersion();
59255
59410
  if (needsInstall) {
@@ -59343,14 +59498,14 @@ function ensureOtelHooks(options) {
59343
59498
  function disableOtelHooksExport() {
59344
59499
  return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
59345
59500
  }
59346
- var import_child_process19, fs66, os22, path73, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
59501
+ var import_child_process19, fs68, os22, path76, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
59347
59502
  var init_otel_hooks = __esm({
59348
59503
  "src/core/otel-hooks.ts"() {
59349
59504
  "use strict";
59350
59505
  import_child_process19 = require("child_process");
59351
- fs66 = __toESM(require("fs"));
59506
+ fs68 = __toESM(require("fs"));
59352
59507
  os22 = __toESM(require("os"));
59353
- path73 = __toESM(require("path"));
59508
+ path76 = __toESM(require("path"));
59354
59509
  init_control_config();
59355
59510
  init_telemetry_config();
59356
59511
  OTEL_HOOKS_PACKAGE = "@osfactory/otel-hook@0.2.0";
@@ -62325,7 +62480,7 @@ var require_compile = __commonJS({
62325
62480
  const schOrFunc = root.refs[ref];
62326
62481
  if (schOrFunc)
62327
62482
  return schOrFunc;
62328
- let _sch = resolve60.call(this, root, ref);
62483
+ let _sch = resolve66.call(this, root, ref);
62329
62484
  if (_sch === void 0) {
62330
62485
  const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
62331
62486
  const { schemaId } = this.opts;
@@ -62352,7 +62507,7 @@ var require_compile = __commonJS({
62352
62507
  function sameSchemaEnv(s1, s2) {
62353
62508
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
62354
62509
  }
62355
- function resolve60(root, ref) {
62510
+ function resolve66(root, ref) {
62356
62511
  let sch;
62357
62512
  while (typeof (sch = this.refs[ref]) == "string")
62358
62513
  ref = sch;
@@ -62570,8 +62725,8 @@ var require_utils2 = __commonJS({
62570
62725
  }
62571
62726
  return ind;
62572
62727
  }
62573
- function removeDotSegments(path91) {
62574
- let input = path91;
62728
+ function removeDotSegments(path97) {
62729
+ let input = path97;
62575
62730
  const output = [];
62576
62731
  let nextSlash = -1;
62577
62732
  let len = 0;
@@ -62823,8 +62978,8 @@ var require_schemes = __commonJS({
62823
62978
  wsComponent.secure = void 0;
62824
62979
  }
62825
62980
  if (wsComponent.resourceName) {
62826
- const [path91, query] = wsComponent.resourceName.split("?");
62827
- wsComponent.path = path91 && path91 !== "/" ? path91 : void 0;
62981
+ const [path97, query] = wsComponent.resourceName.split("?");
62982
+ wsComponent.path = path97 && path97 !== "/" ? path97 : void 0;
62828
62983
  wsComponent.query = query;
62829
62984
  wsComponent.resourceName = void 0;
62830
62985
  }
@@ -62983,55 +63138,55 @@ var require_fast_uri = __commonJS({
62983
63138
  }
62984
63139
  return uri;
62985
63140
  }
62986
- function resolve60(baseURI, relativeURI, options) {
63141
+ function resolve66(baseURI, relativeURI, options) {
62987
63142
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
62988
63143
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
62989
63144
  schemelessOptions.skipEscape = true;
62990
63145
  return serialize(resolved, schemelessOptions);
62991
63146
  }
62992
- function resolveComponent(base, relative5, options, skipNormalization) {
63147
+ function resolveComponent(base, relative6, options, skipNormalization) {
62993
63148
  const target = {};
62994
63149
  if (!skipNormalization) {
62995
63150
  base = parse4(serialize(base, options), options);
62996
- relative5 = parse4(serialize(relative5, options), options);
63151
+ relative6 = parse4(serialize(relative6, options), options);
62997
63152
  }
62998
63153
  options = options || {};
62999
- if (!options.tolerant && relative5.scheme) {
63000
- target.scheme = relative5.scheme;
63001
- target.userinfo = relative5.userinfo;
63002
- target.host = relative5.host;
63003
- target.port = relative5.port;
63004
- target.path = removeDotSegments(relative5.path || "");
63005
- target.query = relative5.query;
63154
+ if (!options.tolerant && relative6.scheme) {
63155
+ target.scheme = relative6.scheme;
63156
+ target.userinfo = relative6.userinfo;
63157
+ target.host = relative6.host;
63158
+ target.port = relative6.port;
63159
+ target.path = removeDotSegments(relative6.path || "");
63160
+ target.query = relative6.query;
63006
63161
  } else {
63007
- if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
63008
- target.userinfo = relative5.userinfo;
63009
- target.host = relative5.host;
63010
- target.port = relative5.port;
63011
- target.path = removeDotSegments(relative5.path || "");
63012
- target.query = relative5.query;
63162
+ if (relative6.userinfo !== void 0 || relative6.host !== void 0 || relative6.port !== void 0) {
63163
+ target.userinfo = relative6.userinfo;
63164
+ target.host = relative6.host;
63165
+ target.port = relative6.port;
63166
+ target.path = removeDotSegments(relative6.path || "");
63167
+ target.query = relative6.query;
63013
63168
  } else {
63014
- if (!relative5.path) {
63169
+ if (!relative6.path) {
63015
63170
  target.path = base.path;
63016
- if (relative5.query !== void 0) {
63017
- target.query = relative5.query;
63171
+ if (relative6.query !== void 0) {
63172
+ target.query = relative6.query;
63018
63173
  } else {
63019
63174
  target.query = base.query;
63020
63175
  }
63021
63176
  } else {
63022
- if (relative5.path[0] === "/") {
63023
- target.path = removeDotSegments(relative5.path);
63177
+ if (relative6.path[0] === "/") {
63178
+ target.path = removeDotSegments(relative6.path);
63024
63179
  } else {
63025
63180
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
63026
- target.path = "/" + relative5.path;
63181
+ target.path = "/" + relative6.path;
63027
63182
  } else if (!base.path) {
63028
- target.path = relative5.path;
63183
+ target.path = relative6.path;
63029
63184
  } else {
63030
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
63185
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
63031
63186
  }
63032
63187
  target.path = removeDotSegments(target.path);
63033
63188
  }
63034
- target.query = relative5.query;
63189
+ target.query = relative6.query;
63035
63190
  }
63036
63191
  target.userinfo = base.userinfo;
63037
63192
  target.host = base.host;
@@ -63039,7 +63194,7 @@ var require_fast_uri = __commonJS({
63039
63194
  }
63040
63195
  target.scheme = base.scheme;
63041
63196
  }
63042
- target.fragment = relative5.fragment;
63197
+ target.fragment = relative6.fragment;
63043
63198
  return target;
63044
63199
  }
63045
63200
  function equal(uriA, uriB, options) {
@@ -63241,7 +63396,7 @@ var require_fast_uri = __commonJS({
63241
63396
  var fastUri = {
63242
63397
  SCHEMES,
63243
63398
  normalize: normalize3,
63244
- resolve: resolve60,
63399
+ resolve: resolve66,
63245
63400
  resolveComponent,
63246
63401
  equal,
63247
63402
  serialize,
@@ -66217,12 +66372,12 @@ var require_dist = __commonJS({
66217
66372
  throw new Error(`Unknown format "${name}"`);
66218
66373
  return f;
66219
66374
  };
66220
- function addFormats(ajv, list, fs76, exportName) {
66375
+ function addFormats(ajv, list, fs79, exportName) {
66221
66376
  var _a2;
66222
66377
  var _b2;
66223
66378
  (_a2 = (_b2 = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
66224
66379
  for (const f of list)
66225
- ajv.addFormat(f, fs76[f]);
66380
+ ajv.addFormat(f, fs79[f]);
66226
66381
  }
66227
66382
  module2.exports = exports2 = formatsPlugin;
66228
66383
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -67506,11 +67661,11 @@ var parser = new YargsParser({
67506
67661
  resolve: import_path2.resolve,
67507
67662
  // TODO: figure out a way to combine ESM and CJS coverage, such that
67508
67663
  // we can exercise all the lines below:
67509
- require: (path91) => {
67664
+ require: (path97) => {
67510
67665
  if (typeof require !== "undefined") {
67511
- return require(path91);
67512
- } else if (path91.match(/\.json$/)) {
67513
- return JSON.parse((0, import_fs2.readFileSync)(path91, "utf8"));
67666
+ return require(path97);
67667
+ } else if (path97.match(/\.json$/)) {
67668
+ return JSON.parse((0, import_fs2.readFileSync)(path97, "utf8"));
67514
67669
  } else {
67515
67670
  throw Error("only .json config files are supported in ESM");
67516
67671
  }
@@ -70036,12 +70191,12 @@ var YargsInstance = class {
70036
70191
  async getCompletion(args, done) {
70037
70192
  argsert("<array> [function]", [args, done], arguments.length);
70038
70193
  if (!done) {
70039
- return new Promise((resolve60, reject) => {
70194
+ return new Promise((resolve66, reject) => {
70040
70195
  __classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
70041
70196
  if (err)
70042
70197
  reject(err);
70043
70198
  else
70044
- resolve60(completions);
70199
+ resolve66(completions);
70045
70200
  });
70046
70201
  });
70047
70202
  } else {
@@ -71291,16 +71446,16 @@ async function handleAgentSkills(options) {
71291
71446
  }
71292
71447
  async function askYesNo(question) {
71293
71448
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
71294
- return new Promise((resolve60) => {
71449
+ return new Promise((resolve66) => {
71295
71450
  process.stderr.write(`${question} `);
71296
71451
  rl.once("line", (answer) => {
71297
71452
  rl.close();
71298
71453
  const trimmed = answer.trim();
71299
71454
  if (trimmed === "") {
71300
- resolve60(true);
71455
+ resolve66(true);
71301
71456
  return;
71302
71457
  }
71303
- resolve60(/^y(es)?$/i.test(trimmed));
71458
+ resolve66(/^y(es)?$/i.test(trimmed));
71304
71459
  });
71305
71460
  });
71306
71461
  }
@@ -71380,8 +71535,8 @@ function handleRemove(argv) {
71380
71535
  }
71381
71536
 
71382
71537
  // src/cli/commands/env.ts
71383
- var fs69 = __toESM(require("fs"));
71384
- var path77 = __toESM(require("path"));
71538
+ var fs71 = __toESM(require("fs"));
71539
+ var path80 = __toESM(require("path"));
71385
71540
 
71386
71541
  // src/cli/finish-command.ts
71387
71542
  init_sync_context();
@@ -71391,8 +71546,8 @@ async function finishCommand(code) {
71391
71546
  }
71392
71547
 
71393
71548
  // src/core/harness.ts
71394
- var fs63 = __toESM(require("fs"));
71395
- var path69 = __toESM(require("path"));
71549
+ var fs65 = __toESM(require("fs"));
71550
+ var path72 = __toESM(require("path"));
71396
71551
 
71397
71552
  // src/harness/generator.ts
71398
71553
  var fs48 = __toESM(require("fs"));
@@ -72901,13 +73056,13 @@ var PromisePolyfill = class extends Promise {
72901
73056
  // Available starting from Node 22
72902
73057
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
72903
73058
  static withResolver() {
72904
- let resolve60;
73059
+ let resolve66;
72905
73060
  let reject;
72906
73061
  const promise = new Promise((res, rej) => {
72907
- resolve60 = res;
73062
+ resolve66 = res;
72908
73063
  reject = rej;
72909
73064
  });
72910
- return { promise, resolve: resolve60, reject };
73065
+ return { promise, resolve: resolve66, reject };
72911
73066
  }
72912
73067
  };
72913
73068
 
@@ -72941,7 +73096,7 @@ function createPrompt(view) {
72941
73096
  output
72942
73097
  });
72943
73098
  const screen = new ScreenManager(rl);
72944
- const { promise, resolve: resolve60, reject } = PromisePolyfill.withResolver();
73099
+ const { promise, resolve: resolve66, reject } = PromisePolyfill.withResolver();
72945
73100
  const cancel = () => reject(new CancelPromptError());
72946
73101
  if (signal) {
72947
73102
  const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
@@ -72968,7 +73123,7 @@ function createPrompt(view) {
72968
73123
  cycle(() => {
72969
73124
  try {
72970
73125
  const nextView = view(config2, (value) => {
72971
- setImmediate(() => resolve60(value));
73126
+ setImmediate(() => resolve66(value));
72972
73127
  });
72973
73128
  if (nextView === void 0) {
72974
73129
  const callerFilename = callSites[1]?.getFileName();
@@ -74277,9 +74432,9 @@ var import_rxjs = __toESM(require_cjs(), 1);
74277
74432
  var import_run_async = __toESM(require_run_async(), 1);
74278
74433
  var import_mute_stream2 = __toESM(require_lib(), 1);
74279
74434
  var _ = {
74280
- set: (obj, path91 = "", value) => {
74435
+ set: (obj, path97 = "", value) => {
74281
74436
  let pointer = obj;
74282
- path91.split(".").forEach((key, index, arr) => {
74437
+ path97.split(".").forEach((key, index, arr) => {
74283
74438
  if (key === "__proto__" || key === "constructor")
74284
74439
  return;
74285
74440
  if (index === arr.length - 1) {
@@ -74290,8 +74445,8 @@ var _ = {
74290
74445
  pointer = pointer[key];
74291
74446
  });
74292
74447
  },
74293
- get: (obj, path91 = "", defaultValue) => {
74294
- const travel = (regexp) => String.prototype.split.call(path91, regexp).filter(Boolean).reduce(
74448
+ get: (obj, path97 = "", defaultValue) => {
74449
+ const travel = (regexp) => String.prototype.split.call(path97, regexp).filter(Boolean).reduce(
74295
74450
  // @ts-expect-error implicit any on res[key]
74296
74451
  (res, key) => res == null ? res : res[key],
74297
74452
  obj
@@ -74409,7 +74564,7 @@ var PromptsRunner = class {
74409
74564
  throw new Error(`Prompt for type ${question.type} not found`);
74410
74565
  }
74411
74566
  let cleanupSignal;
74412
- const promptFn = isPromptConstructor(prompt2) ? (q, opt) => new Promise((resolve60, reject) => {
74567
+ const promptFn = isPromptConstructor(prompt2) ? (q, opt) => new Promise((resolve66, reject) => {
74413
74568
  const { signal: signal2 } = opt;
74414
74569
  if (signal2.aborted) {
74415
74570
  reject(new AbortPromptError({ cause: signal2.reason }));
@@ -74446,7 +74601,7 @@ var PromptsRunner = class {
74446
74601
  signal2.removeEventListener("abort", abort);
74447
74602
  cleanupSignal = void 0;
74448
74603
  };
74449
- activePrompt.run().then(resolve60, reject).finally(cleanup);
74604
+ activePrompt.run().then(resolve66, reject).finally(cleanup);
74450
74605
  }) : prompt2;
74451
74606
  let cleanupModuleSignal;
74452
74607
  const { signal: moduleSignal } = this.opt;
@@ -75418,15 +75573,21 @@ function harnessExists(repoPath) {
75418
75573
  return fs53.existsSync(`${repoPath}/.har/stages.json`);
75419
75574
  }
75420
75575
 
75421
- // src/harness/plugin-resolve.ts
75576
+ // src/harness/bundle-resolve.ts
75422
75577
  var import_child_process16 = require("child_process");
75423
75578
  var fs54 = __toESM(require("fs"));
75424
75579
  var os20 = __toESM(require("os"));
75425
75580
  var path60 = __toESM(require("path"));
75426
75581
  init_paths();
75427
- var LOCAL_PLUGINS_DIR = ".har/plugins";
75428
- function hasManifest(dir) {
75429
- return fs54.existsSync(path60.join(dir, "template.manifest.json"));
75582
+ function findManifestPath(dir, config2) {
75583
+ for (const name of config2.manifestFiles) {
75584
+ const candidate = path60.join(dir, name);
75585
+ if (fs54.existsSync(candidate)) return candidate;
75586
+ }
75587
+ return null;
75588
+ }
75589
+ function hasManifest(dir, config2) {
75590
+ return findManifestPath(dir, config2) !== null;
75430
75591
  }
75431
75592
  function readPackageVersion(dir) {
75432
75593
  const pkgPath = path60.join(dir, "package.json");
@@ -75438,9 +75599,9 @@ function readPackageVersion(dir) {
75438
75599
  return void 0;
75439
75600
  }
75440
75601
  }
75441
- function readManifestId(dir) {
75442
- const manifestPath = path60.join(dir, "template.manifest.json");
75443
- if (!fs54.existsSync(manifestPath)) return void 0;
75602
+ function readManifestId(dir, config2) {
75603
+ const manifestPath = findManifestPath(dir, config2);
75604
+ if (!manifestPath) return void 0;
75444
75605
  try {
75445
75606
  const raw = JSON.parse(fs54.readFileSync(manifestPath, "utf8"));
75446
75607
  return typeof raw.id === "string" ? raw.id : void 0;
@@ -75459,47 +75620,48 @@ function looksLikeNpm(spec) {
75459
75620
  if (spec.includes("/") && !spec.startsWith("@")) return false;
75460
75621
  return /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(@.+)?$/i.test(spec);
75461
75622
  }
75462
- function resolveBundledDir(pluginId) {
75463
- const dir = path60.join(resolveTemplatesDir(), "plugins", pluginId);
75464
- return hasManifest(dir) ? dir : null;
75465
- }
75466
75623
  function expandHome(p) {
75467
75624
  if (p.startsWith("~/")) return path60.join(os20.homedir(), p.slice(2));
75468
75625
  if (p === "~") return os20.homedir();
75469
75626
  return p;
75470
75627
  }
75471
- function isLocalPluginDir(dir, repoPath) {
75472
- const localRoot = path60.resolve(repoPath, LOCAL_PLUGINS_DIR);
75473
- return path60.dirname(dir) === localRoot;
75628
+ function makeTempDir(prefix) {
75629
+ return fs54.mkdtempSync(path60.join(os20.tmpdir(), prefix));
75630
+ }
75631
+ function manifestNames(config2) {
75632
+ return config2.manifestFiles.join(" or ");
75633
+ }
75634
+ function resolveTemplatesBundleDir(id, config2) {
75635
+ const dir = path60.join(resolveTemplatesDir(), config2.templatesSubdir, id);
75636
+ return hasManifest(dir, config2) ? dir : null;
75637
+ }
75638
+ function resolveLocalDir(id, repoPath, config2) {
75639
+ const dir = path60.resolve(repoPath, config2.localDir, id);
75640
+ return hasManifest(dir, config2) ? dir : null;
75474
75641
  }
75475
- function resolvePathSpec(spec, repoPath) {
75642
+ function isLocalBundleDir(dir, repoPath, config2) {
75643
+ return path60.dirname(dir) === path60.resolve(repoPath, config2.localDir);
75644
+ }
75645
+ function resolvePathSpec(spec, repoPath, config2) {
75476
75646
  const raw = spec.startsWith("file:") ? spec.slice("file:".length) : spec;
75477
75647
  const dir = path60.resolve(expandHome(raw));
75478
75648
  if (!fs54.existsSync(dir) || !fs54.statSync(dir).isDirectory()) {
75479
- throw new Error(`Plugin path not found: ${spec}`);
75649
+ throw new Error(`${config2.label} path not found: ${spec}`);
75480
75650
  }
75481
- if (!hasManifest(dir)) {
75482
- throw new Error(`No template.manifest.json in ${dir}`);
75651
+ if (!hasManifest(dir, config2)) {
75652
+ throw new Error(`No ${manifestNames(config2)} in ${dir}`);
75483
75653
  }
75484
- const id = readManifestId(dir) ?? path60.basename(dir);
75485
75654
  return {
75486
- id,
75487
- kind: isLocalPluginDir(dir, repoPath) ? "local" : "path",
75655
+ id: readManifestId(dir, config2) ?? path60.basename(dir),
75656
+ kind: isLocalBundleDir(dir, repoPath, config2) ? "local" : "path",
75488
75657
  dir,
75489
75658
  spec,
75490
75659
  version: readPackageVersion(dir),
75491
75660
  cleanupDirs: []
75492
75661
  };
75493
75662
  }
75494
- function resolveLocalDir(pluginId, repoPath) {
75495
- const dir = path60.resolve(repoPath, LOCAL_PLUGINS_DIR, pluginId);
75496
- return hasManifest(dir) ? dir : null;
75497
- }
75498
- function makeTempDir(prefix) {
75499
- return fs54.mkdtempSync(path60.join(os20.tmpdir(), prefix));
75500
- }
75501
- function resolveNpmSpec(spec) {
75502
- const tmp = makeTempDir("har-plugin-npm-");
75663
+ function resolveNpmSpec(spec, config2) {
75664
+ const tmp = makeTempDir(`har-${config2.label}-npm-`);
75503
75665
  const cleanupDirs = [tmp];
75504
75666
  try {
75505
75667
  (0, import_child_process16.execFileSync)("npm", ["pack", spec, "--pack-destination", tmp], {
@@ -75517,20 +75679,18 @@ function resolveNpmSpec(spec) {
75517
75679
  encoding: "utf8",
75518
75680
  stdio: ["ignore", "pipe", "pipe"]
75519
75681
  });
75520
- const packageDir = path60.join(extractDir, "package");
75521
- let dir = packageDir;
75522
- if (!hasManifest(dir)) {
75523
- const nested = fs54.readdirSync(extractDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(extractDir, e.name)).find((d) => hasManifest(d));
75682
+ let dir = path60.join(extractDir, "package");
75683
+ if (!hasManifest(dir, config2)) {
75684
+ const nested = fs54.readdirSync(extractDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(extractDir, e.name)).find((d) => hasManifest(d, config2));
75524
75685
  if (!nested) {
75525
75686
  throw new Error(
75526
- `npm package ${spec} has no template.manifest.json (not a HAR plugin bundle)`
75687
+ `npm package ${spec} has no ${manifestNames(config2)} (not a HAR ${config2.label} bundle)`
75527
75688
  );
75528
75689
  }
75529
75690
  dir = nested;
75530
75691
  }
75531
- const id = readManifestId(dir) ?? spec.replace(/^@/, "").replace(/[/:].*/g, "");
75532
75692
  return {
75533
- id,
75693
+ id: readManifestId(dir, config2) ?? spec.replace(/^@/, "").replace(/[/:].*/g, ""),
75534
75694
  kind: "npm",
75535
75695
  dir,
75536
75696
  spec,
@@ -75542,11 +75702,11 @@ function resolveNpmSpec(spec) {
75542
75702
  fs54.rmSync(d, { recursive: true, force: true });
75543
75703
  }
75544
75704
  const message = err instanceof Error ? err.message : String(err);
75545
- throw new Error(`Failed to install plugin from npm (${spec}): ${message}`);
75705
+ throw new Error(`Failed to install ${config2.label} from npm (${spec}): ${message}`);
75546
75706
  }
75547
75707
  }
75548
- function resolveGitSpec(spec) {
75549
- const tmp = makeTempDir("har-plugin-git-");
75708
+ function resolveGitSpec(spec, config2) {
75709
+ const tmp = makeTempDir(`har-${config2.label}-git-`);
75550
75710
  const cleanupDirs = [tmp];
75551
75711
  const cloneDir = path60.join(tmp, "repo");
75552
75712
  try {
@@ -75566,19 +75726,20 @@ function resolveGitSpec(spec) {
75566
75726
  timeout: 18e4
75567
75727
  });
75568
75728
  let dir = cloneDir;
75569
- if (!hasManifest(dir)) {
75570
- const pluginsRoot = path60.join(cloneDir, "plugins");
75571
- if (fs54.existsSync(pluginsRoot)) {
75572
- const child = fs54.readdirSync(pluginsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(pluginsRoot, e.name)).find((d) => hasManifest(d));
75729
+ if (!hasManifest(dir, config2)) {
75730
+ const nestedRoot = path60.join(cloneDir, config2.gitNestedDir);
75731
+ if (fs54.existsSync(nestedRoot)) {
75732
+ const child = fs54.readdirSync(nestedRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(nestedRoot, e.name)).find((d) => hasManifest(d, config2));
75573
75733
  if (child) dir = child;
75574
75734
  }
75575
75735
  }
75576
- if (!hasManifest(dir)) {
75577
- throw new Error(`Git repo ${spec} has no template.manifest.json (not a HAR plugin bundle)`);
75736
+ if (!hasManifest(dir, config2)) {
75737
+ throw new Error(
75738
+ `Git repo ${spec} has no ${manifestNames(config2)} (not a HAR ${config2.label} bundle)`
75739
+ );
75578
75740
  }
75579
- const id = readManifestId(dir) ?? path60.basename(url.replace(/\.git$/, ""));
75580
75741
  return {
75581
- id,
75742
+ id: readManifestId(dir, config2) ?? path60.basename(url.replace(/\.git$/, "")),
75582
75743
  kind: "git",
75583
75744
  dir,
75584
75745
  spec,
@@ -75590,21 +75751,21 @@ function resolveGitSpec(spec) {
75590
75751
  fs54.rmSync(d, { recursive: true, force: true });
75591
75752
  }
75592
75753
  const message = err instanceof Error ? err.message : String(err);
75593
- throw new Error(`Failed to install plugin from git (${spec}): ${message}`);
75754
+ throw new Error(`Failed to install ${config2.label} from git (${spec}): ${message}`);
75594
75755
  }
75595
75756
  }
75596
- function resolvePluginSource(spec, repoPath = ".") {
75757
+ function resolveBundleSource(spec, repoPath, config2) {
75597
75758
  const trimmed = spec.trim();
75598
75759
  if (!trimmed) {
75599
- throw new Error("Plugin spec is empty");
75760
+ throw new Error(`${config2.label} spec is empty`);
75600
75761
  }
75601
75762
  if (looksLikePath(trimmed)) {
75602
- return resolvePathSpec(trimmed, repoPath);
75763
+ return resolvePathSpec(trimmed, repoPath, config2);
75603
75764
  }
75604
75765
  if (looksLikeGit(trimmed)) {
75605
- return resolveGitSpec(trimmed);
75766
+ return resolveGitSpec(trimmed, config2);
75606
75767
  }
75607
- const bundled = resolveBundledDir(trimmed);
75768
+ const bundled = resolveTemplatesBundleDir(trimmed, config2);
75608
75769
  if (bundled) {
75609
75770
  return {
75610
75771
  id: trimmed,
@@ -75615,10 +75776,10 @@ function resolvePluginSource(spec, repoPath = ".") {
75615
75776
  cleanupDirs: []
75616
75777
  };
75617
75778
  }
75618
- const local = resolveLocalDir(trimmed, repoPath);
75779
+ const local = resolveLocalDir(trimmed, repoPath, config2);
75619
75780
  if (local) {
75620
75781
  return {
75621
- id: readManifestId(local) ?? trimmed,
75782
+ id: readManifestId(local, config2) ?? trimmed,
75622
75783
  kind: "local",
75623
75784
  dir: local,
75624
75785
  spec: trimmed,
@@ -75626,23 +75787,54 @@ function resolvePluginSource(spec, repoPath = ".") {
75626
75787
  cleanupDirs: []
75627
75788
  };
75628
75789
  }
75629
- if (looksLikeNpm(trimmed) && (trimmed.includes("@") || trimmed.includes("/"))) {
75630
- return resolveNpmSpec(trimmed);
75631
- }
75790
+ const hint = config2.unknownHint?.(repoPath) ?? "";
75632
75791
  if (looksLikeNpm(trimmed)) {
75633
75792
  try {
75634
- return resolveNpmSpec(trimmed);
75793
+ return resolveNpmSpec(trimmed, config2);
75635
75794
  } catch (npmErr) {
75795
+ if (trimmed.includes("@") || trimmed.includes("/")) throw npmErr;
75636
75796
  const npmMessage = npmErr instanceof Error ? npmErr.message : String(npmErr);
75637
75797
  throw new Error(
75638
- `Unknown plugin: ${trimmed}. ${knownPluginsHint(repoPath)} Or pass a path, npm package, or git URL. (${npmMessage})`
75798
+ `Unknown ${config2.label}: ${trimmed}. ${hint} Or pass a path, npm package, or git URL. (${npmMessage})`
75639
75799
  );
75640
75800
  }
75641
75801
  }
75642
75802
  throw new Error(
75643
- `Unknown plugin: ${trimmed}. ${knownPluginsHint(repoPath)} Or pass a path (./plugin), npm package (@org/pkg), or git URL (github:org/repo).`
75803
+ `Unknown ${config2.label}: ${trimmed}. ${hint} Or pass a path (./bundle), npm package (@org/pkg), or git URL (github:org/repo).`
75644
75804
  );
75645
75805
  }
75806
+ function listTemplateBundleIds(config2) {
75807
+ const root = path60.join(resolveTemplatesDir(), config2.templatesSubdir);
75808
+ if (!fs54.existsSync(root)) return [];
75809
+ return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name), config2)).sort();
75810
+ }
75811
+ function listLocalBundleIds(repoPath, config2) {
75812
+ const root = path60.resolve(repoPath, config2.localDir);
75813
+ if (!fs54.existsSync(root)) return [];
75814
+ return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name), config2)).sort();
75815
+ }
75816
+ function cleanupResolvedBundle(source) {
75817
+ for (const dir of source.cleanupDirs) {
75818
+ fs54.rmSync(dir, { recursive: true, force: true });
75819
+ }
75820
+ }
75821
+
75822
+ // src/harness/plugin-resolve.ts
75823
+ var LOCAL_PLUGINS_DIR = ".har/plugins";
75824
+ var PLUGIN_BUNDLE_CONFIG = {
75825
+ // `line.manifest.json` is accepted here on purpose: resolving a line bundle
75826
+ // lets `readPluginManifestFromDir` refuse it with a pointer at `har line add`
75827
+ // instead of a bare "not a HAR plugin bundle".
75828
+ manifestFiles: ["template.manifest.json", "line.manifest.json"],
75829
+ templatesSubdir: "plugins",
75830
+ localDir: LOCAL_PLUGINS_DIR,
75831
+ gitNestedDir: "plugins",
75832
+ label: "plugin",
75833
+ unknownHint: (repoPath) => knownPluginsHint(repoPath)
75834
+ };
75835
+ function resolvePluginSource(spec, repoPath = ".") {
75836
+ return resolveBundleSource(spec, repoPath, PLUGIN_BUNDLE_CONFIG);
75837
+ }
75646
75838
  function knownPluginsHint(repoPath) {
75647
75839
  const bundled = listBundledPluginIds();
75648
75840
  const local = listLocalPluginIds(repoPath);
@@ -75655,19 +75847,13 @@ function knownPluginsHint(repoPath) {
75655
75847
  return parts.join(" ");
75656
75848
  }
75657
75849
  function listLocalPluginIds(repoPath = ".") {
75658
- const root = path60.resolve(repoPath, LOCAL_PLUGINS_DIR);
75659
- if (!fs54.existsSync(root)) return [];
75660
- return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name))).sort();
75850
+ return listLocalBundleIds(repoPath, PLUGIN_BUNDLE_CONFIG);
75661
75851
  }
75662
75852
  function listBundledPluginIds() {
75663
- const root = path60.join(resolveTemplatesDir(), "plugins");
75664
- if (!fs54.existsSync(root)) return [];
75665
- return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name))).sort();
75853
+ return listTemplateBundleIds(PLUGIN_BUNDLE_CONFIG);
75666
75854
  }
75667
75855
  function cleanupResolvedPlugin(source) {
75668
- for (const dir of source.cleanupDirs) {
75669
- fs54.rmSync(dir, { recursive: true, force: true });
75670
- }
75856
+ cleanupResolvedBundle(source);
75671
75857
  }
75672
75858
 
75673
75859
  // src/harness/plugin-prompt.ts
@@ -75774,13 +75960,17 @@ function resolveBundledPluginDir(pluginId) {
75774
75960
  return dir;
75775
75961
  }
75776
75962
  function readPluginManifestFromDir(pluginDir, expectedId) {
75777
- const manifestPath = path62.join(pluginDir, "template.manifest.json");
75778
- if (!fs56.existsSync(manifestPath)) {
75963
+ const manifestPath = findManifestPath(pluginDir, PLUGIN_BUNDLE_CONFIG);
75964
+ if (!manifestPath) {
75779
75965
  throw new Error(`No template.manifest.json in ${pluginDir}`);
75780
75966
  }
75781
- const parsed = PluginManifestSchema.safeParse(
75782
- JSON.parse(fs56.readFileSync(manifestPath, "utf8"))
75783
- );
75967
+ const raw = JSON.parse(fs56.readFileSync(manifestPath, "utf8"));
75968
+ if (raw.kind === LINE_BUNDLE_KIND || path62.basename(manifestPath) === "line.manifest.json") {
75969
+ throw new Error(
75970
+ `${path62.basename(manifestPath)} declares "kind": "line" \u2014 this is a factory line bundle, not a verification plugin. Install it with: har line add <spec>`
75971
+ );
75972
+ }
75973
+ const parsed = PluginManifestSchema.safeParse(raw);
75784
75974
  if (!parsed.success) {
75785
75975
  throw new Error(`Invalid plugin manifest: ${parsed.error.message}`);
75786
75976
  }
@@ -76682,8 +76872,8 @@ init_manifest();
76682
76872
  init_stages();
76683
76873
 
76684
76874
  // src/harness/doctor.ts
76685
- var fs61 = __toESM(require("fs"));
76686
- var path67 = __toESM(require("path"));
76875
+ var fs63 = __toESM(require("fs"));
76876
+ var path70 = __toESM(require("path"));
76687
76877
  init_slot_registry();
76688
76878
 
76689
76879
  // src/harness/eject.ts
@@ -76787,20 +76977,408 @@ function adoptHarness(repoPath) {
76787
76977
  // src/harness/doctor.ts
76788
76978
  init_env();
76789
76979
  init_manifest();
76980
+
76981
+ // src/harness/line-ledger.ts
76982
+ var fs60 = __toESM(require("fs"));
76983
+ var path66 = __toESM(require("path"));
76984
+ init_file_ops();
76985
+ init_manifest();
76986
+ init_schema2();
76987
+ function getLineLedgerPath(repoPath) {
76988
+ return path66.join(path66.resolve(repoPath), DEFAULT_HAR_DIR, "lines.json");
76989
+ }
76990
+ function readLineLedger(repoPath) {
76991
+ const ledgerPath = getLineLedgerPath(repoPath);
76992
+ if (!fs60.existsSync(ledgerPath)) return null;
76993
+ try {
76994
+ const parsed = LineLedgerSchema.safeParse(JSON.parse(fs60.readFileSync(ledgerPath, "utf8")));
76995
+ return parsed.success ? parsed.data : null;
76996
+ } catch {
76997
+ return null;
76998
+ }
76999
+ }
77000
+ function writeLineLedger(repoPath, ledger) {
77001
+ const ledgerPath = getLineLedgerPath(repoPath);
77002
+ const harnessDir = path66.dirname(ledgerPath);
77003
+ if (!fs60.existsSync(harnessDir)) {
77004
+ fs60.mkdirSync(harnessDir, { recursive: true });
77005
+ }
77006
+ writeFileSafe(ledgerPath, `${JSON.stringify(ledger, null, 2)}
77007
+ `);
77008
+ }
77009
+ function upsertLineLedgerEntry(repoPath, entry) {
77010
+ const existing = readLineLedger(repoPath) ?? { version: "1", lines: [] };
77011
+ const lines = existing.lines.filter((l) => l.id !== entry.id);
77012
+ lines.push(entry);
77013
+ lines.sort((a, b2) => a.id.localeCompare(b2.id));
77014
+ const ledger = { version: "1", lines };
77015
+ writeLineLedger(repoPath, ledger);
77016
+ return ledger;
77017
+ }
77018
+
77019
+ // src/harness/lines.ts
77020
+ var fs61 = __toESM(require("fs"));
77021
+ var path68 = __toESM(require("path"));
77022
+ init_logging();
77023
+
77024
+ // src/harness/line-prompt.ts
77025
+ var path67 = __toESM(require("path"));
77026
+ init_file_ops();
77027
+ init_manifest();
77028
+ function lineAdaptationPromptFile(lineId) {
77029
+ return `ADAPT-PROMPT-line-${lineId}.md`;
77030
+ }
77031
+ function buildLineAdaptationPrompt(repoPath, program, result) {
77032
+ const stations = program.stations.map((s, i) => `${i + 1}. \`${s.id}\` \u2014 ${s.title}${s.work?.ids?.length ? ` (work: ${s.work.ids.join(", ")})` : ""}`).join("\n");
77033
+ const gateRows = program.gate.stages.map((s) => `| \`${s.id}\` | \`${s.fromStation}\` | ${s.tier} |`).join("\n");
77034
+ const registered = result.stageIds.length > 0 ? result.stageIds.map((id) => `- \`${id}\` \u2014 registered, **not** in \`verificationStages\``).join("\n") : "- _(this line registered no extra stages)_";
77035
+ const declaredSkills = program.skills.length > 0 ? program.skills.map((s) => `- \`${s.id}\` (${s.role})${s.install ? ` \u2014 install: \`${s.install}\`` : ""}`).join("\n") : "- _(none declared)_";
77036
+ const declaredMcp = program.mcp.length > 0 ? program.mcp.map((m) => `- \`${m.name}\`${m.required ? " (required)" : " (optional)"}${m.why ? ` \u2014 ${m.why}` : ""}`).join("\n") : "- _(none declared)_";
77037
+ const warnings = result.warnings.length > 0 ? result.warnings.map((w2) => `- \u26A0 ${w2}`).join("\n") : "- _(none)_";
77038
+ const optIn = program.gate.optInEnv ? `This line's gate is opt-in: stages run only when \`${program.gate.optInEnv}=1\`.` : "This line has no opt-in env var \u2014 `har line gate` runs its stages on demand.";
77039
+ return `# Adapt the factory line: ${result.lineId}
77040
+
77041
+ Installed from \`${result.source}\` into \`${path67.basename(path67.resolve(repoPath))}\`.
77042
+ Program: \`${result.programPath}\`
77043
+
77044
+ ## What just happened
77045
+
77046
+ A **factory line** was installed. A line is a *program* \u2014 an ordered set of
77047
+ stations plus a cumulative gate. It is not a verification plugin:
77048
+
77049
+ ${registered}
77050
+
77051
+ \`verificationStages\` was **not** modified. Default \`har env verify --full\`
77052
+ takes exactly as long as it did before this install. ${optIn}
77053
+
77054
+ ## Stations
77055
+
77056
+ ${stations}
77057
+
77058
+ ## Cumulative gate (the ratchet)
77059
+
77060
+ A gate stage tagged \`fromStation: X\` is required at X **and every later
77061
+ station**. Adding a station must never drop an earlier station's stages.
77062
+
77063
+ | Stage | From station | Tier |
77064
+ |---|---|---|
77065
+ ${gateRows || "| _(none)_ | | |"}
77066
+
77067
+ Run it with:
77068
+
77069
+ \`\`\`bash
77070
+ har line gate <station> --line ${result.lineId}
77071
+ \`\`\`
77072
+
77073
+ Never with \`har env verify --full\` \u2014 that is the whole point of the split.
77074
+
77075
+ ## Declared dependencies (declarations, not installs)
77076
+
77077
+ Skills:
77078
+
77079
+ ${declaredSkills}
77080
+
77081
+ MCP servers:
77082
+
77083
+ ${declaredMcp}
77084
+
77085
+ HAR does not vendor skill packs or MCP servers. Check they are present, install
77086
+ them the way your agent normally does, and skip tracker steps for any MCP marked
77087
+ optional that is missing.
77088
+
77089
+ ## Warnings from install
77090
+
77091
+ ${warnings}
77092
+
77093
+ ## Your job now
77094
+
77095
+ 1. Read \`${result.programPath}\` and make the stations describe *this* repo's
77096
+ work \u2014 station titles, \`work.source\`/\`work.ids\`, waves.
77097
+ 2. Make each registered stage script real (replace TODO blocks). They live under
77098
+ \`.har/stages/\`.
77099
+ 3. Leave \`gate.cumulative: true\` and \`handoff.autonomousShip: false\` alone \u2014
77100
+ both are contract, not preference.
77101
+ 4. Do **not** add line stages to \`verificationStages\`. If a check should gate
77102
+ every verify, it belongs in a verification plugin instead
77103
+ (\`har env add-plugin\`), not on this line.
77104
+ 5. Verify the harness still passes as before: \`har env verify <slot> --full\`.
77105
+ `;
77106
+ }
77107
+ function writeLineAdaptationPrompt(repoPath, lineId, content) {
77108
+ const filePath = path67.join(getHarnessDir(repoPath), lineAdaptationPromptFile(lineId));
77109
+ writeFileSafe(filePath, content);
77110
+ return filePath;
77111
+ }
77112
+
77113
+ // src/harness/line-resolve.ts
77114
+ init_schema2();
77115
+ var LOCAL_LINES_DIR = ".har/lines";
77116
+ var LINE_BUNDLE_CONFIG = {
77117
+ manifestFiles: LINE_MANIFEST_FILES,
77118
+ templatesSubdir: "lines",
77119
+ localDir: LOCAL_LINES_DIR,
77120
+ gitNestedDir: "lines",
77121
+ label: "line",
77122
+ unknownHint: (repoPath) => knownLinesHint(repoPath)
77123
+ };
77124
+ function resolveLineSource(spec, repoPath = ".") {
77125
+ return resolveBundleSource(spec, repoPath, LINE_BUNDLE_CONFIG);
77126
+ }
77127
+ function knownLinesHint(repoPath) {
77128
+ const bundled = listBundledLineIds();
77129
+ const local = listLocalLineIds(repoPath);
77130
+ const parts = [`Bundled: ${bundled.join(", ") || "(none)"}.`];
77131
+ if (local.length > 0) {
77132
+ parts.push(`Local (${LOCAL_LINES_DIR}/): ${local.join(", ")}.`);
77133
+ } else {
77134
+ parts.push("Scaffold a project-owned one with: har line create <id>.");
77135
+ }
77136
+ return parts.join(" ");
77137
+ }
77138
+ function listLocalLineIds(repoPath = ".") {
77139
+ return listLocalBundleIds(repoPath, LINE_BUNDLE_CONFIG);
77140
+ }
77141
+ function listBundledLineIds() {
77142
+ return listTemplateBundleIds(LINE_BUNDLE_CONFIG);
77143
+ }
77144
+ function cleanupResolvedLine(source) {
77145
+ cleanupResolvedBundle(source);
77146
+ }
77147
+
77148
+ // src/harness/lines.ts
77149
+ init_schema2();
77150
+ init_schema2();
77151
+ init_stages();
77152
+ function lineDirRel(lineId) {
77153
+ return `${LOCAL_LINES_DIR}/${lineId}`;
77154
+ }
77155
+ function lineProgramPathRel(lineId) {
77156
+ return `${lineDirRel(lineId)}/line.json`;
77157
+ }
77158
+ function readLineManifestFromDir(bundleDir, expectedId) {
77159
+ const manifestPath = findManifestPath(bundleDir, LINE_BUNDLE_CONFIG);
77160
+ if (!manifestPath) {
77161
+ throw new Error(`No ${LINE_MANIFEST_FILES.join(" or ")} in ${bundleDir}`);
77162
+ }
77163
+ const raw = JSON.parse(fs61.readFileSync(manifestPath, "utf8"));
77164
+ if (raw.kind !== LINE_BUNDLE_KIND) {
77165
+ throw new Error(
77166
+ `${path68.basename(manifestPath)} is not a factory line bundle (missing "kind": "line"). Verification plugins install with: har env add-plugin <spec>`
77167
+ );
77168
+ }
77169
+ const parsed = LineManifestSchema.safeParse(raw);
77170
+ if (!parsed.success) {
77171
+ throw new Error(`Invalid line manifest: ${parsed.error.message}`);
77172
+ }
77173
+ if (expectedId && parsed.data.id !== expectedId) {
77174
+ throw new Error(`Line manifest id mismatch: expected ${expectedId}, got ${parsed.data.id}`);
77175
+ }
77176
+ return parsed.data;
77177
+ }
77178
+ function readLineProgramFromDir(bundleDir, manifest) {
77179
+ const programPath = path68.join(bundleDir, manifest.program);
77180
+ if (!fs61.existsSync(programPath)) {
77181
+ throw new Error(`Line program missing: ${manifest.program} (referenced by line.manifest.json)`);
77182
+ }
77183
+ const parsed = LineProgramSchema.safeParse(JSON.parse(fs61.readFileSync(programPath, "utf8")));
77184
+ if (!parsed.success) {
77185
+ throw new Error(`Invalid line program (${manifest.program}): ${parsed.error.message}`);
77186
+ }
77187
+ if (parsed.data.id !== manifest.id) {
77188
+ throw new Error(
77189
+ `Line program id "${parsed.data.id}" does not match manifest id "${manifest.id}"`
77190
+ );
77191
+ }
77192
+ return parsed.data;
77193
+ }
77194
+ function readInstalledLineProgram(repoPath, lineId) {
77195
+ const programPath = path68.join(path68.resolve(repoPath), lineProgramPathRel(lineId));
77196
+ if (!fs61.existsSync(programPath)) return null;
77197
+ const parsed = LineProgramSchema.safeParse(JSON.parse(fs61.readFileSync(programPath, "utf8")));
77198
+ return parsed.success ? parsed.data : null;
77199
+ }
77200
+ function ensureParentDir3(filePath) {
77201
+ fs61.mkdirSync(path68.dirname(filePath), { recursive: true });
77202
+ }
77203
+ function copyBundleFile(bundleDir, file, repoPath, force) {
77204
+ const srcPath = path68.join(bundleDir, file.src);
77205
+ const destPath = path68.join(repoPath, file.dest);
77206
+ if (!fs61.existsSync(srcPath)) {
77207
+ throw new Error(`Line file missing: ${file.src}`);
77208
+ }
77209
+ if (path68.resolve(srcPath) === path68.resolve(destPath)) {
77210
+ return file.dest;
77211
+ }
77212
+ if (fs61.existsSync(destPath) && !force) {
77213
+ throw new Error(
77214
+ `File already exists: ${file.dest}. Use --force to overwrite or remove it first.`
77215
+ );
77216
+ }
77217
+ ensureParentDir3(destPath);
77218
+ fs61.copyFileSync(srcPath, destPath);
77219
+ if (file.executable) {
77220
+ fs61.chmodSync(destPath, 493);
77221
+ }
77222
+ return file.dest;
77223
+ }
77224
+ function registerStagesOnly(repoPath, stages, force) {
77225
+ if (stages.length === 0) return;
77226
+ const registry2 = readStageRegistry(repoPath);
77227
+ const before = JSON.stringify(registry2.verificationStages ?? null);
77228
+ let nextStages = [...registry2.stages];
77229
+ for (const stage of stages) {
77230
+ const existing = nextStages.find((s) => s.id === stage.id);
77231
+ if (existing && !force) {
77232
+ throw new Error(
77233
+ `Stage "${stage.id}" already registered in .har/stages.json. Use --force to replace.`
77234
+ );
77235
+ }
77236
+ nextStages = existing ? nextStages.map((s) => s.id === stage.id ? stage : s) : [...nextStages, stage];
77237
+ }
77238
+ const updated = { ...registry2, stages: nextStages };
77239
+ const after = JSON.stringify(updated.verificationStages ?? null);
77240
+ if (before !== after) {
77241
+ throw new Error(
77242
+ "Refusing to write: applying a line changed verificationStages. Line gate stages are opt-in and run via `har line gate`."
77243
+ );
77244
+ }
77245
+ writeStageRegistry(repoPath, updated);
77246
+ }
77247
+ function assertGateStagesResolvable(program, registeredIds, repoPath) {
77248
+ const warnings = [];
77249
+ const registry2 = readStageRegistry(repoPath);
77250
+ const known = /* @__PURE__ */ new Set([...registry2.stages.map((s) => s.id), ...registeredIds]);
77251
+ for (const stage of program.gate.stages) {
77252
+ if (!known.has(stage.id)) {
77253
+ warnings.push(
77254
+ `gate stage "${stage.id}" (from ${stage.fromStation}) is not registered in .har/stages.json \u2014 install the plugin that provides it, or add it to the line bundle.`
77255
+ );
77256
+ }
77257
+ }
77258
+ return warnings;
77259
+ }
77260
+ function applyLineFromDir(repoPath, bundleDir, options, meta) {
77261
+ const resolved = path68.resolve(repoPath);
77262
+ const force = options.force ?? false;
77263
+ const warnings = [];
77264
+ const filesWritten = [];
77265
+ if (!harnessExists(resolved)) {
77266
+ throw new Error('No .har/ harness found. Run "har onboard" first.');
77267
+ }
77268
+ const manifest = readLineManifestFromDir(bundleDir);
77269
+ const program = readLineProgramFromDir(bundleDir, manifest);
77270
+ const stages = manifest.stages;
77271
+ const stageIds = stages.map((s) => s.id);
77272
+ const verificationBefore = JSON.stringify(readStageRegistry(resolved).verificationStages ?? null);
77273
+ for (const file of manifest.files) {
77274
+ filesWritten.push(copyBundleFile(bundleDir, file, resolved, force));
77275
+ }
77276
+ const programDest = path68.join(resolved, lineProgramPathRel(manifest.id));
77277
+ const programSrc = path68.join(bundleDir, manifest.program);
77278
+ if (path68.resolve(programSrc) !== path68.resolve(programDest)) {
77279
+ if (fs61.existsSync(programDest) && !force) {
77280
+ throw new Error(
77281
+ `Line already installed: ${lineProgramPathRel(manifest.id)}. Use --force to replace.`
77282
+ );
77283
+ }
77284
+ ensureParentDir3(programDest);
77285
+ fs61.copyFileSync(programSrc, programDest);
77286
+ }
77287
+ filesWritten.push(lineProgramPathRel(manifest.id));
77288
+ const manifestDest = path68.join(resolved, lineDirRel(manifest.id), "line.manifest.json");
77289
+ const manifestSrc = findManifestPath(bundleDir, LINE_BUNDLE_CONFIG);
77290
+ if (path68.resolve(manifestSrc) !== path68.resolve(manifestDest)) {
77291
+ ensureParentDir3(manifestDest);
77292
+ fs61.copyFileSync(manifestSrc, manifestDest);
77293
+ filesWritten.push(`${lineDirRel(manifest.id)}/line.manifest.json`);
77294
+ }
77295
+ registerStagesOnly(resolved, stages, force);
77296
+ const verificationAfter = JSON.stringify(readStageRegistry(resolved).verificationStages ?? null);
77297
+ if (verificationBefore !== verificationAfter) {
77298
+ throw new Error(
77299
+ "Line apply changed verificationStages \u2014 this is a bug. Line gate stages must stay off `har env verify --full`."
77300
+ );
77301
+ }
77302
+ warnings.push(...assertGateStagesResolvable(program, stageIds, resolved));
77303
+ upsertLineLedgerEntry(resolved, {
77304
+ id: manifest.id,
77305
+ source: meta.source,
77306
+ spec: meta.spec,
77307
+ version: meta.version,
77308
+ stageIds,
77309
+ programPath: lineProgramPathRel(manifest.id),
77310
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
77311
+ });
77312
+ const firstGatedStation = program.stations.find(
77313
+ (station) => program.gate.stages.some((stage) => stage.fromStation === station.id)
77314
+ )?.id ?? program.stations[0].id;
77315
+ const nextSteps = manifest.nextSteps.length > 0 ? manifest.nextSteps : [
77316
+ `har line status ${manifest.id}`,
77317
+ `har line gate ${firstGatedStation} --line ${manifest.id}`,
77318
+ `Adapt the program: ${lineProgramPathRel(manifest.id)}`
77319
+ ];
77320
+ const partial2 = {
77321
+ lineId: manifest.id,
77322
+ title: manifest.title ?? program.title,
77323
+ stageIds,
77324
+ filesWritten,
77325
+ warnings,
77326
+ nextSteps,
77327
+ programPath: lineProgramPathRel(manifest.id),
77328
+ docsPath: manifest.docsPath,
77329
+ source: meta.source,
77330
+ stationIds: program.stations.map((s) => s.id),
77331
+ firstGatedStationId: firstGatedStation
77332
+ };
77333
+ const promptAbsPath = writeLineAdaptationPrompt(
77334
+ resolved,
77335
+ manifest.id,
77336
+ buildLineAdaptationPrompt(resolved, program, { ...partial2, adaptPromptPath: "" })
77337
+ );
77338
+ const adaptPromptPath = path68.relative(resolved, promptAbsPath);
77339
+ success(`Applied line: ${manifest.id}`);
77340
+ info(`Stations: ${partial2.stationIds.join(" \u2192 ")}`);
77341
+ info(
77342
+ stageIds.length > 0 ? `Registered stage(s) (NOT on verify): ${stageIds.join(", ")}` : "No extra stages registered"
77343
+ );
77344
+ for (const file of filesWritten) {
77345
+ info(` + ${file}`);
77346
+ }
77347
+ info(` + ${adaptPromptPath} (adaptation prompt for your coding agent)`);
77348
+ for (const warning of warnings) {
77349
+ warn(` \u26A0 ${warning}`);
77350
+ }
77351
+ return { ...partial2, adaptPromptPath };
77352
+ }
77353
+ function applyLine(repoPath, lineSpec, options = {}) {
77354
+ const spec = options.spec ?? lineSpec;
77355
+ const source = resolveLineSource(spec, repoPath);
77356
+ try {
77357
+ return applyLineFromDir(repoPath, source.dir, options, {
77358
+ source: source.kind,
77359
+ spec: source.spec,
77360
+ version: source.version
77361
+ });
77362
+ } finally {
77363
+ cleanupResolvedLine(source);
77364
+ }
77365
+ }
77366
+
77367
+ // src/harness/doctor.ts
76790
77368
  init_stages();
76791
77369
  init_hooks2();
76792
77370
 
76793
77371
  // src/harness/migrations.ts
76794
- var fs60 = __toESM(require("fs"));
76795
- var path66 = __toESM(require("path"));
77372
+ var fs62 = __toESM(require("fs"));
77373
+ var path69 = __toESM(require("path"));
76796
77374
  init_schema2();
76797
77375
  init_manifest();
76798
77376
  init_profiles();
76799
77377
  init_stages();
76800
77378
  init_manifest();
76801
77379
  var MIGRATE_DIR = "migrate";
76802
- var MIGRATE_BACKUP_DIR = path66.join(MIGRATE_DIR, "backup");
76803
- var MIGRATE_PLAN_FILE = path66.join(MIGRATE_DIR, "plan.json");
77380
+ var MIGRATE_BACKUP_DIR = path69.join(MIGRATE_DIR, "backup");
77381
+ var MIGRATE_PLAN_FILE = path69.join(MIGRATE_DIR, "plan.json");
76804
77382
  var LEGACY_MACHINERY_FILES = [
76805
77383
  "agent-slot.sh",
76806
77384
  "provision-toolchain.sh",
@@ -76841,25 +77419,25 @@ function harnessEnvIsPre10(content) {
76841
77419
  return /^\s*(har_infra_enabled|har_pg|har_node_[a-z_]+|har_pkg_exec)\s*\(\)/m.test(content) || /^export\s+HARNESS_[A-Z0-9_]+_PORT_(DEFAULT|SCAN_START|SCAN_END)=/m.test(content);
76842
77420
  }
76843
77421
  function scriptIsVendored(harnessDir, script) {
76844
- const file = path66.join(harnessDir, script);
76845
- if (!fs60.existsSync(file)) return false;
76846
- const content = fs60.readFileSync(file, "utf8");
77422
+ const file = path69.join(harnessDir, script);
77423
+ if (!fs62.existsSync(file)) return false;
77424
+ const content = fs62.readFileSync(file, "utf8");
76847
77425
  return !content.includes("exec har env") && !content.includes("runtime/har.cjs");
76848
77426
  }
76849
77427
  function isPre10Harness(repoPath) {
76850
- const resolved = path66.resolve(repoPath);
77428
+ const resolved = path69.resolve(repoPath);
76851
77429
  const harnessDir = getHarnessDir(resolved);
76852
- if (!fs60.existsSync(harnessDir)) return false;
77430
+ if (!fs62.existsSync(harnessDir)) return false;
76853
77431
  const manifest = readManifest(resolved);
76854
77432
  if (manifest?.ejected) return false;
76855
77433
  for (const file of LEGACY_MACHINERY_FILES) {
76856
- if (fs60.existsSync(path66.join(harnessDir, file))) return true;
77434
+ if (fs62.existsSync(path69.join(harnessDir, file))) return true;
76857
77435
  }
76858
77436
  for (const script of MANAGED_SHIM_FILES) {
76859
77437
  if (scriptIsVendored(harnessDir, script)) return true;
76860
77438
  }
76861
- const envPath = path66.join(harnessDir, "harness.env");
76862
- if (fs60.existsSync(envPath) && harnessEnvIsPre10(fs60.readFileSync(envPath, "utf8"))) {
77439
+ const envPath = path69.join(harnessDir, "harness.env");
77440
+ if (fs62.existsSync(envPath) && harnessEnvIsPre10(fs62.readFileSync(envPath, "utf8"))) {
76863
77441
  return true;
76864
77442
  }
76865
77443
  return false;
@@ -76992,10 +77570,10 @@ var SCRIPT_RESIDUE_TARGET = {
76992
77570
  "agent-cli.sh": "review"
76993
77571
  };
76994
77572
  function backupRel(file) {
76995
- return path66.join(MIGRATE_BACKUP_DIR, file);
77573
+ return path69.join(MIGRATE_BACKUP_DIR, file);
76996
77574
  }
76997
77575
  function buildPre10Plan(repoPath) {
76998
- const resolved = path66.resolve(repoPath);
77576
+ const resolved = path69.resolve(repoPath);
76999
77577
  const harnessDir = getHarnessDir(resolved);
77000
77578
  const manifest = readManifest(resolved);
77001
77579
  const profile = manifest?.profile ?? "default";
@@ -77004,7 +77582,7 @@ function buildPre10Plan(repoPath) {
77004
77582
  const deleteLifecycleScripts = [];
77005
77583
  const residue = [];
77006
77584
  for (const script of MANAGED_SHIM_FILES) {
77007
- if (!fs60.existsSync(path66.join(harnessDir, script))) continue;
77585
+ if (!fs62.existsSync(path69.join(harnessDir, script))) continue;
77008
77586
  deleteLifecycleScripts.push(script);
77009
77587
  if (scriptIsVendored(harnessDir, script)) {
77010
77588
  const target = SCRIPT_RESIDUE_TARGET[script] ?? "review";
@@ -77018,31 +77596,31 @@ function buildPre10Plan(repoPath) {
77018
77596
  }
77019
77597
  const survivingScripts = [];
77020
77598
  for (const dir of ["stages", "hooks"]) {
77021
- const full = path66.join(harnessDir, dir);
77022
- if (!fs60.existsSync(full)) continue;
77023
- for (const entry of fs60.readdirSync(full)) {
77024
- if (entry.endsWith(".sh") || entry.endsWith(".mjs")) survivingScripts.push(path66.join(dir, entry));
77599
+ const full = path69.join(harnessDir, dir);
77600
+ if (!fs62.existsSync(full)) continue;
77601
+ for (const entry of fs62.readdirSync(full)) {
77602
+ if (entry.endsWith(".sh") || entry.endsWith(".mjs")) survivingScripts.push(path69.join(dir, entry));
77025
77603
  }
77026
77604
  }
77027
- for (const entry of fs60.readdirSync(harnessDir)) {
77605
+ for (const entry of fs62.readdirSync(harnessDir)) {
77028
77606
  if (!entry.endsWith(".sh")) continue;
77029
77607
  if (MANAGED_SHIM_FILES.includes(entry)) continue;
77030
77608
  if (LEGACY_MACHINERY_FILES.includes(entry)) continue;
77031
- if (!fs60.statSync(path66.join(harnessDir, entry)).isFile()) continue;
77609
+ if (!fs62.statSync(path69.join(harnessDir, entry)).isFile()) continue;
77032
77610
  survivingScripts.push(entry);
77033
77611
  }
77034
77612
  const referencedBy = (machineryFile) => {
77035
- const base = path66.basename(machineryFile).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77613
+ const base = path69.basename(machineryFile).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77036
77614
  const loadRe = new RegExp(
77037
77615
  `(?:\\bsource\\b|\\bbash\\b|\\bexec\\b|(?:^|[;&|(])\\s*\\.\\s)[^\\n#]*${base}`
77038
77616
  );
77039
77617
  return survivingScripts.filter(
77040
- (script) => fs60.readFileSync(path66.join(harnessDir, script), "utf8").split("\n").some((line) => !/^\s*#/.test(line) && loadRe.test(line))
77618
+ (script) => fs62.readFileSync(path69.join(harnessDir, script), "utf8").split("\n").some((line) => !/^\s*#/.test(line) && loadRe.test(line))
77041
77619
  );
77042
77620
  };
77043
77621
  const retireDocs = [];
77044
77622
  for (const doc of RETIRED_DOC_FILES) {
77045
- if (!fs60.existsSync(path66.join(harnessDir, doc))) continue;
77623
+ if (!fs62.existsSync(path69.join(harnessDir, doc))) continue;
77046
77624
  retireDocs.push(doc);
77047
77625
  residue.push({
77048
77626
  source: doc,
@@ -77055,8 +77633,8 @@ function buildPre10Plan(repoPath) {
77055
77633
  const editedMachinery = [];
77056
77634
  const retainMachinery = [];
77057
77635
  for (const file of LEGACY_MACHINERY_FILES) {
77058
- const full = path66.join(harnessDir, file);
77059
- if (!fs60.existsSync(full)) continue;
77636
+ const full = path69.join(harnessDir, file);
77637
+ if (!fs62.existsSync(full)) continue;
77060
77638
  const refs = referencedBy(file);
77061
77639
  if (refs.length > 0) {
77062
77640
  retainMachinery.push(file);
@@ -77070,7 +77648,7 @@ function buildPre10Plan(repoPath) {
77070
77648
  }
77071
77649
  deleteMachinery.push(file);
77072
77650
  const recorded = fileBaseline[file];
77073
- const installed = computeFileChecksum(fs60.readFileSync(full, "utf8"));
77651
+ const installed = computeFileChecksum(fs62.readFileSync(full, "utf8"));
77074
77652
  if (recorded !== void 0 && installed !== recorded) {
77075
77653
  editedMachinery.push(file);
77076
77654
  residue.push({
@@ -77082,9 +77660,9 @@ function buildPre10Plan(repoPath) {
77082
77660
  }
77083
77661
  }
77084
77662
  let env3 = null;
77085
- const envPath = path66.join(harnessDir, "harness.env");
77086
- if (fs60.existsSync(envPath)) {
77087
- const content = fs60.readFileSync(envPath, "utf8");
77663
+ const envPath = path69.join(harnessDir, "harness.env");
77664
+ if (fs62.existsSync(envPath)) {
77665
+ const content = fs62.readFileSync(envPath, "utf8");
77088
77666
  if (harnessEnvIsPre10(content) || migrateHarnessEnvContent(content).commentedKeys.length > 0) {
77089
77667
  env3 = migrateHarnessEnvContent(content);
77090
77668
  for (const fn of env3.customFunctions) {
@@ -77109,7 +77687,7 @@ function buildPre10Plan(repoPath) {
77109
77687
  for (const file of composed.keys()) {
77110
77688
  if (MANAGED_SHIM_FILES.includes(file)) continue;
77111
77689
  if (file === "gitignore.template") continue;
77112
- if (!fs60.existsSync(path66.join(harnessDir, file))) installMissing.push(file);
77690
+ if (!fs62.existsSync(path69.join(harnessDir, file))) installMissing.push(file);
77113
77691
  }
77114
77692
  let phantomVerificationIds = [];
77115
77693
  try {
@@ -77140,25 +77718,25 @@ function buildPre10Plan(repoPath) {
77140
77718
  };
77141
77719
  }
77142
77720
  function applyPre10Plan(repoPath, plan) {
77143
- const resolved = path66.resolve(repoPath);
77721
+ const resolved = path69.resolve(repoPath);
77144
77722
  const harnessDir = getHarnessDir(resolved);
77145
77723
  const effective = plan ?? buildPre10Plan(resolved);
77146
- const backupDir = path66.join(harnessDir, MIGRATE_BACKUP_DIR);
77147
- fs60.mkdirSync(backupDir, { recursive: true });
77724
+ const backupDir = path69.join(harnessDir, MIGRATE_BACKUP_DIR);
77725
+ fs62.mkdirSync(backupDir, { recursive: true });
77148
77726
  const backup = (file) => {
77149
- const src = path66.join(harnessDir, file);
77150
- if (!fs60.existsSync(src)) return;
77151
- const dest = path66.join(backupDir, file);
77152
- fs60.mkdirSync(path66.dirname(dest), { recursive: true });
77153
- fs60.copyFileSync(src, dest);
77727
+ const src = path69.join(harnessDir, file);
77728
+ if (!fs62.existsSync(src)) return;
77729
+ const dest = path69.join(backupDir, file);
77730
+ fs62.mkdirSync(path69.dirname(dest), { recursive: true });
77731
+ fs62.copyFileSync(src, dest);
77154
77732
  };
77155
77733
  const written = [];
77156
77734
  const deleted = [];
77157
- const projectName = path66.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
77735
+ const projectName = path69.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
77158
77736
  const composed = composeProfileTemplateMap(effective.profile);
77159
77737
  for (const script of effective.deleteLifecycleScripts) {
77160
77738
  backup(script);
77161
- fs60.rmSync(path66.join(harnessDir, script), { force: true });
77739
+ fs62.rmSync(path69.join(harnessDir, script), { force: true });
77162
77740
  deleted.push(script);
77163
77741
  }
77164
77742
  if (stripLifecycleShimCommands(resolved)) {
@@ -77166,26 +77744,26 @@ function applyPre10Plan(repoPath, plan) {
77166
77744
  }
77167
77745
  for (const file of effective.deleteMachinery) {
77168
77746
  backup(file);
77169
- fs60.rmSync(path66.join(harnessDir, file), { force: true });
77747
+ fs62.rmSync(path69.join(harnessDir, file), { force: true });
77170
77748
  deleted.push(file);
77171
77749
  }
77172
77750
  for (const doc of effective.retireDocs ?? []) {
77173
77751
  backup(doc);
77174
77752
  }
77175
- const libDir = path66.join(harnessDir, "lib");
77176
- if (fs60.existsSync(libDir) && fs60.readdirSync(libDir).length === 0) {
77177
- fs60.rmdirSync(libDir);
77753
+ const libDir = path69.join(harnessDir, "lib");
77754
+ if (fs62.existsSync(libDir) && fs62.readdirSync(libDir).length === 0) {
77755
+ fs62.rmdirSync(libDir);
77178
77756
  deleted.push("lib/");
77179
77757
  }
77180
77758
  for (const file of effective.installMissing ?? []) {
77181
77759
  const source = composed.get(file);
77182
77760
  if (!source) continue;
77183
- const dest = path66.join(harnessDir, file);
77184
- if (fs60.existsSync(dest)) continue;
77761
+ const dest = path69.join(harnessDir, file);
77762
+ if (fs62.existsSync(dest)) continue;
77185
77763
  const rendered = substituteTemplateTokens(readComposedTemplateContent(source), projectName);
77186
- fs60.mkdirSync(path66.dirname(dest), { recursive: true });
77187
- fs60.writeFileSync(dest, rendered);
77188
- if (/\.(sh|mjs)$/.test(file)) fs60.chmodSync(dest, 493);
77764
+ fs62.mkdirSync(path69.dirname(dest), { recursive: true });
77765
+ fs62.writeFileSync(dest, rendered);
77766
+ if (/\.(sh|mjs)$/.test(file)) fs62.chmodSync(dest, 493);
77189
77767
  written.push(file);
77190
77768
  }
77191
77769
  if (effective.phantomVerificationIds.length > 0) {
@@ -77200,7 +77778,7 @@ function applyPre10Plan(repoPath, plan) {
77200
77778
  }
77201
77779
  if (effective.env) {
77202
77780
  backup("harness.env");
77203
- fs60.writeFileSync(path66.join(harnessDir, "harness.env"), effective.env.content);
77781
+ fs62.writeFileSync(path69.join(harnessDir, "harness.env"), effective.env.content);
77204
77782
  written.push("harness.env");
77205
77783
  }
77206
77784
  const manifest = readManifest(resolved);
@@ -77215,8 +77793,8 @@ function applyPre10Plan(repoPath, plan) {
77215
77793
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
77216
77794
  });
77217
77795
  }
77218
- fs60.writeFileSync(
77219
- path66.join(harnessDir, MIGRATE_PLAN_FILE),
77796
+ fs62.writeFileSync(
77797
+ path69.join(harnessDir, MIGRATE_PLAN_FILE),
77220
77798
  JSON.stringify({ ...effective, appliedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
77221
77799
  );
77222
77800
  return { plan: effective, backupDir, written, deleted };
@@ -77236,33 +77814,33 @@ function pendingMigrations(repoPath) {
77236
77814
  );
77237
77815
  }
77238
77816
  function writeMigrationPlan(repoPath, plan) {
77239
- const harnessDir = getHarnessDir(path66.resolve(repoPath));
77240
- const planPath = path66.join(harnessDir, MIGRATE_PLAN_FILE);
77241
- fs60.mkdirSync(path66.dirname(planPath), { recursive: true });
77242
- fs60.writeFileSync(planPath, JSON.stringify(plan, null, 2) + "\n");
77817
+ const harnessDir = getHarnessDir(path69.resolve(repoPath));
77818
+ const planPath = path69.join(harnessDir, MIGRATE_PLAN_FILE);
77819
+ fs62.mkdirSync(path69.dirname(planPath), { recursive: true });
77820
+ fs62.writeFileSync(planPath, JSON.stringify(plan, null, 2) + "\n");
77243
77821
  return planPath;
77244
77822
  }
77245
77823
  function removeMigrationArtifacts(repoPath) {
77246
- const harnessDir = getHarnessDir(path66.resolve(repoPath));
77247
- fs60.rmSync(path66.join(harnessDir, MIGRATE_DIR), { recursive: true, force: true });
77248
- fs60.rmSync(path66.join(harnessDir, "MIGRATE-PROMPT.md"), { force: true });
77824
+ const harnessDir = getHarnessDir(path69.resolve(repoPath));
77825
+ fs62.rmSync(path69.join(harnessDir, MIGRATE_DIR), { recursive: true, force: true });
77826
+ fs62.rmSync(path69.join(harnessDir, "MIGRATE-PROMPT.md"), { force: true });
77249
77827
  }
77250
77828
 
77251
77829
  // src/harness/doctor.ts
77252
77830
  function listHarnessShellScripts(harnessDir) {
77253
77831
  const out = [];
77254
77832
  const push = (dir, prefix) => {
77255
- if (!fs61.existsSync(dir)) return;
77256
- for (const entry of fs61.readdirSync(dir).sort()) {
77833
+ if (!fs63.existsSync(dir)) return;
77834
+ for (const entry of fs63.readdirSync(dir).sort()) {
77257
77835
  if (!entry.endsWith(".sh")) continue;
77258
- const full = path67.join(dir, entry);
77259
- if (!fs61.statSync(full).isFile()) continue;
77836
+ const full = path70.join(dir, entry);
77837
+ if (!fs63.statSync(full).isFile()) continue;
77260
77838
  out.push(prefix ? `${prefix}/${entry}` : entry);
77261
77839
  }
77262
77840
  };
77263
77841
  push(harnessDir, "");
77264
- push(path67.join(harnessDir, "stages"), "stages");
77265
- push(path67.join(harnessDir, "hooks"), "hooks");
77842
+ push(path70.join(harnessDir, "stages"), "stages");
77843
+ push(path70.join(harnessDir, "hooks"), "hooks");
77266
77844
  return out;
77267
77845
  }
77268
77846
  var CHECK_LABELS = {
@@ -77275,14 +77853,15 @@ var CHECK_LABELS = {
77275
77853
  "slot-registry": "slot registry worktrees",
77276
77854
  hooks: "lifecycle hooks (.har/hooks)",
77277
77855
  "retired-machinery": "no references to retired runtime machinery",
77278
- "ejected-runtime": "ejected runtime (user-owned)"
77856
+ "ejected-runtime": "ejected runtime (user-owned)",
77857
+ "factory-lines": "factory lines stay off verify"
77279
77858
  };
77280
77859
  var PRE_1_0_FUNCTION_RE = /^\s*(har_infra_enabled|har_pg|har_node_[a-z_]+|har_pkg_exec)\s*\(\)/m;
77281
77860
  var LEGACY_TRIPLET_RE = /^export\s+HARNESS_[A-Z0-9_]+_PORT_(DEFAULT|SCAN_START|SCAN_END)=/m;
77282
77861
  function detectContract(harnessDir) {
77283
- const envPath = path67.join(harnessDir, "harness.env");
77284
- if (!fs61.existsSync(envPath)) return "none";
77285
- const content = fs61.readFileSync(envPath, "utf8");
77862
+ const envPath = path70.join(harnessDir, "harness.env");
77863
+ if (!fs63.existsSync(envPath)) return "none";
77864
+ const content = fs63.readFileSync(envPath, "utf8");
77286
77865
  if (PRE_1_0_FUNCTION_RE.test(content) || LEGACY_TRIPLET_RE.test(content)) {
77287
77866
  return "pre-1.0";
77288
77867
  }
@@ -77292,18 +77871,18 @@ function resolveStageFile(harnessDir, stage) {
77292
77871
  if (PACKAGE_RUNTIME_KINDS.has(stage.kind) && (!stage.command && !stage.script || stagePointsAtLifecycleShim(stage))) {
77293
77872
  return null;
77294
77873
  }
77295
- if (stage.script) return path67.join(harnessDir, stage.script);
77874
+ if (stage.script) return path70.join(harnessDir, stage.script);
77296
77875
  if (stage.command) {
77297
77876
  const first = stage.command.trim().split(/\s+/)[0];
77298
77877
  if (first.startsWith("./.har/")) {
77299
- return path67.join(harnessDir, first.replace(/^\.\/\.har\//, ""));
77878
+ return path70.join(harnessDir, first.replace(/^\.\/\.har\//, ""));
77300
77879
  }
77301
77880
  if (first.startsWith(".har/")) {
77302
- return path67.join(harnessDir, first.replace(/^\.har\//, ""));
77881
+ return path70.join(harnessDir, first.replace(/^\.har\//, ""));
77303
77882
  }
77304
77883
  return null;
77305
77884
  }
77306
- return path67.join(harnessDir, "stages", `${stage.id}.sh`);
77885
+ return path70.join(harnessDir, "stages", `${stage.id}.sh`);
77307
77886
  }
77308
77887
  function checkStatus(findings, id) {
77309
77888
  const mine = findings.filter((f) => f.check === id);
@@ -77315,7 +77894,7 @@ function runDoctor(repoPath) {
77315
77894
  const harnessDir = getHarnessDir(repoPath);
77316
77895
  const findings = [];
77317
77896
  const skipped = /* @__PURE__ */ new Set();
77318
- if (!fs61.existsSync(harnessDir)) {
77897
+ if (!fs63.existsSync(harnessDir)) {
77319
77898
  return {
77320
77899
  ok: false,
77321
77900
  contract: "none",
@@ -77362,9 +77941,9 @@ function runDoctor(repoPath) {
77362
77941
  }
77363
77942
  let registry2 = null;
77364
77943
  try {
77365
- const stagesPath = path67.join(harnessDir, "stages.json");
77366
- if (fs61.existsSync(stagesPath)) {
77367
- const raw = JSON.parse(fs61.readFileSync(stagesPath, "utf8"));
77944
+ const stagesPath = path70.join(harnessDir, "stages.json");
77945
+ if (fs63.existsSync(stagesPath)) {
77946
+ const raw = JSON.parse(fs63.readFileSync(stagesPath, "utf8"));
77368
77947
  if (typeof raw !== "object" || raw === null || !Array.isArray(raw.stages)) {
77369
77948
  throw new Error("stages.json has no `stages` array \u2014 the registry is structurally invalid");
77370
77949
  }
@@ -77395,8 +77974,8 @@ function runDoctor(repoPath) {
77395
77974
  for (const stage of registry2.stages) {
77396
77975
  const file = resolveStageFile(harnessDir, stage);
77397
77976
  if (!file) continue;
77398
- const rel = path67.relative(harnessDir, file);
77399
- if (!fs61.existsSync(file)) {
77977
+ const rel = path70.relative(harnessDir, file);
77978
+ if (!fs63.existsSync(file)) {
77400
77979
  findings.push({
77401
77980
  check: "stage-files",
77402
77981
  severity: contractSeverity,
@@ -77406,7 +77985,7 @@ function runDoctor(repoPath) {
77406
77985
  });
77407
77986
  continue;
77408
77987
  }
77409
- if (rel.endsWith(".sh") && !(fs61.statSync(file).mode & 73)) {
77988
+ if (rel.endsWith(".sh") && !(fs63.statSync(file).mode & 73)) {
77410
77989
  findings.push({
77411
77990
  check: "stage-files",
77412
77991
  severity: "warning",
@@ -77429,13 +78008,13 @@ function runDoctor(repoPath) {
77429
78008
  continue;
77430
78009
  }
77431
78010
  const file = resolveStageFile(harnessDir, stage);
77432
- if (file && !fs61.existsSync(file)) {
78011
+ if (file && !fs63.existsSync(file)) {
77433
78012
  findings.push({
77434
78013
  check: "lifecycle-stages",
77435
78014
  severity: contractSeverity,
77436
- file: path67.relative(harnessDir, file),
78015
+ file: path70.relative(harnessDir, file),
77437
78016
  message: `Lifecycle stage \`${stage.id}\` (${kind}) points at a missing file`,
77438
- remedy: `Restore .har/${path67.relative(harnessDir, file)} or fix the \`${stage.id}\` entry in stages.json`
78017
+ remedy: `Restore .har/${path70.relative(harnessDir, file)} or fix the \`${stage.id}\` entry in stages.json`
77439
78018
  });
77440
78019
  }
77441
78020
  }
@@ -77480,9 +78059,9 @@ function runDoctor(repoPath) {
77480
78059
  if (!manifest?.ejected) {
77481
78060
  skipped.add("ejected-runtime");
77482
78061
  } else {
77483
- const runtimeRel = path67.join(EJECTED_RUNTIME_DIR, EJECTED_RUNTIME_BUNDLE);
77484
- const runtimePath = path67.join(harnessDir, runtimeRel);
77485
- if (!fs61.existsSync(runtimePath)) {
78062
+ const runtimeRel = path70.join(EJECTED_RUNTIME_DIR, EJECTED_RUNTIME_BUNDLE);
78063
+ const runtimePath = path70.join(harnessDir, runtimeRel);
78064
+ if (!fs63.existsSync(runtimePath)) {
77486
78065
  findings.push({
77487
78066
  check: "ejected-runtime",
77488
78067
  severity: "error",
@@ -77495,7 +78074,7 @@ function runDoctor(repoPath) {
77495
78074
  for (const entry of listSlotRegistryEntries(repoPath)) {
77496
78075
  if (entry.status === "completed") continue;
77497
78076
  const dir = entry.worktreePath ?? entry.workDir;
77498
- if (dir && !fs61.existsSync(dir)) {
78077
+ if (dir && !fs63.existsSync(dir)) {
77499
78078
  findings.push({
77500
78079
  check: "slot-registry",
77501
78080
  severity: "warning",
@@ -77505,11 +78084,11 @@ function runDoctor(repoPath) {
77505
78084
  });
77506
78085
  }
77507
78086
  }
77508
- const hooksDir = path67.join(harnessDir, "hooks");
77509
- if (fs61.existsSync(hooksDir)) {
77510
- for (const file of fs61.readdirSync(hooksDir).sort()) {
77511
- const full = path67.join(hooksDir, file);
77512
- if (!fs61.statSync(full).isFile() || !file.endsWith(".sh")) continue;
78087
+ const hooksDir = path70.join(harnessDir, "hooks");
78088
+ if (fs63.existsSync(hooksDir)) {
78089
+ for (const file of fs63.readdirSync(hooksDir).sort()) {
78090
+ const full = path70.join(hooksDir, file);
78091
+ if (!fs63.statSync(full).isFile() || !file.endsWith(".sh")) continue;
77513
78092
  if (!LIFECYCLE_HOOKS.includes(file.replace(/\.sh$/, ""))) {
77514
78093
  findings.push({
77515
78094
  check: "hooks",
@@ -77520,7 +78099,7 @@ function runDoctor(repoPath) {
77520
78099
  });
77521
78100
  continue;
77522
78101
  }
77523
- if (!(fs61.statSync(full).mode & 73)) {
78102
+ if (!(fs63.statSync(full).mode & 73)) {
77524
78103
  findings.push({
77525
78104
  check: "hooks",
77526
78105
  severity: "warning",
@@ -77532,15 +78111,15 @@ function runDoctor(repoPath) {
77532
78111
  }
77533
78112
  }
77534
78113
  for (const rel of listHarnessShellScripts(harnessDir)) {
77535
- const full = path67.join(harnessDir, rel);
78114
+ const full = path70.join(harnessDir, rel);
77536
78115
  let content;
77537
78116
  try {
77538
- content = fs61.readFileSync(full, "utf8");
78117
+ content = fs63.readFileSync(full, "utf8");
77539
78118
  } catch {
77540
78119
  continue;
77541
78120
  }
77542
78121
  for (const machinery of LEGACY_MACHINERY_FILES) {
77543
- if (fs61.existsSync(path67.join(harnessDir, machinery))) continue;
78122
+ if (fs63.existsSync(path70.join(harnessDir, machinery))) continue;
77544
78123
  const base = machinery.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77545
78124
  const loadRe = new RegExp(
77546
78125
  `(?:\\bsource\\b|\\bbash\\b|\\bexec\\b|(?:^|[;&|(])\\s*\\.\\s)[^\\n#]*${base}`
@@ -77559,6 +78138,46 @@ function runDoctor(repoPath) {
77559
78138
  break;
77560
78139
  }
77561
78140
  }
78141
+ const lineLedger = readLineLedger(repoPath);
78142
+ if (lineLedger) {
78143
+ const verificationStages = registry2?.verificationStages ?? [];
78144
+ const registeredIds = new Set((registry2?.stages ?? []).map((stage) => stage.id));
78145
+ for (const line of lineLedger.lines) {
78146
+ for (const stageId of line.stageIds) {
78147
+ if (verificationStages.includes(stageId)) {
78148
+ findings.push({
78149
+ check: "factory-lines",
78150
+ severity: "error",
78151
+ file: "stages.json",
78152
+ message: `line "${line.id}" stage "${stageId}" is listed in verificationStages`,
78153
+ remedy: "Remove it from verificationStages \u2014 line gate stages run via `har line gate <station>`. If it must gate every verify, ship it as a verification plugin instead."
78154
+ });
78155
+ }
78156
+ }
78157
+ const program = readInstalledLineProgram(repoPath, line.id);
78158
+ if (!program) {
78159
+ findings.push({
78160
+ check: "factory-lines",
78161
+ severity: "warning",
78162
+ file: line.programPath,
78163
+ message: `line "${line.id}" is in .har/lines.json but its program is missing or invalid`,
78164
+ remedy: `Reinstall it (\`har line add ${line.id}\`) or drop the entry from .har/lines.json`
78165
+ });
78166
+ continue;
78167
+ }
78168
+ for (const gateStage of program.gate.stages) {
78169
+ if (!registeredIds.has(gateStage.id)) {
78170
+ findings.push({
78171
+ check: "factory-lines",
78172
+ severity: "warning",
78173
+ file: line.programPath,
78174
+ message: `line "${line.id}" gate stage "${gateStage.id}" (from ${gateStage.fromStation}) is not registered in stages.json`,
78175
+ remedy: "Install the plugin that provides it, add it to the line bundle, or drop the gate tag"
78176
+ });
78177
+ }
78178
+ }
78179
+ }
78180
+ }
77562
78181
  const checks = Object.keys(CHECK_LABELS).map(
77563
78182
  (id) => skipped.has(id) ? { id, label: CHECK_LABELS[id], status: "skip" } : checkStatus(findings, id)
77564
78183
  );
@@ -77604,8 +78223,8 @@ function summarizeDoctorReport(report) {
77604
78223
  }
77605
78224
 
77606
78225
  // src/harness/migrate-prompt.ts
77607
- var fs62 = __toESM(require("fs"));
77608
- var path68 = __toESM(require("path"));
78226
+ var fs64 = __toESM(require("fs"));
78227
+ var path71 = __toESM(require("path"));
77609
78228
  init_paths();
77610
78229
  init_file_ops();
77611
78230
  init_manifest();
@@ -77719,13 +78338,13 @@ function loadTemplate2() {
77719
78338
  if (!filePath) {
77720
78339
  throw new Error("Migration prompt template not found: migration-prompt.md. Run npm run build.");
77721
78340
  }
77722
- return fs62.readFileSync(filePath, "utf8");
78341
+ return fs64.readFileSync(filePath, "utf8");
77723
78342
  }
77724
78343
  function buildMigrationPrompt(plan, applied) {
77725
78344
  return loadTemplate2().replace("{{MIGRATION_SECTION}}", buildMigrationSection(plan, applied));
77726
78345
  }
77727
78346
  function writeMigrationPrompt(repoPath, content) {
77728
- const filePath = path68.join(getHarnessDir(repoPath), MIGRATE_PROMPT_FILE);
78347
+ const filePath = path71.join(getHarnessDir(repoPath), MIGRATE_PROMPT_FILE);
77729
78348
  writeFileSafe(filePath, content);
77730
78349
  return filePath;
77731
78350
  }
@@ -77733,11 +78352,11 @@ function writeMigrationPrompt(repoPath, content) {
77733
78352
  // src/core/harness.ts
77734
78353
  function listHarnessScripts(repoPath) {
77735
78354
  const harnessDir = getHarnessDir(repoPath);
77736
- if (!fs63.existsSync(harnessDir)) return [];
77737
- return fs63.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
78355
+ if (!fs65.existsSync(harnessDir)) return [];
78356
+ return fs65.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
77738
78357
  }
77739
78358
  function describeProject(repoPath) {
77740
- const resolved = path69.resolve(repoPath);
78359
+ const resolved = path72.resolve(repoPath);
77741
78360
  const manifest = readManifest(resolved);
77742
78361
  const present = harnessExists(resolved);
77743
78362
  return {
@@ -77757,8 +78376,8 @@ function describeProject(repoPath) {
77757
78376
  };
77758
78377
  }
77759
78378
  async function initHarness(options) {
77760
- const repoPath = path69.resolve(options.repoPath);
77761
- if (!fs63.existsSync(repoPath)) {
78379
+ const repoPath = path72.resolve(options.repoPath);
78380
+ if (!fs65.existsSync(repoPath)) {
77762
78381
  throw new Error(`Path not found: ${repoPath}`);
77763
78382
  }
77764
78383
  const scaffold = scaffoldHarnessBoilerplate(repoPath, {
@@ -77779,9 +78398,9 @@ async function initHarness(options) {
77779
78398
  };
77780
78399
  }
77781
78400
  async function maintainHarness(options) {
77782
- const repoPath = path69.resolve(options.repoPath);
78401
+ const repoPath = path72.resolve(options.repoPath);
77783
78402
  const harnessDir = getHarnessDir(repoPath);
77784
- if (!fs63.existsSync(harnessDir)) {
78403
+ if (!fs65.existsSync(harnessDir)) {
77785
78404
  throw new Error('No .har/ found. Run "har onboard" first.');
77786
78405
  }
77787
78406
  let migration;
@@ -77858,8 +78477,8 @@ function addPlugin(repoPath, pluginId, options = {}) {
77858
78477
  }
77859
78478
 
77860
78479
  // src/harness/adaptation-prompt.ts
77861
- var fs64 = __toESM(require("fs"));
77862
- var path70 = __toESM(require("path"));
78480
+ var fs66 = __toESM(require("fs"));
78481
+ var path73 = __toESM(require("path"));
77863
78482
  var readline5 = __toESM(require("readline"));
77864
78483
 
77865
78484
  // src/utils/clipboard.ts
@@ -77950,7 +78569,7 @@ function loadTemplate3(name) {
77950
78569
  if (!filePath) {
77951
78570
  throw new Error(`Adaptation prompt template not found: ${name}. Run npm run build.`);
77952
78571
  }
77953
- return fs64.readFileSync(filePath, "utf8");
78572
+ return fs66.readFileSync(filePath, "utf8");
77954
78573
  }
77955
78574
  function applyProfilePlaceholders(content, profile) {
77956
78575
  return content.replace(/\{\{PROFILE\}\}/g, profile).replace(/\{\{PROFILE_HINT\}\}/g, PROFILE_HINTS[profile]);
@@ -77972,7 +78591,7 @@ function buildMaintainAdaptationPrompt(_repoPath, bundleReport) {
77972
78591
  }
77973
78592
  function writeAdaptationPrompt(repoPath, content) {
77974
78593
  const harnessDir = getHarnessDir(repoPath);
77975
- const filePath = path70.join(harnessDir, ADAPTATION_PROMPT_FILE);
78594
+ const filePath = path73.join(harnessDir, ADAPTATION_PROMPT_FILE);
77976
78595
  writeFileSafe(filePath, content);
77977
78596
  return filePath;
77978
78597
  }
@@ -77991,12 +78610,12 @@ function printAdaptationPrompt(content) {
77991
78610
  }
77992
78611
  function askYesNo2(question) {
77993
78612
  const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
77994
- return new Promise((resolve60) => {
78613
+ return new Promise((resolve66) => {
77995
78614
  process.stderr.write(`${question} `);
77996
78615
  rl.once("line", (answer) => {
77997
78616
  rl.close();
77998
78617
  const trimmed = answer.trim();
77999
- resolve60(trimmed === "" || /^y(es)?$/i.test(trimmed));
78618
+ resolve66(trimmed === "" || /^y(es)?$/i.test(trimmed));
78000
78619
  });
78001
78620
  });
78002
78621
  }
@@ -78028,28 +78647,28 @@ async function offerAdaptationPromptClipboard(content, options = {}) {
78028
78647
  }
78029
78648
 
78030
78649
  // src/harness/cursor-rule.ts
78031
- var fs65 = __toESM(require("fs"));
78032
- var path71 = __toESM(require("path"));
78650
+ var fs67 = __toESM(require("fs"));
78651
+ var path74 = __toESM(require("path"));
78033
78652
  var readline6 = __toESM(require("readline"));
78034
78653
  init_file_ops();
78035
78654
  init_logging();
78036
78655
  init_paths();
78037
78656
  var CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/har-workflow.mdc";
78038
78657
  function getCursorRulePath(repoPath) {
78039
- return path71.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
78658
+ return path74.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
78040
78659
  }
78041
78660
  function isCursorWorkspace(repoPath) {
78042
- return fs65.existsSync(path71.join(repoPath, ".cursor"));
78661
+ return fs67.existsSync(path74.join(repoPath, ".cursor"));
78043
78662
  }
78044
78663
  function cursorRuleExists(repoPath) {
78045
- return fs65.existsSync(getCursorRulePath(repoPath));
78664
+ return fs67.existsSync(getCursorRulePath(repoPath));
78046
78665
  }
78047
78666
  function scaffoldCursorRule(repoPath) {
78048
78667
  const templatePath = resolveTemplateFile("cursor-rule.mdc.template");
78049
78668
  if (!templatePath) {
78050
78669
  throw new Error("Cursor rule template not found (cursor-rule.mdc.template). Run npm run build.");
78051
78670
  }
78052
- const content = fs65.readFileSync(templatePath, "utf8");
78671
+ const content = fs67.readFileSync(templatePath, "utf8");
78053
78672
  writeFileSafe(getCursorRulePath(repoPath), content);
78054
78673
  }
78055
78674
  async function handleCursorRule(options) {
@@ -78087,16 +78706,16 @@ async function promptScaffoldCursorRule(exists) {
78087
78706
  }
78088
78707
  async function askYesNo3(question) {
78089
78708
  const rl = readline6.createInterface({ input: process.stdin, output: process.stderr });
78090
- return new Promise((resolve60) => {
78709
+ return new Promise((resolve66) => {
78091
78710
  process.stderr.write(`${question} `);
78092
78711
  rl.once("line", (answer) => {
78093
78712
  rl.close();
78094
78713
  const trimmed = answer.trim();
78095
78714
  if (trimmed === "") {
78096
- resolve60(true);
78715
+ resolve66(true);
78097
78716
  return;
78098
78717
  }
78099
- resolve60(/^y(es)?$/i.test(trimmed));
78718
+ resolve66(/^y(es)?$/i.test(trimmed));
78100
78719
  });
78101
78720
  });
78102
78721
  }
@@ -78140,7 +78759,7 @@ init_control_config();
78140
78759
 
78141
78760
  // src/core/control-lifecycle.ts
78142
78761
  var import_child_process18 = require("child_process");
78143
- var path72 = __toESM(require("path"));
78762
+ var path75 = __toESM(require("path"));
78144
78763
 
78145
78764
  // src/core/control-image.ts
78146
78765
  init_package_version();
@@ -78167,10 +78786,10 @@ var CONTROL_DATA_VOLUME = "har_control_data";
78167
78786
  var CONTROL_CONTAINER_PORT = 3847;
78168
78787
  var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
78169
78788
  function resolveControlDir() {
78170
- return path72.resolve(__dirname, "..", "control");
78789
+ return path75.resolve(__dirname, "..", "control");
78171
78790
  }
78172
78791
  function resolveControlBuildContext() {
78173
- return path72.resolve(resolveControlDir(), "..");
78792
+ return path75.resolve(resolveControlDir(), "..");
78174
78793
  }
78175
78794
  function buildDockerRunArgs(options) {
78176
78795
  const name = options.containerName ?? CONTROL_CONTAINER_NAME;
@@ -78226,7 +78845,7 @@ async function startMissionControl(options) {
78226
78845
  "-t",
78227
78846
  imageRef,
78228
78847
  "-f",
78229
- path72.join(resolveControlDir(), "Dockerfile"),
78848
+ path75.join(resolveControlDir(), "Dockerfile"),
78230
78849
  resolveControlBuildContext()
78231
78850
  ]);
78232
78851
  if (buildCode !== 0) {
@@ -78373,7 +78992,7 @@ async function ensureTelemetryInfrastructure(options) {
78373
78992
  // src/core/run-service.ts
78374
78993
  init_telemetry_config();
78375
78994
  init_telemetry_env();
78376
- var path74 = __toESM(require("path"));
78995
+ var path77 = __toESM(require("path"));
78377
78996
  init_local_executor();
78378
78997
  function extractShellOutput(result) {
78379
78998
  const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
@@ -78596,12 +79215,12 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78596
79215
  suffix: session.suffix,
78597
79216
  createdAt: session.createdAt
78598
79217
  });
78599
- const envFile = path74.join(session.workDir, `.env.agent.${options.agentId}`);
79218
+ const envFile = path77.join(session.workDir, `.env.agent.${options.agentId}`);
78600
79219
  try {
78601
79220
  appendTelemetryEnvToFile(envFile, {
78602
79221
  sessionKey,
78603
79222
  agentId: options.agentId,
78604
- repoPath: path74.resolve(options.repoPath),
79223
+ repoPath: path77.resolve(options.repoPath),
78605
79224
  workDir: session.workDir,
78606
79225
  branch: session.branch,
78607
79226
  suffix: session.suffix,
@@ -78722,9 +79341,9 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78722
79341
  return envResult;
78723
79342
  }
78724
79343
  /**
78725
- * Finish a session: full verification (recorded as a validation keyed by the
78726
- * worktree tree hash), then teardown. The session branch is kept so the user
78727
- * can push it and open a PR.
79344
+ * Finish a session: reuse the last matching passing full validation (or
79345
+ * re-run verify when asked), then teardown. The session branch is kept so
79346
+ * the user can push it and open a PR.
78728
79347
  */
78729
79348
  async completeEnvironment(options) {
78730
79349
  const harnessRoot = resolveHarnessRoot(options.repoPath);
@@ -78736,9 +79355,11 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78736
79355
  stderr: `No active session for agent ${options.agentId}. Run launch first.`
78737
79356
  };
78738
79357
  }
79358
+ const runVerify = shouldReverifyOnComplete(options);
78739
79359
  let verification;
78740
79360
  let validation2;
78741
- if (!options.skipVerify) {
79361
+ let reusedValidation = false;
79362
+ if (runVerify) {
78742
79363
  const verify = await this.runVerification({
78743
79364
  repoPath: options.repoPath,
78744
79365
  agentId: options.agentId,
@@ -78751,11 +79372,27 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78751
79372
  if (verify.code !== 0 || verification?.status !== "pass") {
78752
79373
  return {
78753
79374
  ...verify,
78754
- stderr: verify.stderr + "\nVerification failed \u2014 session NOT completed. Fix the failures and rerun, or complete with skipVerify."
79375
+ stderr: verify.stderr + "\nVerification failed \u2014 session NOT completed. Fix the failures and rerun `complete --verify`, or use teardown to free the slot without claiming completion."
79376
+ };
79377
+ }
79378
+ } else {
79379
+ const checkoutDir = resolveValidationCheckoutDir({
79380
+ worktreePath: session.worktreePath,
79381
+ workDir: session.workDir,
79382
+ harnessRoot
79383
+ });
79384
+ validation2 = findPassingFullValidation({ checkoutDir, harnessRoot });
79385
+ if (!validation2) {
79386
+ return {
79387
+ code: 1,
79388
+ stdout: "",
79389
+ stderr: `No passing full validation matches the current worktree. The tree may have changed since the last \`har env verify --full\`. Re-run verification with: har env complete ${options.agentId} --verify
79390
+ Or free the slot without claiming completion: har env teardown ${options.agentId}`
78755
79391
  };
78756
79392
  }
79393
+ reusedValidation = true;
78757
79394
  }
78758
- if (session.workUnitId && session.attemptId && !options.skipVerify && !validation2) {
79395
+ if (session.workUnitId && session.attemptId && runVerify && !validation2) {
78759
79396
  return {
78760
79397
  code: 1,
78761
79398
  stdout: "",
@@ -78779,11 +79416,13 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78779
79416
  trigger: options.trigger
78780
79417
  });
78781
79418
  if (teardown.code !== 0) return { ...teardown, verification };
79419
+ const reusedNote = reusedValidation && validation2 ? `Reused passing full validation ${validation2.validationId} for tree ${validation2.treeHash}.
79420
+ ` : "";
78782
79421
  const branchNote = session.branch ? `Branch kept: ${session.branch} \u2014 push it with: git push -u origin ${session.branch}
78783
79422
  ` : "";
78784
79423
  return {
78785
79424
  ...teardown,
78786
- stdout: teardown.stdout + branchNote,
79425
+ stdout: teardown.stdout + reusedNote + branchNote,
78787
79426
  branch: session.branch,
78788
79427
  workDir: session.workDir,
78789
79428
  worktreePath: session.worktreePath,
@@ -78822,6 +79461,10 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78822
79461
  return toEnvironmentRunResult(result);
78823
79462
  }
78824
79463
  };
79464
+ function shouldReverifyOnComplete(options) {
79465
+ if (options.verify === true) return true;
79466
+ return options.skipVerify === false;
79467
+ }
78825
79468
  var defaultRunService = new RunService();
78826
79469
  function createRunService(executor = localScriptExecutor) {
78827
79470
  return new RunService(executor);
@@ -78843,9 +79486,9 @@ init_work_units();
78843
79486
  init_manifest();
78844
79487
 
78845
79488
  // src/core/cleanup-service.ts
78846
- var fs67 = __toESM(require("fs"));
79489
+ var fs69 = __toESM(require("fs"));
78847
79490
  var os23 = __toESM(require("os"));
78848
- var path75 = __toESM(require("path"));
79491
+ var path78 = __toESM(require("path"));
78849
79492
  var import_child_process20 = require("child_process");
78850
79493
  init_control_sync();
78851
79494
  init_control_repo_path();
@@ -78854,9 +79497,9 @@ init_manifest();
78854
79497
  var DEFAULT_STALE_DAYS = 7;
78855
79498
  function getWorktreesRoot() {
78856
79499
  if (process.env.HAR_WORKTREES_ROOT) {
78857
- return path75.resolve(process.env.HAR_WORKTREES_ROOT);
79500
+ return path78.resolve(process.env.HAR_WORKTREES_ROOT);
78858
79501
  }
78859
- return path75.join(process.env.HOME || os23.homedir(), "worktrees");
79502
+ return path78.join(process.env.HOME || os23.homedir(), "worktrees");
78860
79503
  }
78861
79504
  function runGit3(cwd, args) {
78862
79505
  try {
@@ -78870,7 +79513,7 @@ function runGit3(cwd, args) {
78870
79513
  }
78871
79514
  }
78872
79515
  function basenameRepo(repoPath) {
78873
- return path75.basename(canonicalizeControlRepoPath(repoPath));
79516
+ return path78.basename(canonicalizeControlRepoPath(repoPath));
78874
79517
  }
78875
79518
  function ageDaysFrom(iso) {
78876
79519
  if (!iso) return void 0;
@@ -78882,12 +79525,12 @@ function matchesKeep(candidate, keep) {
78882
79525
  if (!keep?.length) return false;
78883
79526
  for (const pin of keep) {
78884
79527
  if (pin.worktreePath && candidate.worktreePath) {
78885
- if (path75.resolve(pin.worktreePath) === path75.resolve(candidate.worktreePath)) return true;
79528
+ if (path78.resolve(pin.worktreePath) === path78.resolve(candidate.worktreePath)) return true;
78886
79529
  }
78887
79530
  if (pin.repoPath && pin.agentId !== void 0 && candidate.agentId !== void 0) {
78888
79531
  const repo = canonicalizeControlRepoPath(pin.repoPath);
78889
- const candidateRepo = path75.resolve(candidate.repoPath);
78890
- if ((repo === candidateRepo || path75.resolve(pin.repoPath) === candidateRepo) && pin.agentId === candidate.agentId) {
79532
+ const candidateRepo = path78.resolve(candidate.repoPath);
79533
+ if ((repo === candidateRepo || path78.resolve(pin.repoPath) === candidateRepo) && pin.agentId === candidate.agentId) {
78891
79534
  return true;
78892
79535
  }
78893
79536
  }
@@ -78942,20 +79585,20 @@ function classifySlotCandidate(repoPath, projectName, slot, options) {
78942
79585
  return candidate;
78943
79586
  }
78944
79587
  function resolveRepoForWorktree(worktreePath) {
78945
- if (!fs67.existsSync(worktreePath)) return void 0;
79588
+ if (!fs69.existsSync(worktreePath)) return void 0;
78946
79589
  const commonDir = runGit3(worktreePath, "rev-parse --git-common-dir");
78947
79590
  if (!commonDir) return void 0;
78948
- const gitRoot = path75.resolve(worktreePath, commonDir, "..");
79591
+ const gitRoot = path78.resolve(worktreePath, commonDir, "..");
78949
79592
  if (!readManifest(gitRoot)) return void 0;
78950
79593
  return canonicalizeControlRepoPath(gitRoot);
78951
79594
  }
78952
79595
  function discoverOrphanWorktrees(knownPaths) {
78953
79596
  const worktreesRoot = getWorktreesRoot();
78954
- if (!fs67.existsSync(worktreesRoot)) return [];
79597
+ if (!fs69.existsSync(worktreesRoot)) return [];
78955
79598
  const orphans = [];
78956
- for (const entry of fs67.readdirSync(worktreesRoot, { withFileTypes: true })) {
79599
+ for (const entry of fs69.readdirSync(worktreesRoot, { withFileTypes: true })) {
78957
79600
  if (!entry.isDirectory() || !entry.name.includes("-har-agent-")) continue;
78958
- const worktreePath = path75.resolve(path75.join(worktreesRoot, entry.name));
79601
+ const worktreePath = path78.resolve(path78.join(worktreesRoot, entry.name));
78959
79602
  if (knownPaths.has(worktreePath)) continue;
78960
79603
  const repoPath = resolveRepoForWorktree(worktreePath) ?? worktreesRoot;
78961
79604
  const porcelain = runGit3(worktreePath, "status --porcelain");
@@ -78980,14 +79623,14 @@ async function discoverCleanupCandidates(options = {}) {
78980
79623
  const candidates = [];
78981
79624
  const knownWorktreePaths = /* @__PURE__ */ new Set();
78982
79625
  for (const repoPath of repoPaths) {
78983
- if (!fs67.existsSync(repoPath) || !readManifest(repoPath)) continue;
79626
+ if (!fs69.existsSync(repoPath) || !readManifest(repoPath)) continue;
78984
79627
  const harnessRoot = resolveHarnessRoot(repoPath);
78985
79628
  const canonicalRepo = canonicalizeControlRepoPath(harnessRoot);
78986
79629
  const status = collectEnvironmentStatus(harnessRoot);
78987
79630
  const projectName = basenameRepo(harnessRoot);
78988
79631
  for (const slot of status.slots) {
78989
- if (slot.worktreePath) knownWorktreePaths.add(path75.resolve(slot.worktreePath));
78990
- if (slot.workDir) knownWorktreePaths.add(path75.resolve(slot.workDir));
79632
+ if (slot.worktreePath) knownWorktreePaths.add(path78.resolve(slot.worktreePath));
79633
+ if (slot.workDir) knownWorktreePaths.add(path78.resolve(slot.workDir));
78991
79634
  const candidate = classifySlotCandidate(canonicalRepo, projectName, slot, {
78992
79635
  staleDays: options.staleDays,
78993
79636
  keep: options.keep
@@ -79047,8 +79690,8 @@ async function askYesNo4(question) {
79047
79690
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
79048
79691
  const readline8 = await import("readline");
79049
79692
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
79050
- const answer = await new Promise((resolve60) => {
79051
- rl.question(question, resolve60);
79693
+ const answer = await new Promise((resolve66) => {
79694
+ rl.question(question, resolve66);
79052
79695
  });
79053
79696
  rl.close();
79054
79697
  return /^[Yy]$/.test(answer.trim());
@@ -79093,7 +79736,7 @@ async function executeCleanupCandidates(candidates, options = {}) {
79093
79736
  continue;
79094
79737
  }
79095
79738
  if (candidate.kind === "orphan_worktree" && candidate.worktreePath) {
79096
- if (fs67.existsSync(candidate.worktreePath)) {
79739
+ if (fs69.existsSync(candidate.worktreePath)) {
79097
79740
  const repoRoot = candidate.repoPath !== getWorktreesRoot() ? candidate.repoPath : resolveRepoForWorktree(candidate.worktreePath);
79098
79741
  if (repoRoot) {
79099
79742
  try {
@@ -79104,8 +79747,8 @@ async function executeCleanupCandidates(candidates, options = {}) {
79104
79747
  } catch {
79105
79748
  }
79106
79749
  }
79107
- if (fs67.existsSync(candidate.worktreePath)) {
79108
- fs67.rmSync(candidate.worktreePath, { recursive: true, force: true });
79750
+ if (fs69.existsSync(candidate.worktreePath)) {
79751
+ fs69.rmSync(candidate.worktreePath, { recursive: true, force: true });
79109
79752
  }
79110
79753
  }
79111
79754
  outcomes.push({ candidate, ok: true });
@@ -79131,7 +79774,7 @@ function parseCleanupKeepPins(values) {
79131
79774
  }
79132
79775
  if (raw.startsWith("/") || raw.startsWith("~")) {
79133
79776
  return {
79134
- worktreePath: raw.startsWith("~") ? path75.join(process.env.HOME || os23.homedir(), raw.slice(2)) : path75.resolve(raw)
79777
+ worktreePath: raw.startsWith("~") ? path78.join(process.env.HOME || os23.homedir(), raw.slice(2)) : path78.resolve(raw)
79135
79778
  };
79136
79779
  }
79137
79780
  return { repoPath: raw };
@@ -79145,12 +79788,12 @@ init_stages();
79145
79788
  init_logging();
79146
79789
  function askYesNo5(question) {
79147
79790
  const rl = readline7.createInterface({ input: process.stdin, output: process.stderr });
79148
- return new Promise((resolve60) => {
79791
+ return new Promise((resolve66) => {
79149
79792
  process.stderr.write(`${question} `);
79150
79793
  rl.once("line", (answer) => {
79151
79794
  rl.close();
79152
79795
  const trimmed = answer.trim();
79153
- resolve60(trimmed === "" || /^y(es)?$/i.test(trimmed));
79796
+ resolve66(trimmed === "" || /^y(es)?$/i.test(trimmed));
79154
79797
  });
79155
79798
  });
79156
79799
  }
@@ -79251,21 +79894,21 @@ function warnIfDockerUnavailable(status = detectDockerStatus()) {
79251
79894
  }
79252
79895
 
79253
79896
  // src/core/onboarding-preferences.ts
79254
- var fs68 = __toESM(require("fs"));
79897
+ var fs70 = __toESM(require("fs"));
79255
79898
  var os24 = __toESM(require("os"));
79256
- var path76 = __toESM(require("path"));
79899
+ var path79 = __toESM(require("path"));
79257
79900
  init_schema2();
79258
79901
  function getOnboardingPreferencesPath() {
79259
79902
  if (process.env.HAR_PREFERENCES_PATH) {
79260
- return path76.resolve(process.env.HAR_PREFERENCES_PATH);
79903
+ return path79.resolve(process.env.HAR_PREFERENCES_PATH);
79261
79904
  }
79262
- return path76.join(os24.homedir(), ".har", "preferences.json");
79905
+ return path79.join(os24.homedir(), ".har", "preferences.json");
79263
79906
  }
79264
79907
  function readOnboardingPreferences() {
79265
79908
  const preferencePath = getOnboardingPreferencesPath();
79266
79909
  try {
79267
- if (!fs68.existsSync(preferencePath)) return OnboardingPreferencesSchema.parse({});
79268
- return OnboardingPreferencesSchema.parse(JSON.parse(fs68.readFileSync(preferencePath, "utf8")));
79910
+ if (!fs70.existsSync(preferencePath)) return OnboardingPreferencesSchema.parse({});
79911
+ return OnboardingPreferencesSchema.parse(JSON.parse(fs70.readFileSync(preferencePath, "utf8")));
79269
79912
  } catch {
79270
79913
  return OnboardingPreferencesSchema.parse({});
79271
79914
  }
@@ -79290,8 +79933,8 @@ function writeOnboardingPreferences(overrides) {
79290
79933
  };
79291
79934
  const parsed = OnboardingPreferencesSchema.parse(preference);
79292
79935
  const preferencePath = getOnboardingPreferencesPath();
79293
- fs68.mkdirSync(path76.dirname(preferencePath), { recursive: true });
79294
- fs68.writeFileSync(preferencePath, `${JSON.stringify(parsed, null, 2)}
79936
+ fs70.mkdirSync(path79.dirname(preferencePath), { recursive: true });
79937
+ fs70.writeFileSync(preferencePath, `${JSON.stringify(parsed, null, 2)}
79295
79938
  `);
79296
79939
  return parsed;
79297
79940
  }
@@ -79311,12 +79954,12 @@ Typical agent workflow:
79311
79954
  har env launch <id> # new session from the main checkout's HEAD
79312
79955
  # \u2026edit only under the printed work dir\u2026
79313
79956
  har env verify <id> --full
79314
- har env complete <id> # done: verify + free the slot (branch kept for PR)
79957
+ har env complete <id> # done: reuse last full validation + free the slot (branch kept for PR)
79315
79958
 
79316
79959
  Slot lifecycle (do not confuse these):
79317
79960
  launch start a NEW session (worktree from $REPO_ROOT HEAD)
79318
79961
  recover resume a failed/partial launch (keeps the existing worktree)
79319
- complete finish successfully: full verify, then teardown (frees the slot)
79962
+ complete finish successfully: reuse last matching full validation, then teardown (frees the slot)
79320
79963
  teardown abandon / free the slot without a completion validation
79321
79964
 
79322
79965
  Occupied slots always block a new launch:
@@ -79332,7 +79975,7 @@ var HAR_ENV_EPILOG = `Environment lifecycle:
79332
79975
  launch <id> fresh session worktree from the main checkout's current HEAD
79333
79976
  recover <id> alias for launch --resume (failed/starting only)
79334
79977
  verify <id> run checks (--full before declaring done)
79335
- complete <id> full verify + teardown; keeps branch for push/PR (frees slot)
79978
+ complete <id> reuse last full validation + teardown; --verify to re-run; keeps branch (frees slot)
79336
79979
  teardown <id> free the slot without recording a completion validation
79337
79980
 
79338
79981
  Occupied slots:
@@ -79560,11 +80203,15 @@ var envCommand = {
79560
80203
  handleTeardown
79561
80204
  ).command(
79562
80205
  "complete <id>",
79563
- "Finish a session: full verify (recorded as validation), teardown, keep the branch for a PR",
79564
- (y) => y.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", { type: "string", default: "." }).option("skip-verify", {
80206
+ "Finish a session: reuse the last matching full validation, teardown, keep the branch for a PR",
80207
+ (y) => y.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", { type: "string", default: "." }).option("verify", {
79565
80208
  type: "boolean",
79566
80209
  default: false,
79567
- describe: "Tear down without running verification (no validation is recorded)"
80210
+ describe: "Re-run full verification before teardown (use when the tree may have changed since the last passing --full)"
80211
+ }).option("skip-verify", {
80212
+ type: "boolean",
80213
+ hidden: true,
80214
+ describe: "Deprecated no-op: complete already skips re-verification by default"
79568
80215
  }),
79569
80216
  handleComplete
79570
80217
  ).command(
@@ -79677,7 +80324,7 @@ var envCommand = {
79677
80324
  }
79678
80325
  };
79679
80326
  async function handleInit(argv) {
79680
- const repoPath = path77.resolve(argv.repo);
80327
+ const repoPath = path80.resolve(argv.repo);
79681
80328
  const onboarding = resolveOnboardingOptions(argv);
79682
80329
  header("har env init");
79683
80330
  info(`Repository: ${repoPath}`);
@@ -79724,7 +80371,7 @@ async function handleInit(argv) {
79724
80371
  }
79725
80372
  }
79726
80373
  async function handleMaintain(argv) {
79727
- const repoPath = path77.resolve(argv.repo);
80374
+ const repoPath = path80.resolve(argv.repo);
79728
80375
  const onboarding = resolveOnboardingOptions(argv);
79729
80376
  header("har env maintain");
79730
80377
  info(`Repository: ${repoPath}`);
@@ -79879,7 +80526,7 @@ async function applyAgentIntegrations(options) {
79879
80526
  }
79880
80527
  }
79881
80528
  async function handleAddPlugin(argv) {
79882
- const repoPath = path77.resolve(argv.repo);
80529
+ const repoPath = path80.resolve(argv.repo);
79883
80530
  const available = listPluginIds();
79884
80531
  if (argv.list) {
79885
80532
  for (const id of available) {
@@ -79915,7 +80562,7 @@ async function handleAddPlugin(argv) {
79915
80562
  console.error(` Docs: ${result.docsPath}`);
79916
80563
  console.error("");
79917
80564
  info(`Adaptation prompt saved to ${result.adaptPromptPath}`);
79918
- const promptContent = fs69.readFileSync(path77.join(repoPath, result.adaptPromptPath), "utf8");
80565
+ const promptContent = fs71.readFileSync(path80.join(repoPath, result.adaptPromptPath), "utf8");
79919
80566
  await offerAdaptationPromptClipboard(promptContent, { fileLabel: result.adaptPromptPath });
79920
80567
  } catch (err) {
79921
80568
  error(err.message);
@@ -79957,7 +80604,7 @@ async function handleAddStage(argv) {
79957
80604
  });
79958
80605
  }
79959
80606
  async function handlePreflight(argv) {
79960
- const repo = path77.resolve(argv.repo);
80607
+ const repo = path80.resolve(argv.repo);
79961
80608
  const agentId = validateAgentId(argv.id, repo);
79962
80609
  const result = await preflightEnvironment({
79963
80610
  repoPath: repo,
@@ -79973,7 +80620,7 @@ async function handlePreflight(argv) {
79973
80620
  return finishCommand(result.code);
79974
80621
  }
79975
80622
  async function handleLaunch(argv) {
79976
- const repo = path77.resolve(argv.repo);
80623
+ const repo = path80.resolve(argv.repo);
79977
80624
  const agentId = validateAgentId(argv.id, repo);
79978
80625
  const result = await launchEnvironment({
79979
80626
  repoPath: repo,
@@ -80015,7 +80662,7 @@ async function handleWorkLink(argv) {
80015
80662
  error("Provide --link or both --source and --url");
80016
80663
  return finishCommand(1);
80017
80664
  }
80018
- const harnessRoot = resolveHarnessRoot(path77.resolve(argv.repo));
80665
+ const harnessRoot = resolveHarnessRoot(path80.resolve(argv.repo));
80019
80666
  const links = argv.link ? [parseWorkLinkSpec(argv.link)] : [{ source: argv.source, url: argv.url, label: argv.label }];
80020
80667
  try {
80021
80668
  const record2 = addWorkUnitLinks(harnessRoot, argv.workId, links);
@@ -80041,7 +80688,7 @@ async function handleRecover(argv) {
80041
80688
  });
80042
80689
  }
80043
80690
  async function handleVerify(argv) {
80044
- const repo = path77.resolve(argv.repo);
80691
+ const repo = path80.resolve(argv.repo);
80045
80692
  const agentId = validateAgentId(argv.id, repo);
80046
80693
  const result = await runVerification({
80047
80694
  repoPath: repo,
@@ -80070,7 +80717,7 @@ async function handleVerify(argv) {
80070
80717
  return finishCommand(result.code);
80071
80718
  }
80072
80719
  async function handleTeardown(argv) {
80073
- const repo = path77.resolve(argv.repo);
80720
+ const repo = path80.resolve(argv.repo);
80074
80721
  const agentId = validateAgentId(argv.id, repo);
80075
80722
  const result = await teardownEnvironment({
80076
80723
  repoPath: repo,
@@ -80081,11 +80728,12 @@ async function handleTeardown(argv) {
80081
80728
  return finishCommand(result.code);
80082
80729
  }
80083
80730
  async function handleComplete(argv) {
80084
- const repo = path77.resolve(argv.repo);
80731
+ const repo = path80.resolve(argv.repo);
80085
80732
  const agentId = validateAgentId(argv.id, repo);
80086
80733
  const result = await completeEnvironment({
80087
80734
  repoPath: repo,
80088
80735
  agentId,
80736
+ verify: argv.verify,
80089
80737
  skipVerify: argv.skipVerify,
80090
80738
  capture: false
80091
80739
  });
@@ -80102,7 +80750,7 @@ async function handleComplete(argv) {
80102
80750
  return finishCommand(result.code);
80103
80751
  }
80104
80752
  async function handleSetupInfra(argv) {
80105
- const repo = path77.resolve(argv.repo);
80753
+ const repo = path80.resolve(argv.repo);
80106
80754
  const result = await runStage({
80107
80755
  repoPath: repo,
80108
80756
  kind: "setup",
@@ -80112,7 +80760,7 @@ async function handleSetupInfra(argv) {
80112
80760
  return finishCommand(result.code ?? (result.status === "pass" ? 0 : 1));
80113
80761
  }
80114
80762
  async function handleAgent(argv) {
80115
- const repo = path77.resolve(argv.repo);
80763
+ const repo = path80.resolve(argv.repo);
80116
80764
  const agentId = validateAgentId(argv.id, repo);
80117
80765
  const result = await runAgentOp({
80118
80766
  repoPath: repo,
@@ -80123,7 +80771,7 @@ async function handleAgent(argv) {
80123
80771
  return finishCommand(result.code);
80124
80772
  }
80125
80773
  async function handleDoctor(argv) {
80126
- const repoPath = path77.resolve(argv.repo);
80774
+ const repoPath = path80.resolve(argv.repo);
80127
80775
  const report = runDoctor(repoPath);
80128
80776
  if (argv.json) {
80129
80777
  console.log(JSON.stringify(report, null, 2));
@@ -80147,12 +80795,12 @@ async function confirm(question) {
80147
80795
  if (!process.stdin.isTTY) return false;
80148
80796
  const readline8 = await import("readline");
80149
80797
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
80150
- const answer = await new Promise((resolve60) => rl.question(question, resolve60));
80798
+ const answer = await new Promise((resolve66) => rl.question(question, resolve66));
80151
80799
  rl.close();
80152
80800
  return /^y(es)?$/i.test(answer.trim());
80153
80801
  }
80154
80802
  async function handleEject(argv) {
80155
- const repoPath = resolveHarnessRoot(path77.resolve(argv.repo));
80803
+ const repoPath = resolveHarnessRoot(path80.resolve(argv.repo));
80156
80804
  header("har env eject");
80157
80805
  warn("This vendors the complete HAR runtime into .har/runtime/ \u2014 from then on");
80158
80806
  warn("YOU OWN that directory:");
@@ -80183,7 +80831,7 @@ async function handleEject(argv) {
80183
80831
  return finishCommand(0);
80184
80832
  }
80185
80833
  async function handleAdopt(argv) {
80186
- const repoPath = resolveHarnessRoot(path77.resolve(argv.repo));
80834
+ const repoPath = resolveHarnessRoot(path80.resolve(argv.repo));
80187
80835
  try {
80188
80836
  const result = adoptHarness(repoPath);
80189
80837
  success("Returned to the packaged runtime \u2014 .har/runtime/ removed, eject flag cleared.");
@@ -80198,7 +80846,7 @@ async function handleAdopt(argv) {
80198
80846
  return finishCommand(0);
80199
80847
  }
80200
80848
  async function handleStatus(argv) {
80201
- const repoPath = path77.resolve(argv.repo);
80849
+ const repoPath = path80.resolve(argv.repo);
80202
80850
  const result = await getEnvironmentStatus({
80203
80851
  repoPath,
80204
80852
  capture: false
@@ -80211,7 +80859,7 @@ async function handleStatus(argv) {
80211
80859
  if (result.stdout) process.stdout.write(result.stdout);
80212
80860
  }
80213
80861
  async function handleLogs(argv) {
80214
- const repo = path77.resolve(argv.repo);
80862
+ const repo = path80.resolve(argv.repo);
80215
80863
  const agentId = validateAgentId(argv.id, repo);
80216
80864
  const result = await getEnvironmentLogs({
80217
80865
  repoPath: repo,
@@ -80223,7 +80871,7 @@ async function handleLogs(argv) {
80223
80871
  return finishCommand(result.code);
80224
80872
  }
80225
80873
  async function handleRunStage(argv) {
80226
- const repo = path77.resolve(argv.repo);
80874
+ const repo = path80.resolve(argv.repo);
80227
80875
  const agentId = validateAgentId(argv.id, repo);
80228
80876
  if (!argv.stage) {
80229
80877
  error("Missing stage id. Usage: har env run-stage <id> <stage> [args..]");
@@ -80249,7 +80897,7 @@ async function handleRunStage(argv) {
80249
80897
  }
80250
80898
  async function handleArtifacts(argv) {
80251
80899
  const artifacts = listArtifacts({
80252
- repoPath: path77.resolve(argv.repo),
80900
+ repoPath: path80.resolve(argv.repo),
80253
80901
  stageId: argv.stage
80254
80902
  });
80255
80903
  if (argv.json) {
@@ -80268,7 +80916,7 @@ async function handleArtifacts(argv) {
80268
80916
  async function handleCleanup(argv) {
80269
80917
  const keepValues = argv.keep?.split(",").map((value) => value.trim()).filter(Boolean);
80270
80918
  const keep = parseCleanupKeepPins(keepValues);
80271
- const repoPaths = argv.repo ? [path77.resolve(argv.repo)] : void 0;
80919
+ const repoPaths = argv.repo ? [path80.resolve(argv.repo)] : void 0;
80272
80920
  const plan = await discoverCleanupCandidates({
80273
80921
  cwd: process.cwd(),
80274
80922
  repoPaths,
@@ -80317,7 +80965,7 @@ async function handleCleanup(argv) {
80317
80965
  return finishCommand(failed > 0 ? 1 : 0);
80318
80966
  }
80319
80967
  async function handleRunsList(argv) {
80320
- const repoPath = path77.resolve(argv.repo);
80968
+ const repoPath = path80.resolve(argv.repo);
80321
80969
  const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
80322
80970
  if (argv.json) {
80323
80971
  process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
@@ -80334,7 +80982,7 @@ async function handleRunsList(argv) {
80334
80982
  }
80335
80983
  }
80336
80984
  async function handleRunsGet(argv) {
80337
- const run2 = getRun(path77.resolve(argv.repo), argv.runId);
80985
+ const run2 = getRun(path80.resolve(argv.repo), argv.runId);
80338
80986
  if (!run2) {
80339
80987
  error(`Run not found: ${argv.runId}`);
80340
80988
  return finishCommand(1);
@@ -80451,7 +81099,7 @@ function printNextSteps() {
80451
81099
  }
80452
81100
 
80453
81101
  // src/cli/commands/control.ts
80454
- var path82 = __toESM(require("path"));
81102
+ var path85 = __toESM(require("path"));
80455
81103
  init_control_config();
80456
81104
  init_portal_targets();
80457
81105
 
@@ -80459,8 +81107,8 @@ init_portal_targets();
80459
81107
  init_control_config();
80460
81108
 
80461
81109
  // src/core/portal-connect.ts
80462
- var fs70 = __toESM(require("fs"));
80463
- var path78 = __toESM(require("path"));
81110
+ var fs72 = __toESM(require("fs"));
81111
+ var path81 = __toESM(require("path"));
80464
81112
  var import_crypto2 = require("crypto");
80465
81113
  init_control_repo_path();
80466
81114
  init_control_registry();
@@ -80506,7 +81154,7 @@ async function loginViaBrowser(portalUrl, openUrlOrOptions = openBrowser) {
80506
81154
  const options = typeof openUrlOrOptions === "function" ? { openUrl: openUrlOrOptions } : openUrlOrOptions;
80507
81155
  const openUrl = options.openUrl ?? openBrowser;
80508
81156
  const state = (0, import_crypto.randomUUID)();
80509
- return new Promise((resolve60, reject) => {
81157
+ return new Promise((resolve66, reject) => {
80510
81158
  let timer;
80511
81159
  const server = http.createServer((req, res) => {
80512
81160
  const requestUrl = new URL(req.url ?? "", "http://127.0.0.1");
@@ -80534,7 +81182,7 @@ async function loginViaBrowser(portalUrl, openUrlOrOptions = openBrowser) {
80534
81182
  res.end("<h1>Logged in</h1><p>You can close this tab and return to the terminal.</p>");
80535
81183
  cleanup();
80536
81184
  const targets = parseCliTargetsParam(requestUrl.searchParams.get("targets"));
80537
- resolve60({
81185
+ resolve66({
80538
81186
  portalUrl,
80539
81187
  token,
80540
81188
  workspace: workspaceSlug ?? workspace,
@@ -80629,8 +81277,8 @@ function remainingConnections(connections, usedAliases) {
80629
81277
  // src/core/portal-connect.ts
80630
81278
  init_portal_targets();
80631
81279
  function attachRepoIfPresent(alias, repoPath) {
80632
- const resolved = path78.resolve(repoPath ?? ".");
80633
- if (!fs70.existsSync(resolved)) return null;
81280
+ const resolved = path81.resolve(repoPath ?? ".");
81281
+ if (!fs72.existsSync(resolved)) return null;
80634
81282
  const canonical = canonicalizeControlRepoPath(resolved);
80635
81283
  attachRepoPortalTarget(canonical, alias);
80636
81284
  recordRepoForControlSync(canonical);
@@ -80697,8 +81345,8 @@ async function runPortalConnect(input) {
80697
81345
  return { record: record2, attachedRepo, extraAttached, mappedInBrowser: true };
80698
81346
  }
80699
81347
  function listRegisteredReposForConnect(repoPath) {
80700
- const resolved = repoPath ? path78.resolve(repoPath) : null;
80701
- const current = resolved && fs70.existsSync(resolved) ? canonicalizeControlRepoPath(resolved) : null;
81348
+ const resolved = repoPath ? path81.resolve(repoPath) : null;
81349
+ const current = resolved && fs72.existsSync(resolved) ? canonicalizeControlRepoPath(resolved) : null;
80702
81350
  return [.../* @__PURE__ */ new Set([...current ? [current] : [], ...listRegisteredRepos()])];
80703
81351
  }
80704
81352
 
@@ -81014,21 +81662,21 @@ init_control_port();
81014
81662
  init_control_sync();
81015
81663
 
81016
81664
  // src/core/control-sync-selection.ts
81017
- var fs71 = __toESM(require("fs"));
81665
+ var fs73 = __toESM(require("fs"));
81018
81666
  var os25 = __toESM(require("os"));
81019
- var path79 = __toESM(require("path"));
81667
+ var path82 = __toESM(require("path"));
81020
81668
  init_control_repo_path();
81021
81669
  function getSelectionPath() {
81022
81670
  if (process.env.HAR_CONTROL_SYNC_SELECTION_PATH) {
81023
- return path79.resolve(process.env.HAR_CONTROL_SYNC_SELECTION_PATH);
81671
+ return path82.resolve(process.env.HAR_CONTROL_SYNC_SELECTION_PATH);
81024
81672
  }
81025
- return path79.join(os25.homedir(), ".har", "sync-selection.json");
81673
+ return path82.join(os25.homedir(), ".har", "sync-selection.json");
81026
81674
  }
81027
81675
  function readSyncSelection() {
81028
81676
  const selectionPath = getSelectionPath();
81029
81677
  try {
81030
- if (!fs71.existsSync(selectionPath)) return null;
81031
- const parsed = JSON.parse(fs71.readFileSync(selectionPath, "utf8"));
81678
+ if (!fs73.existsSync(selectionPath)) return null;
81679
+ const parsed = JSON.parse(fs73.readFileSync(selectionPath, "utf8"));
81032
81680
  if (!Array.isArray(parsed.repos)) return null;
81033
81681
  return parsed.repos.map((repoPath) => canonicalizeControlRepoPath(repoPath));
81034
81682
  } catch {
@@ -81038,8 +81686,8 @@ function readSyncSelection() {
81038
81686
  function writeSyncSelection(repos) {
81039
81687
  const selectionPath = getSelectionPath();
81040
81688
  const canonical = Array.from(new Set(repos.map((repoPath) => canonicalizeControlRepoPath(repoPath))));
81041
- fs71.mkdirSync(path79.dirname(selectionPath), { recursive: true });
81042
- fs71.writeFileSync(selectionPath, JSON.stringify({ repos: canonical }, null, 2) + "\n");
81689
+ fs73.mkdirSync(path82.dirname(selectionPath), { recursive: true });
81690
+ fs73.writeFileSync(selectionPath, JSON.stringify({ repos: canonical }, null, 2) + "\n");
81043
81691
  }
81044
81692
  function resolveSyncSelection(input) {
81045
81693
  const { discovered, stored, forceSelect, isTTY } = input;
@@ -81066,8 +81714,8 @@ function resolveSyncSelection(input) {
81066
81714
  }
81067
81715
 
81068
81716
  // src/core/control-unregister.ts
81069
- var fs72 = __toESM(require("fs"));
81070
- var path80 = __toESM(require("path"));
81717
+ var fs74 = __toESM(require("fs"));
81718
+ var path83 = __toESM(require("path"));
81071
81719
  init_control_repo_path();
81072
81720
  init_control_registry();
81073
81721
  init_control_config();
@@ -81077,9 +81725,9 @@ function listUnregisterWorktreeCandidates(repoPath) {
81077
81725
  const harnessRoot = canonicalizeControlRepoPath(repoPath);
81078
81726
  const byPath = /* @__PURE__ */ new Map();
81079
81727
  const add = (partial2) => {
81080
- const resolved = path80.resolve(partial2.worktreePath);
81728
+ const resolved = path83.resolve(partial2.worktreePath);
81081
81729
  if (!resolved || resolved === harnessRoot) return;
81082
- const exists = partial2.exists ?? fs72.existsSync(resolved);
81730
+ const exists = partial2.exists ?? fs74.existsSync(resolved);
81083
81731
  const prev = byPath.get(resolved);
81084
81732
  if (prev) {
81085
81733
  byPath.set(resolved, {
@@ -81130,8 +81778,8 @@ async function askYesNo6(question) {
81130
81778
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
81131
81779
  const readline8 = await import("readline");
81132
81780
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
81133
- const answer = await new Promise((resolve60) => {
81134
- rl.question(question, resolve60);
81781
+ const answer = await new Promise((resolve66) => {
81782
+ rl.question(question, resolve66);
81135
81783
  });
81136
81784
  rl.close();
81137
81785
  return /^[Yy]$/.test(answer.trim());
@@ -81193,28 +81841,28 @@ async function teardownWorktrees(repoPath, candidates, dryRun) {
81193
81841
  trigger: "cli"
81194
81842
  });
81195
81843
  if (result.code !== 0) {
81196
- if (fs72.existsSync(candidate.worktreePath)) {
81197
- fs72.rmSync(candidate.worktreePath, { recursive: true, force: true });
81844
+ if (fs74.existsSync(candidate.worktreePath)) {
81845
+ fs74.rmSync(candidate.worktreePath, { recursive: true, force: true });
81198
81846
  }
81199
81847
  }
81200
81848
  outcomes.push({
81201
81849
  path: candidate.worktreePath,
81202
81850
  agentId: candidate.agentId,
81203
- deleted: !fs72.existsSync(candidate.worktreePath),
81204
- error: result.code !== 0 && fs72.existsSync(candidate.worktreePath) ? result.stderr : void 0
81851
+ deleted: !fs74.existsSync(candidate.worktreePath),
81852
+ error: result.code !== 0 && fs74.existsSync(candidate.worktreePath) ? result.stderr : void 0
81205
81853
  });
81206
81854
  } catch (err) {
81207
81855
  const message = err instanceof Error ? err.message : String(err);
81208
81856
  try {
81209
- if (fs72.existsSync(candidate.worktreePath)) {
81210
- fs72.rmSync(candidate.worktreePath, { recursive: true, force: true });
81857
+ if (fs74.existsSync(candidate.worktreePath)) {
81858
+ fs74.rmSync(candidate.worktreePath, { recursive: true, force: true });
81211
81859
  }
81212
81860
  } catch {
81213
81861
  }
81214
81862
  outcomes.push({
81215
81863
  path: candidate.worktreePath,
81216
81864
  agentId: candidate.agentId,
81217
- deleted: !fs72.existsSync(candidate.worktreePath),
81865
+ deleted: !fs74.existsSync(candidate.worktreePath),
81218
81866
  error: message
81219
81867
  });
81220
81868
  }
@@ -81277,16 +81925,16 @@ init_control_config();
81277
81925
  init_control_registry();
81278
81926
 
81279
81927
  // src/core/harness-scrub.ts
81280
- var fs73 = __toESM(require("fs"));
81281
- var path81 = __toESM(require("path"));
81928
+ var fs75 = __toESM(require("fs"));
81929
+ var path84 = __toESM(require("path"));
81282
81930
  var HARNESS_SCRUB_DIRS = ["runs", "validations", "state", "slots"];
81283
81931
  function scrubLocalHarnessDirs(repoPath) {
81284
81932
  const results = [];
81285
- const resolved = path81.resolve(repoPath);
81286
- if (!fs73.existsSync(resolved)) {
81933
+ const resolved = path84.resolve(repoPath);
81934
+ if (!fs75.existsSync(resolved)) {
81287
81935
  for (const directory of HARNESS_SCRUB_DIRS) {
81288
81936
  results.push({
81289
- path: path81.join(resolved, ".har", directory),
81937
+ path: path84.join(resolved, ".har", directory),
81290
81938
  directory,
81291
81939
  deleted: false,
81292
81940
  error: "repository path does not exist"
@@ -81295,24 +81943,24 @@ function scrubLocalHarnessDirs(repoPath) {
81295
81943
  return results;
81296
81944
  }
81297
81945
  for (const directory of HARNESS_SCRUB_DIRS) {
81298
- const target = path81.join(resolved, ".har", directory);
81299
- if (!fs73.existsSync(target)) {
81946
+ const target = path84.join(resolved, ".har", directory);
81947
+ if (!fs75.existsSync(target)) {
81300
81948
  results.push({ path: target, directory, deleted: true });
81301
81949
  continue;
81302
81950
  }
81303
81951
  try {
81304
- fs73.rmSync(target, { recursive: true, force: true });
81952
+ fs75.rmSync(target, { recursive: true, force: true });
81305
81953
  results.push({
81306
81954
  path: target,
81307
81955
  directory,
81308
- deleted: !fs73.existsSync(target),
81309
- error: fs73.existsSync(target) ? "failed to remove directory" : void 0
81956
+ deleted: !fs75.existsSync(target),
81957
+ error: fs75.existsSync(target) ? "failed to remove directory" : void 0
81310
81958
  });
81311
81959
  } catch (err) {
81312
81960
  results.push({
81313
81961
  path: target,
81314
81962
  directory,
81315
- deleted: !fs73.existsSync(target),
81963
+ deleted: !fs75.existsSync(target),
81316
81964
  error: err instanceof Error ? err.message : String(err)
81317
81965
  });
81318
81966
  }
@@ -81323,7 +81971,7 @@ function scrubLocalHarnessForRepos(repoPaths) {
81323
81971
  const results = [];
81324
81972
  const seen = /* @__PURE__ */ new Set();
81325
81973
  for (const repoPath of repoPaths) {
81326
- const key = path81.resolve(repoPath);
81974
+ const key = path84.resolve(repoPath);
81327
81975
  if (seen.has(key)) continue;
81328
81976
  seen.add(key);
81329
81977
  results.push(...scrubLocalHarnessDirs(key));
@@ -81336,8 +81984,8 @@ async function askYesNo7(question) {
81336
81984
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
81337
81985
  const readline8 = await import("readline");
81338
81986
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
81339
- const answer = await new Promise((resolve60) => {
81340
- rl.question(question, resolve60);
81987
+ const answer = await new Promise((resolve66) => {
81988
+ rl.question(question, resolve66);
81341
81989
  });
81342
81990
  rl.close();
81343
81991
  return /^[Yy]$/.test(answer.trim());
@@ -81616,7 +82264,7 @@ async function handleDown() {
81616
82264
  return finishCommand(code);
81617
82265
  }
81618
82266
  async function handleRegister(argv) {
81619
- const repoPath = path82.resolve(argv.repo);
82267
+ const repoPath = path85.resolve(argv.repo);
81620
82268
  header("har control register");
81621
82269
  info(`Repository: ${repoPath}`);
81622
82270
  try {
@@ -81714,7 +82362,7 @@ async function handleReset(argv) {
81714
82362
  }
81715
82363
  }
81716
82364
  async function handleUnregister(argv) {
81717
- const repoPath = path82.resolve(argv.repo);
82365
+ const repoPath = path85.resolve(argv.repo);
81718
82366
  const worktrees = listUnregisterWorktreeCandidates(repoPath);
81719
82367
  try {
81720
82368
  let deleteWorktrees = argv.deleteWorktrees;
@@ -81995,12 +82643,12 @@ async function handleTrajectory(argv) {
81995
82643
  }
81996
82644
 
81997
82645
  // src/cli/commands/hooks.ts
81998
- var path84 = __toESM(require("path"));
82646
+ var path87 = __toESM(require("path"));
81999
82647
  init_hooks();
82000
82648
 
82001
82649
  // src/core/claude-hooks.ts
82002
- var fs74 = __toESM(require("fs"));
82003
- var path83 = __toESM(require("path"));
82650
+ var fs76 = __toESM(require("fs"));
82651
+ var path86 = __toESM(require("path"));
82004
82652
  init_paths();
82005
82653
  var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
82006
82654
  var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
@@ -82012,8 +82660,8 @@ function isHarGuardEntry(entry) {
82012
82660
  );
82013
82661
  }
82014
82662
  function readSettings(settingsPath) {
82015
- if (!fs74.existsSync(settingsPath)) return {};
82016
- const raw = fs74.readFileSync(settingsPath, "utf8").trim();
82663
+ if (!fs76.existsSync(settingsPath)) return {};
82664
+ const raw = fs76.readFileSync(settingsPath, "utf8").trim();
82017
82665
  if (raw === "") return {};
82018
82666
  const parsed = JSON.parse(raw);
82019
82667
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -82022,19 +82670,19 @@ function readSettings(settingsPath) {
82022
82670
  return parsed;
82023
82671
  }
82024
82672
  function writeSettings(settingsPath, settings) {
82025
- fs74.mkdirSync(path83.dirname(settingsPath), { recursive: true });
82026
- fs74.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
82673
+ fs76.mkdirSync(path86.dirname(settingsPath), { recursive: true });
82674
+ fs76.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
82027
82675
  }
82028
82676
  function installClaudeGuard(repoPath) {
82029
82677
  const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
82030
82678
  if (!templatePath) {
82031
82679
  throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
82032
82680
  }
82033
- const guardScript = path83.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82034
- fs74.mkdirSync(path83.dirname(guardScript), { recursive: true });
82035
- fs74.copyFileSync(templatePath, guardScript);
82036
- fs74.chmodSync(guardScript, 493);
82037
- const settingsPath = path83.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82681
+ const guardScript = path86.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82682
+ fs76.mkdirSync(path86.dirname(guardScript), { recursive: true });
82683
+ fs76.copyFileSync(templatePath, guardScript);
82684
+ fs76.chmodSync(guardScript, 493);
82685
+ const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82038
82686
  const settings = readSettings(settingsPath);
82039
82687
  const hooks = settings.hooks ?? {};
82040
82688
  const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
@@ -82048,15 +82696,15 @@ function installClaudeGuard(repoPath) {
82048
82696
  }
82049
82697
  function uninstallClaudeGuard(repoPath) {
82050
82698
  let removed = false;
82051
- const guardScript = path83.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82052
- if (fs74.existsSync(guardScript)) {
82053
- fs74.rmSync(guardScript);
82699
+ const guardScript = path86.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82700
+ if (fs76.existsSync(guardScript)) {
82701
+ fs76.rmSync(guardScript);
82054
82702
  removed = true;
82055
- const parent = path83.dirname(guardScript);
82056
- if (fs74.existsSync(parent) && fs74.readdirSync(parent).length === 0) fs74.rmdirSync(parent);
82703
+ const parent = path86.dirname(guardScript);
82704
+ if (fs76.existsSync(parent) && fs76.readdirSync(parent).length === 0) fs76.rmdirSync(parent);
82057
82705
  }
82058
- const settingsPath = path83.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82059
- if (fs74.existsSync(settingsPath)) {
82706
+ const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82707
+ if (fs76.existsSync(settingsPath)) {
82060
82708
  const settings = readSettings(settingsPath);
82061
82709
  const preToolUse = settings.hooks?.PreToolUse;
82062
82710
  if (preToolUse) {
@@ -82081,8 +82729,8 @@ function uninstallClaudeGuard(repoPath) {
82081
82729
  return { removed };
82082
82730
  }
82083
82731
  function claudeGuardInstalled(repoPath) {
82084
- const settingsPath = path83.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82085
- if (!fs74.existsSync(settingsPath)) return false;
82732
+ const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82733
+ if (!fs76.existsSync(settingsPath)) return false;
82086
82734
  try {
82087
82735
  const settings = readSettings(settingsPath);
82088
82736
  return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
@@ -82099,7 +82747,7 @@ function repoOption(y) {
82099
82747
  function handleInstall2(argv) {
82100
82748
  try {
82101
82749
  if (argv.claude) {
82102
- const result2 = installClaudeGuard(path84.resolve(argv.repo));
82750
+ const result2 = installClaudeGuard(path87.resolve(argv.repo));
82103
82751
  success("Claude Code worktree guard installed.");
82104
82752
  info(`Guard script: ${result2.guardScript}`);
82105
82753
  info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
@@ -82107,7 +82755,7 @@ function handleInstall2(argv) {
82107
82755
  info("Commit both files so the guard travels with the repo.");
82108
82756
  return;
82109
82757
  }
82110
- const result = installHooks({ repoPath: path84.resolve(argv.repo), force: argv.force });
82758
+ const result = installHooks({ repoPath: path87.resolve(argv.repo), force: argv.force });
82111
82759
  success(`Commit gate installed in ${result.hooksDir}`);
82112
82760
  info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
82113
82761
  info("Commits of unverified change batches will be blocked in agent worktrees.");
@@ -82120,7 +82768,7 @@ function handleInstall2(argv) {
82120
82768
  function handleUninstall(argv) {
82121
82769
  try {
82122
82770
  if (argv.claude) {
82123
- const result2 = uninstallClaudeGuard(path84.resolve(argv.repo));
82771
+ const result2 = uninstallClaudeGuard(path87.resolve(argv.repo));
82124
82772
  if (result2.removed) {
82125
82773
  success("Claude Code worktree guard removed.");
82126
82774
  } else {
@@ -82128,7 +82776,7 @@ function handleUninstall(argv) {
82128
82776
  }
82129
82777
  return;
82130
82778
  }
82131
- const result = uninstallHooks(path84.resolve(argv.repo));
82779
+ const result = uninstallHooks(path87.resolve(argv.repo));
82132
82780
  if (result.removed) {
82133
82781
  success(`Commit gate removed from ${result.hooksDir}`);
82134
82782
  } else {
@@ -82141,7 +82789,7 @@ function handleUninstall(argv) {
82141
82789
  }
82142
82790
  function handleStatus2(argv) {
82143
82791
  try {
82144
- const status = getHooksStatus(path84.resolve(argv.repo));
82792
+ const status = getHooksStatus(path87.resolve(argv.repo));
82145
82793
  if (argv.json) {
82146
82794
  process.stdout.write(`${JSON.stringify(status, null, 2)}
82147
82795
  `);
@@ -82159,7 +82807,7 @@ function handleStatus2(argv) {
82159
82807
  );
82160
82808
  info(`Effective: ${status.effectiveMode} (in this checkout)`);
82161
82809
  info(
82162
- `Claude guard: ${claudeGuardInstalled(path84.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
82810
+ `Claude guard: ${claudeGuardInstalled(path87.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
82163
82811
  );
82164
82812
  } catch (err) {
82165
82813
  error(err instanceof Error ? err.message : String(err));
@@ -82420,10 +83068,10 @@ function assignProp(target, prop, value) {
82420
83068
  configurable: true
82421
83069
  });
82422
83070
  }
82423
- function getElementAtPath(obj, path91) {
82424
- if (!path91)
83071
+ function getElementAtPath(obj, path97) {
83072
+ if (!path97)
82425
83073
  return obj;
82426
- return path91.reduce((acc, key) => acc?.[key], obj);
83074
+ return path97.reduce((acc, key) => acc?.[key], obj);
82427
83075
  }
82428
83076
  function promiseAllObject(promisesObj) {
82429
83077
  const keys = Object.keys(promisesObj);
@@ -82743,11 +83391,11 @@ function aborted(x, startIndex = 0) {
82743
83391
  }
82744
83392
  return false;
82745
83393
  }
82746
- function prefixIssues(path91, issues) {
83394
+ function prefixIssues(path97, issues) {
82747
83395
  return issues.map((iss) => {
82748
83396
  var _a2;
82749
83397
  (_a2 = iss).path ?? (_a2.path = []);
82750
- iss.path.unshift(path91);
83398
+ iss.path.unshift(path97);
82751
83399
  return iss;
82752
83400
  });
82753
83401
  }
@@ -88070,7 +88718,7 @@ var Protocol = class {
88070
88718
  return;
88071
88719
  }
88072
88720
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
88073
- await new Promise((resolve60) => setTimeout(resolve60, pollInterval));
88721
+ await new Promise((resolve66) => setTimeout(resolve66, pollInterval));
88074
88722
  options?.signal?.throwIfAborted();
88075
88723
  }
88076
88724
  } catch (error3) {
@@ -88087,7 +88735,7 @@ var Protocol = class {
88087
88735
  */
88088
88736
  request(request, resultSchema, options) {
88089
88737
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
88090
- return new Promise((resolve60, reject) => {
88738
+ return new Promise((resolve66, reject) => {
88091
88739
  const earlyReject = (error3) => {
88092
88740
  reject(error3);
88093
88741
  };
@@ -88165,7 +88813,7 @@ var Protocol = class {
88165
88813
  if (!parseResult.success) {
88166
88814
  reject(parseResult.error);
88167
88815
  } else {
88168
- resolve60(parseResult.data);
88816
+ resolve66(parseResult.data);
88169
88817
  }
88170
88818
  } catch (error3) {
88171
88819
  reject(error3);
@@ -88426,12 +89074,12 @@ var Protocol = class {
88426
89074
  }
88427
89075
  } catch {
88428
89076
  }
88429
- return new Promise((resolve60, reject) => {
89077
+ return new Promise((resolve66, reject) => {
88430
89078
  if (signal.aborted) {
88431
89079
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
88432
89080
  return;
88433
89081
  }
88434
- const timeoutId = setTimeout(resolve60, interval);
89082
+ const timeoutId = setTimeout(resolve66, interval);
88435
89083
  signal.addEventListener("abort", () => {
88436
89084
  clearTimeout(timeoutId);
88437
89085
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -89221,7 +89869,7 @@ var Server = class extends Protocol {
89221
89869
  };
89222
89870
 
89223
89871
  // src/mcp/server.ts
89224
- var path85 = __toESM(require("path"));
89872
+ var path90 = __toESM(require("path"));
89225
89873
 
89226
89874
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
89227
89875
  var import_node_process3 = __toESM(require("node:process"), 1);
@@ -89304,12 +89952,12 @@ var StdioServerTransport = class {
89304
89952
  this.onclose?.();
89305
89953
  }
89306
89954
  send(message) {
89307
- return new Promise((resolve60) => {
89955
+ return new Promise((resolve66) => {
89308
89956
  const json = serializeMessage(message);
89309
89957
  if (this._stdout.write(json)) {
89310
- resolve60();
89958
+ resolve66();
89311
89959
  } else {
89312
- this._stdout.once("drain", resolve60);
89960
+ this._stdout.once("drain", resolve66);
89313
89961
  }
89314
89962
  });
89315
89963
  }
@@ -89319,6 +89967,387 @@ var StdioServerTransport = class {
89319
89967
  init_package_version();
89320
89968
  init_telemetry_config();
89321
89969
  init_runs();
89970
+
89971
+ // src/core/lines.ts
89972
+ var path89 = __toESM(require("path"));
89973
+
89974
+ // src/harness/line-create.ts
89975
+ var fs77 = __toESM(require("fs"));
89976
+ var path88 = __toESM(require("path"));
89977
+ init_paths();
89978
+ init_schema2();
89979
+ var LINE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
89980
+ function renderSkeleton(templatePath, tokens) {
89981
+ let content = fs77.readFileSync(templatePath, "utf8");
89982
+ for (const [token, value] of Object.entries(tokens)) {
89983
+ content = content.replace(new RegExp(token, "g"), value);
89984
+ }
89985
+ return content;
89986
+ }
89987
+ function createLine(repoPath, options) {
89988
+ const resolved = path88.resolve(repoPath);
89989
+ if (!harnessExists(resolved)) {
89990
+ throw new Error('No .har/ harness found. Run "har onboard" first.');
89991
+ }
89992
+ const id = options.id.trim();
89993
+ if (!LINE_ID_PATTERN.test(id)) {
89994
+ throw new Error(
89995
+ `Invalid line id "${id}". Use lowercase letters, digits, dots, dashes (e.g. "onboarding-line").`
89996
+ );
89997
+ }
89998
+ const lineDir = path88.join(resolved, LOCAL_LINES_DIR, id);
89999
+ const lineDirRel2 = `${LOCAL_LINES_DIR}/${id}`;
90000
+ if (fs77.existsSync(lineDir) && !options.force) {
90001
+ throw new Error(`Line already exists: ${lineDirRel2}/. Use --force to overwrite.`);
90002
+ }
90003
+ const stationIds = options.stations?.length ? options.stations : ["S1", "S2"];
90004
+ const title = options.title ?? `${id} factory line`;
90005
+ const gateStageId = `${id}-gate`;
90006
+ const withGateStage = options.gateStage ?? true;
90007
+ const program = LineProgramSchema.parse({
90008
+ contractVersion: LINE_CONTRACT_VERSION,
90009
+ id,
90010
+ title,
90011
+ description: options.description ?? 'Describe the program: what it produces, and what "done" means at the last station.',
90012
+ skills: [{ id: "factory-line", role: "orchestrator", install: "repo:.claude/skills/factory-line" }],
90013
+ mcp: [],
90014
+ plugins: [],
90015
+ stations: stationIds.map((stationId, i) => ({
90016
+ id: stationId,
90017
+ title: `Station ${i + 1}`,
90018
+ description: "What this station produces.",
90019
+ work: { source: "none", ids: [], optional: true }
90020
+ })),
90021
+ gate: {
90022
+ cumulative: true,
90023
+ optInEnv: options.optInEnv ?? null,
90024
+ stages: withGateStage ? [{ id: gateStageId, fromStation: stationIds[0], tier: "full" }] : []
90025
+ },
90026
+ extraStages: withGateStage ? [
90027
+ {
90028
+ id: gateStageId,
90029
+ kind: "test",
90030
+ tier: "full",
90031
+ description: `Line-owned gate for ${id} \u2014 registered, never on verify`
90032
+ }
90033
+ ] : [],
90034
+ handoff: {
90035
+ autonomousShip: false,
90036
+ waitFor: "human review before merge or release"
90037
+ },
90038
+ prototypeNotes: []
90039
+ });
90040
+ const manifest = LineManifestSchema.parse({
90041
+ kind: LINE_BUNDLE_KIND,
90042
+ id,
90043
+ title,
90044
+ program: "line.json",
90045
+ files: withGateStage ? [{ src: `stages/${gateStageId}.sh`, dest: `.har/stages/${gateStageId}.sh`, executable: true }] : [],
90046
+ stages: withGateStage ? [
90047
+ {
90048
+ id: gateStageId,
90049
+ kind: "test",
90050
+ description: `Line-owned gate for ${id} \u2014 registered, never on verify`,
90051
+ script: `stages/${gateStageId}.sh`,
90052
+ requiresAgentId: true,
90053
+ artifacts: [
90054
+ {
90055
+ path: `.har/artifacts/${gateStageId}`,
90056
+ kind: "directory",
90057
+ description: `Artifacts for the ${gateStageId} stage`
90058
+ }
90059
+ ],
90060
+ tier: "full"
90061
+ }
90062
+ ] : [],
90063
+ nextSteps: [
90064
+ `Edit ${lineDirRel2}/line.json \u2014 name the stations and bind tracker work`,
90065
+ ...withGateStage ? [`Edit ${lineDirRel2}/stages/${gateStageId}.sh \u2014 replace the TODO with the real gate`] : [],
90066
+ `har line status ${id}`,
90067
+ `har line gate ${stationIds[0]} --line ${id}`
90068
+ ],
90069
+ docsPath: `${lineDirRel2}/README.md`
90070
+ });
90071
+ const filesWritten = [];
90072
+ const write = (rel, content, executable = false) => {
90073
+ const abs = path88.join(lineDir, rel);
90074
+ fs77.mkdirSync(path88.dirname(abs), { recursive: true });
90075
+ fs77.writeFileSync(abs, content);
90076
+ if (executable) fs77.chmodSync(abs, 493);
90077
+ filesWritten.push(`${lineDirRel2}/${rel}`);
90078
+ };
90079
+ write("line.manifest.json", `${JSON.stringify(manifest, null, 2)}
90080
+ `);
90081
+ write("line.json", `${JSON.stringify(program, null, 2)}
90082
+ `);
90083
+ if (withGateStage) {
90084
+ const stageScript = renderSkeleton(
90085
+ path88.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
90086
+ {
90087
+ __STAGE_ID__: gateStageId,
90088
+ __STAGE_KIND__: "test",
90089
+ __STAGE_DESCRIPTION__: `Line-owned gate for ${id} (not on verify)`
90090
+ }
90091
+ );
90092
+ write(`stages/${gateStageId}.sh`, stageScript, true);
90093
+ }
90094
+ write("README.md", renderLineReadme(id, title, stationIds, gateStageId, withGateStage));
90095
+ return {
90096
+ lineId: id,
90097
+ lineDir,
90098
+ filesWritten,
90099
+ nextSteps: [
90100
+ `Edit ${lineDirRel2}/line.json \u2014 stations, work binding, gate tags`,
90101
+ ...withGateStage ? [`Edit ${lineDirRel2}/stages/${gateStageId}.sh \u2014 replace the TODO with the real gate`] : [],
90102
+ `Install it: har line add ${id} (re-run with --force after changes)`,
90103
+ `Installing writes .har/ADAPT-PROMPT-line-${id}.md \u2014 a ready adaptation prompt for your coding agent`,
90104
+ "Publishing later (npm/git) needs zero format changes \u2014 see the README"
90105
+ ]
90106
+ };
90107
+ }
90108
+ function renderLineReadme(id, title, stationIds, gateStageId, withGateStage) {
90109
+ return `# ${title}
90110
+
90111
+ A HAR **factory line**: an ordered program of stations plus a cumulative gate.
90112
+
90113
+ A line is not a verification plugin. Installing it registers stages but never
90114
+ adds them to \`verificationStages\` \u2014 default \`har env verify --full\` stays
90115
+ exactly as fast as it was. Line gate stages run on demand:
90116
+
90117
+ \`\`\`bash
90118
+ har line gate ${stationIds[0]} --line ${id}
90119
+ \`\`\`
90120
+
90121
+ If a check should gate *every* verify, it belongs in a verification plugin
90122
+ (\`har plugin create\` / \`har env add-plugin\`), not here.
90123
+
90124
+ ## Layout
90125
+
90126
+ \`\`\`text
90127
+ ${id}/
90128
+ \u251C\u2500\u2500 line.manifest.json # kind: line \u2014 what \`har line add\` applies
90129
+ \u251C\u2500\u2500 line.json # the program: stations, gate, handoff
90130
+ ${withGateStage ? `\u251C\u2500\u2500 stages/${gateStageId}.sh # extra gate stage \u2014 registered, NOT on verify
90131
+ ` : ""}\u2514\u2500\u2500 README.md
90132
+ \`\`\`
90133
+
90134
+ ## Stations
90135
+
90136
+ ${stationIds.map((s, i) => `${i + 1}. \`${s}\``).join("\n")}
90137
+
90138
+ ## The ratchet
90139
+
90140
+ \`gate.stages[].fromStation\` tags a stage as required at that station **and
90141
+ every later one**. Adding a station must never drop an earlier station's
90142
+ stages \u2014 that is what \`gate.cumulative: true\` means, and it is contract.
90143
+
90144
+ ## Install channels
90145
+
90146
+ \`\`\`bash
90147
+ har line add ${id} # this repo (.har/lines/${id})
90148
+ har line add ./${id} # local path
90149
+ har line add github:acme/${id} # git
90150
+ har line add @acme/${id} # npm
90151
+ \`\`\`
90152
+
90153
+ Publishing needs no format change: add a \`package.json\` and push.
90154
+ `;
90155
+ }
90156
+
90157
+ // src/core/lines.ts
90158
+ init_stages();
90159
+ init_slot_registry();
90160
+ init_runs();
90161
+ function listInstalledLineIds(repoPath) {
90162
+ const ledger = readLineLedger(repoPath);
90163
+ const fromLedger = ledger?.lines.map((l) => l.id) ?? [];
90164
+ const onDisk = listLocalLineIds(repoPath).filter(
90165
+ (id) => readInstalledLineProgram(repoPath, id) !== null
90166
+ );
90167
+ return [.../* @__PURE__ */ new Set([...fromLedger, ...onDisk])].sort();
90168
+ }
90169
+ function cumulativeGateStages(program, stationId) {
90170
+ const stationIndex = program.stations.findIndex((s) => s.id === stationId);
90171
+ if (stationIndex < 0) {
90172
+ throw new Error(
90173
+ `Unknown station "${stationId}". Stations: ${program.stations.map((s) => s.id).join(", ")}`
90174
+ );
90175
+ }
90176
+ const indexOf = new Map(program.stations.map((s, i) => [s.id, i]));
90177
+ return program.gate.stages.filter((stage) => {
90178
+ const from2 = indexOf.get(stage.fromStation);
90179
+ return from2 !== void 0 && from2 <= stationIndex;
90180
+ });
90181
+ }
90182
+ function latestRunStatusByStage(repoPath, stageIds) {
90183
+ const statuses = /* @__PURE__ */ new Map();
90184
+ for (const stageId of stageIds) {
90185
+ const [latest] = listRuns(repoPath, { stageId, limit: 1 });
90186
+ if (latest) statuses.set(stageId, latest.status);
90187
+ }
90188
+ return statuses;
90189
+ }
90190
+ function getLineStatus(repoPath, lineId) {
90191
+ const resolved = path89.resolve(repoPath);
90192
+ const program = readInstalledLineProgram(resolved, lineId);
90193
+ if (!program) {
90194
+ throw new Error(
90195
+ `Line not installed: ${lineId}. Install one with "har line add <spec>" or scaffold with "har line create <id>".`
90196
+ );
90197
+ }
90198
+ const ledger = readLineLedger(resolved);
90199
+ const entry = ledger?.lines.find((l) => l.id === lineId);
90200
+ const registry2 = readStageRegistry(resolved);
90201
+ const registeredIds = new Set(registry2.stages.map((s) => s.id));
90202
+ const verificationStages = registry2.verificationStages ?? [];
90203
+ const allGateStageIds = [...new Set(program.gate.stages.map((s) => s.id))];
90204
+ const runStatuses = latestRunStatusByStage(resolved, allGateStageIds);
90205
+ const stations = program.stations.map((station, index) => {
90206
+ const required2 = cumulativeGateStages(program, station.id);
90207
+ const requiredStageIds = [...new Set(required2.map((s) => s.id))];
90208
+ const missingStageIds = requiredStageIds.filter((id) => !registeredIds.has(id));
90209
+ const passedStageIds = requiredStageIds.filter((id) => runStatuses.get(id) === "pass");
90210
+ const neverRunStageIds = requiredStageIds.filter((id) => !runStatuses.has(id));
90211
+ return {
90212
+ id: station.id,
90213
+ title: station.title,
90214
+ index,
90215
+ requiredStageIds,
90216
+ passedStageIds,
90217
+ neverRunStageIds,
90218
+ missingStageIds,
90219
+ green: missingStageIds.length === 0 && passedStageIds.length === requiredStageIds.length,
90220
+ work: station.work
90221
+ };
90222
+ });
90223
+ let currentStationId;
90224
+ for (const station of stations) {
90225
+ if (station.green) currentStationId = station.id;
90226
+ else break;
90227
+ }
90228
+ const nextStationId = stations.find((s) => !s.green)?.id;
90229
+ const lineStageIds = entry?.stageIds ?? [];
90230
+ const verifyLeaks = lineStageIds.filter((id) => verificationStages.includes(id));
90231
+ const warnings = [];
90232
+ if (verifyLeaks.length > 0) {
90233
+ warnings.push(
90234
+ `Line stage(s) ${verifyLeaks.join(", ")} are listed in verificationStages \u2014 line gate stages must stay off \`har env verify --full\`. Remove them from .har/stages.json.`
90235
+ );
90236
+ }
90237
+ for (const station of stations) {
90238
+ for (const missing of station.missingStageIds) {
90239
+ warnings.push(`Gate stage "${missing}" (station ${station.id}) is not registered in .har/stages.json`);
90240
+ }
90241
+ }
90242
+ const slotsInFlight = listSlotRegistryEntries(resolved).map((slot) => ({
90243
+ agentId: slot.agentId,
90244
+ branch: slot.branch,
90245
+ workUnitId: slot.workUnitId,
90246
+ workDir: slot.workDir
90247
+ }));
90248
+ return {
90249
+ lineId,
90250
+ title: program.title,
90251
+ programPath: entry?.programPath ?? `.har/lines/${lineId}/line.json`,
90252
+ installed: true,
90253
+ source: entry?.source,
90254
+ stations,
90255
+ currentStationId,
90256
+ nextStationId,
90257
+ optInEnv: program.gate.optInEnv,
90258
+ registeredStageIds: lineStageIds,
90259
+ verifyLeaks,
90260
+ slotsInFlight,
90261
+ handoffAutonomousShip: false,
90262
+ warnings: [...new Set(warnings)]
90263
+ };
90264
+ }
90265
+ function getAllLineStatuses(repoPath) {
90266
+ return listInstalledLineIds(repoPath).map((id) => getLineStatus(repoPath, id));
90267
+ }
90268
+ function resolveLineId(repoPath, lineId) {
90269
+ if (lineId) return lineId;
90270
+ const ids = listInstalledLineIds(repoPath);
90271
+ if (ids.length === 0) {
90272
+ throw new Error('No factory line installed. Run "har line create <id>" or "har line add <spec>".');
90273
+ }
90274
+ if (ids.length > 1) {
90275
+ throw new Error(`Multiple lines installed (${ids.join(", ")}). Pass --line <id>.`);
90276
+ }
90277
+ return ids[0];
90278
+ }
90279
+ async function runLineGate(options) {
90280
+ const repoPath = path89.resolve(options.repoPath);
90281
+ const lineId = resolveLineId(repoPath, options.lineId);
90282
+ const program = readInstalledLineProgram(repoPath, lineId);
90283
+ if (!program) {
90284
+ throw new Error(`Line not installed: ${lineId}`);
90285
+ }
90286
+ const required2 = cumulativeGateStages(program, options.station);
90287
+ if (program.gate.optInEnv && !options.force && process.env[program.gate.optInEnv] !== "1") {
90288
+ return {
90289
+ lineId,
90290
+ station: options.station,
90291
+ stages: required2.map((stage) => ({
90292
+ stageId: stage.id,
90293
+ fromStation: stage.fromStation,
90294
+ status: "skipped",
90295
+ reason: `${program.gate.optInEnv} is not "1"`
90296
+ })),
90297
+ pass: true,
90298
+ skipped: true,
90299
+ skipReason: `Gate is opt-in: set ${program.gate.optInEnv}=1 (or pass --force) to run it.`
90300
+ };
90301
+ }
90302
+ const registry2 = readStageRegistry(repoPath);
90303
+ const registeredIds = new Set(registry2.stages.map((s) => s.id));
90304
+ if (options.agentId === void 0) {
90305
+ const needSlot = required2.filter((stage) => registry2.stages.find((s) => s.id === stage.id)?.requiresAgentId).map((stage) => stage.id);
90306
+ if (needSlot.length > 0) {
90307
+ throw new Error(
90308
+ `Gate stage(s) ${needSlot.join(", ")} run in an agent slot \u2014 pass --agent <id> (har line gate ${options.station} --line ${lineId} --agent 1).`
90309
+ );
90310
+ }
90311
+ }
90312
+ const results = [];
90313
+ let pass = true;
90314
+ for (const stage of required2) {
90315
+ if (!registeredIds.has(stage.id)) {
90316
+ results.push({
90317
+ stageId: stage.id,
90318
+ fromStation: stage.fromStation,
90319
+ status: "skipped",
90320
+ reason: "stage is not registered in .har/stages.json"
90321
+ });
90322
+ pass = false;
90323
+ continue;
90324
+ }
90325
+ const result = await runStage({
90326
+ repoPath,
90327
+ stageId: stage.id,
90328
+ agentId: options.agentId,
90329
+ trigger: "cli"
90330
+ });
90331
+ results.push({
90332
+ stageId: stage.id,
90333
+ fromStation: stage.fromStation,
90334
+ status: result.status,
90335
+ durationMs: result.durationMs
90336
+ });
90337
+ if (result.status !== "pass") {
90338
+ pass = false;
90339
+ }
90340
+ }
90341
+ return { lineId, station: options.station, stages: results, pass, skipped: false };
90342
+ }
90343
+ function createLineBundle(repoPath, options) {
90344
+ return createLine(repoPath, options);
90345
+ }
90346
+ function addLine(repoPath, spec, options = {}) {
90347
+ return applyLine(repoPath, spec, options);
90348
+ }
90349
+
90350
+ // src/mcp/server.ts
89322
90351
  init_work_units();
89323
90352
  init_manifest();
89324
90353
 
@@ -89450,7 +90479,12 @@ var PreflightEnvironmentOutputSchema = ShellRunOutputSchema.extend({
89450
90479
  var CompleteEnvironmentInputSchema = external_exports.object({
89451
90480
  repo: external_exports.string().default("."),
89452
90481
  agentId: agentIdSchema,
89453
- skipVerify: external_exports.boolean().default(false).describe("Tear down without running verification (no validation is recorded)")
90482
+ verify: external_exports.boolean().optional().describe(
90483
+ "Re-run full verification before teardown when the tree may have changed since the last passing --full"
90484
+ ),
90485
+ skipVerify: external_exports.boolean().optional().describe(
90486
+ "Deprecated. Complete already skips re-verification by default. Pass verify=true to re-run; skipVerify=false still forces a re-run."
90487
+ )
89454
90488
  });
89455
90489
  var CompleteEnvironmentOutputSchema = ShellRunOutputSchema.extend({
89456
90490
  branch: external_exports.string().optional().describe("Session branch kept for the user to push / open a PR"),
@@ -89532,6 +90566,32 @@ var ControlUpOutputSchema = external_exports.object({
89532
90566
  failed: external_exports.number().int(),
89533
90567
  apiReady: external_exports.boolean()
89534
90568
  });
90569
+ var CreateLineInputSchema = external_exports.object({
90570
+ repo: external_exports.string().default("."),
90571
+ id: external_exports.string().min(1).describe("Line id (lowercase slug, e.g. onboarding-line)"),
90572
+ title: external_exports.string().min(1).optional().describe("Human-readable line title"),
90573
+ description: external_exports.string().min(1).max(2e3).optional(),
90574
+ stations: external_exports.array(external_exports.string().min(1)).optional().describe("Station ids in order (default: S1, S2)"),
90575
+ gateStage: external_exports.boolean().default(true).describe("Scaffold one registered-but-off-verify gate stage"),
90576
+ optInEnv: external_exports.string().min(1).optional().describe('Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'),
90577
+ force: external_exports.boolean().default(false)
90578
+ });
90579
+ var AddLineInputSchema = external_exports.object({
90580
+ repo: external_exports.string().default("."),
90581
+ line: external_exports.string().min(1).describe("Line id, local path (./line), npm package (@org/pkg), or git URL (github:org/repo)"),
90582
+ force: external_exports.boolean().default(false).describe("Overwrite existing line files and stage entries")
90583
+ });
90584
+ var LineStatusInputSchema = external_exports.object({
90585
+ repo: external_exports.string().default("."),
90586
+ line: external_exports.string().min(1).optional().describe("Line id (default: every installed line)")
90587
+ });
90588
+ var RunLineGateInputSchema = external_exports.object({
90589
+ repo: external_exports.string().default("."),
90590
+ station: external_exports.string().min(1).describe("Station id whose cumulative gate should run"),
90591
+ line: external_exports.string().min(1).optional().describe("Line id when more than one is installed"),
90592
+ agentId: external_exports.number().int().optional().describe("Agent slot to run the stages in"),
90593
+ force: external_exports.boolean().default(false).describe("Run even when the program declares an opt-in env var that is not set")
90594
+ });
89535
90595
 
89536
90596
  // src/mcp/server.ts
89537
90597
  var HAR_MCP_TOOLS = [
@@ -89732,14 +90792,18 @@ var HAR_MCP_TOOLS = [
89732
90792
  },
89733
90793
  {
89734
90794
  name: "har_complete_environment",
89735
- description: "Finish a session when the work is done: runs full verification (recorded as a validation of the worktree tree hash), tears the slot down, and KEEPS the session branch so the user can push it and open a PR.",
90795
+ description: "Finish a session when the work is done: reuses the last passing full validation for the current worktree (or re-runs verify when verify=true), tears the slot down, and KEEPS the session branch so the user can push it and open a PR.",
89736
90796
  inputSchema: objectJsonSchema(
89737
90797
  {
89738
90798
  repo: repoJsonProperty,
89739
90799
  agentId: agentIdJsonProperty,
90800
+ verify: {
90801
+ type: "boolean",
90802
+ description: "Re-run full verification before teardown when the tree may have changed since the last passing --full"
90803
+ },
89740
90804
  skipVerify: {
89741
90805
  type: "boolean",
89742
- description: "Tear down without running verification (no validation is recorded)"
90806
+ description: "Deprecated. Complete already skips re-verification by default. Pass verify=true to re-run."
89743
90807
  }
89744
90808
  },
89745
90809
  ["agentId"]
@@ -89780,6 +90844,73 @@ var HAR_MCP_TOOLS = [
89780
90844
  repo: repoJsonProperty,
89781
90845
  detach: { type: "boolean", description: "Run the container in detached mode (default true)" }
89782
90846
  })
90847
+ },
90848
+ {
90849
+ name: "har_line_create",
90850
+ description: "Scaffold a project-owned factory line at .har/lines/<id>/ (manifest, program, optional gate stage, README). A line is a multi-station program, not a verification plugin.",
90851
+ inputSchema: objectJsonSchema(
90852
+ {
90853
+ repo: repoJsonProperty,
90854
+ id: { type: "string", description: "Line id (lowercase slug, e.g. onboarding-line)" },
90855
+ title: { type: "string", description: "Human-readable line title" },
90856
+ description: { type: "string", description: "One-paragraph description of the program" },
90857
+ stations: {
90858
+ type: "array",
90859
+ items: { type: "string" },
90860
+ description: "Station ids in order (default: S1, S2)"
90861
+ },
90862
+ gateStage: {
90863
+ type: "boolean",
90864
+ description: "Scaffold one registered-but-off-verify gate stage (default true)"
90865
+ },
90866
+ optInEnv: {
90867
+ type: "string",
90868
+ description: 'Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'
90869
+ },
90870
+ force: { type: "boolean", description: "Overwrite an existing line" }
90871
+ },
90872
+ ["id"]
90873
+ )
90874
+ },
90875
+ {
90876
+ name: "har_add_line",
90877
+ description: "Install a factory line bundle (local id, path, npm package, or git URL). Registers the line's stages but NEVER adds them to verificationStages \u2014 default verify stays unchanged. Verification plugins use har_add_plugin instead.",
90878
+ inputSchema: objectJsonSchema(
90879
+ {
90880
+ repo: repoJsonProperty,
90881
+ line: {
90882
+ type: "string",
90883
+ description: "Line id, local path (./line), npm package (@org/pkg), or git URL"
90884
+ },
90885
+ force: { type: "boolean", description: "Overwrite existing line files and stage entries" }
90886
+ },
90887
+ ["line"]
90888
+ )
90889
+ },
90890
+ {
90891
+ name: "har_line_status",
90892
+ description: "Stations, cumulative gate progress derived from .har/runs/ records, and slots in flight for installed factory lines. Pure read \u2014 writes no run records.",
90893
+ inputSchema: objectJsonSchema({
90894
+ repo: repoJsonProperty,
90895
+ line: { type: "string", description: "Line id (default: every installed line)" }
90896
+ })
90897
+ },
90898
+ {
90899
+ name: "har_run_line_gate",
90900
+ description: "Run one station's cumulative gate: every gate stage tagged at that station or earlier. Runs through the normal stage runner and writes run records; does not call verify and does not widen the verify plan.",
90901
+ inputSchema: objectJsonSchema(
90902
+ {
90903
+ repo: repoJsonProperty,
90904
+ station: { type: "string", description: "Station id whose cumulative gate should run" },
90905
+ line: { type: "string", description: "Line id when more than one is installed" },
90906
+ agentId: agentIdJsonProperty,
90907
+ force: {
90908
+ type: "boolean",
90909
+ description: "Run even when the program declares an opt-in env var that is not set"
90910
+ }
90911
+ },
90912
+ ["station"]
90913
+ )
89783
90914
  }
89784
90915
  ];
89785
90916
  function jsonContent(data) {
@@ -90009,6 +91140,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
90009
91140
  const result = await completeEnvironment({
90010
91141
  repoPath: repo,
90011
91142
  agentId,
91143
+ verify: input.verify,
90012
91144
  skipVerify: input.skipVerify,
90013
91145
  capture: true,
90014
91146
  trigger: "mcp"
@@ -90045,7 +91177,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
90045
91177
  const input = ControlUpInputSchema.parse({ ...args, repo });
90046
91178
  const result = await startControlAndSync({
90047
91179
  detach: input.detach,
90048
- cwd: path85.resolve(input.repo)
91180
+ cwd: path90.resolve(input.repo)
90049
91181
  });
90050
91182
  if (result.code !== 0) {
90051
91183
  return {
@@ -90067,6 +91199,59 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
90067
91199
  })
90068
91200
  );
90069
91201
  }
91202
+ case "har_line_create": {
91203
+ const input = CreateLineInputSchema.parse({ ...args, repo });
91204
+ const result = createLineBundle(repo, {
91205
+ id: input.id,
91206
+ title: input.title,
91207
+ description: input.description,
91208
+ stations: input.stations,
91209
+ gateStage: input.gateStage,
91210
+ optInEnv: input.optInEnv,
91211
+ force: input.force
91212
+ });
91213
+ return jsonContent({
91214
+ lineId: result.lineId,
91215
+ filesWritten: result.filesWritten,
91216
+ nextSteps: result.nextSteps
91217
+ });
91218
+ }
91219
+ case "har_add_line": {
91220
+ const input = AddLineInputSchema.parse({ ...args, repo });
91221
+ const result = addLine(repo, input.line, { force: input.force, spec: input.line });
91222
+ return jsonContent({
91223
+ lineId: result.lineId,
91224
+ title: result.title,
91225
+ stationIds: result.stationIds,
91226
+ firstGatedStationId: result.firstGatedStationId,
91227
+ stageIds: result.stageIds,
91228
+ filesWritten: result.filesWritten,
91229
+ warnings: result.warnings,
91230
+ nextSteps: result.nextSteps,
91231
+ programPath: result.programPath,
91232
+ docsPath: result.docsPath,
91233
+ source: result.source,
91234
+ adaptPromptPath: result.adaptPromptPath,
91235
+ verificationStagesUnchanged: true
91236
+ });
91237
+ }
91238
+ case "har_line_status": {
91239
+ const input = LineStatusInputSchema.parse({ ...args, repo });
91240
+ const statuses = input.line ? [getLineStatus(repo, input.line)] : getAllLineStatuses(repo);
91241
+ return jsonContent(input.line ? statuses[0] : { lines: statuses });
91242
+ }
91243
+ case "har_run_line_gate": {
91244
+ const input = RunLineGateInputSchema.parse({ ...args, repo });
91245
+ const agentId = input.agentId === void 0 ? void 0 : validateAgentId(input.agentId, repo);
91246
+ const result = await runLineGate({
91247
+ repoPath: repo,
91248
+ lineId: input.line,
91249
+ station: input.station,
91250
+ agentId,
91251
+ force: input.force
91252
+ });
91253
+ return jsonContent(result);
91254
+ }
90070
91255
  default:
90071
91256
  throw new Error(`Unknown tool: ${toolName}`);
90072
91257
  }
@@ -90109,11 +91294,11 @@ var mcpCommand = {
90109
91294
  };
90110
91295
 
90111
91296
  // src/cli/commands/onboard.ts
90112
- var path87 = __toESM(require("path"));
91297
+ var path92 = __toESM(require("path"));
90113
91298
  init_stages();
90114
91299
 
90115
91300
  // src/core/onboarding.ts
90116
- var path86 = __toESM(require("path"));
91301
+ var path91 = __toESM(require("path"));
90117
91302
  init_control_config();
90118
91303
  init_control_sync();
90119
91304
  init_otel_hooks();
@@ -90150,7 +91335,7 @@ var ONBOARDING_GUIDE_STEPS = [
90150
91335
  body: [
90151
91336
  "Quick: har env verify <id>",
90152
91337
  "Done: har env verify <id> --full then har env complete <id>",
90153
- "`complete` records a validation, frees the slot, and keeps the session branch for a PR."
91338
+ "`complete` reuses the last matching full validation, frees the slot, and keeps the session branch for a PR. Pass --verify if the tree changed."
90154
91339
  ].join("\n")
90155
91340
  },
90156
91341
  {
@@ -90257,7 +91442,7 @@ async function defaultEnsureControl(options) {
90257
91442
  return { started: true, apiUrl: result.apiUrl };
90258
91443
  }
90259
91444
  async function runOnboarding(options, deps = {}) {
90260
- const repoPath = path86.resolve(options.repoPath);
91445
+ const repoPath = path91.resolve(options.repoPath);
90261
91446
  const applyTelemetry = deps.applyTelemetry ?? applyOnboardingTelemetry;
90262
91447
  const ensureControl = deps.ensureControl ?? defaultEnsureControl;
90263
91448
  const init = deps.initHarness ?? initHarness;
@@ -90455,7 +91640,7 @@ async function promptChoices(args, docker) {
90455
91640
  };
90456
91641
  }
90457
91642
  const pluginChoices = listPluginChoices();
90458
- const repoPath = path87.resolve(args.repo);
91643
+ const repoPath = path92.resolve(args.repo);
90459
91644
  const alreadyPresent = harnessExists(repoPath);
90460
91645
  const needProfile = !alreadyPresent && !args.skipInit && args.profile === void 0;
90461
91646
  const needAgentSlots = shouldConfigureSlots && flaggedSlots === void 0;
@@ -90576,7 +91761,7 @@ function printSummary(result) {
90576
91761
  console.error("");
90577
91762
  }
90578
91763
  async function handleOnboard(argv) {
90579
- const repoPath = path87.resolve(argv.repo);
91764
+ const repoPath = path92.resolve(argv.repo);
90580
91765
  header("har onboard");
90581
91766
  info(`Repository: ${repoPath}`);
90582
91767
  try {
@@ -90693,24 +91878,288 @@ var onboardCommand = {
90693
91878
  handler: (argv) => handleOnboard(argv)
90694
91879
  };
90695
91880
 
91881
+ // src/cli/commands/line.ts
91882
+ var path93 = __toESM(require("path"));
91883
+ init_logging();
91884
+ function handleLineCreate(argv) {
91885
+ if (!argv.id) {
91886
+ error('Missing line id. Usage: har line create <id> [--stations S1,S2] [--title "..."]');
91887
+ return finishCommand(1);
91888
+ }
91889
+ const repoPath = path93.resolve(argv.repo);
91890
+ header("har line create");
91891
+ info(`Repository: ${repoPath}`);
91892
+ info(`Line: ${argv.id}`);
91893
+ try {
91894
+ const result = createLineBundle(repoPath, {
91895
+ id: argv.id,
91896
+ title: argv.title,
91897
+ description: argv.description,
91898
+ stations: argv.stations,
91899
+ gateStage: argv.gateStage,
91900
+ optInEnv: argv.optInEnv,
91901
+ force: argv.force
91902
+ });
91903
+ divider();
91904
+ success(`Factory line scaffolded: .har/lines/${result.lineId}/`);
91905
+ for (const file of result.filesWritten) {
91906
+ info(` + ${file}`);
91907
+ }
91908
+ console.error("");
91909
+ console.error(" Next steps:");
91910
+ for (const step of result.nextSteps) {
91911
+ console.error(` ${step}`);
91912
+ }
91913
+ console.error("");
91914
+ console.error(` Docs: .har/lines/${result.lineId}/README.md`);
91915
+ console.error("");
91916
+ } catch (err) {
91917
+ error(err.message);
91918
+ return finishCommand(1);
91919
+ }
91920
+ }
91921
+ function handleLineAdd(argv) {
91922
+ if (!argv.spec) {
91923
+ error("Missing line spec. Usage: har line add <id|path|npm|git>");
91924
+ return finishCommand(1);
91925
+ }
91926
+ const repoPath = path93.resolve(argv.repo);
91927
+ header("har line add");
91928
+ info(`Repository: ${repoPath}`);
91929
+ info(`Line: ${argv.spec}`);
91930
+ try {
91931
+ const result = addLine(repoPath, argv.spec, { force: argv.force, spec: argv.spec });
91932
+ divider();
91933
+ console.error("");
91934
+ console.error(" verificationStages unchanged \u2014 line gate stages are opt-in.");
91935
+ console.error(
91936
+ ` Run the gate with: har line gate ${result.firstGatedStationId} --line ${result.lineId}`
91937
+ );
91938
+ console.error("");
91939
+ console.error(" Next steps:");
91940
+ for (const step of result.nextSteps) {
91941
+ console.error(` ${step}`);
91942
+ }
91943
+ console.error("");
91944
+ } catch (err) {
91945
+ error(err.message);
91946
+ return finishCommand(1);
91947
+ }
91948
+ }
91949
+ function renderStatus(status) {
91950
+ header(`Line: ${status.lineId} \u2014 ${status.title}`);
91951
+ info(`Program: ${status.programPath}${status.source ? ` (source: ${status.source})` : ""}`);
91952
+ info(
91953
+ status.optInEnv ? `Gate: opt-in via ${status.optInEnv}=1` : "Gate: on demand (har line gate <station>)"
91954
+ );
91955
+ info(
91956
+ status.registeredStageIds.length > 0 ? `Registered stages (off verify): ${status.registeredStageIds.join(", ")}` : "Registered stages (off verify): none"
91957
+ );
91958
+ console.error("");
91959
+ for (const station of status.stations) {
91960
+ const marker = station.green ? "\u2713" : station.id === status.nextStationId ? "\u25B6" : "\xB7";
91961
+ const required2 = station.requiredStageIds.length > 0 ? `${station.passedStageIds.length}/${station.requiredStageIds.length} gate stages green` : "no gate stages";
91962
+ console.error(` ${marker} ${station.id} ${station.title} \u2014 ${required2}`);
91963
+ if (station.missingStageIds.length > 0) {
91964
+ console.error(` missing (not registered): ${station.missingStageIds.join(", ")}`);
91965
+ }
91966
+ const pending = station.requiredStageIds.filter((id) => !station.passedStageIds.includes(id));
91967
+ if (pending.length > 0 && station.missingStageIds.length === 0) {
91968
+ console.error(` pending: ${pending.join(", ")}`);
91969
+ }
91970
+ }
91971
+ console.error("");
91972
+ if (status.slotsInFlight.length > 0) {
91973
+ console.error(" Slots in flight:");
91974
+ for (const slot of status.slotsInFlight) {
91975
+ const work = slot.workUnitId ? ` work=${slot.workUnitId}` : "";
91976
+ console.error(` agent ${slot.agentId}: ${slot.branch ?? slot.workDir}${work}`);
91977
+ }
91978
+ console.error("");
91979
+ }
91980
+ for (const warning of status.warnings) {
91981
+ warn(` \u26A0 ${warning}`);
91982
+ }
91983
+ console.error(
91984
+ status.nextStationId ? ` Next station: ${status.nextStationId}` : " All stations green \u2014 hand off for human review (autonomousShip: false)."
91985
+ );
91986
+ console.error("");
91987
+ }
91988
+ function handleLineStatus(argv) {
91989
+ const repoPath = path93.resolve(argv.repo);
91990
+ try {
91991
+ const statuses = argv.id ? [getLineStatus(repoPath, argv.id)] : getAllLineStatuses(repoPath);
91992
+ if (argv.json) {
91993
+ console.log(JSON.stringify(argv.id ? statuses[0] : statuses, null, 2));
91994
+ return;
91995
+ }
91996
+ if (statuses.length === 0) {
91997
+ info("No factory line installed.");
91998
+ info("Scaffold one: har line create <id> \u2022 Install one: har line add <spec>");
91999
+ return;
92000
+ }
92001
+ for (const status of statuses) {
92002
+ renderStatus(status);
92003
+ }
92004
+ } catch (err) {
92005
+ error(err.message);
92006
+ return finishCommand(1);
92007
+ }
92008
+ }
92009
+ async function handleLineGate(argv) {
92010
+ if (!argv.station) {
92011
+ error("Missing station. Usage: har line gate <station> [--line <id>] [--agent <slot>]");
92012
+ return finishCommand(1);
92013
+ }
92014
+ const repoPath = path93.resolve(argv.repo);
92015
+ try {
92016
+ const result = await runLineGate({
92017
+ repoPath,
92018
+ lineId: argv.line,
92019
+ station: argv.station,
92020
+ agentId: argv.agent,
92021
+ force: argv.force
92022
+ });
92023
+ if (argv.json) {
92024
+ console.log(JSON.stringify(result, null, 2));
92025
+ return finishCommand(result.pass ? 0 : 1);
92026
+ }
92027
+ header(`har line gate ${result.station} (${result.lineId})`);
92028
+ if (result.skipped) {
92029
+ info(result.skipReason ?? "Gate skipped.");
92030
+ for (const stage of result.stages) {
92031
+ info(` \xB7 ${stage.stageId} (from ${stage.fromStation}) \u2014 skipped`);
92032
+ }
92033
+ return;
92034
+ }
92035
+ for (const stage of result.stages) {
92036
+ const mark = stage.status === "pass" ? "\u2713" : stage.status === "skipped" ? "\xB7" : "\u2717";
92037
+ const detail = stage.reason ? ` \u2014 ${stage.reason}` : "";
92038
+ const ms = stage.durationMs !== void 0 ? ` (${Math.round(stage.durationMs / 100) / 10}s)` : "";
92039
+ console.error(` ${mark} ${stage.stageId} from ${stage.fromStation} ${stage.status}${ms}${detail}`);
92040
+ }
92041
+ divider();
92042
+ if (result.pass) {
92043
+ success(`Gate passed for station ${result.station}`);
92044
+ } else {
92045
+ error(`Gate failed for station ${result.station}`);
92046
+ }
92047
+ return finishCommand(result.pass ? 0 : 1);
92048
+ } catch (err) {
92049
+ error(err.message);
92050
+ return finishCommand(1);
92051
+ }
92052
+ }
92053
+ function handleLineList(argv) {
92054
+ const repoPath = path93.resolve(argv.repo);
92055
+ const installed = new Set(listInstalledLineIds(repoPath));
92056
+ for (const id of listBundledLineIds()) {
92057
+ console.log(`${id} (bundled)`);
92058
+ }
92059
+ for (const id of listLocalLineIds(repoPath)) {
92060
+ console.log(`${id} (local: .har/lines/${id})${installed.has(id) ? " [installed]" : ""}`);
92061
+ }
92062
+ }
92063
+ function parseStations(value) {
92064
+ if (typeof value !== "string" || value.trim() === "") return void 0;
92065
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
92066
+ }
92067
+ var lineCommand = {
92068
+ command: "line <subcommand>",
92069
+ describe: "Factory lines: multi-station programs with a cumulative gate that never joins verificationStages",
92070
+ builder: (yargs) => yargs.command(
92071
+ "create [id]",
92072
+ "Scaffold a project-owned line at .har/lines/<id>/ (manifest, program, gate stage, README)",
92073
+ (y) => y.positional("id", { type: "string", describe: "Line id (lowercase slug, e.g. onboarding-line)" }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("title", { type: "string", describe: "Human-readable line title" }).option("description", { type: "string", describe: "One-paragraph description of the program" }).option("stations", {
92074
+ type: "string",
92075
+ describe: "Comma-separated station ids in order (default: S1,S2)"
92076
+ }).option("gate-stage", {
92077
+ type: "boolean",
92078
+ default: true,
92079
+ describe: "Scaffold one registered-but-off-verify gate stage"
92080
+ }).option("opt-in-env", {
92081
+ type: "string",
92082
+ describe: 'Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'
92083
+ }).option("force", { type: "boolean", default: false, describe: "Overwrite an existing line" }),
92084
+ (argv) => handleLineCreate({
92085
+ id: argv.id,
92086
+ repo: argv.repo,
92087
+ title: argv.title,
92088
+ description: argv.description,
92089
+ stations: parseStations(argv.stations),
92090
+ gateStage: argv["gate-stage"],
92091
+ optInEnv: argv["opt-in-env"],
92092
+ force: argv.force
92093
+ })
92094
+ ).command(
92095
+ "add [spec]",
92096
+ "Install a line bundle (local id, path, npm package, or git URL) \u2014 never touches verificationStages",
92097
+ (y) => y.positional("spec", {
92098
+ type: "string",
92099
+ describe: "Line id, path (./line), npm package (@org/pkg), or git URL (github:org/repo)"
92100
+ }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("force", {
92101
+ type: "boolean",
92102
+ default: false,
92103
+ describe: "Overwrite existing line files and stage entries"
92104
+ }),
92105
+ (argv) => handleLineAdd({
92106
+ spec: argv.spec,
92107
+ repo: argv.repo,
92108
+ force: argv.force
92109
+ })
92110
+ ).command(
92111
+ "status [id]",
92112
+ "Show stations, cumulative gate progress from run records, and slots in flight",
92113
+ (y) => y.positional("id", { type: "string", describe: "Line id (default: all installed lines)" }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("json", { type: "boolean", default: false, describe: "Emit structured JSON" }),
92114
+ (argv) => handleLineStatus({
92115
+ id: argv.id,
92116
+ repo: argv.repo,
92117
+ json: argv.json
92118
+ })
92119
+ ).command(
92120
+ "gate [station]",
92121
+ "Run the cumulative gate for a station (does NOT run verify and does not widen it)",
92122
+ (y) => y.positional("station", { type: "string", describe: "Station id (e.g. S1)" }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("line", { type: "string", describe: "Line id when more than one is installed" }).option("agent", { type: "number", describe: "Agent slot to run the stages in" }).option("force", {
92123
+ type: "boolean",
92124
+ default: false,
92125
+ describe: "Run even when the program declares an opt-in env var that is not set"
92126
+ }).option("json", { type: "boolean", default: false, describe: "Emit structured JSON" }),
92127
+ (argv) => handleLineGate({
92128
+ station: argv.station,
92129
+ repo: argv.repo,
92130
+ line: argv.line,
92131
+ agent: argv.agent,
92132
+ force: argv.force,
92133
+ json: argv.json
92134
+ })
92135
+ ).command(
92136
+ "list",
92137
+ "List lines available to this repository (bundled and local)",
92138
+ (y) => y.option("repo", { type: "string", default: ".", describe: "Path to the repository" }),
92139
+ (argv) => handleLineList({ repo: argv.repo })
92140
+ ).demandCommand(1, "Please specify a subcommand: create, add, status, gate, list"),
92141
+ handler: () => {
92142
+ }
92143
+ };
92144
+
90696
92145
  // src/cli/commands/plugin.ts
90697
- var path89 = __toESM(require("path"));
92146
+ var path95 = __toESM(require("path"));
90698
92147
 
90699
92148
  // src/harness/plugin-create.ts
90700
- var fs75 = __toESM(require("fs"));
90701
- var path88 = __toESM(require("path"));
92149
+ var fs78 = __toESM(require("fs"));
92150
+ var path94 = __toESM(require("path"));
90702
92151
  init_paths();
90703
92152
  init_schema2();
90704
92153
  var PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
90705
- function renderSkeleton(templatePath, tokens) {
90706
- let content = fs75.readFileSync(templatePath, "utf8");
92154
+ function renderSkeleton2(templatePath, tokens) {
92155
+ let content = fs78.readFileSync(templatePath, "utf8");
90707
92156
  for (const [token, value] of Object.entries(tokens)) {
90708
92157
  content = content.replace(new RegExp(token, "g"), value);
90709
92158
  }
90710
92159
  return content;
90711
92160
  }
90712
92161
  function createLocalPlugin(repoPath, options) {
90713
- const resolved = path88.resolve(repoPath);
92162
+ const resolved = path94.resolve(repoPath);
90714
92163
  if (!harnessExists(resolved)) {
90715
92164
  throw new Error('No .har/ harness found. Run "har onboard" first.');
90716
92165
  }
@@ -90726,9 +92175,9 @@ function createLocalPlugin(repoPath, options) {
90726
92175
  `Invalid stage kind "${kind}". Available: ${HarnessStageKindSchema.options.join(", ")}`
90727
92176
  );
90728
92177
  }
90729
- const pluginDir = path88.join(resolved, LOCAL_PLUGINS_DIR, id);
92178
+ const pluginDir = path94.join(resolved, LOCAL_PLUGINS_DIR, id);
90730
92179
  const pluginDirRel = `${LOCAL_PLUGINS_DIR}/${id}`;
90731
- if (fs75.existsSync(pluginDir) && !options.force) {
92180
+ if (fs78.existsSync(pluginDir) && !options.force) {
90732
92181
  throw new Error(`Plugin already exists: ${pluginDirRel}/. Use --force to overwrite.`);
90733
92182
  }
90734
92183
  const description = options.description ?? `Custom ${kind} stage (local plugin: ${id})`;
@@ -90770,16 +92219,16 @@ function createLocalPlugin(repoPath, options) {
90770
92219
  });
90771
92220
  const filesWritten = [];
90772
92221
  const write = (rel, content, executable = false) => {
90773
- const abs = path88.join(pluginDir, rel);
90774
- fs75.mkdirSync(path88.dirname(abs), { recursive: true });
90775
- fs75.writeFileSync(abs, content);
90776
- if (executable) fs75.chmodSync(abs, 493);
92222
+ const abs = path94.join(pluginDir, rel);
92223
+ fs78.mkdirSync(path94.dirname(abs), { recursive: true });
92224
+ fs78.writeFileSync(abs, content);
92225
+ if (executable) fs78.chmodSync(abs, 493);
90777
92226
  filesWritten.push(`${pluginDirRel}/${rel}`);
90778
92227
  };
90779
92228
  write("template.manifest.json", `${JSON.stringify(manifest, null, 2)}
90780
92229
  `);
90781
- const stageScript = renderSkeleton(
90782
- path88.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
92230
+ const stageScript = renderSkeleton2(
92231
+ path94.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
90783
92232
  {
90784
92233
  __STAGE_ID__: id,
90785
92234
  __STAGE_KIND__: kind,
@@ -90787,8 +92236,8 @@ function createLocalPlugin(repoPath, options) {
90787
92236
  }
90788
92237
  );
90789
92238
  write(`stages/${id}.sh`, stageScript, true);
90790
- const readme = renderSkeleton(
90791
- path88.join(resolveTemplatesDir(), "plugins", "local-plugin-skeleton", "README.md"),
92239
+ const readme = renderSkeleton2(
92240
+ path94.join(resolveTemplatesDir(), "plugins", "local-plugin-skeleton", "README.md"),
90792
92241
  { __PLUGIN_ID__: id }
90793
92242
  );
90794
92243
  write("README.md", readme);
@@ -90820,7 +92269,7 @@ async function handlePluginCreate(argv) {
90820
92269
  error('Missing plugin id. Usage: har plugin create <id> [--kind test] [--description "..."]');
90821
92270
  return finishCommand(1);
90822
92271
  }
90823
- const repoPath = path89.resolve(argv.repo);
92272
+ const repoPath = path95.resolve(argv.repo);
90824
92273
  header("har plugin create");
90825
92274
  info(`Repository: ${repoPath}`);
90826
92275
  info(`Plugin: ${argv.id}`);
@@ -90851,7 +92300,7 @@ async function handlePluginCreate(argv) {
90851
92300
  }
90852
92301
  }
90853
92302
  function handlePluginList(argv) {
90854
- const repoPath = path89.resolve(argv.repo);
92303
+ const repoPath = path95.resolve(argv.repo);
90855
92304
  for (const id of listPluginIds()) {
90856
92305
  console.log(`${id} (bundled)`);
90857
92306
  }
@@ -91018,7 +92467,7 @@ var preferencesCommand = {
91018
92467
  };
91019
92468
 
91020
92469
  // src/cli/commands/telemetry.ts
91021
- var path90 = __toESM(require("path"));
92470
+ var path96 = __toESM(require("path"));
91022
92471
  init_control_config();
91023
92472
  init_control_sync();
91024
92473
  init_manifest();
@@ -91111,7 +92560,7 @@ async function handleOff() {
91111
92560
  info("Mission Control will not auto-start. Existing usage rows are kept.");
91112
92561
  }
91113
92562
  async function handleWriteEnv(argv) {
91114
- const repoPath = path90.resolve(argv.repo);
92563
+ const repoPath = path96.resolve(argv.repo);
91115
92564
  const harnessRoot = resolveHarnessRoot(repoPath);
91116
92565
  const session = readSlotRegistry(harnessRoot, argv.agentId);
91117
92566
  const workDir = argv.workDir ?? session?.workDir ?? repoPath;
@@ -91123,7 +92572,7 @@ async function handleWriteEnv(argv) {
91123
92572
  suffix,
91124
92573
  createdAt: session?.createdAt
91125
92574
  });
91126
- const envFile = argv.envFile ?? path90.join(workDir, `.env.agent.${argv.agentId}`);
92575
+ const envFile = argv.envFile ?? path96.join(workDir, `.env.agent.${argv.agentId}`);
91127
92576
  const attrs = {
91128
92577
  sessionKey,
91129
92578
  agentId: argv.agentId,
@@ -91139,7 +92588,7 @@ async function handleWriteEnv(argv) {
91139
92588
  success(`Wrote session attribution to ${envFile}`);
91140
92589
  }
91141
92590
  function handlePrintEnv(argv) {
91142
- const repoPath = path90.resolve(argv.repo);
92591
+ const repoPath = path96.resolve(argv.repo);
91143
92592
  const workDir = argv.workDir ?? repoPath;
91144
92593
  const sessionKey = buildSessionKey({
91145
92594
  branch: argv.branch,
@@ -91262,7 +92711,7 @@ var telemetryCommand = {
91262
92711
  // src/cli/index.ts
91263
92712
  async function runCli() {
91264
92713
  ensureDefaultTelemetryPreference();
91265
- await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(onboardCommand).command(envCommand).command(agentsCommand).command(controlCommand).command(hqCommand).command(hooksCommand).command(mcpCommand).command(pluginCommand).command(preferencesCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har onboard or har env --help").epilog(HAR_ROOT_EPILOG).strict().help().version(getHarPackageVersion()).parse();
92714
+ await yargs_default(hideBin(process.argv)).scriptName("har").usage("$0 <command> [options]").command(onboardCommand).command(envCommand).command(agentsCommand).command(controlCommand).command(hqCommand).command(hooksCommand).command(mcpCommand).command(pluginCommand).command(lineCommand).command(preferencesCommand).command(telemetryCommand).demandCommand(1, "Please specify a command. Try: har onboard or har env --help").epilog(HAR_ROOT_EPILOG).strict().help().version(getHarPackageVersion()).parse();
91266
92715
  await syncDirtyRepos();
91267
92716
  }
91268
92717