@osfactory/har 1.1.0 → 1.3.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() {
@@ -28632,6 +28632,149 @@ var init_usage_pricing = __esm({
28632
28632
  }
28633
28633
  });
28634
28634
 
28635
+ // packages/schemas/src/line.ts
28636
+ 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;
28637
+ var init_line = __esm({
28638
+ "packages/schemas/src/line.ts"() {
28639
+ "use strict";
28640
+ init_zod();
28641
+ init_schema();
28642
+ LINE_CONTRACT_VERSION = 1;
28643
+ LINE_BUNDLE_KIND = "line";
28644
+ LINE_MANIFEST_FILES = ["line.manifest.json", "template.manifest.json"];
28645
+ LineIdSchema = external_exports.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/, "Line id must be a lowercase slug (letters, digits, ._-)");
28646
+ StationIdSchema = external_exports.string().min(1).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "Station id must be stable and shell-friendly");
28647
+ LineSkillSchema = external_exports.object({
28648
+ id: external_exports.string().min(1),
28649
+ role: external_exports.enum(["orchestrator", "station"]).default("station"),
28650
+ /** Install hint only — HAR never vendors third-party skill packs. */
28651
+ install: external_exports.string().min(1).optional()
28652
+ });
28653
+ LineMcpSchema = external_exports.object({
28654
+ name: external_exports.string().min(1),
28655
+ why: external_exports.string().min(1).optional(),
28656
+ required: external_exports.boolean().default(false)
28657
+ });
28658
+ LineWorkSchema = external_exports.object({
28659
+ /** `github`, `linear`, `none`, or a free string. */
28660
+ source: external_exports.string().min(1).default("none"),
28661
+ ids: external_exports.array(external_exports.string().min(1)).default([]),
28662
+ url: external_exports.string().min(1).optional(),
28663
+ optional: external_exports.boolean().default(false)
28664
+ });
28665
+ LineWaveCellSchema = external_exports.object({
28666
+ workId: external_exports.string().min(1).optional(),
28667
+ title: external_exports.string().min(1).optional()
28668
+ });
28669
+ LineStationSchema = external_exports.object({
28670
+ id: StationIdSchema,
28671
+ title: external_exports.string().min(1),
28672
+ description: external_exports.string().min(1).optional(),
28673
+ work: LineWorkSchema.optional(),
28674
+ /** Each inner array is one wave; cells inside a wave run in parallel slots. */
28675
+ waves: external_exports.array(external_exports.array(LineWaveCellSchema)).optional(),
28676
+ skills: external_exports.array(external_exports.string().min(1)).optional(),
28677
+ mcp: external_exports.array(external_exports.string().min(1)).optional()
28678
+ });
28679
+ LineGateStageSchema = external_exports.object({
28680
+ id: external_exports.string().min(1),
28681
+ /** Required from this station onward — this is the ratchet tag. */
28682
+ fromStation: StationIdSchema,
28683
+ tier: external_exports.enum(["quick", "full"]).default("full")
28684
+ });
28685
+ LineGateSchema = external_exports.object({
28686
+ /** Must be true: a factory line never drops an earlier station's stages. */
28687
+ cumulative: external_exports.literal(true),
28688
+ /** When set, the gate is skipped unless this env var is "1". */
28689
+ optInEnv: external_exports.string().min(1).nullable().default(null),
28690
+ stages: external_exports.array(LineGateStageSchema).default([])
28691
+ });
28692
+ LineExtraStageSchema = external_exports.object({
28693
+ id: external_exports.string().min(1),
28694
+ kind: HarnessStageKindSchema.default("test"),
28695
+ tier: external_exports.enum(["quick", "full"]).default("full"),
28696
+ description: external_exports.string().min(1).optional()
28697
+ });
28698
+ LineHandoffSchema = external_exports.object({
28699
+ /** Must be false: agents hand off, they do not merge or release. */
28700
+ autonomousShip: external_exports.literal(false),
28701
+ waitFor: external_exports.string().min(1).optional()
28702
+ });
28703
+ LineTravelerSchema = external_exports.object({
28704
+ kind: external_exports.string().min(1),
28705
+ ref: external_exports.string().min(1).optional(),
28706
+ note: external_exports.string().min(1).optional()
28707
+ });
28708
+ LineProgramSchema = external_exports.object({
28709
+ contractVersion: external_exports.literal(LINE_CONTRACT_VERSION),
28710
+ id: LineIdSchema,
28711
+ title: external_exports.string().min(1),
28712
+ description: external_exports.string().min(1).optional(),
28713
+ skills: external_exports.array(LineSkillSchema).default([]),
28714
+ mcp: external_exports.array(LineMcpSchema).default([]),
28715
+ plugins: external_exports.array(external_exports.string().min(1)).default([]),
28716
+ stations: external_exports.array(LineStationSchema).min(1),
28717
+ gate: LineGateSchema,
28718
+ extraStages: external_exports.array(LineExtraStageSchema).default([]),
28719
+ handoff: LineHandoffSchema,
28720
+ traveler: LineTravelerSchema.optional(),
28721
+ prototypeNotes: external_exports.array(external_exports.string().min(1)).default([])
28722
+ }).superRefine((data, ctx) => {
28723
+ const stationIds = new Set(data.stations.map((s) => s.id));
28724
+ if (stationIds.size !== data.stations.length) {
28725
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: "station ids must be unique" });
28726
+ }
28727
+ for (const stage of data.gate.stages) {
28728
+ if (!stationIds.has(stage.fromStation)) {
28729
+ ctx.addIssue({
28730
+ code: external_exports.ZodIssueCode.custom,
28731
+ message: `gate.stages[].fromStation "${stage.fromStation}" is not a station id`
28732
+ });
28733
+ }
28734
+ }
28735
+ });
28736
+ LineManifestFileSchema = external_exports.object({
28737
+ src: external_exports.string().min(1),
28738
+ dest: external_exports.string().min(1),
28739
+ executable: external_exports.boolean().optional()
28740
+ });
28741
+ LineManifestSchema = external_exports.object({
28742
+ kind: external_exports.literal(LINE_BUNDLE_KIND),
28743
+ id: LineIdSchema,
28744
+ title: external_exports.string().min(1).optional(),
28745
+ /** Bundle-relative path to the program JSON. */
28746
+ program: external_exports.string().min(1).default("line.json"),
28747
+ files: external_exports.array(LineManifestFileSchema).default([]),
28748
+ /** Stages to REGISTER in .har/stages.json — never added to verificationStages. */
28749
+ stages: external_exports.array(HarnessStageSchema).default([]),
28750
+ nextSteps: external_exports.array(external_exports.string().min(1)).default([]),
28751
+ docsPath: external_exports.string().min(1).optional()
28752
+ }).passthrough().superRefine((data, ctx) => {
28753
+ const raw = data;
28754
+ if (raw.verificationStages !== void 0) {
28755
+ ctx.addIssue({
28756
+ code: external_exports.ZodIssueCode.custom,
28757
+ message: "Line manifests must not declare verificationStages \u2014 line gate stages are opt-in and run via `har line gate`."
28758
+ });
28759
+ }
28760
+ });
28761
+ LineLedgerEntrySchema = external_exports.object({
28762
+ id: LineIdSchema,
28763
+ source: external_exports.enum(["bundled", "local", "path", "npm", "git"]),
28764
+ spec: external_exports.string().min(1),
28765
+ version: external_exports.string().optional(),
28766
+ /** Stages this line registered (registered only — not on verify). */
28767
+ stageIds: external_exports.array(external_exports.string().min(1)).default([]),
28768
+ programPath: external_exports.string().min(1),
28769
+ installedAt: external_exports.string()
28770
+ });
28771
+ LineLedgerSchema = external_exports.object({
28772
+ version: external_exports.literal("1"),
28773
+ lines: external_exports.array(LineLedgerEntrySchema).default([])
28774
+ });
28775
+ }
28776
+ });
28777
+
28635
28778
  // src/harness/schema.ts
28636
28779
  var init_schema2 = __esm({
28637
28780
  "src/harness/schema.ts"() {
@@ -28640,6 +28783,7 @@ var init_schema2 = __esm({
28640
28783
  init_harness_env();
28641
28784
  init_usage_authority();
28642
28785
  init_usage_pricing();
28786
+ init_line();
28643
28787
  }
28644
28788
  });
28645
28789
 
@@ -29043,7 +29187,7 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
29043
29187
  const stream = options.stream ?? false;
29044
29188
  const echoStdout = stream && (options.streamStdout ?? true);
29045
29189
  const { stream: _stream, streamStdout: _streamStdout, ...spawnOptions } = options;
29046
- return new Promise((resolve60) => {
29190
+ return new Promise((resolve66) => {
29047
29191
  const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
29048
29192
  ...spawnOptions,
29049
29193
  stdio: ["inherit", "pipe", "pipe"]
@@ -29058,14 +29202,14 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
29058
29202
  stderr += d;
29059
29203
  if (stream) process.stderr.write(d);
29060
29204
  });
29061
- proc.on("close", (code) => resolve60({ stdout, stderr, code: code ?? 0 }));
29205
+ proc.on("close", (code) => resolve66({ stdout, stderr, code: code ?? 0 }));
29062
29206
  });
29063
29207
  }
29064
29208
  function runShellCommand(command2, options = {}) {
29065
29209
  const stream = options.stream ?? false;
29066
29210
  const echoStdout = stream && (options.streamStdout ?? true);
29067
29211
  const { stream: _stream, streamStdout: _streamStdout, ...spawnOptions } = options;
29068
- return new Promise((resolve60) => {
29212
+ return new Promise((resolve66) => {
29069
29213
  const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
29070
29214
  ...spawnOptions,
29071
29215
  stdio: ["inherit", "pipe", "pipe"]
@@ -29080,7 +29224,7 @@ function runShellCommand(command2, options = {}) {
29080
29224
  stderr += d;
29081
29225
  if (stream) process.stderr.write(d);
29082
29226
  });
29083
- proc.on("close", (code) => resolve60({ stdout, stderr, code: code ?? 0 }));
29227
+ proc.on("close", (code) => resolve66({ stdout, stderr, code: code ?? 0 }));
29084
29228
  });
29085
29229
  }
29086
29230
  var import_child_process;
@@ -30071,7 +30215,7 @@ var init_slot_registry = __esm({
30071
30215
 
30072
30216
  // src/runtime/exec.ts
30073
30217
  function spawnResult(cmd, args, opts, shell) {
30074
- return new Promise((resolve60) => {
30218
+ return new Promise((resolve66) => {
30075
30219
  const stream = opts.stream ?? false;
30076
30220
  const proc = (0, import_child_process3.spawn)(cmd, args, {
30077
30221
  cwd: opts.cwd,
@@ -30083,8 +30227,8 @@ function spawnResult(cmd, args, opts, shell) {
30083
30227
  let stderr = "";
30084
30228
  proc.stdout?.on("data", (c) => stdout += c.toString());
30085
30229
  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 }));
30230
+ proc.on("error", (err) => resolve66({ code: 127, stdout, stderr: stderr || String(err) }));
30231
+ proc.on("close", (code) => resolve66({ code: code ?? 1, stdout, stderr }));
30088
30232
  });
30089
30233
  }
30090
30234
  var import_child_process3, fs13, path13, realSystemOps, defaultExec, stderrLog, realSleep;
@@ -30147,7 +30291,7 @@ var init_exec = __esm({
30147
30291
  process.stderr.write(`==> ${message}
30148
30292
  `);
30149
30293
  };
30150
- realSleep = (seconds) => new Promise((resolve60) => setTimeout(resolve60, seconds * 1e3));
30294
+ realSleep = (seconds) => new Promise((resolve66) => setTimeout(resolve66, seconds * 1e3));
30151
30295
  }
30152
30296
  });
30153
30297
 
@@ -34738,16 +34882,16 @@ var init_local_executor = __esm({
34738
34882
  const walk = (dir, prefix) => {
34739
34883
  for (const entry of fs35.readdirSync(dir, { withFileTypes: true })) {
34740
34884
  const full = path36.join(dir, entry.name);
34741
- const relative5 = path36.join(prefix, entry.name);
34885
+ const relative6 = path36.join(prefix, entry.name);
34742
34886
  if (entry.isDirectory()) {
34743
- walk(full, relative5);
34887
+ walk(full, relative6);
34744
34888
  continue;
34745
34889
  }
34746
- if (filter2?.stageId && !relative5.includes(filter2.stageId)) continue;
34890
+ if (filter2?.stageId && !relative6.includes(filter2.stageId)) continue;
34747
34891
  const stat = fs35.statSync(full);
34748
34892
  entries.push({
34749
34893
  path: full,
34750
- relativePath: relative5,
34894
+ relativePath: relative6,
34751
34895
  sizeBytes: stat.size,
34752
34896
  modifiedAt: stat.mtime.toISOString()
34753
34897
  });
@@ -36663,7 +36807,7 @@ async function attempt(url) {
36663
36807
  async function readControlJson(url) {
36664
36808
  const first = await attempt(url);
36665
36809
  if (first.read.ok || !first.retryable) return first.read;
36666
- await new Promise((resolve60) => setTimeout(resolve60, RETRY_DELAY_MS));
36810
+ await new Promise((resolve66) => setTimeout(resolve66, RETRY_DELAY_MS));
36667
36811
  return (await attempt(url)).read;
36668
36812
  }
36669
36813
  async function readControlPages(options) {
@@ -37148,7 +37292,7 @@ async function postForResponse(url, headers, payload) {
37148
37292
  } catch (err) {
37149
37293
  lastErr = err;
37150
37294
  if (attempt2 < MAX_FETCH_ATTEMPTS) {
37151
- await new Promise((resolve60) => setTimeout(resolve60, attempt2 * 100));
37295
+ await new Promise((resolve66) => setTimeout(resolve66, attempt2 * 100));
37152
37296
  }
37153
37297
  }
37154
37298
  }
@@ -37388,7 +37532,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
37388
37532
  const deadline = Date.now() + timeoutMs;
37389
37533
  while (Date.now() < deadline) {
37390
37534
  if (await isControlApiReachable(apiUrl)) return true;
37391
- await new Promise((resolve60) => setTimeout(resolve60, intervalMs));
37535
+ await new Promise((resolve66) => setTimeout(resolve66, intervalMs));
37392
37536
  }
37393
37537
  return false;
37394
37538
  }
@@ -39037,15 +39181,15 @@ var require_route = __commonJS({
39037
39181
  };
39038
39182
  }
39039
39183
  function wrapConversion(toModel, graph) {
39040
- const path91 = [graph[toModel].parent, toModel];
39184
+ const path97 = [graph[toModel].parent, toModel];
39041
39185
  let fn = conversions[graph[toModel].parent][toModel];
39042
39186
  let cur = graph[toModel].parent;
39043
39187
  while (graph[cur].parent) {
39044
- path91.unshift(graph[cur].parent);
39188
+ path97.unshift(graph[cur].parent);
39045
39189
  fn = link(conversions[graph[cur].parent][cur], fn);
39046
39190
  cur = graph[cur].parent;
39047
39191
  }
39048
- fn.conversion = path91;
39192
+ fn.conversion = path97;
39049
39193
  return fn;
39050
39194
  }
39051
39195
  module2.exports = function(fromModel) {
@@ -45539,26 +45683,26 @@ var require_lib2 = __commonJS({
45539
45683
  return matches;
45540
45684
  };
45541
45685
  exports2.analyse = analyse;
45542
- var detectFile = (filepath, opts = {}) => new Promise((resolve60, reject) => {
45686
+ var detectFile = (filepath, opts = {}) => new Promise((resolve66, reject) => {
45543
45687
  let fd;
45544
- const fs76 = (0, node_1.default)();
45688
+ const fs79 = (0, node_1.default)();
45545
45689
  const handler = (err, buffer) => {
45546
45690
  if (fd) {
45547
- fs76.closeSync(fd);
45691
+ fs79.closeSync(fd);
45548
45692
  }
45549
45693
  if (err) {
45550
45694
  reject(err);
45551
45695
  } else if (buffer) {
45552
- resolve60((0, exports2.detect)(buffer));
45696
+ resolve66((0, exports2.detect)(buffer));
45553
45697
  } else {
45554
45698
  reject(new Error("No error and no buffer received"));
45555
45699
  }
45556
45700
  };
45557
45701
  const sampleSize = (opts === null || opts === void 0 ? void 0 : opts.sampleSize) || 0;
45558
45702
  if (sampleSize > 0) {
45559
- fd = fs76.openSync(filepath, "r");
45703
+ fd = fs79.openSync(filepath, "r");
45560
45704
  let sample = Buffer.allocUnsafe(sampleSize);
45561
- fs76.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
45705
+ fs79.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
45562
45706
  if (err) {
45563
45707
  handler(err, null);
45564
45708
  } else {
@@ -45570,22 +45714,22 @@ var require_lib2 = __commonJS({
45570
45714
  });
45571
45715
  return;
45572
45716
  }
45573
- fs76.readFile(filepath, handler);
45717
+ fs79.readFile(filepath, handler);
45574
45718
  });
45575
45719
  exports2.detectFile = detectFile;
45576
45720
  var detectFileSync = (filepath, opts = {}) => {
45577
- const fs76 = (0, node_1.default)();
45721
+ const fs79 = (0, node_1.default)();
45578
45722
  if (opts && opts.sampleSize) {
45579
- const fd = fs76.openSync(filepath, "r");
45723
+ const fd = fs79.openSync(filepath, "r");
45580
45724
  let sample = Buffer.allocUnsafe(opts.sampleSize);
45581
- const bytesRead = fs76.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
45725
+ const bytesRead = fs79.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
45582
45726
  if (bytesRead < opts.sampleSize) {
45583
45727
  sample = sample.subarray(0, bytesRead);
45584
45728
  }
45585
- fs76.closeSync(fd);
45729
+ fs79.closeSync(fd);
45586
45730
  return (0, exports2.detect)(sample);
45587
45731
  }
45588
- return (0, exports2.detect)(fs76.readFileSync(filepath));
45732
+ return (0, exports2.detect)(fs79.readFileSync(filepath));
45589
45733
  };
45590
45734
  exports2.detectFileSync = detectFileSync;
45591
45735
  exports2.default = {
@@ -50098,7 +50242,7 @@ var require_Observable = __commonJS({
50098
50242
  Observable2.prototype.forEach = function(next, promiseCtor) {
50099
50243
  var _this = this;
50100
50244
  promiseCtor = getPromiseCtor(promiseCtor);
50101
- return new promiseCtor(function(resolve60, reject) {
50245
+ return new promiseCtor(function(resolve66, reject) {
50102
50246
  var subscriber = new Subscriber_1.SafeSubscriber({
50103
50247
  next: function(value) {
50104
50248
  try {
@@ -50109,7 +50253,7 @@ var require_Observable = __commonJS({
50109
50253
  }
50110
50254
  },
50111
50255
  error: reject,
50112
- complete: resolve60
50256
+ complete: resolve66
50113
50257
  });
50114
50258
  _this.subscribe(subscriber);
50115
50259
  });
@@ -50131,14 +50275,14 @@ var require_Observable = __commonJS({
50131
50275
  Observable2.prototype.toPromise = function(promiseCtor) {
50132
50276
  var _this = this;
50133
50277
  promiseCtor = getPromiseCtor(promiseCtor);
50134
- return new promiseCtor(function(resolve60, reject) {
50278
+ return new promiseCtor(function(resolve66, reject) {
50135
50279
  var value;
50136
50280
  _this.subscribe(function(x) {
50137
50281
  return value = x;
50138
50282
  }, function(err) {
50139
50283
  return reject(err);
50140
50284
  }, function() {
50141
- return resolve60(value);
50285
+ return resolve66(value);
50142
50286
  });
50143
50287
  });
50144
50288
  };
@@ -52234,11 +52378,11 @@ var require_innerFrom = __commonJS({
52234
52378
  "use strict";
52235
52379
  var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P2, generator) {
52236
52380
  function adopt(value) {
52237
- return value instanceof P2 ? value : new P2(function(resolve60) {
52238
- resolve60(value);
52381
+ return value instanceof P2 ? value : new P2(function(resolve66) {
52382
+ resolve66(value);
52239
52383
  });
52240
52384
  }
52241
- return new (P2 || (P2 = Promise))(function(resolve60, reject) {
52385
+ return new (P2 || (P2 = Promise))(function(resolve66, reject) {
52242
52386
  function fulfilled(value) {
52243
52387
  try {
52244
52388
  step(generator.next(value));
@@ -52254,7 +52398,7 @@ var require_innerFrom = __commonJS({
52254
52398
  }
52255
52399
  }
52256
52400
  function step(result) {
52257
- result.done ? resolve60(result.value) : adopt(result.value).then(fulfilled, rejected);
52401
+ result.done ? resolve66(result.value) : adopt(result.value).then(fulfilled, rejected);
52258
52402
  }
52259
52403
  step((generator = generator.apply(thisArg, _arguments || [])).next());
52260
52404
  });
@@ -52336,14 +52480,14 @@ var require_innerFrom = __commonJS({
52336
52480
  }, i);
52337
52481
  function verb(n) {
52338
52482
  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);
52483
+ return new Promise(function(resolve66, reject) {
52484
+ v2 = o[n](v2), settle(resolve66, reject, v2.done, v2.value);
52341
52485
  });
52342
52486
  };
52343
52487
  }
52344
- function settle(resolve60, reject, d, v2) {
52488
+ function settle(resolve66, reject, d, v2) {
52345
52489
  Promise.resolve(v2).then(function(v3) {
52346
- resolve60({ value: v3, done: d });
52490
+ resolve66({ value: v3, done: d });
52347
52491
  }, reject);
52348
52492
  }
52349
52493
  };
@@ -52962,7 +53106,7 @@ var require_lastValueFrom = __commonJS({
52962
53106
  var EmptyError_1 = require_EmptyError();
52963
53107
  function lastValueFrom2(source, config2) {
52964
53108
  var hasConfig = typeof config2 === "object";
52965
- return new Promise(function(resolve60, reject) {
53109
+ return new Promise(function(resolve66, reject) {
52966
53110
  var _hasValue = false;
52967
53111
  var _value;
52968
53112
  source.subscribe({
@@ -52973,9 +53117,9 @@ var require_lastValueFrom = __commonJS({
52973
53117
  error: reject,
52974
53118
  complete: function() {
52975
53119
  if (_hasValue) {
52976
- resolve60(_value);
53120
+ resolve66(_value);
52977
53121
  } else if (hasConfig) {
52978
- resolve60(config2.defaultValue);
53122
+ resolve66(config2.defaultValue);
52979
53123
  } else {
52980
53124
  reject(new EmptyError_1.EmptyError());
52981
53125
  }
@@ -52997,16 +53141,16 @@ var require_firstValueFrom = __commonJS({
52997
53141
  var Subscriber_1 = require_Subscriber();
52998
53142
  function firstValueFrom(source, config2) {
52999
53143
  var hasConfig = typeof config2 === "object";
53000
- return new Promise(function(resolve60, reject) {
53144
+ return new Promise(function(resolve66, reject) {
53001
53145
  var subscriber = new Subscriber_1.SafeSubscriber({
53002
53146
  next: function(value) {
53003
- resolve60(value);
53147
+ resolve66(value);
53004
53148
  subscriber.unsubscribe();
53005
53149
  },
53006
53150
  error: reject,
53007
53151
  complete: function() {
53008
53152
  if (hasConfig) {
53009
- resolve60(config2.defaultValue);
53153
+ resolve66(config2.defaultValue);
53010
53154
  } else {
53011
53155
  reject(new EmptyError_1.EmptyError());
53012
53156
  }
@@ -58798,14 +58942,14 @@ var require_run_async = __commonJS({
58798
58942
  return function() {
58799
58943
  var args = arguments;
58800
58944
  var originalThis = this;
58801
- var promise = new Promise(function(resolve60, reject) {
58945
+ var promise = new Promise(function(resolve66, reject) {
58802
58946
  var resolved = false;
58803
58947
  const wrappedResolve = function(value) {
58804
58948
  if (resolved) {
58805
58949
  console.warn("Run-async promise already resolved.");
58806
58950
  }
58807
58951
  resolved = true;
58808
- resolve60(value);
58952
+ resolve66(value);
58809
58953
  };
58810
58954
  var rejected = false;
58811
58955
  const wrappedReject = function(value) {
@@ -58913,32 +59057,32 @@ __export(otel_hooks_exports, {
58913
59057
  });
58914
59058
  function getOtelHooksHome() {
58915
59059
  if (process.env.HAR_OTEL_HOOKS_HOME) {
58916
- return path73.resolve(process.env.HAR_OTEL_HOOKS_HOME);
59060
+ return path76.resolve(process.env.HAR_OTEL_HOOKS_HOME);
58917
59061
  }
58918
- return path73.join(os22.homedir(), ".har", "otel-hooks");
59062
+ return path76.join(os22.homedir(), ".har", "otel-hooks");
58919
59063
  }
58920
59064
  function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
58921
- return path73.join(hooksHome, "otel_config.json");
59065
+ return path76.join(hooksHome, "otel_config.json");
58922
59066
  }
58923
59067
  function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
58924
- return path73.join(hooksHome, "run-otel-hook.sh");
59068
+ return path76.join(hooksHome, "run-otel-hook.sh");
58925
59069
  }
58926
59070
  function getOtelHooksStateDir(hooksHome = getOtelHooksHome()) {
58927
- return path73.join(hooksHome, "state");
59071
+ return path76.join(hooksHome, "state");
58928
59072
  }
58929
59073
  function localOtelHookBin(hooksHome) {
58930
- return path73.join(hooksHome, "node_modules", ".bin", "otel-hook");
59074
+ return path76.join(hooksHome, "node_modules", ".bin", "otel-hook");
58931
59075
  }
58932
59076
  function installedPackageVersion(hooksHome) {
58933
59077
  try {
58934
- const pkgPath = path73.join(
59078
+ const pkgPath = path76.join(
58935
59079
  hooksHome,
58936
59080
  "node_modules",
58937
59081
  "@osfactory",
58938
59082
  "otel-hook",
58939
59083
  "package.json"
58940
59084
  );
58941
- const pkg = JSON.parse(fs66.readFileSync(pkgPath, "utf8"));
59085
+ const pkg = JSON.parse(fs68.readFileSync(pkgPath, "utf8"));
58942
59086
  return typeof pkg.version === "string" ? pkg.version : null;
58943
59087
  } catch {
58944
59088
  return null;
@@ -58989,15 +59133,15 @@ function buildOtelHooksConfig(options) {
58989
59133
  };
58990
59134
  }
58991
59135
  function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
58992
- fs66.mkdirSync(hooksHome, { recursive: true });
59136
+ fs68.mkdirSync(hooksHome, { recursive: true });
58993
59137
  const configPath = getOtelHooksConfigPath(hooksHome);
58994
- fs66.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
59138
+ fs68.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
58995
59139
  `);
58996
59140
  return configPath;
58997
59141
  }
58998
59142
  function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
58999
- fs66.mkdirSync(hooksHome, { recursive: true });
59000
- fs66.mkdirSync(getOtelHooksStateDir(hooksHome), { recursive: true });
59143
+ fs68.mkdirSync(hooksHome, { recursive: true });
59144
+ fs68.mkdirSync(getOtelHooksStateDir(hooksHome), { recursive: true });
59001
59145
  const wrapperPath = getOtelHooksWrapperPath(hooksHome);
59002
59146
  const configPath = getOtelHooksConfigPath(hooksHome);
59003
59147
  const stateDir = getOtelHooksStateDir(hooksHome);
@@ -59009,9 +59153,9 @@ exec "${otelHookCommand}" run \\
59009
59153
  --state-dir "${stateDir}" \\
59010
59154
  "$@"
59011
59155
  `;
59012
- fs66.writeFileSync(wrapperPath, script, { mode: 493 });
59156
+ fs68.writeFileSync(wrapperPath, script, { mode: 493 });
59013
59157
  try {
59014
- fs66.chmodSync(wrapperPath, 493);
59158
+ fs68.chmodSync(wrapperPath, 493);
59015
59159
  } catch {
59016
59160
  }
59017
59161
  return wrapperPath;
@@ -59024,7 +59168,7 @@ function commandExists3(command2) {
59024
59168
  }
59025
59169
  function resolveOtelHookCommand(hooksHome) {
59026
59170
  const local = localOtelHookBin(hooksHome);
59027
- if (fs66.existsSync(local)) return local;
59171
+ if (fs68.existsSync(local)) return local;
59028
59172
  return null;
59029
59173
  }
59030
59174
  function resolveHooksInstaller(hooksHome) {
@@ -59039,7 +59183,7 @@ function resolveHooksInstaller(hooksHome) {
59039
59183
  return candidates.find((candidate) => commandExists3(candidate.command)) ?? null;
59040
59184
  }
59041
59185
  function tryInstallPackage(hooksHome) {
59042
- fs66.mkdirSync(hooksHome, { recursive: true });
59186
+ fs68.mkdirSync(hooksHome, { recursive: true });
59043
59187
  const installer = resolveHooksInstaller(hooksHome);
59044
59188
  if (!installer) {
59045
59189
  return {
@@ -59052,7 +59196,7 @@ function tryInstallPackage(hooksHome) {
59052
59196
  encoding: "utf8",
59053
59197
  timeout: 18e4
59054
59198
  });
59055
- if (result.status === 0 && fs66.existsSync(localOtelHookBin(hooksHome))) {
59199
+ if (result.status === 0 && fs68.existsSync(localOtelHookBin(hooksHome))) {
59056
59200
  return { ok: true, detail: described };
59057
59201
  }
59058
59202
  return {
@@ -59078,14 +59222,14 @@ function uninstallLegacyPythonHooks(hooksHome) {
59078
59222
  }
59079
59223
  function removeLegacyPythonArtifacts(hooksHome) {
59080
59224
  const legacyPaths = [
59081
- path73.join(hooksHome, ".venv"),
59082
- path73.join(hooksHome, "otel_config.yaml"),
59083
- path73.join(hooksHome, "venv")
59225
+ path76.join(hooksHome, ".venv"),
59226
+ path76.join(hooksHome, "otel_config.yaml"),
59227
+ path76.join(hooksHome, "venv")
59084
59228
  ];
59085
59229
  for (const target of legacyPaths) {
59086
59230
  try {
59087
- if (fs66.existsSync(target)) {
59088
- fs66.rmSync(target, { recursive: true, force: true });
59231
+ if (fs68.existsSync(target)) {
59232
+ fs68.rmSync(target, { recursive: true, force: true });
59089
59233
  }
59090
59234
  } catch {
59091
59235
  }
@@ -59126,10 +59270,10 @@ function runSetupProvider(providerId, otelHookCommand, wrapperPath) {
59126
59270
  };
59127
59271
  }
59128
59272
  function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
59129
- if (!fs66.existsSync(filePath)) return false;
59273
+ if (!fs68.existsSync(filePath)) return false;
59130
59274
  let raw;
59131
59275
  try {
59132
- raw = fs66.readFileSync(filePath, "utf8");
59276
+ raw = fs68.readFileSync(filePath, "utf8");
59133
59277
  } catch {
59134
59278
  return false;
59135
59279
  }
@@ -59176,15 +59320,15 @@ function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
59176
59320
  return changed;
59177
59321
  };
59178
59322
  if (!walk(parsed)) return false;
59179
- fs66.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59323
+ fs68.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59180
59324
  `);
59181
59325
  return true;
59182
59326
  }
59183
59327
  function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
59184
- if (!fs66.existsSync(filePath)) return false;
59328
+ if (!fs68.existsSync(filePath)) return false;
59185
59329
  let raw;
59186
59330
  try {
59187
- raw = fs66.readFileSync(filePath, "utf8");
59331
+ raw = fs68.readFileSync(filePath, "utf8");
59188
59332
  } catch {
59189
59333
  return false;
59190
59334
  }
@@ -59219,21 +59363,21 @@ function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
59219
59363
  }
59220
59364
  }
59221
59365
  if (!changed) return false;
59222
- fs66.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59366
+ fs68.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
59223
59367
  `);
59224
59368
  return true;
59225
59369
  }
59226
59370
  function rewriteKnownHookRegistrations(wrapperPath) {
59227
59371
  const home = os22.homedir();
59228
59372
  const candidates = [
59229
- path73.join(home, ".cursor", "hooks.json"),
59230
- path73.join(home, ".claude", "settings.json"),
59231
- path73.join(home, ".codex", "hooks.json")
59373
+ path76.join(home, ".cursor", "hooks.json"),
59374
+ path76.join(home, ".claude", "settings.json"),
59375
+ path76.join(home, ".codex", "hooks.json")
59232
59376
  ];
59233
59377
  for (const filePath of candidates) {
59234
59378
  try {
59235
59379
  rewriteHookCommandsToWrapper(filePath, wrapperPath);
59236
- if (filePath.endsWith(`${path73.sep}.cursor${path73.sep}hooks.json`)) {
59380
+ if (filePath.endsWith(`${path76.sep}.cursor${path76.sep}hooks.json`)) {
59237
59381
  pruneLegacyCursorHookEvents(filePath, wrapperPath);
59238
59382
  }
59239
59383
  } catch {
@@ -59249,7 +59393,7 @@ function ensureOtelHooks(options) {
59249
59393
  const hooksHome = getOtelHooksHome();
59250
59394
  const errors = [];
59251
59395
  const providersSetup = [];
59252
- fs66.mkdirSync(hooksHome, { recursive: true });
59396
+ fs68.mkdirSync(hooksHome, { recursive: true });
59253
59397
  let otelHookCommand = resolveOtelHookCommand(hooksHome);
59254
59398
  const needsInstall = !otelHookCommand || installedPackageVersion(hooksHome) !== pinnedVersion();
59255
59399
  if (needsInstall) {
@@ -59343,14 +59487,14 @@ function ensureOtelHooks(options) {
59343
59487
  function disableOtelHooksExport() {
59344
59488
  return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
59345
59489
  }
59346
- var import_child_process19, fs66, os22, path73, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
59490
+ var import_child_process19, fs68, os22, path76, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
59347
59491
  var init_otel_hooks = __esm({
59348
59492
  "src/core/otel-hooks.ts"() {
59349
59493
  "use strict";
59350
59494
  import_child_process19 = require("child_process");
59351
- fs66 = __toESM(require("fs"));
59495
+ fs68 = __toESM(require("fs"));
59352
59496
  os22 = __toESM(require("os"));
59353
- path73 = __toESM(require("path"));
59497
+ path76 = __toESM(require("path"));
59354
59498
  init_control_config();
59355
59499
  init_telemetry_config();
59356
59500
  OTEL_HOOKS_PACKAGE = "@osfactory/otel-hook@0.2.0";
@@ -62325,7 +62469,7 @@ var require_compile = __commonJS({
62325
62469
  const schOrFunc = root.refs[ref];
62326
62470
  if (schOrFunc)
62327
62471
  return schOrFunc;
62328
- let _sch = resolve60.call(this, root, ref);
62472
+ let _sch = resolve66.call(this, root, ref);
62329
62473
  if (_sch === void 0) {
62330
62474
  const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
62331
62475
  const { schemaId } = this.opts;
@@ -62352,7 +62496,7 @@ var require_compile = __commonJS({
62352
62496
  function sameSchemaEnv(s1, s2) {
62353
62497
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
62354
62498
  }
62355
- function resolve60(root, ref) {
62499
+ function resolve66(root, ref) {
62356
62500
  let sch;
62357
62501
  while (typeof (sch = this.refs[ref]) == "string")
62358
62502
  ref = sch;
@@ -62570,8 +62714,8 @@ var require_utils2 = __commonJS({
62570
62714
  }
62571
62715
  return ind;
62572
62716
  }
62573
- function removeDotSegments(path91) {
62574
- let input = path91;
62717
+ function removeDotSegments(path97) {
62718
+ let input = path97;
62575
62719
  const output = [];
62576
62720
  let nextSlash = -1;
62577
62721
  let len = 0;
@@ -62823,8 +62967,8 @@ var require_schemes = __commonJS({
62823
62967
  wsComponent.secure = void 0;
62824
62968
  }
62825
62969
  if (wsComponent.resourceName) {
62826
- const [path91, query] = wsComponent.resourceName.split("?");
62827
- wsComponent.path = path91 && path91 !== "/" ? path91 : void 0;
62970
+ const [path97, query] = wsComponent.resourceName.split("?");
62971
+ wsComponent.path = path97 && path97 !== "/" ? path97 : void 0;
62828
62972
  wsComponent.query = query;
62829
62973
  wsComponent.resourceName = void 0;
62830
62974
  }
@@ -62983,55 +63127,55 @@ var require_fast_uri = __commonJS({
62983
63127
  }
62984
63128
  return uri;
62985
63129
  }
62986
- function resolve60(baseURI, relativeURI, options) {
63130
+ function resolve66(baseURI, relativeURI, options) {
62987
63131
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
62988
63132
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
62989
63133
  schemelessOptions.skipEscape = true;
62990
63134
  return serialize(resolved, schemelessOptions);
62991
63135
  }
62992
- function resolveComponent(base, relative5, options, skipNormalization) {
63136
+ function resolveComponent(base, relative6, options, skipNormalization) {
62993
63137
  const target = {};
62994
63138
  if (!skipNormalization) {
62995
63139
  base = parse4(serialize(base, options), options);
62996
- relative5 = parse4(serialize(relative5, options), options);
63140
+ relative6 = parse4(serialize(relative6, options), options);
62997
63141
  }
62998
63142
  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;
63143
+ if (!options.tolerant && relative6.scheme) {
63144
+ target.scheme = relative6.scheme;
63145
+ target.userinfo = relative6.userinfo;
63146
+ target.host = relative6.host;
63147
+ target.port = relative6.port;
63148
+ target.path = removeDotSegments(relative6.path || "");
63149
+ target.query = relative6.query;
63006
63150
  } 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;
63151
+ if (relative6.userinfo !== void 0 || relative6.host !== void 0 || relative6.port !== void 0) {
63152
+ target.userinfo = relative6.userinfo;
63153
+ target.host = relative6.host;
63154
+ target.port = relative6.port;
63155
+ target.path = removeDotSegments(relative6.path || "");
63156
+ target.query = relative6.query;
63013
63157
  } else {
63014
- if (!relative5.path) {
63158
+ if (!relative6.path) {
63015
63159
  target.path = base.path;
63016
- if (relative5.query !== void 0) {
63017
- target.query = relative5.query;
63160
+ if (relative6.query !== void 0) {
63161
+ target.query = relative6.query;
63018
63162
  } else {
63019
63163
  target.query = base.query;
63020
63164
  }
63021
63165
  } else {
63022
- if (relative5.path[0] === "/") {
63023
- target.path = removeDotSegments(relative5.path);
63166
+ if (relative6.path[0] === "/") {
63167
+ target.path = removeDotSegments(relative6.path);
63024
63168
  } else {
63025
63169
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
63026
- target.path = "/" + relative5.path;
63170
+ target.path = "/" + relative6.path;
63027
63171
  } else if (!base.path) {
63028
- target.path = relative5.path;
63172
+ target.path = relative6.path;
63029
63173
  } else {
63030
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
63174
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
63031
63175
  }
63032
63176
  target.path = removeDotSegments(target.path);
63033
63177
  }
63034
- target.query = relative5.query;
63178
+ target.query = relative6.query;
63035
63179
  }
63036
63180
  target.userinfo = base.userinfo;
63037
63181
  target.host = base.host;
@@ -63039,7 +63183,7 @@ var require_fast_uri = __commonJS({
63039
63183
  }
63040
63184
  target.scheme = base.scheme;
63041
63185
  }
63042
- target.fragment = relative5.fragment;
63186
+ target.fragment = relative6.fragment;
63043
63187
  return target;
63044
63188
  }
63045
63189
  function equal(uriA, uriB, options) {
@@ -63241,7 +63385,7 @@ var require_fast_uri = __commonJS({
63241
63385
  var fastUri = {
63242
63386
  SCHEMES,
63243
63387
  normalize: normalize3,
63244
- resolve: resolve60,
63388
+ resolve: resolve66,
63245
63389
  resolveComponent,
63246
63390
  equal,
63247
63391
  serialize,
@@ -66217,12 +66361,12 @@ var require_dist = __commonJS({
66217
66361
  throw new Error(`Unknown format "${name}"`);
66218
66362
  return f;
66219
66363
  };
66220
- function addFormats(ajv, list, fs76, exportName) {
66364
+ function addFormats(ajv, list, fs79, exportName) {
66221
66365
  var _a2;
66222
66366
  var _b2;
66223
66367
  (_a2 = (_b2 = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
66224
66368
  for (const f of list)
66225
- ajv.addFormat(f, fs76[f]);
66369
+ ajv.addFormat(f, fs79[f]);
66226
66370
  }
66227
66371
  module2.exports = exports2 = formatsPlugin;
66228
66372
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -67506,11 +67650,11 @@ var parser = new YargsParser({
67506
67650
  resolve: import_path2.resolve,
67507
67651
  // TODO: figure out a way to combine ESM and CJS coverage, such that
67508
67652
  // we can exercise all the lines below:
67509
- require: (path91) => {
67653
+ require: (path97) => {
67510
67654
  if (typeof require !== "undefined") {
67511
- return require(path91);
67512
- } else if (path91.match(/\.json$/)) {
67513
- return JSON.parse((0, import_fs2.readFileSync)(path91, "utf8"));
67655
+ return require(path97);
67656
+ } else if (path97.match(/\.json$/)) {
67657
+ return JSON.parse((0, import_fs2.readFileSync)(path97, "utf8"));
67514
67658
  } else {
67515
67659
  throw Error("only .json config files are supported in ESM");
67516
67660
  }
@@ -70036,12 +70180,12 @@ var YargsInstance = class {
70036
70180
  async getCompletion(args, done) {
70037
70181
  argsert("<array> [function]", [args, done], arguments.length);
70038
70182
  if (!done) {
70039
- return new Promise((resolve60, reject) => {
70183
+ return new Promise((resolve66, reject) => {
70040
70184
  __classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
70041
70185
  if (err)
70042
70186
  reject(err);
70043
70187
  else
70044
- resolve60(completions);
70188
+ resolve66(completions);
70045
70189
  });
70046
70190
  });
70047
70191
  } else {
@@ -71274,7 +71418,7 @@ async function handleAgentSkills(options) {
71274
71418
  const shouldPrompt = !options.autoYes && !(mode === "maintain" && alreadyScaffolded);
71275
71419
  if (shouldPrompt) {
71276
71420
  const accepted = await askYesNo(
71277
- `Scaffold agent skills (/setup-har, /har-wt, /har-maintain) for: ${targets.join(", ")}? [Y/n]`
71421
+ `Scaffold agent skills (/setup-har, /har-wt, /har-maintain, /factory-line) for: ${targets.join(", ")}? [Y/n]`
71278
71422
  );
71279
71423
  if (!accepted) {
71280
71424
  info("Skipped agent skills");
@@ -71291,16 +71435,16 @@ async function handleAgentSkills(options) {
71291
71435
  }
71292
71436
  async function askYesNo(question) {
71293
71437
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
71294
- return new Promise((resolve60) => {
71438
+ return new Promise((resolve66) => {
71295
71439
  process.stderr.write(`${question} `);
71296
71440
  rl.once("line", (answer) => {
71297
71441
  rl.close();
71298
71442
  const trimmed = answer.trim();
71299
71443
  if (trimmed === "") {
71300
- resolve60(true);
71444
+ resolve66(true);
71301
71445
  return;
71302
71446
  }
71303
- resolve60(/^y(es)?$/i.test(trimmed));
71447
+ resolve66(/^y(es)?$/i.test(trimmed));
71304
71448
  });
71305
71449
  });
71306
71450
  }
@@ -71319,7 +71463,7 @@ function resolveTargets(argv, repoPath) {
71319
71463
  }
71320
71464
  var agentsCommand = {
71321
71465
  command: "agents <subcommand>",
71322
- describe: "Manage scaffolded agent skills (/setup-har, /har-wt, /har-maintain)",
71466
+ describe: "Manage scaffolded agent skills (/setup-har, /har-wt, /har-maintain, /factory-line)",
71323
71467
  builder: (yargs) => yargs.command(
71324
71468
  "install",
71325
71469
  "Scaffold agent skills into this repo (Claude/Cursor) and globally (Codex)",
@@ -71380,8 +71524,8 @@ function handleRemove(argv) {
71380
71524
  }
71381
71525
 
71382
71526
  // src/cli/commands/env.ts
71383
- var fs69 = __toESM(require("fs"));
71384
- var path77 = __toESM(require("path"));
71527
+ var fs71 = __toESM(require("fs"));
71528
+ var path80 = __toESM(require("path"));
71385
71529
 
71386
71530
  // src/cli/finish-command.ts
71387
71531
  init_sync_context();
@@ -71391,8 +71535,8 @@ async function finishCommand(code) {
71391
71535
  }
71392
71536
 
71393
71537
  // src/core/harness.ts
71394
- var fs63 = __toESM(require("fs"));
71395
- var path69 = __toESM(require("path"));
71538
+ var fs65 = __toESM(require("fs"));
71539
+ var path72 = __toESM(require("path"));
71396
71540
 
71397
71541
  // src/harness/generator.ts
71398
71542
  var fs48 = __toESM(require("fs"));
@@ -72901,13 +73045,13 @@ var PromisePolyfill = class extends Promise {
72901
73045
  // Available starting from Node 22
72902
73046
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
72903
73047
  static withResolver() {
72904
- let resolve60;
73048
+ let resolve66;
72905
73049
  let reject;
72906
73050
  const promise = new Promise((res, rej) => {
72907
- resolve60 = res;
73051
+ resolve66 = res;
72908
73052
  reject = rej;
72909
73053
  });
72910
- return { promise, resolve: resolve60, reject };
73054
+ return { promise, resolve: resolve66, reject };
72911
73055
  }
72912
73056
  };
72913
73057
 
@@ -72941,7 +73085,7 @@ function createPrompt(view) {
72941
73085
  output
72942
73086
  });
72943
73087
  const screen = new ScreenManager(rl);
72944
- const { promise, resolve: resolve60, reject } = PromisePolyfill.withResolver();
73088
+ const { promise, resolve: resolve66, reject } = PromisePolyfill.withResolver();
72945
73089
  const cancel = () => reject(new CancelPromptError());
72946
73090
  if (signal) {
72947
73091
  const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
@@ -72968,7 +73112,7 @@ function createPrompt(view) {
72968
73112
  cycle(() => {
72969
73113
  try {
72970
73114
  const nextView = view(config2, (value) => {
72971
- setImmediate(() => resolve60(value));
73115
+ setImmediate(() => resolve66(value));
72972
73116
  });
72973
73117
  if (nextView === void 0) {
72974
73118
  const callerFilename = callSites[1]?.getFileName();
@@ -74277,9 +74421,9 @@ var import_rxjs = __toESM(require_cjs(), 1);
74277
74421
  var import_run_async = __toESM(require_run_async(), 1);
74278
74422
  var import_mute_stream2 = __toESM(require_lib(), 1);
74279
74423
  var _ = {
74280
- set: (obj, path91 = "", value) => {
74424
+ set: (obj, path97 = "", value) => {
74281
74425
  let pointer = obj;
74282
- path91.split(".").forEach((key, index, arr) => {
74426
+ path97.split(".").forEach((key, index, arr) => {
74283
74427
  if (key === "__proto__" || key === "constructor")
74284
74428
  return;
74285
74429
  if (index === arr.length - 1) {
@@ -74290,8 +74434,8 @@ var _ = {
74290
74434
  pointer = pointer[key];
74291
74435
  });
74292
74436
  },
74293
- get: (obj, path91 = "", defaultValue) => {
74294
- const travel = (regexp) => String.prototype.split.call(path91, regexp).filter(Boolean).reduce(
74437
+ get: (obj, path97 = "", defaultValue) => {
74438
+ const travel = (regexp) => String.prototype.split.call(path97, regexp).filter(Boolean).reduce(
74295
74439
  // @ts-expect-error implicit any on res[key]
74296
74440
  (res, key) => res == null ? res : res[key],
74297
74441
  obj
@@ -74409,7 +74553,7 @@ var PromptsRunner = class {
74409
74553
  throw new Error(`Prompt for type ${question.type} not found`);
74410
74554
  }
74411
74555
  let cleanupSignal;
74412
- const promptFn = isPromptConstructor(prompt2) ? (q, opt) => new Promise((resolve60, reject) => {
74556
+ const promptFn = isPromptConstructor(prompt2) ? (q, opt) => new Promise((resolve66, reject) => {
74413
74557
  const { signal: signal2 } = opt;
74414
74558
  if (signal2.aborted) {
74415
74559
  reject(new AbortPromptError({ cause: signal2.reason }));
@@ -74446,7 +74590,7 @@ var PromptsRunner = class {
74446
74590
  signal2.removeEventListener("abort", abort);
74447
74591
  cleanupSignal = void 0;
74448
74592
  };
74449
- activePrompt.run().then(resolve60, reject).finally(cleanup);
74593
+ activePrompt.run().then(resolve66, reject).finally(cleanup);
74450
74594
  }) : prompt2;
74451
74595
  let cleanupModuleSignal;
74452
74596
  const { signal: moduleSignal } = this.opt;
@@ -75418,15 +75562,21 @@ function harnessExists(repoPath) {
75418
75562
  return fs53.existsSync(`${repoPath}/.har/stages.json`);
75419
75563
  }
75420
75564
 
75421
- // src/harness/plugin-resolve.ts
75565
+ // src/harness/bundle-resolve.ts
75422
75566
  var import_child_process16 = require("child_process");
75423
75567
  var fs54 = __toESM(require("fs"));
75424
75568
  var os20 = __toESM(require("os"));
75425
75569
  var path60 = __toESM(require("path"));
75426
75570
  init_paths();
75427
- var LOCAL_PLUGINS_DIR = ".har/plugins";
75428
- function hasManifest(dir) {
75429
- return fs54.existsSync(path60.join(dir, "template.manifest.json"));
75571
+ function findManifestPath(dir, config2) {
75572
+ for (const name of config2.manifestFiles) {
75573
+ const candidate = path60.join(dir, name);
75574
+ if (fs54.existsSync(candidate)) return candidate;
75575
+ }
75576
+ return null;
75577
+ }
75578
+ function hasManifest(dir, config2) {
75579
+ return findManifestPath(dir, config2) !== null;
75430
75580
  }
75431
75581
  function readPackageVersion(dir) {
75432
75582
  const pkgPath = path60.join(dir, "package.json");
@@ -75438,9 +75588,9 @@ function readPackageVersion(dir) {
75438
75588
  return void 0;
75439
75589
  }
75440
75590
  }
75441
- function readManifestId(dir) {
75442
- const manifestPath = path60.join(dir, "template.manifest.json");
75443
- if (!fs54.existsSync(manifestPath)) return void 0;
75591
+ function readManifestId(dir, config2) {
75592
+ const manifestPath = findManifestPath(dir, config2);
75593
+ if (!manifestPath) return void 0;
75444
75594
  try {
75445
75595
  const raw = JSON.parse(fs54.readFileSync(manifestPath, "utf8"));
75446
75596
  return typeof raw.id === "string" ? raw.id : void 0;
@@ -75459,47 +75609,48 @@ function looksLikeNpm(spec) {
75459
75609
  if (spec.includes("/") && !spec.startsWith("@")) return false;
75460
75610
  return /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(@.+)?$/i.test(spec);
75461
75611
  }
75462
- function resolveBundledDir(pluginId) {
75463
- const dir = path60.join(resolveTemplatesDir(), "plugins", pluginId);
75464
- return hasManifest(dir) ? dir : null;
75465
- }
75466
75612
  function expandHome(p) {
75467
75613
  if (p.startsWith("~/")) return path60.join(os20.homedir(), p.slice(2));
75468
75614
  if (p === "~") return os20.homedir();
75469
75615
  return p;
75470
75616
  }
75471
- function isLocalPluginDir(dir, repoPath) {
75472
- const localRoot = path60.resolve(repoPath, LOCAL_PLUGINS_DIR);
75473
- return path60.dirname(dir) === localRoot;
75617
+ function makeTempDir(prefix) {
75618
+ return fs54.mkdtempSync(path60.join(os20.tmpdir(), prefix));
75619
+ }
75620
+ function manifestNames(config2) {
75621
+ return config2.manifestFiles.join(" or ");
75622
+ }
75623
+ function resolveTemplatesBundleDir(id, config2) {
75624
+ const dir = path60.join(resolveTemplatesDir(), config2.templatesSubdir, id);
75625
+ return hasManifest(dir, config2) ? dir : null;
75626
+ }
75627
+ function resolveLocalDir(id, repoPath, config2) {
75628
+ const dir = path60.resolve(repoPath, config2.localDir, id);
75629
+ return hasManifest(dir, config2) ? dir : null;
75474
75630
  }
75475
- function resolvePathSpec(spec, repoPath) {
75631
+ function isLocalBundleDir(dir, repoPath, config2) {
75632
+ return path60.dirname(dir) === path60.resolve(repoPath, config2.localDir);
75633
+ }
75634
+ function resolvePathSpec(spec, repoPath, config2) {
75476
75635
  const raw = spec.startsWith("file:") ? spec.slice("file:".length) : spec;
75477
75636
  const dir = path60.resolve(expandHome(raw));
75478
75637
  if (!fs54.existsSync(dir) || !fs54.statSync(dir).isDirectory()) {
75479
- throw new Error(`Plugin path not found: ${spec}`);
75638
+ throw new Error(`${config2.label} path not found: ${spec}`);
75480
75639
  }
75481
- if (!hasManifest(dir)) {
75482
- throw new Error(`No template.manifest.json in ${dir}`);
75640
+ if (!hasManifest(dir, config2)) {
75641
+ throw new Error(`No ${manifestNames(config2)} in ${dir}`);
75483
75642
  }
75484
- const id = readManifestId(dir) ?? path60.basename(dir);
75485
75643
  return {
75486
- id,
75487
- kind: isLocalPluginDir(dir, repoPath) ? "local" : "path",
75644
+ id: readManifestId(dir, config2) ?? path60.basename(dir),
75645
+ kind: isLocalBundleDir(dir, repoPath, config2) ? "local" : "path",
75488
75646
  dir,
75489
75647
  spec,
75490
75648
  version: readPackageVersion(dir),
75491
75649
  cleanupDirs: []
75492
75650
  };
75493
75651
  }
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-");
75652
+ function resolveNpmSpec(spec, config2) {
75653
+ const tmp = makeTempDir(`har-${config2.label}-npm-`);
75503
75654
  const cleanupDirs = [tmp];
75504
75655
  try {
75505
75656
  (0, import_child_process16.execFileSync)("npm", ["pack", spec, "--pack-destination", tmp], {
@@ -75517,20 +75668,18 @@ function resolveNpmSpec(spec) {
75517
75668
  encoding: "utf8",
75518
75669
  stdio: ["ignore", "pipe", "pipe"]
75519
75670
  });
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));
75671
+ let dir = path60.join(extractDir, "package");
75672
+ if (!hasManifest(dir, config2)) {
75673
+ const nested = fs54.readdirSync(extractDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(extractDir, e.name)).find((d) => hasManifest(d, config2));
75524
75674
  if (!nested) {
75525
75675
  throw new Error(
75526
- `npm package ${spec} has no template.manifest.json (not a HAR plugin bundle)`
75676
+ `npm package ${spec} has no ${manifestNames(config2)} (not a HAR ${config2.label} bundle)`
75527
75677
  );
75528
75678
  }
75529
75679
  dir = nested;
75530
75680
  }
75531
- const id = readManifestId(dir) ?? spec.replace(/^@/, "").replace(/[/:].*/g, "");
75532
75681
  return {
75533
- id,
75682
+ id: readManifestId(dir, config2) ?? spec.replace(/^@/, "").replace(/[/:].*/g, ""),
75534
75683
  kind: "npm",
75535
75684
  dir,
75536
75685
  spec,
@@ -75542,11 +75691,11 @@ function resolveNpmSpec(spec) {
75542
75691
  fs54.rmSync(d, { recursive: true, force: true });
75543
75692
  }
75544
75693
  const message = err instanceof Error ? err.message : String(err);
75545
- throw new Error(`Failed to install plugin from npm (${spec}): ${message}`);
75694
+ throw new Error(`Failed to install ${config2.label} from npm (${spec}): ${message}`);
75546
75695
  }
75547
75696
  }
75548
- function resolveGitSpec(spec) {
75549
- const tmp = makeTempDir("har-plugin-git-");
75697
+ function resolveGitSpec(spec, config2) {
75698
+ const tmp = makeTempDir(`har-${config2.label}-git-`);
75550
75699
  const cleanupDirs = [tmp];
75551
75700
  const cloneDir = path60.join(tmp, "repo");
75552
75701
  try {
@@ -75566,19 +75715,20 @@ function resolveGitSpec(spec) {
75566
75715
  timeout: 18e4
75567
75716
  });
75568
75717
  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));
75718
+ if (!hasManifest(dir, config2)) {
75719
+ const nestedRoot = path60.join(cloneDir, config2.gitNestedDir);
75720
+ if (fs54.existsSync(nestedRoot)) {
75721
+ const child = fs54.readdirSync(nestedRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path60.join(nestedRoot, e.name)).find((d) => hasManifest(d, config2));
75573
75722
  if (child) dir = child;
75574
75723
  }
75575
75724
  }
75576
- if (!hasManifest(dir)) {
75577
- throw new Error(`Git repo ${spec} has no template.manifest.json (not a HAR plugin bundle)`);
75725
+ if (!hasManifest(dir, config2)) {
75726
+ throw new Error(
75727
+ `Git repo ${spec} has no ${manifestNames(config2)} (not a HAR ${config2.label} bundle)`
75728
+ );
75578
75729
  }
75579
- const id = readManifestId(dir) ?? path60.basename(url.replace(/\.git$/, ""));
75580
75730
  return {
75581
- id,
75731
+ id: readManifestId(dir, config2) ?? path60.basename(url.replace(/\.git$/, "")),
75582
75732
  kind: "git",
75583
75733
  dir,
75584
75734
  spec,
@@ -75590,21 +75740,21 @@ function resolveGitSpec(spec) {
75590
75740
  fs54.rmSync(d, { recursive: true, force: true });
75591
75741
  }
75592
75742
  const message = err instanceof Error ? err.message : String(err);
75593
- throw new Error(`Failed to install plugin from git (${spec}): ${message}`);
75743
+ throw new Error(`Failed to install ${config2.label} from git (${spec}): ${message}`);
75594
75744
  }
75595
75745
  }
75596
- function resolvePluginSource(spec, repoPath = ".") {
75746
+ function resolveBundleSource(spec, repoPath, config2) {
75597
75747
  const trimmed = spec.trim();
75598
75748
  if (!trimmed) {
75599
- throw new Error("Plugin spec is empty");
75749
+ throw new Error(`${config2.label} spec is empty`);
75600
75750
  }
75601
75751
  if (looksLikePath(trimmed)) {
75602
- return resolvePathSpec(trimmed, repoPath);
75752
+ return resolvePathSpec(trimmed, repoPath, config2);
75603
75753
  }
75604
75754
  if (looksLikeGit(trimmed)) {
75605
- return resolveGitSpec(trimmed);
75755
+ return resolveGitSpec(trimmed, config2);
75606
75756
  }
75607
- const bundled = resolveBundledDir(trimmed);
75757
+ const bundled = resolveTemplatesBundleDir(trimmed, config2);
75608
75758
  if (bundled) {
75609
75759
  return {
75610
75760
  id: trimmed,
@@ -75615,10 +75765,10 @@ function resolvePluginSource(spec, repoPath = ".") {
75615
75765
  cleanupDirs: []
75616
75766
  };
75617
75767
  }
75618
- const local = resolveLocalDir(trimmed, repoPath);
75768
+ const local = resolveLocalDir(trimmed, repoPath, config2);
75619
75769
  if (local) {
75620
75770
  return {
75621
- id: readManifestId(local) ?? trimmed,
75771
+ id: readManifestId(local, config2) ?? trimmed,
75622
75772
  kind: "local",
75623
75773
  dir: local,
75624
75774
  spec: trimmed,
@@ -75626,23 +75776,54 @@ function resolvePluginSource(spec, repoPath = ".") {
75626
75776
  cleanupDirs: []
75627
75777
  };
75628
75778
  }
75629
- if (looksLikeNpm(trimmed) && (trimmed.includes("@") || trimmed.includes("/"))) {
75630
- return resolveNpmSpec(trimmed);
75631
- }
75779
+ const hint = config2.unknownHint?.(repoPath) ?? "";
75632
75780
  if (looksLikeNpm(trimmed)) {
75633
75781
  try {
75634
- return resolveNpmSpec(trimmed);
75782
+ return resolveNpmSpec(trimmed, config2);
75635
75783
  } catch (npmErr) {
75784
+ if (trimmed.includes("@") || trimmed.includes("/")) throw npmErr;
75636
75785
  const npmMessage = npmErr instanceof Error ? npmErr.message : String(npmErr);
75637
75786
  throw new Error(
75638
- `Unknown plugin: ${trimmed}. ${knownPluginsHint(repoPath)} Or pass a path, npm package, or git URL. (${npmMessage})`
75787
+ `Unknown ${config2.label}: ${trimmed}. ${hint} Or pass a path, npm package, or git URL. (${npmMessage})`
75639
75788
  );
75640
75789
  }
75641
75790
  }
75642
75791
  throw new Error(
75643
- `Unknown plugin: ${trimmed}. ${knownPluginsHint(repoPath)} Or pass a path (./plugin), npm package (@org/pkg), or git URL (github:org/repo).`
75792
+ `Unknown ${config2.label}: ${trimmed}. ${hint} Or pass a path (./bundle), npm package (@org/pkg), or git URL (github:org/repo).`
75644
75793
  );
75645
75794
  }
75795
+ function listTemplateBundleIds(config2) {
75796
+ const root = path60.join(resolveTemplatesDir(), config2.templatesSubdir);
75797
+ if (!fs54.existsSync(root)) return [];
75798
+ return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name), config2)).sort();
75799
+ }
75800
+ function listLocalBundleIds(repoPath, config2) {
75801
+ const root = path60.resolve(repoPath, config2.localDir);
75802
+ if (!fs54.existsSync(root)) return [];
75803
+ return fs54.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => hasManifest(path60.join(root, name), config2)).sort();
75804
+ }
75805
+ function cleanupResolvedBundle(source) {
75806
+ for (const dir of source.cleanupDirs) {
75807
+ fs54.rmSync(dir, { recursive: true, force: true });
75808
+ }
75809
+ }
75810
+
75811
+ // src/harness/plugin-resolve.ts
75812
+ var LOCAL_PLUGINS_DIR = ".har/plugins";
75813
+ var PLUGIN_BUNDLE_CONFIG = {
75814
+ // `line.manifest.json` is accepted here on purpose: resolving a line bundle
75815
+ // lets `readPluginManifestFromDir` refuse it with a pointer at `har line add`
75816
+ // instead of a bare "not a HAR plugin bundle".
75817
+ manifestFiles: ["template.manifest.json", "line.manifest.json"],
75818
+ templatesSubdir: "plugins",
75819
+ localDir: LOCAL_PLUGINS_DIR,
75820
+ gitNestedDir: "plugins",
75821
+ label: "plugin",
75822
+ unknownHint: (repoPath) => knownPluginsHint(repoPath)
75823
+ };
75824
+ function resolvePluginSource(spec, repoPath = ".") {
75825
+ return resolveBundleSource(spec, repoPath, PLUGIN_BUNDLE_CONFIG);
75826
+ }
75646
75827
  function knownPluginsHint(repoPath) {
75647
75828
  const bundled = listBundledPluginIds();
75648
75829
  const local = listLocalPluginIds(repoPath);
@@ -75655,19 +75836,13 @@ function knownPluginsHint(repoPath) {
75655
75836
  return parts.join(" ");
75656
75837
  }
75657
75838
  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();
75839
+ return listLocalBundleIds(repoPath, PLUGIN_BUNDLE_CONFIG);
75661
75840
  }
75662
75841
  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();
75842
+ return listTemplateBundleIds(PLUGIN_BUNDLE_CONFIG);
75666
75843
  }
75667
75844
  function cleanupResolvedPlugin(source) {
75668
- for (const dir of source.cleanupDirs) {
75669
- fs54.rmSync(dir, { recursive: true, force: true });
75670
- }
75845
+ cleanupResolvedBundle(source);
75671
75846
  }
75672
75847
 
75673
75848
  // src/harness/plugin-prompt.ts
@@ -75774,13 +75949,17 @@ function resolveBundledPluginDir(pluginId) {
75774
75949
  return dir;
75775
75950
  }
75776
75951
  function readPluginManifestFromDir(pluginDir, expectedId) {
75777
- const manifestPath = path62.join(pluginDir, "template.manifest.json");
75778
- if (!fs56.existsSync(manifestPath)) {
75952
+ const manifestPath = findManifestPath(pluginDir, PLUGIN_BUNDLE_CONFIG);
75953
+ if (!manifestPath) {
75779
75954
  throw new Error(`No template.manifest.json in ${pluginDir}`);
75780
75955
  }
75781
- const parsed = PluginManifestSchema.safeParse(
75782
- JSON.parse(fs56.readFileSync(manifestPath, "utf8"))
75783
- );
75956
+ const raw = JSON.parse(fs56.readFileSync(manifestPath, "utf8"));
75957
+ if (raw.kind === LINE_BUNDLE_KIND || path62.basename(manifestPath) === "line.manifest.json") {
75958
+ throw new Error(
75959
+ `${path62.basename(manifestPath)} declares "kind": "line" \u2014 this is a factory line bundle, not a verification plugin. Install it with: har line add <spec>`
75960
+ );
75961
+ }
75962
+ const parsed = PluginManifestSchema.safeParse(raw);
75784
75963
  if (!parsed.success) {
75785
75964
  throw new Error(`Invalid plugin manifest: ${parsed.error.message}`);
75786
75965
  }
@@ -76682,8 +76861,8 @@ init_manifest();
76682
76861
  init_stages();
76683
76862
 
76684
76863
  // src/harness/doctor.ts
76685
- var fs61 = __toESM(require("fs"));
76686
- var path67 = __toESM(require("path"));
76864
+ var fs63 = __toESM(require("fs"));
76865
+ var path70 = __toESM(require("path"));
76687
76866
  init_slot_registry();
76688
76867
 
76689
76868
  // src/harness/eject.ts
@@ -76787,20 +76966,408 @@ function adoptHarness(repoPath) {
76787
76966
  // src/harness/doctor.ts
76788
76967
  init_env();
76789
76968
  init_manifest();
76969
+
76970
+ // src/harness/line-ledger.ts
76971
+ var fs60 = __toESM(require("fs"));
76972
+ var path66 = __toESM(require("path"));
76973
+ init_file_ops();
76974
+ init_manifest();
76975
+ init_schema2();
76976
+ function getLineLedgerPath(repoPath) {
76977
+ return path66.join(path66.resolve(repoPath), DEFAULT_HAR_DIR, "lines.json");
76978
+ }
76979
+ function readLineLedger(repoPath) {
76980
+ const ledgerPath = getLineLedgerPath(repoPath);
76981
+ if (!fs60.existsSync(ledgerPath)) return null;
76982
+ try {
76983
+ const parsed = LineLedgerSchema.safeParse(JSON.parse(fs60.readFileSync(ledgerPath, "utf8")));
76984
+ return parsed.success ? parsed.data : null;
76985
+ } catch {
76986
+ return null;
76987
+ }
76988
+ }
76989
+ function writeLineLedger(repoPath, ledger) {
76990
+ const ledgerPath = getLineLedgerPath(repoPath);
76991
+ const harnessDir = path66.dirname(ledgerPath);
76992
+ if (!fs60.existsSync(harnessDir)) {
76993
+ fs60.mkdirSync(harnessDir, { recursive: true });
76994
+ }
76995
+ writeFileSafe(ledgerPath, `${JSON.stringify(ledger, null, 2)}
76996
+ `);
76997
+ }
76998
+ function upsertLineLedgerEntry(repoPath, entry) {
76999
+ const existing = readLineLedger(repoPath) ?? { version: "1", lines: [] };
77000
+ const lines = existing.lines.filter((l) => l.id !== entry.id);
77001
+ lines.push(entry);
77002
+ lines.sort((a, b2) => a.id.localeCompare(b2.id));
77003
+ const ledger = { version: "1", lines };
77004
+ writeLineLedger(repoPath, ledger);
77005
+ return ledger;
77006
+ }
77007
+
77008
+ // src/harness/lines.ts
77009
+ var fs61 = __toESM(require("fs"));
77010
+ var path68 = __toESM(require("path"));
77011
+ init_logging();
77012
+
77013
+ // src/harness/line-prompt.ts
77014
+ var path67 = __toESM(require("path"));
77015
+ init_file_ops();
77016
+ init_manifest();
77017
+ function lineAdaptationPromptFile(lineId) {
77018
+ return `ADAPT-PROMPT-line-${lineId}.md`;
77019
+ }
77020
+ function buildLineAdaptationPrompt(repoPath, program, result) {
77021
+ 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");
77022
+ const gateRows = program.gate.stages.map((s) => `| \`${s.id}\` | \`${s.fromStation}\` | ${s.tier} |`).join("\n");
77023
+ const registered = result.stageIds.length > 0 ? result.stageIds.map((id) => `- \`${id}\` \u2014 registered, **not** in \`verificationStages\``).join("\n") : "- _(this line registered no extra stages)_";
77024
+ const declaredSkills = program.skills.length > 0 ? program.skills.map((s) => `- \`${s.id}\` (${s.role})${s.install ? ` \u2014 install: \`${s.install}\`` : ""}`).join("\n") : "- _(none declared)_";
77025
+ 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)_";
77026
+ const warnings = result.warnings.length > 0 ? result.warnings.map((w2) => `- \u26A0 ${w2}`).join("\n") : "- _(none)_";
77027
+ 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.";
77028
+ return `# Adapt the factory line: ${result.lineId}
77029
+
77030
+ Installed from \`${result.source}\` into \`${path67.basename(path67.resolve(repoPath))}\`.
77031
+ Program: \`${result.programPath}\`
77032
+
77033
+ ## What just happened
77034
+
77035
+ A **factory line** was installed. A line is a *program* \u2014 an ordered set of
77036
+ stations plus a cumulative gate. It is not a verification plugin:
77037
+
77038
+ ${registered}
77039
+
77040
+ \`verificationStages\` was **not** modified. Default \`har env verify --full\`
77041
+ takes exactly as long as it did before this install. ${optIn}
77042
+
77043
+ ## Stations
77044
+
77045
+ ${stations}
77046
+
77047
+ ## Cumulative gate (the ratchet)
77048
+
77049
+ A gate stage tagged \`fromStation: X\` is required at X **and every later
77050
+ station**. Adding a station must never drop an earlier station's stages.
77051
+
77052
+ | Stage | From station | Tier |
77053
+ |---|---|---|
77054
+ ${gateRows || "| _(none)_ | | |"}
77055
+
77056
+ Run it with:
77057
+
77058
+ \`\`\`bash
77059
+ har line gate <station> --line ${result.lineId}
77060
+ \`\`\`
77061
+
77062
+ Never with \`har env verify --full\` \u2014 that is the whole point of the split.
77063
+
77064
+ ## Declared dependencies (declarations, not installs)
77065
+
77066
+ Skills:
77067
+
77068
+ ${declaredSkills}
77069
+
77070
+ MCP servers:
77071
+
77072
+ ${declaredMcp}
77073
+
77074
+ HAR does not vendor skill packs or MCP servers. Check they are present, install
77075
+ them the way your agent normally does, and skip tracker steps for any MCP marked
77076
+ optional that is missing.
77077
+
77078
+ ## Warnings from install
77079
+
77080
+ ${warnings}
77081
+
77082
+ ## Your job now
77083
+
77084
+ 1. Read \`${result.programPath}\` and make the stations describe *this* repo's
77085
+ work \u2014 station titles, \`work.source\`/\`work.ids\`, waves.
77086
+ 2. Make each registered stage script real (replace TODO blocks). They live under
77087
+ \`.har/stages/\`.
77088
+ 3. Leave \`gate.cumulative: true\` and \`handoff.autonomousShip: false\` alone \u2014
77089
+ both are contract, not preference.
77090
+ 4. Do **not** add line stages to \`verificationStages\`. If a check should gate
77091
+ every verify, it belongs in a verification plugin instead
77092
+ (\`har env add-plugin\`), not on this line.
77093
+ 5. Verify the harness still passes as before: \`har env verify <slot> --full\`.
77094
+ `;
77095
+ }
77096
+ function writeLineAdaptationPrompt(repoPath, lineId, content) {
77097
+ const filePath = path67.join(getHarnessDir(repoPath), lineAdaptationPromptFile(lineId));
77098
+ writeFileSafe(filePath, content);
77099
+ return filePath;
77100
+ }
77101
+
77102
+ // src/harness/line-resolve.ts
77103
+ init_schema2();
77104
+ var LOCAL_LINES_DIR = ".har/lines";
77105
+ var LINE_BUNDLE_CONFIG = {
77106
+ manifestFiles: LINE_MANIFEST_FILES,
77107
+ templatesSubdir: "lines",
77108
+ localDir: LOCAL_LINES_DIR,
77109
+ gitNestedDir: "lines",
77110
+ label: "line",
77111
+ unknownHint: (repoPath) => knownLinesHint(repoPath)
77112
+ };
77113
+ function resolveLineSource(spec, repoPath = ".") {
77114
+ return resolveBundleSource(spec, repoPath, LINE_BUNDLE_CONFIG);
77115
+ }
77116
+ function knownLinesHint(repoPath) {
77117
+ const bundled = listBundledLineIds();
77118
+ const local = listLocalLineIds(repoPath);
77119
+ const parts = [`Bundled: ${bundled.join(", ") || "(none)"}.`];
77120
+ if (local.length > 0) {
77121
+ parts.push(`Local (${LOCAL_LINES_DIR}/): ${local.join(", ")}.`);
77122
+ } else {
77123
+ parts.push("Scaffold a project-owned one with: har line create <id>.");
77124
+ }
77125
+ return parts.join(" ");
77126
+ }
77127
+ function listLocalLineIds(repoPath = ".") {
77128
+ return listLocalBundleIds(repoPath, LINE_BUNDLE_CONFIG);
77129
+ }
77130
+ function listBundledLineIds() {
77131
+ return listTemplateBundleIds(LINE_BUNDLE_CONFIG);
77132
+ }
77133
+ function cleanupResolvedLine(source) {
77134
+ cleanupResolvedBundle(source);
77135
+ }
77136
+
77137
+ // src/harness/lines.ts
77138
+ init_schema2();
77139
+ init_schema2();
77140
+ init_stages();
77141
+ function lineDirRel(lineId) {
77142
+ return `${LOCAL_LINES_DIR}/${lineId}`;
77143
+ }
77144
+ function lineProgramPathRel(lineId) {
77145
+ return `${lineDirRel(lineId)}/line.json`;
77146
+ }
77147
+ function readLineManifestFromDir(bundleDir, expectedId) {
77148
+ const manifestPath = findManifestPath(bundleDir, LINE_BUNDLE_CONFIG);
77149
+ if (!manifestPath) {
77150
+ throw new Error(`No ${LINE_MANIFEST_FILES.join(" or ")} in ${bundleDir}`);
77151
+ }
77152
+ const raw = JSON.parse(fs61.readFileSync(manifestPath, "utf8"));
77153
+ if (raw.kind !== LINE_BUNDLE_KIND) {
77154
+ throw new Error(
77155
+ `${path68.basename(manifestPath)} is not a factory line bundle (missing "kind": "line"). Verification plugins install with: har env add-plugin <spec>`
77156
+ );
77157
+ }
77158
+ const parsed = LineManifestSchema.safeParse(raw);
77159
+ if (!parsed.success) {
77160
+ throw new Error(`Invalid line manifest: ${parsed.error.message}`);
77161
+ }
77162
+ if (expectedId && parsed.data.id !== expectedId) {
77163
+ throw new Error(`Line manifest id mismatch: expected ${expectedId}, got ${parsed.data.id}`);
77164
+ }
77165
+ return parsed.data;
77166
+ }
77167
+ function readLineProgramFromDir(bundleDir, manifest) {
77168
+ const programPath = path68.join(bundleDir, manifest.program);
77169
+ if (!fs61.existsSync(programPath)) {
77170
+ throw new Error(`Line program missing: ${manifest.program} (referenced by line.manifest.json)`);
77171
+ }
77172
+ const parsed = LineProgramSchema.safeParse(JSON.parse(fs61.readFileSync(programPath, "utf8")));
77173
+ if (!parsed.success) {
77174
+ throw new Error(`Invalid line program (${manifest.program}): ${parsed.error.message}`);
77175
+ }
77176
+ if (parsed.data.id !== manifest.id) {
77177
+ throw new Error(
77178
+ `Line program id "${parsed.data.id}" does not match manifest id "${manifest.id}"`
77179
+ );
77180
+ }
77181
+ return parsed.data;
77182
+ }
77183
+ function readInstalledLineProgram(repoPath, lineId) {
77184
+ const programPath = path68.join(path68.resolve(repoPath), lineProgramPathRel(lineId));
77185
+ if (!fs61.existsSync(programPath)) return null;
77186
+ const parsed = LineProgramSchema.safeParse(JSON.parse(fs61.readFileSync(programPath, "utf8")));
77187
+ return parsed.success ? parsed.data : null;
77188
+ }
77189
+ function ensureParentDir3(filePath) {
77190
+ fs61.mkdirSync(path68.dirname(filePath), { recursive: true });
77191
+ }
77192
+ function copyBundleFile(bundleDir, file, repoPath, force) {
77193
+ const srcPath = path68.join(bundleDir, file.src);
77194
+ const destPath = path68.join(repoPath, file.dest);
77195
+ if (!fs61.existsSync(srcPath)) {
77196
+ throw new Error(`Line file missing: ${file.src}`);
77197
+ }
77198
+ if (path68.resolve(srcPath) === path68.resolve(destPath)) {
77199
+ return file.dest;
77200
+ }
77201
+ if (fs61.existsSync(destPath) && !force) {
77202
+ throw new Error(
77203
+ `File already exists: ${file.dest}. Use --force to overwrite or remove it first.`
77204
+ );
77205
+ }
77206
+ ensureParentDir3(destPath);
77207
+ fs61.copyFileSync(srcPath, destPath);
77208
+ if (file.executable) {
77209
+ fs61.chmodSync(destPath, 493);
77210
+ }
77211
+ return file.dest;
77212
+ }
77213
+ function registerStagesOnly(repoPath, stages, force) {
77214
+ if (stages.length === 0) return;
77215
+ const registry2 = readStageRegistry(repoPath);
77216
+ const before = JSON.stringify(registry2.verificationStages ?? null);
77217
+ let nextStages = [...registry2.stages];
77218
+ for (const stage of stages) {
77219
+ const existing = nextStages.find((s) => s.id === stage.id);
77220
+ if (existing && !force) {
77221
+ throw new Error(
77222
+ `Stage "${stage.id}" already registered in .har/stages.json. Use --force to replace.`
77223
+ );
77224
+ }
77225
+ nextStages = existing ? nextStages.map((s) => s.id === stage.id ? stage : s) : [...nextStages, stage];
77226
+ }
77227
+ const updated = { ...registry2, stages: nextStages };
77228
+ const after = JSON.stringify(updated.verificationStages ?? null);
77229
+ if (before !== after) {
77230
+ throw new Error(
77231
+ "Refusing to write: applying a line changed verificationStages. Line gate stages are opt-in and run via `har line gate`."
77232
+ );
77233
+ }
77234
+ writeStageRegistry(repoPath, updated);
77235
+ }
77236
+ function assertGateStagesResolvable(program, registeredIds, repoPath) {
77237
+ const warnings = [];
77238
+ const registry2 = readStageRegistry(repoPath);
77239
+ const known = /* @__PURE__ */ new Set([...registry2.stages.map((s) => s.id), ...registeredIds]);
77240
+ for (const stage of program.gate.stages) {
77241
+ if (!known.has(stage.id)) {
77242
+ warnings.push(
77243
+ `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.`
77244
+ );
77245
+ }
77246
+ }
77247
+ return warnings;
77248
+ }
77249
+ function applyLineFromDir(repoPath, bundleDir, options, meta) {
77250
+ const resolved = path68.resolve(repoPath);
77251
+ const force = options.force ?? false;
77252
+ const warnings = [];
77253
+ const filesWritten = [];
77254
+ if (!harnessExists(resolved)) {
77255
+ throw new Error('No .har/ harness found. Run "har onboard" first.');
77256
+ }
77257
+ const manifest = readLineManifestFromDir(bundleDir);
77258
+ const program = readLineProgramFromDir(bundleDir, manifest);
77259
+ const stages = manifest.stages;
77260
+ const stageIds = stages.map((s) => s.id);
77261
+ const verificationBefore = JSON.stringify(readStageRegistry(resolved).verificationStages ?? null);
77262
+ for (const file of manifest.files) {
77263
+ filesWritten.push(copyBundleFile(bundleDir, file, resolved, force));
77264
+ }
77265
+ const programDest = path68.join(resolved, lineProgramPathRel(manifest.id));
77266
+ const programSrc = path68.join(bundleDir, manifest.program);
77267
+ if (path68.resolve(programSrc) !== path68.resolve(programDest)) {
77268
+ if (fs61.existsSync(programDest) && !force) {
77269
+ throw new Error(
77270
+ `Line already installed: ${lineProgramPathRel(manifest.id)}. Use --force to replace.`
77271
+ );
77272
+ }
77273
+ ensureParentDir3(programDest);
77274
+ fs61.copyFileSync(programSrc, programDest);
77275
+ }
77276
+ filesWritten.push(lineProgramPathRel(manifest.id));
77277
+ const manifestDest = path68.join(resolved, lineDirRel(manifest.id), "line.manifest.json");
77278
+ const manifestSrc = findManifestPath(bundleDir, LINE_BUNDLE_CONFIG);
77279
+ if (path68.resolve(manifestSrc) !== path68.resolve(manifestDest)) {
77280
+ ensureParentDir3(manifestDest);
77281
+ fs61.copyFileSync(manifestSrc, manifestDest);
77282
+ filesWritten.push(`${lineDirRel(manifest.id)}/line.manifest.json`);
77283
+ }
77284
+ registerStagesOnly(resolved, stages, force);
77285
+ const verificationAfter = JSON.stringify(readStageRegistry(resolved).verificationStages ?? null);
77286
+ if (verificationBefore !== verificationAfter) {
77287
+ throw new Error(
77288
+ "Line apply changed verificationStages \u2014 this is a bug. Line gate stages must stay off `har env verify --full`."
77289
+ );
77290
+ }
77291
+ warnings.push(...assertGateStagesResolvable(program, stageIds, resolved));
77292
+ upsertLineLedgerEntry(resolved, {
77293
+ id: manifest.id,
77294
+ source: meta.source,
77295
+ spec: meta.spec,
77296
+ version: meta.version,
77297
+ stageIds,
77298
+ programPath: lineProgramPathRel(manifest.id),
77299
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
77300
+ });
77301
+ const firstGatedStation = program.stations.find(
77302
+ (station) => program.gate.stages.some((stage) => stage.fromStation === station.id)
77303
+ )?.id ?? program.stations[0].id;
77304
+ const nextSteps = manifest.nextSteps.length > 0 ? manifest.nextSteps : [
77305
+ `har line status ${manifest.id}`,
77306
+ `har line gate ${firstGatedStation} --line ${manifest.id}`,
77307
+ `Adapt the program: ${lineProgramPathRel(manifest.id)}`
77308
+ ];
77309
+ const partial2 = {
77310
+ lineId: manifest.id,
77311
+ title: manifest.title ?? program.title,
77312
+ stageIds,
77313
+ filesWritten,
77314
+ warnings,
77315
+ nextSteps,
77316
+ programPath: lineProgramPathRel(manifest.id),
77317
+ docsPath: manifest.docsPath,
77318
+ source: meta.source,
77319
+ stationIds: program.stations.map((s) => s.id),
77320
+ firstGatedStationId: firstGatedStation
77321
+ };
77322
+ const promptAbsPath = writeLineAdaptationPrompt(
77323
+ resolved,
77324
+ manifest.id,
77325
+ buildLineAdaptationPrompt(resolved, program, { ...partial2, adaptPromptPath: "" })
77326
+ );
77327
+ const adaptPromptPath = path68.relative(resolved, promptAbsPath);
77328
+ success(`Applied line: ${manifest.id}`);
77329
+ info(`Stations: ${partial2.stationIds.join(" \u2192 ")}`);
77330
+ info(
77331
+ stageIds.length > 0 ? `Registered stage(s) (NOT on verify): ${stageIds.join(", ")}` : "No extra stages registered"
77332
+ );
77333
+ for (const file of filesWritten) {
77334
+ info(` + ${file}`);
77335
+ }
77336
+ info(` + ${adaptPromptPath} (adaptation prompt for your coding agent)`);
77337
+ for (const warning of warnings) {
77338
+ warn(` \u26A0 ${warning}`);
77339
+ }
77340
+ return { ...partial2, adaptPromptPath };
77341
+ }
77342
+ function applyLine(repoPath, lineSpec, options = {}) {
77343
+ const spec = options.spec ?? lineSpec;
77344
+ const source = resolveLineSource(spec, repoPath);
77345
+ try {
77346
+ return applyLineFromDir(repoPath, source.dir, options, {
77347
+ source: source.kind,
77348
+ spec: source.spec,
77349
+ version: source.version
77350
+ });
77351
+ } finally {
77352
+ cleanupResolvedLine(source);
77353
+ }
77354
+ }
77355
+
77356
+ // src/harness/doctor.ts
76790
77357
  init_stages();
76791
77358
  init_hooks2();
76792
77359
 
76793
77360
  // src/harness/migrations.ts
76794
- var fs60 = __toESM(require("fs"));
76795
- var path66 = __toESM(require("path"));
77361
+ var fs62 = __toESM(require("fs"));
77362
+ var path69 = __toESM(require("path"));
76796
77363
  init_schema2();
76797
77364
  init_manifest();
76798
77365
  init_profiles();
76799
77366
  init_stages();
76800
77367
  init_manifest();
76801
77368
  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");
77369
+ var MIGRATE_BACKUP_DIR = path69.join(MIGRATE_DIR, "backup");
77370
+ var MIGRATE_PLAN_FILE = path69.join(MIGRATE_DIR, "plan.json");
76804
77371
  var LEGACY_MACHINERY_FILES = [
76805
77372
  "agent-slot.sh",
76806
77373
  "provision-toolchain.sh",
@@ -76841,25 +77408,25 @@ function harnessEnvIsPre10(content) {
76841
77408
  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
77409
  }
76843
77410
  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");
77411
+ const file = path69.join(harnessDir, script);
77412
+ if (!fs62.existsSync(file)) return false;
77413
+ const content = fs62.readFileSync(file, "utf8");
76847
77414
  return !content.includes("exec har env") && !content.includes("runtime/har.cjs");
76848
77415
  }
76849
77416
  function isPre10Harness(repoPath) {
76850
- const resolved = path66.resolve(repoPath);
77417
+ const resolved = path69.resolve(repoPath);
76851
77418
  const harnessDir = getHarnessDir(resolved);
76852
- if (!fs60.existsSync(harnessDir)) return false;
77419
+ if (!fs62.existsSync(harnessDir)) return false;
76853
77420
  const manifest = readManifest(resolved);
76854
77421
  if (manifest?.ejected) return false;
76855
77422
  for (const file of LEGACY_MACHINERY_FILES) {
76856
- if (fs60.existsSync(path66.join(harnessDir, file))) return true;
77423
+ if (fs62.existsSync(path69.join(harnessDir, file))) return true;
76857
77424
  }
76858
77425
  for (const script of MANAGED_SHIM_FILES) {
76859
77426
  if (scriptIsVendored(harnessDir, script)) return true;
76860
77427
  }
76861
- const envPath = path66.join(harnessDir, "harness.env");
76862
- if (fs60.existsSync(envPath) && harnessEnvIsPre10(fs60.readFileSync(envPath, "utf8"))) {
77428
+ const envPath = path69.join(harnessDir, "harness.env");
77429
+ if (fs62.existsSync(envPath) && harnessEnvIsPre10(fs62.readFileSync(envPath, "utf8"))) {
76863
77430
  return true;
76864
77431
  }
76865
77432
  return false;
@@ -76992,10 +77559,10 @@ var SCRIPT_RESIDUE_TARGET = {
76992
77559
  "agent-cli.sh": "review"
76993
77560
  };
76994
77561
  function backupRel(file) {
76995
- return path66.join(MIGRATE_BACKUP_DIR, file);
77562
+ return path69.join(MIGRATE_BACKUP_DIR, file);
76996
77563
  }
76997
77564
  function buildPre10Plan(repoPath) {
76998
- const resolved = path66.resolve(repoPath);
77565
+ const resolved = path69.resolve(repoPath);
76999
77566
  const harnessDir = getHarnessDir(resolved);
77000
77567
  const manifest = readManifest(resolved);
77001
77568
  const profile = manifest?.profile ?? "default";
@@ -77004,7 +77571,7 @@ function buildPre10Plan(repoPath) {
77004
77571
  const deleteLifecycleScripts = [];
77005
77572
  const residue = [];
77006
77573
  for (const script of MANAGED_SHIM_FILES) {
77007
- if (!fs60.existsSync(path66.join(harnessDir, script))) continue;
77574
+ if (!fs62.existsSync(path69.join(harnessDir, script))) continue;
77008
77575
  deleteLifecycleScripts.push(script);
77009
77576
  if (scriptIsVendored(harnessDir, script)) {
77010
77577
  const target = SCRIPT_RESIDUE_TARGET[script] ?? "review";
@@ -77018,31 +77585,31 @@ function buildPre10Plan(repoPath) {
77018
77585
  }
77019
77586
  const survivingScripts = [];
77020
77587
  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));
77588
+ const full = path69.join(harnessDir, dir);
77589
+ if (!fs62.existsSync(full)) continue;
77590
+ for (const entry of fs62.readdirSync(full)) {
77591
+ if (entry.endsWith(".sh") || entry.endsWith(".mjs")) survivingScripts.push(path69.join(dir, entry));
77025
77592
  }
77026
77593
  }
77027
- for (const entry of fs60.readdirSync(harnessDir)) {
77594
+ for (const entry of fs62.readdirSync(harnessDir)) {
77028
77595
  if (!entry.endsWith(".sh")) continue;
77029
77596
  if (MANAGED_SHIM_FILES.includes(entry)) continue;
77030
77597
  if (LEGACY_MACHINERY_FILES.includes(entry)) continue;
77031
- if (!fs60.statSync(path66.join(harnessDir, entry)).isFile()) continue;
77598
+ if (!fs62.statSync(path69.join(harnessDir, entry)).isFile()) continue;
77032
77599
  survivingScripts.push(entry);
77033
77600
  }
77034
77601
  const referencedBy = (machineryFile) => {
77035
- const base = path66.basename(machineryFile).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77602
+ const base = path69.basename(machineryFile).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77036
77603
  const loadRe = new RegExp(
77037
77604
  `(?:\\bsource\\b|\\bbash\\b|\\bexec\\b|(?:^|[;&|(])\\s*\\.\\s)[^\\n#]*${base}`
77038
77605
  );
77039
77606
  return survivingScripts.filter(
77040
- (script) => fs60.readFileSync(path66.join(harnessDir, script), "utf8").split("\n").some((line) => !/^\s*#/.test(line) && loadRe.test(line))
77607
+ (script) => fs62.readFileSync(path69.join(harnessDir, script), "utf8").split("\n").some((line) => !/^\s*#/.test(line) && loadRe.test(line))
77041
77608
  );
77042
77609
  };
77043
77610
  const retireDocs = [];
77044
77611
  for (const doc of RETIRED_DOC_FILES) {
77045
- if (!fs60.existsSync(path66.join(harnessDir, doc))) continue;
77612
+ if (!fs62.existsSync(path69.join(harnessDir, doc))) continue;
77046
77613
  retireDocs.push(doc);
77047
77614
  residue.push({
77048
77615
  source: doc,
@@ -77055,8 +77622,8 @@ function buildPre10Plan(repoPath) {
77055
77622
  const editedMachinery = [];
77056
77623
  const retainMachinery = [];
77057
77624
  for (const file of LEGACY_MACHINERY_FILES) {
77058
- const full = path66.join(harnessDir, file);
77059
- if (!fs60.existsSync(full)) continue;
77625
+ const full = path69.join(harnessDir, file);
77626
+ if (!fs62.existsSync(full)) continue;
77060
77627
  const refs = referencedBy(file);
77061
77628
  if (refs.length > 0) {
77062
77629
  retainMachinery.push(file);
@@ -77070,7 +77637,7 @@ function buildPre10Plan(repoPath) {
77070
77637
  }
77071
77638
  deleteMachinery.push(file);
77072
77639
  const recorded = fileBaseline[file];
77073
- const installed = computeFileChecksum(fs60.readFileSync(full, "utf8"));
77640
+ const installed = computeFileChecksum(fs62.readFileSync(full, "utf8"));
77074
77641
  if (recorded !== void 0 && installed !== recorded) {
77075
77642
  editedMachinery.push(file);
77076
77643
  residue.push({
@@ -77082,9 +77649,9 @@ function buildPre10Plan(repoPath) {
77082
77649
  }
77083
77650
  }
77084
77651
  let env3 = null;
77085
- const envPath = path66.join(harnessDir, "harness.env");
77086
- if (fs60.existsSync(envPath)) {
77087
- const content = fs60.readFileSync(envPath, "utf8");
77652
+ const envPath = path69.join(harnessDir, "harness.env");
77653
+ if (fs62.existsSync(envPath)) {
77654
+ const content = fs62.readFileSync(envPath, "utf8");
77088
77655
  if (harnessEnvIsPre10(content) || migrateHarnessEnvContent(content).commentedKeys.length > 0) {
77089
77656
  env3 = migrateHarnessEnvContent(content);
77090
77657
  for (const fn of env3.customFunctions) {
@@ -77109,7 +77676,7 @@ function buildPre10Plan(repoPath) {
77109
77676
  for (const file of composed.keys()) {
77110
77677
  if (MANAGED_SHIM_FILES.includes(file)) continue;
77111
77678
  if (file === "gitignore.template") continue;
77112
- if (!fs60.existsSync(path66.join(harnessDir, file))) installMissing.push(file);
77679
+ if (!fs62.existsSync(path69.join(harnessDir, file))) installMissing.push(file);
77113
77680
  }
77114
77681
  let phantomVerificationIds = [];
77115
77682
  try {
@@ -77140,25 +77707,25 @@ function buildPre10Plan(repoPath) {
77140
77707
  };
77141
77708
  }
77142
77709
  function applyPre10Plan(repoPath, plan) {
77143
- const resolved = path66.resolve(repoPath);
77710
+ const resolved = path69.resolve(repoPath);
77144
77711
  const harnessDir = getHarnessDir(resolved);
77145
77712
  const effective = plan ?? buildPre10Plan(resolved);
77146
- const backupDir = path66.join(harnessDir, MIGRATE_BACKUP_DIR);
77147
- fs60.mkdirSync(backupDir, { recursive: true });
77713
+ const backupDir = path69.join(harnessDir, MIGRATE_BACKUP_DIR);
77714
+ fs62.mkdirSync(backupDir, { recursive: true });
77148
77715
  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);
77716
+ const src = path69.join(harnessDir, file);
77717
+ if (!fs62.existsSync(src)) return;
77718
+ const dest = path69.join(backupDir, file);
77719
+ fs62.mkdirSync(path69.dirname(dest), { recursive: true });
77720
+ fs62.copyFileSync(src, dest);
77154
77721
  };
77155
77722
  const written = [];
77156
77723
  const deleted = [];
77157
- const projectName = path66.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
77724
+ const projectName = path69.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
77158
77725
  const composed = composeProfileTemplateMap(effective.profile);
77159
77726
  for (const script of effective.deleteLifecycleScripts) {
77160
77727
  backup(script);
77161
- fs60.rmSync(path66.join(harnessDir, script), { force: true });
77728
+ fs62.rmSync(path69.join(harnessDir, script), { force: true });
77162
77729
  deleted.push(script);
77163
77730
  }
77164
77731
  if (stripLifecycleShimCommands(resolved)) {
@@ -77166,26 +77733,26 @@ function applyPre10Plan(repoPath, plan) {
77166
77733
  }
77167
77734
  for (const file of effective.deleteMachinery) {
77168
77735
  backup(file);
77169
- fs60.rmSync(path66.join(harnessDir, file), { force: true });
77736
+ fs62.rmSync(path69.join(harnessDir, file), { force: true });
77170
77737
  deleted.push(file);
77171
77738
  }
77172
77739
  for (const doc of effective.retireDocs ?? []) {
77173
77740
  backup(doc);
77174
77741
  }
77175
- const libDir = path66.join(harnessDir, "lib");
77176
- if (fs60.existsSync(libDir) && fs60.readdirSync(libDir).length === 0) {
77177
- fs60.rmdirSync(libDir);
77742
+ const libDir = path69.join(harnessDir, "lib");
77743
+ if (fs62.existsSync(libDir) && fs62.readdirSync(libDir).length === 0) {
77744
+ fs62.rmdirSync(libDir);
77178
77745
  deleted.push("lib/");
77179
77746
  }
77180
77747
  for (const file of effective.installMissing ?? []) {
77181
77748
  const source = composed.get(file);
77182
77749
  if (!source) continue;
77183
- const dest = path66.join(harnessDir, file);
77184
- if (fs60.existsSync(dest)) continue;
77750
+ const dest = path69.join(harnessDir, file);
77751
+ if (fs62.existsSync(dest)) continue;
77185
77752
  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);
77753
+ fs62.mkdirSync(path69.dirname(dest), { recursive: true });
77754
+ fs62.writeFileSync(dest, rendered);
77755
+ if (/\.(sh|mjs)$/.test(file)) fs62.chmodSync(dest, 493);
77189
77756
  written.push(file);
77190
77757
  }
77191
77758
  if (effective.phantomVerificationIds.length > 0) {
@@ -77200,7 +77767,7 @@ function applyPre10Plan(repoPath, plan) {
77200
77767
  }
77201
77768
  if (effective.env) {
77202
77769
  backup("harness.env");
77203
- fs60.writeFileSync(path66.join(harnessDir, "harness.env"), effective.env.content);
77770
+ fs62.writeFileSync(path69.join(harnessDir, "harness.env"), effective.env.content);
77204
77771
  written.push("harness.env");
77205
77772
  }
77206
77773
  const manifest = readManifest(resolved);
@@ -77215,8 +77782,8 @@ function applyPre10Plan(repoPath, plan) {
77215
77782
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
77216
77783
  });
77217
77784
  }
77218
- fs60.writeFileSync(
77219
- path66.join(harnessDir, MIGRATE_PLAN_FILE),
77785
+ fs62.writeFileSync(
77786
+ path69.join(harnessDir, MIGRATE_PLAN_FILE),
77220
77787
  JSON.stringify({ ...effective, appliedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
77221
77788
  );
77222
77789
  return { plan: effective, backupDir, written, deleted };
@@ -77236,33 +77803,33 @@ function pendingMigrations(repoPath) {
77236
77803
  );
77237
77804
  }
77238
77805
  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");
77806
+ const harnessDir = getHarnessDir(path69.resolve(repoPath));
77807
+ const planPath = path69.join(harnessDir, MIGRATE_PLAN_FILE);
77808
+ fs62.mkdirSync(path69.dirname(planPath), { recursive: true });
77809
+ fs62.writeFileSync(planPath, JSON.stringify(plan, null, 2) + "\n");
77243
77810
  return planPath;
77244
77811
  }
77245
77812
  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 });
77813
+ const harnessDir = getHarnessDir(path69.resolve(repoPath));
77814
+ fs62.rmSync(path69.join(harnessDir, MIGRATE_DIR), { recursive: true, force: true });
77815
+ fs62.rmSync(path69.join(harnessDir, "MIGRATE-PROMPT.md"), { force: true });
77249
77816
  }
77250
77817
 
77251
77818
  // src/harness/doctor.ts
77252
77819
  function listHarnessShellScripts(harnessDir) {
77253
77820
  const out = [];
77254
77821
  const push = (dir, prefix) => {
77255
- if (!fs61.existsSync(dir)) return;
77256
- for (const entry of fs61.readdirSync(dir).sort()) {
77822
+ if (!fs63.existsSync(dir)) return;
77823
+ for (const entry of fs63.readdirSync(dir).sort()) {
77257
77824
  if (!entry.endsWith(".sh")) continue;
77258
- const full = path67.join(dir, entry);
77259
- if (!fs61.statSync(full).isFile()) continue;
77825
+ const full = path70.join(dir, entry);
77826
+ if (!fs63.statSync(full).isFile()) continue;
77260
77827
  out.push(prefix ? `${prefix}/${entry}` : entry);
77261
77828
  }
77262
77829
  };
77263
77830
  push(harnessDir, "");
77264
- push(path67.join(harnessDir, "stages"), "stages");
77265
- push(path67.join(harnessDir, "hooks"), "hooks");
77831
+ push(path70.join(harnessDir, "stages"), "stages");
77832
+ push(path70.join(harnessDir, "hooks"), "hooks");
77266
77833
  return out;
77267
77834
  }
77268
77835
  var CHECK_LABELS = {
@@ -77275,14 +77842,15 @@ var CHECK_LABELS = {
77275
77842
  "slot-registry": "slot registry worktrees",
77276
77843
  hooks: "lifecycle hooks (.har/hooks)",
77277
77844
  "retired-machinery": "no references to retired runtime machinery",
77278
- "ejected-runtime": "ejected runtime (user-owned)"
77845
+ "ejected-runtime": "ejected runtime (user-owned)",
77846
+ "factory-lines": "factory lines stay off verify"
77279
77847
  };
77280
77848
  var PRE_1_0_FUNCTION_RE = /^\s*(har_infra_enabled|har_pg|har_node_[a-z_]+|har_pkg_exec)\s*\(\)/m;
77281
77849
  var LEGACY_TRIPLET_RE = /^export\s+HARNESS_[A-Z0-9_]+_PORT_(DEFAULT|SCAN_START|SCAN_END)=/m;
77282
77850
  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");
77851
+ const envPath = path70.join(harnessDir, "harness.env");
77852
+ if (!fs63.existsSync(envPath)) return "none";
77853
+ const content = fs63.readFileSync(envPath, "utf8");
77286
77854
  if (PRE_1_0_FUNCTION_RE.test(content) || LEGACY_TRIPLET_RE.test(content)) {
77287
77855
  return "pre-1.0";
77288
77856
  }
@@ -77292,18 +77860,18 @@ function resolveStageFile(harnessDir, stage) {
77292
77860
  if (PACKAGE_RUNTIME_KINDS.has(stage.kind) && (!stage.command && !stage.script || stagePointsAtLifecycleShim(stage))) {
77293
77861
  return null;
77294
77862
  }
77295
- if (stage.script) return path67.join(harnessDir, stage.script);
77863
+ if (stage.script) return path70.join(harnessDir, stage.script);
77296
77864
  if (stage.command) {
77297
77865
  const first = stage.command.trim().split(/\s+/)[0];
77298
77866
  if (first.startsWith("./.har/")) {
77299
- return path67.join(harnessDir, first.replace(/^\.\/\.har\//, ""));
77867
+ return path70.join(harnessDir, first.replace(/^\.\/\.har\//, ""));
77300
77868
  }
77301
77869
  if (first.startsWith(".har/")) {
77302
- return path67.join(harnessDir, first.replace(/^\.har\//, ""));
77870
+ return path70.join(harnessDir, first.replace(/^\.har\//, ""));
77303
77871
  }
77304
77872
  return null;
77305
77873
  }
77306
- return path67.join(harnessDir, "stages", `${stage.id}.sh`);
77874
+ return path70.join(harnessDir, "stages", `${stage.id}.sh`);
77307
77875
  }
77308
77876
  function checkStatus(findings, id) {
77309
77877
  const mine = findings.filter((f) => f.check === id);
@@ -77315,7 +77883,7 @@ function runDoctor(repoPath) {
77315
77883
  const harnessDir = getHarnessDir(repoPath);
77316
77884
  const findings = [];
77317
77885
  const skipped = /* @__PURE__ */ new Set();
77318
- if (!fs61.existsSync(harnessDir)) {
77886
+ if (!fs63.existsSync(harnessDir)) {
77319
77887
  return {
77320
77888
  ok: false,
77321
77889
  contract: "none",
@@ -77362,9 +77930,9 @@ function runDoctor(repoPath) {
77362
77930
  }
77363
77931
  let registry2 = null;
77364
77932
  try {
77365
- const stagesPath = path67.join(harnessDir, "stages.json");
77366
- if (fs61.existsSync(stagesPath)) {
77367
- const raw = JSON.parse(fs61.readFileSync(stagesPath, "utf8"));
77933
+ const stagesPath = path70.join(harnessDir, "stages.json");
77934
+ if (fs63.existsSync(stagesPath)) {
77935
+ const raw = JSON.parse(fs63.readFileSync(stagesPath, "utf8"));
77368
77936
  if (typeof raw !== "object" || raw === null || !Array.isArray(raw.stages)) {
77369
77937
  throw new Error("stages.json has no `stages` array \u2014 the registry is structurally invalid");
77370
77938
  }
@@ -77395,8 +77963,8 @@ function runDoctor(repoPath) {
77395
77963
  for (const stage of registry2.stages) {
77396
77964
  const file = resolveStageFile(harnessDir, stage);
77397
77965
  if (!file) continue;
77398
- const rel = path67.relative(harnessDir, file);
77399
- if (!fs61.existsSync(file)) {
77966
+ const rel = path70.relative(harnessDir, file);
77967
+ if (!fs63.existsSync(file)) {
77400
77968
  findings.push({
77401
77969
  check: "stage-files",
77402
77970
  severity: contractSeverity,
@@ -77406,7 +77974,7 @@ function runDoctor(repoPath) {
77406
77974
  });
77407
77975
  continue;
77408
77976
  }
77409
- if (rel.endsWith(".sh") && !(fs61.statSync(file).mode & 73)) {
77977
+ if (rel.endsWith(".sh") && !(fs63.statSync(file).mode & 73)) {
77410
77978
  findings.push({
77411
77979
  check: "stage-files",
77412
77980
  severity: "warning",
@@ -77429,13 +77997,13 @@ function runDoctor(repoPath) {
77429
77997
  continue;
77430
77998
  }
77431
77999
  const file = resolveStageFile(harnessDir, stage);
77432
- if (file && !fs61.existsSync(file)) {
78000
+ if (file && !fs63.existsSync(file)) {
77433
78001
  findings.push({
77434
78002
  check: "lifecycle-stages",
77435
78003
  severity: contractSeverity,
77436
- file: path67.relative(harnessDir, file),
78004
+ file: path70.relative(harnessDir, file),
77437
78005
  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`
78006
+ remedy: `Restore .har/${path70.relative(harnessDir, file)} or fix the \`${stage.id}\` entry in stages.json`
77439
78007
  });
77440
78008
  }
77441
78009
  }
@@ -77480,9 +78048,9 @@ function runDoctor(repoPath) {
77480
78048
  if (!manifest?.ejected) {
77481
78049
  skipped.add("ejected-runtime");
77482
78050
  } else {
77483
- const runtimeRel = path67.join(EJECTED_RUNTIME_DIR, EJECTED_RUNTIME_BUNDLE);
77484
- const runtimePath = path67.join(harnessDir, runtimeRel);
77485
- if (!fs61.existsSync(runtimePath)) {
78051
+ const runtimeRel = path70.join(EJECTED_RUNTIME_DIR, EJECTED_RUNTIME_BUNDLE);
78052
+ const runtimePath = path70.join(harnessDir, runtimeRel);
78053
+ if (!fs63.existsSync(runtimePath)) {
77486
78054
  findings.push({
77487
78055
  check: "ejected-runtime",
77488
78056
  severity: "error",
@@ -77495,7 +78063,7 @@ function runDoctor(repoPath) {
77495
78063
  for (const entry of listSlotRegistryEntries(repoPath)) {
77496
78064
  if (entry.status === "completed") continue;
77497
78065
  const dir = entry.worktreePath ?? entry.workDir;
77498
- if (dir && !fs61.existsSync(dir)) {
78066
+ if (dir && !fs63.existsSync(dir)) {
77499
78067
  findings.push({
77500
78068
  check: "slot-registry",
77501
78069
  severity: "warning",
@@ -77505,11 +78073,11 @@ function runDoctor(repoPath) {
77505
78073
  });
77506
78074
  }
77507
78075
  }
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;
78076
+ const hooksDir = path70.join(harnessDir, "hooks");
78077
+ if (fs63.existsSync(hooksDir)) {
78078
+ for (const file of fs63.readdirSync(hooksDir).sort()) {
78079
+ const full = path70.join(hooksDir, file);
78080
+ if (!fs63.statSync(full).isFile() || !file.endsWith(".sh")) continue;
77513
78081
  if (!LIFECYCLE_HOOKS.includes(file.replace(/\.sh$/, ""))) {
77514
78082
  findings.push({
77515
78083
  check: "hooks",
@@ -77520,7 +78088,7 @@ function runDoctor(repoPath) {
77520
78088
  });
77521
78089
  continue;
77522
78090
  }
77523
- if (!(fs61.statSync(full).mode & 73)) {
78091
+ if (!(fs63.statSync(full).mode & 73)) {
77524
78092
  findings.push({
77525
78093
  check: "hooks",
77526
78094
  severity: "warning",
@@ -77532,15 +78100,15 @@ function runDoctor(repoPath) {
77532
78100
  }
77533
78101
  }
77534
78102
  for (const rel of listHarnessShellScripts(harnessDir)) {
77535
- const full = path67.join(harnessDir, rel);
78103
+ const full = path70.join(harnessDir, rel);
77536
78104
  let content;
77537
78105
  try {
77538
- content = fs61.readFileSync(full, "utf8");
78106
+ content = fs63.readFileSync(full, "utf8");
77539
78107
  } catch {
77540
78108
  continue;
77541
78109
  }
77542
78110
  for (const machinery of LEGACY_MACHINERY_FILES) {
77543
- if (fs61.existsSync(path67.join(harnessDir, machinery))) continue;
78111
+ if (fs63.existsSync(path70.join(harnessDir, machinery))) continue;
77544
78112
  const base = machinery.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77545
78113
  const loadRe = new RegExp(
77546
78114
  `(?:\\bsource\\b|\\bbash\\b|\\bexec\\b|(?:^|[;&|(])\\s*\\.\\s)[^\\n#]*${base}`
@@ -77559,6 +78127,46 @@ function runDoctor(repoPath) {
77559
78127
  break;
77560
78128
  }
77561
78129
  }
78130
+ const lineLedger = readLineLedger(repoPath);
78131
+ if (lineLedger) {
78132
+ const verificationStages = registry2?.verificationStages ?? [];
78133
+ const registeredIds = new Set((registry2?.stages ?? []).map((stage) => stage.id));
78134
+ for (const line of lineLedger.lines) {
78135
+ for (const stageId of line.stageIds) {
78136
+ if (verificationStages.includes(stageId)) {
78137
+ findings.push({
78138
+ check: "factory-lines",
78139
+ severity: "error",
78140
+ file: "stages.json",
78141
+ message: `line "${line.id}" stage "${stageId}" is listed in verificationStages`,
78142
+ 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."
78143
+ });
78144
+ }
78145
+ }
78146
+ const program = readInstalledLineProgram(repoPath, line.id);
78147
+ if (!program) {
78148
+ findings.push({
78149
+ check: "factory-lines",
78150
+ severity: "warning",
78151
+ file: line.programPath,
78152
+ message: `line "${line.id}" is in .har/lines.json but its program is missing or invalid`,
78153
+ remedy: `Reinstall it (\`har line add ${line.id}\`) or drop the entry from .har/lines.json`
78154
+ });
78155
+ continue;
78156
+ }
78157
+ for (const gateStage of program.gate.stages) {
78158
+ if (!registeredIds.has(gateStage.id)) {
78159
+ findings.push({
78160
+ check: "factory-lines",
78161
+ severity: "warning",
78162
+ file: line.programPath,
78163
+ message: `line "${line.id}" gate stage "${gateStage.id}" (from ${gateStage.fromStation}) is not registered in stages.json`,
78164
+ remedy: "Install the plugin that provides it, add it to the line bundle, or drop the gate tag"
78165
+ });
78166
+ }
78167
+ }
78168
+ }
78169
+ }
77562
78170
  const checks = Object.keys(CHECK_LABELS).map(
77563
78171
  (id) => skipped.has(id) ? { id, label: CHECK_LABELS[id], status: "skip" } : checkStatus(findings, id)
77564
78172
  );
@@ -77604,8 +78212,8 @@ function summarizeDoctorReport(report) {
77604
78212
  }
77605
78213
 
77606
78214
  // src/harness/migrate-prompt.ts
77607
- var fs62 = __toESM(require("fs"));
77608
- var path68 = __toESM(require("path"));
78215
+ var fs64 = __toESM(require("fs"));
78216
+ var path71 = __toESM(require("path"));
77609
78217
  init_paths();
77610
78218
  init_file_ops();
77611
78219
  init_manifest();
@@ -77719,13 +78327,13 @@ function loadTemplate2() {
77719
78327
  if (!filePath) {
77720
78328
  throw new Error("Migration prompt template not found: migration-prompt.md. Run npm run build.");
77721
78329
  }
77722
- return fs62.readFileSync(filePath, "utf8");
78330
+ return fs64.readFileSync(filePath, "utf8");
77723
78331
  }
77724
78332
  function buildMigrationPrompt(plan, applied) {
77725
78333
  return loadTemplate2().replace("{{MIGRATION_SECTION}}", buildMigrationSection(plan, applied));
77726
78334
  }
77727
78335
  function writeMigrationPrompt(repoPath, content) {
77728
- const filePath = path68.join(getHarnessDir(repoPath), MIGRATE_PROMPT_FILE);
78336
+ const filePath = path71.join(getHarnessDir(repoPath), MIGRATE_PROMPT_FILE);
77729
78337
  writeFileSafe(filePath, content);
77730
78338
  return filePath;
77731
78339
  }
@@ -77733,11 +78341,11 @@ function writeMigrationPrompt(repoPath, content) {
77733
78341
  // src/core/harness.ts
77734
78342
  function listHarnessScripts(repoPath) {
77735
78343
  const harnessDir = getHarnessDir(repoPath);
77736
- if (!fs63.existsSync(harnessDir)) return [];
77737
- return fs63.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
78344
+ if (!fs65.existsSync(harnessDir)) return [];
78345
+ return fs65.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
77738
78346
  }
77739
78347
  function describeProject(repoPath) {
77740
- const resolved = path69.resolve(repoPath);
78348
+ const resolved = path72.resolve(repoPath);
77741
78349
  const manifest = readManifest(resolved);
77742
78350
  const present = harnessExists(resolved);
77743
78351
  return {
@@ -77757,8 +78365,8 @@ function describeProject(repoPath) {
77757
78365
  };
77758
78366
  }
77759
78367
  async function initHarness(options) {
77760
- const repoPath = path69.resolve(options.repoPath);
77761
- if (!fs63.existsSync(repoPath)) {
78368
+ const repoPath = path72.resolve(options.repoPath);
78369
+ if (!fs65.existsSync(repoPath)) {
77762
78370
  throw new Error(`Path not found: ${repoPath}`);
77763
78371
  }
77764
78372
  const scaffold = scaffoldHarnessBoilerplate(repoPath, {
@@ -77779,9 +78387,9 @@ async function initHarness(options) {
77779
78387
  };
77780
78388
  }
77781
78389
  async function maintainHarness(options) {
77782
- const repoPath = path69.resolve(options.repoPath);
78390
+ const repoPath = path72.resolve(options.repoPath);
77783
78391
  const harnessDir = getHarnessDir(repoPath);
77784
- if (!fs63.existsSync(harnessDir)) {
78392
+ if (!fs65.existsSync(harnessDir)) {
77785
78393
  throw new Error('No .har/ found. Run "har onboard" first.');
77786
78394
  }
77787
78395
  let migration;
@@ -77858,8 +78466,8 @@ function addPlugin(repoPath, pluginId, options = {}) {
77858
78466
  }
77859
78467
 
77860
78468
  // src/harness/adaptation-prompt.ts
77861
- var fs64 = __toESM(require("fs"));
77862
- var path70 = __toESM(require("path"));
78469
+ var fs66 = __toESM(require("fs"));
78470
+ var path73 = __toESM(require("path"));
77863
78471
  var readline5 = __toESM(require("readline"));
77864
78472
 
77865
78473
  // src/utils/clipboard.ts
@@ -77950,7 +78558,7 @@ function loadTemplate3(name) {
77950
78558
  if (!filePath) {
77951
78559
  throw new Error(`Adaptation prompt template not found: ${name}. Run npm run build.`);
77952
78560
  }
77953
- return fs64.readFileSync(filePath, "utf8");
78561
+ return fs66.readFileSync(filePath, "utf8");
77954
78562
  }
77955
78563
  function applyProfilePlaceholders(content, profile) {
77956
78564
  return content.replace(/\{\{PROFILE\}\}/g, profile).replace(/\{\{PROFILE_HINT\}\}/g, PROFILE_HINTS[profile]);
@@ -77972,7 +78580,7 @@ function buildMaintainAdaptationPrompt(_repoPath, bundleReport) {
77972
78580
  }
77973
78581
  function writeAdaptationPrompt(repoPath, content) {
77974
78582
  const harnessDir = getHarnessDir(repoPath);
77975
- const filePath = path70.join(harnessDir, ADAPTATION_PROMPT_FILE);
78583
+ const filePath = path73.join(harnessDir, ADAPTATION_PROMPT_FILE);
77976
78584
  writeFileSafe(filePath, content);
77977
78585
  return filePath;
77978
78586
  }
@@ -77991,12 +78599,12 @@ function printAdaptationPrompt(content) {
77991
78599
  }
77992
78600
  function askYesNo2(question) {
77993
78601
  const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
77994
- return new Promise((resolve60) => {
78602
+ return new Promise((resolve66) => {
77995
78603
  process.stderr.write(`${question} `);
77996
78604
  rl.once("line", (answer) => {
77997
78605
  rl.close();
77998
78606
  const trimmed = answer.trim();
77999
- resolve60(trimmed === "" || /^y(es)?$/i.test(trimmed));
78607
+ resolve66(trimmed === "" || /^y(es)?$/i.test(trimmed));
78000
78608
  });
78001
78609
  });
78002
78610
  }
@@ -78028,28 +78636,28 @@ async function offerAdaptationPromptClipboard(content, options = {}) {
78028
78636
  }
78029
78637
 
78030
78638
  // src/harness/cursor-rule.ts
78031
- var fs65 = __toESM(require("fs"));
78032
- var path71 = __toESM(require("path"));
78639
+ var fs67 = __toESM(require("fs"));
78640
+ var path74 = __toESM(require("path"));
78033
78641
  var readline6 = __toESM(require("readline"));
78034
78642
  init_file_ops();
78035
78643
  init_logging();
78036
78644
  init_paths();
78037
78645
  var CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/har-workflow.mdc";
78038
78646
  function getCursorRulePath(repoPath) {
78039
- return path71.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
78647
+ return path74.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
78040
78648
  }
78041
78649
  function isCursorWorkspace(repoPath) {
78042
- return fs65.existsSync(path71.join(repoPath, ".cursor"));
78650
+ return fs67.existsSync(path74.join(repoPath, ".cursor"));
78043
78651
  }
78044
78652
  function cursorRuleExists(repoPath) {
78045
- return fs65.existsSync(getCursorRulePath(repoPath));
78653
+ return fs67.existsSync(getCursorRulePath(repoPath));
78046
78654
  }
78047
78655
  function scaffoldCursorRule(repoPath) {
78048
78656
  const templatePath = resolveTemplateFile("cursor-rule.mdc.template");
78049
78657
  if (!templatePath) {
78050
78658
  throw new Error("Cursor rule template not found (cursor-rule.mdc.template). Run npm run build.");
78051
78659
  }
78052
- const content = fs65.readFileSync(templatePath, "utf8");
78660
+ const content = fs67.readFileSync(templatePath, "utf8");
78053
78661
  writeFileSafe(getCursorRulePath(repoPath), content);
78054
78662
  }
78055
78663
  async function handleCursorRule(options) {
@@ -78087,16 +78695,16 @@ async function promptScaffoldCursorRule(exists) {
78087
78695
  }
78088
78696
  async function askYesNo3(question) {
78089
78697
  const rl = readline6.createInterface({ input: process.stdin, output: process.stderr });
78090
- return new Promise((resolve60) => {
78698
+ return new Promise((resolve66) => {
78091
78699
  process.stderr.write(`${question} `);
78092
78700
  rl.once("line", (answer) => {
78093
78701
  rl.close();
78094
78702
  const trimmed = answer.trim();
78095
78703
  if (trimmed === "") {
78096
- resolve60(true);
78704
+ resolve66(true);
78097
78705
  return;
78098
78706
  }
78099
- resolve60(/^y(es)?$/i.test(trimmed));
78707
+ resolve66(/^y(es)?$/i.test(trimmed));
78100
78708
  });
78101
78709
  });
78102
78710
  }
@@ -78140,7 +78748,7 @@ init_control_config();
78140
78748
 
78141
78749
  // src/core/control-lifecycle.ts
78142
78750
  var import_child_process18 = require("child_process");
78143
- var path72 = __toESM(require("path"));
78751
+ var path75 = __toESM(require("path"));
78144
78752
 
78145
78753
  // src/core/control-image.ts
78146
78754
  init_package_version();
@@ -78167,10 +78775,10 @@ var CONTROL_DATA_VOLUME = "har_control_data";
78167
78775
  var CONTROL_CONTAINER_PORT = 3847;
78168
78776
  var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
78169
78777
  function resolveControlDir() {
78170
- return path72.resolve(__dirname, "..", "control");
78778
+ return path75.resolve(__dirname, "..", "control");
78171
78779
  }
78172
78780
  function resolveControlBuildContext() {
78173
- return path72.resolve(resolveControlDir(), "..");
78781
+ return path75.resolve(resolveControlDir(), "..");
78174
78782
  }
78175
78783
  function buildDockerRunArgs(options) {
78176
78784
  const name = options.containerName ?? CONTROL_CONTAINER_NAME;
@@ -78226,7 +78834,7 @@ async function startMissionControl(options) {
78226
78834
  "-t",
78227
78835
  imageRef,
78228
78836
  "-f",
78229
- path72.join(resolveControlDir(), "Dockerfile"),
78837
+ path75.join(resolveControlDir(), "Dockerfile"),
78230
78838
  resolveControlBuildContext()
78231
78839
  ]);
78232
78840
  if (buildCode !== 0) {
@@ -78373,7 +78981,7 @@ async function ensureTelemetryInfrastructure(options) {
78373
78981
  // src/core/run-service.ts
78374
78982
  init_telemetry_config();
78375
78983
  init_telemetry_env();
78376
- var path74 = __toESM(require("path"));
78984
+ var path77 = __toESM(require("path"));
78377
78985
  init_local_executor();
78378
78986
  function extractShellOutput(result) {
78379
78987
  const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
@@ -78596,12 +79204,12 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
78596
79204
  suffix: session.suffix,
78597
79205
  createdAt: session.createdAt
78598
79206
  });
78599
- const envFile = path74.join(session.workDir, `.env.agent.${options.agentId}`);
79207
+ const envFile = path77.join(session.workDir, `.env.agent.${options.agentId}`);
78600
79208
  try {
78601
79209
  appendTelemetryEnvToFile(envFile, {
78602
79210
  sessionKey,
78603
79211
  agentId: options.agentId,
78604
- repoPath: path74.resolve(options.repoPath),
79212
+ repoPath: path77.resolve(options.repoPath),
78605
79213
  workDir: session.workDir,
78606
79214
  branch: session.branch,
78607
79215
  suffix: session.suffix,
@@ -78843,9 +79451,9 @@ init_work_units();
78843
79451
  init_manifest();
78844
79452
 
78845
79453
  // src/core/cleanup-service.ts
78846
- var fs67 = __toESM(require("fs"));
79454
+ var fs69 = __toESM(require("fs"));
78847
79455
  var os23 = __toESM(require("os"));
78848
- var path75 = __toESM(require("path"));
79456
+ var path78 = __toESM(require("path"));
78849
79457
  var import_child_process20 = require("child_process");
78850
79458
  init_control_sync();
78851
79459
  init_control_repo_path();
@@ -78854,9 +79462,9 @@ init_manifest();
78854
79462
  var DEFAULT_STALE_DAYS = 7;
78855
79463
  function getWorktreesRoot() {
78856
79464
  if (process.env.HAR_WORKTREES_ROOT) {
78857
- return path75.resolve(process.env.HAR_WORKTREES_ROOT);
79465
+ return path78.resolve(process.env.HAR_WORKTREES_ROOT);
78858
79466
  }
78859
- return path75.join(process.env.HOME || os23.homedir(), "worktrees");
79467
+ return path78.join(process.env.HOME || os23.homedir(), "worktrees");
78860
79468
  }
78861
79469
  function runGit3(cwd, args) {
78862
79470
  try {
@@ -78870,7 +79478,7 @@ function runGit3(cwd, args) {
78870
79478
  }
78871
79479
  }
78872
79480
  function basenameRepo(repoPath) {
78873
- return path75.basename(canonicalizeControlRepoPath(repoPath));
79481
+ return path78.basename(canonicalizeControlRepoPath(repoPath));
78874
79482
  }
78875
79483
  function ageDaysFrom(iso) {
78876
79484
  if (!iso) return void 0;
@@ -78882,12 +79490,12 @@ function matchesKeep(candidate, keep) {
78882
79490
  if (!keep?.length) return false;
78883
79491
  for (const pin of keep) {
78884
79492
  if (pin.worktreePath && candidate.worktreePath) {
78885
- if (path75.resolve(pin.worktreePath) === path75.resolve(candidate.worktreePath)) return true;
79493
+ if (path78.resolve(pin.worktreePath) === path78.resolve(candidate.worktreePath)) return true;
78886
79494
  }
78887
79495
  if (pin.repoPath && pin.agentId !== void 0 && candidate.agentId !== void 0) {
78888
79496
  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) {
79497
+ const candidateRepo = path78.resolve(candidate.repoPath);
79498
+ if ((repo === candidateRepo || path78.resolve(pin.repoPath) === candidateRepo) && pin.agentId === candidate.agentId) {
78891
79499
  return true;
78892
79500
  }
78893
79501
  }
@@ -78942,20 +79550,20 @@ function classifySlotCandidate(repoPath, projectName, slot, options) {
78942
79550
  return candidate;
78943
79551
  }
78944
79552
  function resolveRepoForWorktree(worktreePath) {
78945
- if (!fs67.existsSync(worktreePath)) return void 0;
79553
+ if (!fs69.existsSync(worktreePath)) return void 0;
78946
79554
  const commonDir = runGit3(worktreePath, "rev-parse --git-common-dir");
78947
79555
  if (!commonDir) return void 0;
78948
- const gitRoot = path75.resolve(worktreePath, commonDir, "..");
79556
+ const gitRoot = path78.resolve(worktreePath, commonDir, "..");
78949
79557
  if (!readManifest(gitRoot)) return void 0;
78950
79558
  return canonicalizeControlRepoPath(gitRoot);
78951
79559
  }
78952
79560
  function discoverOrphanWorktrees(knownPaths) {
78953
79561
  const worktreesRoot = getWorktreesRoot();
78954
- if (!fs67.existsSync(worktreesRoot)) return [];
79562
+ if (!fs69.existsSync(worktreesRoot)) return [];
78955
79563
  const orphans = [];
78956
- for (const entry of fs67.readdirSync(worktreesRoot, { withFileTypes: true })) {
79564
+ for (const entry of fs69.readdirSync(worktreesRoot, { withFileTypes: true })) {
78957
79565
  if (!entry.isDirectory() || !entry.name.includes("-har-agent-")) continue;
78958
- const worktreePath = path75.resolve(path75.join(worktreesRoot, entry.name));
79566
+ const worktreePath = path78.resolve(path78.join(worktreesRoot, entry.name));
78959
79567
  if (knownPaths.has(worktreePath)) continue;
78960
79568
  const repoPath = resolveRepoForWorktree(worktreePath) ?? worktreesRoot;
78961
79569
  const porcelain = runGit3(worktreePath, "status --porcelain");
@@ -78980,14 +79588,14 @@ async function discoverCleanupCandidates(options = {}) {
78980
79588
  const candidates = [];
78981
79589
  const knownWorktreePaths = /* @__PURE__ */ new Set();
78982
79590
  for (const repoPath of repoPaths) {
78983
- if (!fs67.existsSync(repoPath) || !readManifest(repoPath)) continue;
79591
+ if (!fs69.existsSync(repoPath) || !readManifest(repoPath)) continue;
78984
79592
  const harnessRoot = resolveHarnessRoot(repoPath);
78985
79593
  const canonicalRepo = canonicalizeControlRepoPath(harnessRoot);
78986
79594
  const status = collectEnvironmentStatus(harnessRoot);
78987
79595
  const projectName = basenameRepo(harnessRoot);
78988
79596
  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));
79597
+ if (slot.worktreePath) knownWorktreePaths.add(path78.resolve(slot.worktreePath));
79598
+ if (slot.workDir) knownWorktreePaths.add(path78.resolve(slot.workDir));
78991
79599
  const candidate = classifySlotCandidate(canonicalRepo, projectName, slot, {
78992
79600
  staleDays: options.staleDays,
78993
79601
  keep: options.keep
@@ -79047,8 +79655,8 @@ async function askYesNo4(question) {
79047
79655
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
79048
79656
  const readline8 = await import("readline");
79049
79657
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
79050
- const answer = await new Promise((resolve60) => {
79051
- rl.question(question, resolve60);
79658
+ const answer = await new Promise((resolve66) => {
79659
+ rl.question(question, resolve66);
79052
79660
  });
79053
79661
  rl.close();
79054
79662
  return /^[Yy]$/.test(answer.trim());
@@ -79093,7 +79701,7 @@ async function executeCleanupCandidates(candidates, options = {}) {
79093
79701
  continue;
79094
79702
  }
79095
79703
  if (candidate.kind === "orphan_worktree" && candidate.worktreePath) {
79096
- if (fs67.existsSync(candidate.worktreePath)) {
79704
+ if (fs69.existsSync(candidate.worktreePath)) {
79097
79705
  const repoRoot = candidate.repoPath !== getWorktreesRoot() ? candidate.repoPath : resolveRepoForWorktree(candidate.worktreePath);
79098
79706
  if (repoRoot) {
79099
79707
  try {
@@ -79104,8 +79712,8 @@ async function executeCleanupCandidates(candidates, options = {}) {
79104
79712
  } catch {
79105
79713
  }
79106
79714
  }
79107
- if (fs67.existsSync(candidate.worktreePath)) {
79108
- fs67.rmSync(candidate.worktreePath, { recursive: true, force: true });
79715
+ if (fs69.existsSync(candidate.worktreePath)) {
79716
+ fs69.rmSync(candidate.worktreePath, { recursive: true, force: true });
79109
79717
  }
79110
79718
  }
79111
79719
  outcomes.push({ candidate, ok: true });
@@ -79131,7 +79739,7 @@ function parseCleanupKeepPins(values) {
79131
79739
  }
79132
79740
  if (raw.startsWith("/") || raw.startsWith("~")) {
79133
79741
  return {
79134
- worktreePath: raw.startsWith("~") ? path75.join(process.env.HOME || os23.homedir(), raw.slice(2)) : path75.resolve(raw)
79742
+ worktreePath: raw.startsWith("~") ? path78.join(process.env.HOME || os23.homedir(), raw.slice(2)) : path78.resolve(raw)
79135
79743
  };
79136
79744
  }
79137
79745
  return { repoPath: raw };
@@ -79145,12 +79753,12 @@ init_stages();
79145
79753
  init_logging();
79146
79754
  function askYesNo5(question) {
79147
79755
  const rl = readline7.createInterface({ input: process.stdin, output: process.stderr });
79148
- return new Promise((resolve60) => {
79756
+ return new Promise((resolve66) => {
79149
79757
  process.stderr.write(`${question} `);
79150
79758
  rl.once("line", (answer) => {
79151
79759
  rl.close();
79152
79760
  const trimmed = answer.trim();
79153
- resolve60(trimmed === "" || /^y(es)?$/i.test(trimmed));
79761
+ resolve66(trimmed === "" || /^y(es)?$/i.test(trimmed));
79154
79762
  });
79155
79763
  });
79156
79764
  }
@@ -79251,21 +79859,21 @@ function warnIfDockerUnavailable(status = detectDockerStatus()) {
79251
79859
  }
79252
79860
 
79253
79861
  // src/core/onboarding-preferences.ts
79254
- var fs68 = __toESM(require("fs"));
79862
+ var fs70 = __toESM(require("fs"));
79255
79863
  var os24 = __toESM(require("os"));
79256
- var path76 = __toESM(require("path"));
79864
+ var path79 = __toESM(require("path"));
79257
79865
  init_schema2();
79258
79866
  function getOnboardingPreferencesPath() {
79259
79867
  if (process.env.HAR_PREFERENCES_PATH) {
79260
- return path76.resolve(process.env.HAR_PREFERENCES_PATH);
79868
+ return path79.resolve(process.env.HAR_PREFERENCES_PATH);
79261
79869
  }
79262
- return path76.join(os24.homedir(), ".har", "preferences.json");
79870
+ return path79.join(os24.homedir(), ".har", "preferences.json");
79263
79871
  }
79264
79872
  function readOnboardingPreferences() {
79265
79873
  const preferencePath = getOnboardingPreferencesPath();
79266
79874
  try {
79267
- if (!fs68.existsSync(preferencePath)) return OnboardingPreferencesSchema.parse({});
79268
- return OnboardingPreferencesSchema.parse(JSON.parse(fs68.readFileSync(preferencePath, "utf8")));
79875
+ if (!fs70.existsSync(preferencePath)) return OnboardingPreferencesSchema.parse({});
79876
+ return OnboardingPreferencesSchema.parse(JSON.parse(fs70.readFileSync(preferencePath, "utf8")));
79269
79877
  } catch {
79270
79878
  return OnboardingPreferencesSchema.parse({});
79271
79879
  }
@@ -79290,8 +79898,8 @@ function writeOnboardingPreferences(overrides) {
79290
79898
  };
79291
79899
  const parsed = OnboardingPreferencesSchema.parse(preference);
79292
79900
  const preferencePath = getOnboardingPreferencesPath();
79293
- fs68.mkdirSync(path76.dirname(preferencePath), { recursive: true });
79294
- fs68.writeFileSync(preferencePath, `${JSON.stringify(parsed, null, 2)}
79901
+ fs70.mkdirSync(path79.dirname(preferencePath), { recursive: true });
79902
+ fs70.writeFileSync(preferencePath, `${JSON.stringify(parsed, null, 2)}
79295
79903
  `);
79296
79904
  return parsed;
79297
79905
  }
@@ -79677,7 +80285,7 @@ var envCommand = {
79677
80285
  }
79678
80286
  };
79679
80287
  async function handleInit(argv) {
79680
- const repoPath = path77.resolve(argv.repo);
80288
+ const repoPath = path80.resolve(argv.repo);
79681
80289
  const onboarding = resolveOnboardingOptions(argv);
79682
80290
  header("har env init");
79683
80291
  info(`Repository: ${repoPath}`);
@@ -79724,7 +80332,7 @@ async function handleInit(argv) {
79724
80332
  }
79725
80333
  }
79726
80334
  async function handleMaintain(argv) {
79727
- const repoPath = path77.resolve(argv.repo);
80335
+ const repoPath = path80.resolve(argv.repo);
79728
80336
  const onboarding = resolveOnboardingOptions(argv);
79729
80337
  header("har env maintain");
79730
80338
  info(`Repository: ${repoPath}`);
@@ -79879,7 +80487,7 @@ async function applyAgentIntegrations(options) {
79879
80487
  }
79880
80488
  }
79881
80489
  async function handleAddPlugin(argv) {
79882
- const repoPath = path77.resolve(argv.repo);
80490
+ const repoPath = path80.resolve(argv.repo);
79883
80491
  const available = listPluginIds();
79884
80492
  if (argv.list) {
79885
80493
  for (const id of available) {
@@ -79915,7 +80523,7 @@ async function handleAddPlugin(argv) {
79915
80523
  console.error(` Docs: ${result.docsPath}`);
79916
80524
  console.error("");
79917
80525
  info(`Adaptation prompt saved to ${result.adaptPromptPath}`);
79918
- const promptContent = fs69.readFileSync(path77.join(repoPath, result.adaptPromptPath), "utf8");
80526
+ const promptContent = fs71.readFileSync(path80.join(repoPath, result.adaptPromptPath), "utf8");
79919
80527
  await offerAdaptationPromptClipboard(promptContent, { fileLabel: result.adaptPromptPath });
79920
80528
  } catch (err) {
79921
80529
  error(err.message);
@@ -79957,7 +80565,7 @@ async function handleAddStage(argv) {
79957
80565
  });
79958
80566
  }
79959
80567
  async function handlePreflight(argv) {
79960
- const repo = path77.resolve(argv.repo);
80568
+ const repo = path80.resolve(argv.repo);
79961
80569
  const agentId = validateAgentId(argv.id, repo);
79962
80570
  const result = await preflightEnvironment({
79963
80571
  repoPath: repo,
@@ -79973,7 +80581,7 @@ async function handlePreflight(argv) {
79973
80581
  return finishCommand(result.code);
79974
80582
  }
79975
80583
  async function handleLaunch(argv) {
79976
- const repo = path77.resolve(argv.repo);
80584
+ const repo = path80.resolve(argv.repo);
79977
80585
  const agentId = validateAgentId(argv.id, repo);
79978
80586
  const result = await launchEnvironment({
79979
80587
  repoPath: repo,
@@ -80015,7 +80623,7 @@ async function handleWorkLink(argv) {
80015
80623
  error("Provide --link or both --source and --url");
80016
80624
  return finishCommand(1);
80017
80625
  }
80018
- const harnessRoot = resolveHarnessRoot(path77.resolve(argv.repo));
80626
+ const harnessRoot = resolveHarnessRoot(path80.resolve(argv.repo));
80019
80627
  const links = argv.link ? [parseWorkLinkSpec(argv.link)] : [{ source: argv.source, url: argv.url, label: argv.label }];
80020
80628
  try {
80021
80629
  const record2 = addWorkUnitLinks(harnessRoot, argv.workId, links);
@@ -80041,7 +80649,7 @@ async function handleRecover(argv) {
80041
80649
  });
80042
80650
  }
80043
80651
  async function handleVerify(argv) {
80044
- const repo = path77.resolve(argv.repo);
80652
+ const repo = path80.resolve(argv.repo);
80045
80653
  const agentId = validateAgentId(argv.id, repo);
80046
80654
  const result = await runVerification({
80047
80655
  repoPath: repo,
@@ -80070,7 +80678,7 @@ async function handleVerify(argv) {
80070
80678
  return finishCommand(result.code);
80071
80679
  }
80072
80680
  async function handleTeardown(argv) {
80073
- const repo = path77.resolve(argv.repo);
80681
+ const repo = path80.resolve(argv.repo);
80074
80682
  const agentId = validateAgentId(argv.id, repo);
80075
80683
  const result = await teardownEnvironment({
80076
80684
  repoPath: repo,
@@ -80081,7 +80689,7 @@ async function handleTeardown(argv) {
80081
80689
  return finishCommand(result.code);
80082
80690
  }
80083
80691
  async function handleComplete(argv) {
80084
- const repo = path77.resolve(argv.repo);
80692
+ const repo = path80.resolve(argv.repo);
80085
80693
  const agentId = validateAgentId(argv.id, repo);
80086
80694
  const result = await completeEnvironment({
80087
80695
  repoPath: repo,
@@ -80102,7 +80710,7 @@ async function handleComplete(argv) {
80102
80710
  return finishCommand(result.code);
80103
80711
  }
80104
80712
  async function handleSetupInfra(argv) {
80105
- const repo = path77.resolve(argv.repo);
80713
+ const repo = path80.resolve(argv.repo);
80106
80714
  const result = await runStage({
80107
80715
  repoPath: repo,
80108
80716
  kind: "setup",
@@ -80112,7 +80720,7 @@ async function handleSetupInfra(argv) {
80112
80720
  return finishCommand(result.code ?? (result.status === "pass" ? 0 : 1));
80113
80721
  }
80114
80722
  async function handleAgent(argv) {
80115
- const repo = path77.resolve(argv.repo);
80723
+ const repo = path80.resolve(argv.repo);
80116
80724
  const agentId = validateAgentId(argv.id, repo);
80117
80725
  const result = await runAgentOp({
80118
80726
  repoPath: repo,
@@ -80123,7 +80731,7 @@ async function handleAgent(argv) {
80123
80731
  return finishCommand(result.code);
80124
80732
  }
80125
80733
  async function handleDoctor(argv) {
80126
- const repoPath = path77.resolve(argv.repo);
80734
+ const repoPath = path80.resolve(argv.repo);
80127
80735
  const report = runDoctor(repoPath);
80128
80736
  if (argv.json) {
80129
80737
  console.log(JSON.stringify(report, null, 2));
@@ -80147,12 +80755,12 @@ async function confirm(question) {
80147
80755
  if (!process.stdin.isTTY) return false;
80148
80756
  const readline8 = await import("readline");
80149
80757
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
80150
- const answer = await new Promise((resolve60) => rl.question(question, resolve60));
80758
+ const answer = await new Promise((resolve66) => rl.question(question, resolve66));
80151
80759
  rl.close();
80152
80760
  return /^y(es)?$/i.test(answer.trim());
80153
80761
  }
80154
80762
  async function handleEject(argv) {
80155
- const repoPath = resolveHarnessRoot(path77.resolve(argv.repo));
80763
+ const repoPath = resolveHarnessRoot(path80.resolve(argv.repo));
80156
80764
  header("har env eject");
80157
80765
  warn("This vendors the complete HAR runtime into .har/runtime/ \u2014 from then on");
80158
80766
  warn("YOU OWN that directory:");
@@ -80183,7 +80791,7 @@ async function handleEject(argv) {
80183
80791
  return finishCommand(0);
80184
80792
  }
80185
80793
  async function handleAdopt(argv) {
80186
- const repoPath = resolveHarnessRoot(path77.resolve(argv.repo));
80794
+ const repoPath = resolveHarnessRoot(path80.resolve(argv.repo));
80187
80795
  try {
80188
80796
  const result = adoptHarness(repoPath);
80189
80797
  success("Returned to the packaged runtime \u2014 .har/runtime/ removed, eject flag cleared.");
@@ -80198,7 +80806,7 @@ async function handleAdopt(argv) {
80198
80806
  return finishCommand(0);
80199
80807
  }
80200
80808
  async function handleStatus(argv) {
80201
- const repoPath = path77.resolve(argv.repo);
80809
+ const repoPath = path80.resolve(argv.repo);
80202
80810
  const result = await getEnvironmentStatus({
80203
80811
  repoPath,
80204
80812
  capture: false
@@ -80211,7 +80819,7 @@ async function handleStatus(argv) {
80211
80819
  if (result.stdout) process.stdout.write(result.stdout);
80212
80820
  }
80213
80821
  async function handleLogs(argv) {
80214
- const repo = path77.resolve(argv.repo);
80822
+ const repo = path80.resolve(argv.repo);
80215
80823
  const agentId = validateAgentId(argv.id, repo);
80216
80824
  const result = await getEnvironmentLogs({
80217
80825
  repoPath: repo,
@@ -80223,7 +80831,7 @@ async function handleLogs(argv) {
80223
80831
  return finishCommand(result.code);
80224
80832
  }
80225
80833
  async function handleRunStage(argv) {
80226
- const repo = path77.resolve(argv.repo);
80834
+ const repo = path80.resolve(argv.repo);
80227
80835
  const agentId = validateAgentId(argv.id, repo);
80228
80836
  if (!argv.stage) {
80229
80837
  error("Missing stage id. Usage: har env run-stage <id> <stage> [args..]");
@@ -80249,7 +80857,7 @@ async function handleRunStage(argv) {
80249
80857
  }
80250
80858
  async function handleArtifacts(argv) {
80251
80859
  const artifacts = listArtifacts({
80252
- repoPath: path77.resolve(argv.repo),
80860
+ repoPath: path80.resolve(argv.repo),
80253
80861
  stageId: argv.stage
80254
80862
  });
80255
80863
  if (argv.json) {
@@ -80268,7 +80876,7 @@ async function handleArtifacts(argv) {
80268
80876
  async function handleCleanup(argv) {
80269
80877
  const keepValues = argv.keep?.split(",").map((value) => value.trim()).filter(Boolean);
80270
80878
  const keep = parseCleanupKeepPins(keepValues);
80271
- const repoPaths = argv.repo ? [path77.resolve(argv.repo)] : void 0;
80879
+ const repoPaths = argv.repo ? [path80.resolve(argv.repo)] : void 0;
80272
80880
  const plan = await discoverCleanupCandidates({
80273
80881
  cwd: process.cwd(),
80274
80882
  repoPaths,
@@ -80317,7 +80925,7 @@ async function handleCleanup(argv) {
80317
80925
  return finishCommand(failed > 0 ? 1 : 0);
80318
80926
  }
80319
80927
  async function handleRunsList(argv) {
80320
- const repoPath = path77.resolve(argv.repo);
80928
+ const repoPath = path80.resolve(argv.repo);
80321
80929
  const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
80322
80930
  if (argv.json) {
80323
80931
  process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
@@ -80334,7 +80942,7 @@ async function handleRunsList(argv) {
80334
80942
  }
80335
80943
  }
80336
80944
  async function handleRunsGet(argv) {
80337
- const run2 = getRun(path77.resolve(argv.repo), argv.runId);
80945
+ const run2 = getRun(path80.resolve(argv.repo), argv.runId);
80338
80946
  if (!run2) {
80339
80947
  error(`Run not found: ${argv.runId}`);
80340
80948
  return finishCommand(1);
@@ -80451,7 +81059,7 @@ function printNextSteps() {
80451
81059
  }
80452
81060
 
80453
81061
  // src/cli/commands/control.ts
80454
- var path82 = __toESM(require("path"));
81062
+ var path85 = __toESM(require("path"));
80455
81063
  init_control_config();
80456
81064
  init_portal_targets();
80457
81065
 
@@ -80459,8 +81067,8 @@ init_portal_targets();
80459
81067
  init_control_config();
80460
81068
 
80461
81069
  // src/core/portal-connect.ts
80462
- var fs70 = __toESM(require("fs"));
80463
- var path78 = __toESM(require("path"));
81070
+ var fs72 = __toESM(require("fs"));
81071
+ var path81 = __toESM(require("path"));
80464
81072
  var import_crypto2 = require("crypto");
80465
81073
  init_control_repo_path();
80466
81074
  init_control_registry();
@@ -80506,7 +81114,7 @@ async function loginViaBrowser(portalUrl, openUrlOrOptions = openBrowser) {
80506
81114
  const options = typeof openUrlOrOptions === "function" ? { openUrl: openUrlOrOptions } : openUrlOrOptions;
80507
81115
  const openUrl = options.openUrl ?? openBrowser;
80508
81116
  const state = (0, import_crypto.randomUUID)();
80509
- return new Promise((resolve60, reject) => {
81117
+ return new Promise((resolve66, reject) => {
80510
81118
  let timer;
80511
81119
  const server = http.createServer((req, res) => {
80512
81120
  const requestUrl = new URL(req.url ?? "", "http://127.0.0.1");
@@ -80534,7 +81142,7 @@ async function loginViaBrowser(portalUrl, openUrlOrOptions = openBrowser) {
80534
81142
  res.end("<h1>Logged in</h1><p>You can close this tab and return to the terminal.</p>");
80535
81143
  cleanup();
80536
81144
  const targets = parseCliTargetsParam(requestUrl.searchParams.get("targets"));
80537
- resolve60({
81145
+ resolve66({
80538
81146
  portalUrl,
80539
81147
  token,
80540
81148
  workspace: workspaceSlug ?? workspace,
@@ -80629,8 +81237,8 @@ function remainingConnections(connections, usedAliases) {
80629
81237
  // src/core/portal-connect.ts
80630
81238
  init_portal_targets();
80631
81239
  function attachRepoIfPresent(alias, repoPath) {
80632
- const resolved = path78.resolve(repoPath ?? ".");
80633
- if (!fs70.existsSync(resolved)) return null;
81240
+ const resolved = path81.resolve(repoPath ?? ".");
81241
+ if (!fs72.existsSync(resolved)) return null;
80634
81242
  const canonical = canonicalizeControlRepoPath(resolved);
80635
81243
  attachRepoPortalTarget(canonical, alias);
80636
81244
  recordRepoForControlSync(canonical);
@@ -80697,8 +81305,8 @@ async function runPortalConnect(input) {
80697
81305
  return { record: record2, attachedRepo, extraAttached, mappedInBrowser: true };
80698
81306
  }
80699
81307
  function listRegisteredReposForConnect(repoPath) {
80700
- const resolved = repoPath ? path78.resolve(repoPath) : null;
80701
- const current = resolved && fs70.existsSync(resolved) ? canonicalizeControlRepoPath(resolved) : null;
81308
+ const resolved = repoPath ? path81.resolve(repoPath) : null;
81309
+ const current = resolved && fs72.existsSync(resolved) ? canonicalizeControlRepoPath(resolved) : null;
80702
81310
  return [.../* @__PURE__ */ new Set([...current ? [current] : [], ...listRegisteredRepos()])];
80703
81311
  }
80704
81312
 
@@ -81014,21 +81622,21 @@ init_control_port();
81014
81622
  init_control_sync();
81015
81623
 
81016
81624
  // src/core/control-sync-selection.ts
81017
- var fs71 = __toESM(require("fs"));
81625
+ var fs73 = __toESM(require("fs"));
81018
81626
  var os25 = __toESM(require("os"));
81019
- var path79 = __toESM(require("path"));
81627
+ var path82 = __toESM(require("path"));
81020
81628
  init_control_repo_path();
81021
81629
  function getSelectionPath() {
81022
81630
  if (process.env.HAR_CONTROL_SYNC_SELECTION_PATH) {
81023
- return path79.resolve(process.env.HAR_CONTROL_SYNC_SELECTION_PATH);
81631
+ return path82.resolve(process.env.HAR_CONTROL_SYNC_SELECTION_PATH);
81024
81632
  }
81025
- return path79.join(os25.homedir(), ".har", "sync-selection.json");
81633
+ return path82.join(os25.homedir(), ".har", "sync-selection.json");
81026
81634
  }
81027
81635
  function readSyncSelection() {
81028
81636
  const selectionPath = getSelectionPath();
81029
81637
  try {
81030
- if (!fs71.existsSync(selectionPath)) return null;
81031
- const parsed = JSON.parse(fs71.readFileSync(selectionPath, "utf8"));
81638
+ if (!fs73.existsSync(selectionPath)) return null;
81639
+ const parsed = JSON.parse(fs73.readFileSync(selectionPath, "utf8"));
81032
81640
  if (!Array.isArray(parsed.repos)) return null;
81033
81641
  return parsed.repos.map((repoPath) => canonicalizeControlRepoPath(repoPath));
81034
81642
  } catch {
@@ -81038,8 +81646,8 @@ function readSyncSelection() {
81038
81646
  function writeSyncSelection(repos) {
81039
81647
  const selectionPath = getSelectionPath();
81040
81648
  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");
81649
+ fs73.mkdirSync(path82.dirname(selectionPath), { recursive: true });
81650
+ fs73.writeFileSync(selectionPath, JSON.stringify({ repos: canonical }, null, 2) + "\n");
81043
81651
  }
81044
81652
  function resolveSyncSelection(input) {
81045
81653
  const { discovered, stored, forceSelect, isTTY } = input;
@@ -81066,8 +81674,8 @@ function resolveSyncSelection(input) {
81066
81674
  }
81067
81675
 
81068
81676
  // src/core/control-unregister.ts
81069
- var fs72 = __toESM(require("fs"));
81070
- var path80 = __toESM(require("path"));
81677
+ var fs74 = __toESM(require("fs"));
81678
+ var path83 = __toESM(require("path"));
81071
81679
  init_control_repo_path();
81072
81680
  init_control_registry();
81073
81681
  init_control_config();
@@ -81077,9 +81685,9 @@ function listUnregisterWorktreeCandidates(repoPath) {
81077
81685
  const harnessRoot = canonicalizeControlRepoPath(repoPath);
81078
81686
  const byPath = /* @__PURE__ */ new Map();
81079
81687
  const add = (partial2) => {
81080
- const resolved = path80.resolve(partial2.worktreePath);
81688
+ const resolved = path83.resolve(partial2.worktreePath);
81081
81689
  if (!resolved || resolved === harnessRoot) return;
81082
- const exists = partial2.exists ?? fs72.existsSync(resolved);
81690
+ const exists = partial2.exists ?? fs74.existsSync(resolved);
81083
81691
  const prev = byPath.get(resolved);
81084
81692
  if (prev) {
81085
81693
  byPath.set(resolved, {
@@ -81130,8 +81738,8 @@ async function askYesNo6(question) {
81130
81738
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
81131
81739
  const readline8 = await import("readline");
81132
81740
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
81133
- const answer = await new Promise((resolve60) => {
81134
- rl.question(question, resolve60);
81741
+ const answer = await new Promise((resolve66) => {
81742
+ rl.question(question, resolve66);
81135
81743
  });
81136
81744
  rl.close();
81137
81745
  return /^[Yy]$/.test(answer.trim());
@@ -81193,28 +81801,28 @@ async function teardownWorktrees(repoPath, candidates, dryRun) {
81193
81801
  trigger: "cli"
81194
81802
  });
81195
81803
  if (result.code !== 0) {
81196
- if (fs72.existsSync(candidate.worktreePath)) {
81197
- fs72.rmSync(candidate.worktreePath, { recursive: true, force: true });
81804
+ if (fs74.existsSync(candidate.worktreePath)) {
81805
+ fs74.rmSync(candidate.worktreePath, { recursive: true, force: true });
81198
81806
  }
81199
81807
  }
81200
81808
  outcomes.push({
81201
81809
  path: candidate.worktreePath,
81202
81810
  agentId: candidate.agentId,
81203
- deleted: !fs72.existsSync(candidate.worktreePath),
81204
- error: result.code !== 0 && fs72.existsSync(candidate.worktreePath) ? result.stderr : void 0
81811
+ deleted: !fs74.existsSync(candidate.worktreePath),
81812
+ error: result.code !== 0 && fs74.existsSync(candidate.worktreePath) ? result.stderr : void 0
81205
81813
  });
81206
81814
  } catch (err) {
81207
81815
  const message = err instanceof Error ? err.message : String(err);
81208
81816
  try {
81209
- if (fs72.existsSync(candidate.worktreePath)) {
81210
- fs72.rmSync(candidate.worktreePath, { recursive: true, force: true });
81817
+ if (fs74.existsSync(candidate.worktreePath)) {
81818
+ fs74.rmSync(candidate.worktreePath, { recursive: true, force: true });
81211
81819
  }
81212
81820
  } catch {
81213
81821
  }
81214
81822
  outcomes.push({
81215
81823
  path: candidate.worktreePath,
81216
81824
  agentId: candidate.agentId,
81217
- deleted: !fs72.existsSync(candidate.worktreePath),
81825
+ deleted: !fs74.existsSync(candidate.worktreePath),
81218
81826
  error: message
81219
81827
  });
81220
81828
  }
@@ -81277,16 +81885,16 @@ init_control_config();
81277
81885
  init_control_registry();
81278
81886
 
81279
81887
  // src/core/harness-scrub.ts
81280
- var fs73 = __toESM(require("fs"));
81281
- var path81 = __toESM(require("path"));
81888
+ var fs75 = __toESM(require("fs"));
81889
+ var path84 = __toESM(require("path"));
81282
81890
  var HARNESS_SCRUB_DIRS = ["runs", "validations", "state", "slots"];
81283
81891
  function scrubLocalHarnessDirs(repoPath) {
81284
81892
  const results = [];
81285
- const resolved = path81.resolve(repoPath);
81286
- if (!fs73.existsSync(resolved)) {
81893
+ const resolved = path84.resolve(repoPath);
81894
+ if (!fs75.existsSync(resolved)) {
81287
81895
  for (const directory of HARNESS_SCRUB_DIRS) {
81288
81896
  results.push({
81289
- path: path81.join(resolved, ".har", directory),
81897
+ path: path84.join(resolved, ".har", directory),
81290
81898
  directory,
81291
81899
  deleted: false,
81292
81900
  error: "repository path does not exist"
@@ -81295,24 +81903,24 @@ function scrubLocalHarnessDirs(repoPath) {
81295
81903
  return results;
81296
81904
  }
81297
81905
  for (const directory of HARNESS_SCRUB_DIRS) {
81298
- const target = path81.join(resolved, ".har", directory);
81299
- if (!fs73.existsSync(target)) {
81906
+ const target = path84.join(resolved, ".har", directory);
81907
+ if (!fs75.existsSync(target)) {
81300
81908
  results.push({ path: target, directory, deleted: true });
81301
81909
  continue;
81302
81910
  }
81303
81911
  try {
81304
- fs73.rmSync(target, { recursive: true, force: true });
81912
+ fs75.rmSync(target, { recursive: true, force: true });
81305
81913
  results.push({
81306
81914
  path: target,
81307
81915
  directory,
81308
- deleted: !fs73.existsSync(target),
81309
- error: fs73.existsSync(target) ? "failed to remove directory" : void 0
81916
+ deleted: !fs75.existsSync(target),
81917
+ error: fs75.existsSync(target) ? "failed to remove directory" : void 0
81310
81918
  });
81311
81919
  } catch (err) {
81312
81920
  results.push({
81313
81921
  path: target,
81314
81922
  directory,
81315
- deleted: !fs73.existsSync(target),
81923
+ deleted: !fs75.existsSync(target),
81316
81924
  error: err instanceof Error ? err.message : String(err)
81317
81925
  });
81318
81926
  }
@@ -81323,7 +81931,7 @@ function scrubLocalHarnessForRepos(repoPaths) {
81323
81931
  const results = [];
81324
81932
  const seen = /* @__PURE__ */ new Set();
81325
81933
  for (const repoPath of repoPaths) {
81326
- const key = path81.resolve(repoPath);
81934
+ const key = path84.resolve(repoPath);
81327
81935
  if (seen.has(key)) continue;
81328
81936
  seen.add(key);
81329
81937
  results.push(...scrubLocalHarnessDirs(key));
@@ -81336,8 +81944,8 @@ async function askYesNo7(question) {
81336
81944
  if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
81337
81945
  const readline8 = await import("readline");
81338
81946
  const rl = readline8.createInterface({ input: process.stdin, output: process.stderr });
81339
- const answer = await new Promise((resolve60) => {
81340
- rl.question(question, resolve60);
81947
+ const answer = await new Promise((resolve66) => {
81948
+ rl.question(question, resolve66);
81341
81949
  });
81342
81950
  rl.close();
81343
81951
  return /^[Yy]$/.test(answer.trim());
@@ -81616,7 +82224,7 @@ async function handleDown() {
81616
82224
  return finishCommand(code);
81617
82225
  }
81618
82226
  async function handleRegister(argv) {
81619
- const repoPath = path82.resolve(argv.repo);
82227
+ const repoPath = path85.resolve(argv.repo);
81620
82228
  header("har control register");
81621
82229
  info(`Repository: ${repoPath}`);
81622
82230
  try {
@@ -81714,7 +82322,7 @@ async function handleReset(argv) {
81714
82322
  }
81715
82323
  }
81716
82324
  async function handleUnregister(argv) {
81717
- const repoPath = path82.resolve(argv.repo);
82325
+ const repoPath = path85.resolve(argv.repo);
81718
82326
  const worktrees = listUnregisterWorktreeCandidates(repoPath);
81719
82327
  try {
81720
82328
  let deleteWorktrees = argv.deleteWorktrees;
@@ -81995,12 +82603,12 @@ async function handleTrajectory(argv) {
81995
82603
  }
81996
82604
 
81997
82605
  // src/cli/commands/hooks.ts
81998
- var path84 = __toESM(require("path"));
82606
+ var path87 = __toESM(require("path"));
81999
82607
  init_hooks();
82000
82608
 
82001
82609
  // src/core/claude-hooks.ts
82002
- var fs74 = __toESM(require("fs"));
82003
- var path83 = __toESM(require("path"));
82610
+ var fs76 = __toESM(require("fs"));
82611
+ var path86 = __toESM(require("path"));
82004
82612
  init_paths();
82005
82613
  var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
82006
82614
  var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
@@ -82012,8 +82620,8 @@ function isHarGuardEntry(entry) {
82012
82620
  );
82013
82621
  }
82014
82622
  function readSettings(settingsPath) {
82015
- if (!fs74.existsSync(settingsPath)) return {};
82016
- const raw = fs74.readFileSync(settingsPath, "utf8").trim();
82623
+ if (!fs76.existsSync(settingsPath)) return {};
82624
+ const raw = fs76.readFileSync(settingsPath, "utf8").trim();
82017
82625
  if (raw === "") return {};
82018
82626
  const parsed = JSON.parse(raw);
82019
82627
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -82022,19 +82630,19 @@ function readSettings(settingsPath) {
82022
82630
  return parsed;
82023
82631
  }
82024
82632
  function writeSettings(settingsPath, settings) {
82025
- fs74.mkdirSync(path83.dirname(settingsPath), { recursive: true });
82026
- fs74.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
82633
+ fs76.mkdirSync(path86.dirname(settingsPath), { recursive: true });
82634
+ fs76.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
82027
82635
  }
82028
82636
  function installClaudeGuard(repoPath) {
82029
82637
  const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
82030
82638
  if (!templatePath) {
82031
82639
  throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
82032
82640
  }
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);
82641
+ const guardScript = path86.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82642
+ fs76.mkdirSync(path86.dirname(guardScript), { recursive: true });
82643
+ fs76.copyFileSync(templatePath, guardScript);
82644
+ fs76.chmodSync(guardScript, 493);
82645
+ const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82038
82646
  const settings = readSettings(settingsPath);
82039
82647
  const hooks = settings.hooks ?? {};
82040
82648
  const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
@@ -82048,15 +82656,15 @@ function installClaudeGuard(repoPath) {
82048
82656
  }
82049
82657
  function uninstallClaudeGuard(repoPath) {
82050
82658
  let removed = false;
82051
- const guardScript = path83.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82052
- if (fs74.existsSync(guardScript)) {
82053
- fs74.rmSync(guardScript);
82659
+ const guardScript = path86.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
82660
+ if (fs76.existsSync(guardScript)) {
82661
+ fs76.rmSync(guardScript);
82054
82662
  removed = true;
82055
- const parent = path83.dirname(guardScript);
82056
- if (fs74.existsSync(parent) && fs74.readdirSync(parent).length === 0) fs74.rmdirSync(parent);
82663
+ const parent = path86.dirname(guardScript);
82664
+ if (fs76.existsSync(parent) && fs76.readdirSync(parent).length === 0) fs76.rmdirSync(parent);
82057
82665
  }
82058
- const settingsPath = path83.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82059
- if (fs74.existsSync(settingsPath)) {
82666
+ const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82667
+ if (fs76.existsSync(settingsPath)) {
82060
82668
  const settings = readSettings(settingsPath);
82061
82669
  const preToolUse = settings.hooks?.PreToolUse;
82062
82670
  if (preToolUse) {
@@ -82081,8 +82689,8 @@ function uninstallClaudeGuard(repoPath) {
82081
82689
  return { removed };
82082
82690
  }
82083
82691
  function claudeGuardInstalled(repoPath) {
82084
- const settingsPath = path83.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82085
- if (!fs74.existsSync(settingsPath)) return false;
82692
+ const settingsPath = path86.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
82693
+ if (!fs76.existsSync(settingsPath)) return false;
82086
82694
  try {
82087
82695
  const settings = readSettings(settingsPath);
82088
82696
  return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
@@ -82099,7 +82707,7 @@ function repoOption(y) {
82099
82707
  function handleInstall2(argv) {
82100
82708
  try {
82101
82709
  if (argv.claude) {
82102
- const result2 = installClaudeGuard(path84.resolve(argv.repo));
82710
+ const result2 = installClaudeGuard(path87.resolve(argv.repo));
82103
82711
  success("Claude Code worktree guard installed.");
82104
82712
  info(`Guard script: ${result2.guardScript}`);
82105
82713
  info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
@@ -82107,7 +82715,7 @@ function handleInstall2(argv) {
82107
82715
  info("Commit both files so the guard travels with the repo.");
82108
82716
  return;
82109
82717
  }
82110
- const result = installHooks({ repoPath: path84.resolve(argv.repo), force: argv.force });
82718
+ const result = installHooks({ repoPath: path87.resolve(argv.repo), force: argv.force });
82111
82719
  success(`Commit gate installed in ${result.hooksDir}`);
82112
82720
  info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
82113
82721
  info("Commits of unverified change batches will be blocked in agent worktrees.");
@@ -82120,7 +82728,7 @@ function handleInstall2(argv) {
82120
82728
  function handleUninstall(argv) {
82121
82729
  try {
82122
82730
  if (argv.claude) {
82123
- const result2 = uninstallClaudeGuard(path84.resolve(argv.repo));
82731
+ const result2 = uninstallClaudeGuard(path87.resolve(argv.repo));
82124
82732
  if (result2.removed) {
82125
82733
  success("Claude Code worktree guard removed.");
82126
82734
  } else {
@@ -82128,7 +82736,7 @@ function handleUninstall(argv) {
82128
82736
  }
82129
82737
  return;
82130
82738
  }
82131
- const result = uninstallHooks(path84.resolve(argv.repo));
82739
+ const result = uninstallHooks(path87.resolve(argv.repo));
82132
82740
  if (result.removed) {
82133
82741
  success(`Commit gate removed from ${result.hooksDir}`);
82134
82742
  } else {
@@ -82141,7 +82749,7 @@ function handleUninstall(argv) {
82141
82749
  }
82142
82750
  function handleStatus2(argv) {
82143
82751
  try {
82144
- const status = getHooksStatus(path84.resolve(argv.repo));
82752
+ const status = getHooksStatus(path87.resolve(argv.repo));
82145
82753
  if (argv.json) {
82146
82754
  process.stdout.write(`${JSON.stringify(status, null, 2)}
82147
82755
  `);
@@ -82159,7 +82767,7 @@ function handleStatus2(argv) {
82159
82767
  );
82160
82768
  info(`Effective: ${status.effectiveMode} (in this checkout)`);
82161
82769
  info(
82162
- `Claude guard: ${claudeGuardInstalled(path84.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
82770
+ `Claude guard: ${claudeGuardInstalled(path87.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
82163
82771
  );
82164
82772
  } catch (err) {
82165
82773
  error(err instanceof Error ? err.message : String(err));
@@ -82420,10 +83028,10 @@ function assignProp(target, prop, value) {
82420
83028
  configurable: true
82421
83029
  });
82422
83030
  }
82423
- function getElementAtPath(obj, path91) {
82424
- if (!path91)
83031
+ function getElementAtPath(obj, path97) {
83032
+ if (!path97)
82425
83033
  return obj;
82426
- return path91.reduce((acc, key) => acc?.[key], obj);
83034
+ return path97.reduce((acc, key) => acc?.[key], obj);
82427
83035
  }
82428
83036
  function promiseAllObject(promisesObj) {
82429
83037
  const keys = Object.keys(promisesObj);
@@ -82743,11 +83351,11 @@ function aborted(x, startIndex = 0) {
82743
83351
  }
82744
83352
  return false;
82745
83353
  }
82746
- function prefixIssues(path91, issues) {
83354
+ function prefixIssues(path97, issues) {
82747
83355
  return issues.map((iss) => {
82748
83356
  var _a2;
82749
83357
  (_a2 = iss).path ?? (_a2.path = []);
82750
- iss.path.unshift(path91);
83358
+ iss.path.unshift(path97);
82751
83359
  return iss;
82752
83360
  });
82753
83361
  }
@@ -88070,7 +88678,7 @@ var Protocol = class {
88070
88678
  return;
88071
88679
  }
88072
88680
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
88073
- await new Promise((resolve60) => setTimeout(resolve60, pollInterval));
88681
+ await new Promise((resolve66) => setTimeout(resolve66, pollInterval));
88074
88682
  options?.signal?.throwIfAborted();
88075
88683
  }
88076
88684
  } catch (error3) {
@@ -88087,7 +88695,7 @@ var Protocol = class {
88087
88695
  */
88088
88696
  request(request, resultSchema, options) {
88089
88697
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
88090
- return new Promise((resolve60, reject) => {
88698
+ return new Promise((resolve66, reject) => {
88091
88699
  const earlyReject = (error3) => {
88092
88700
  reject(error3);
88093
88701
  };
@@ -88165,7 +88773,7 @@ var Protocol = class {
88165
88773
  if (!parseResult.success) {
88166
88774
  reject(parseResult.error);
88167
88775
  } else {
88168
- resolve60(parseResult.data);
88776
+ resolve66(parseResult.data);
88169
88777
  }
88170
88778
  } catch (error3) {
88171
88779
  reject(error3);
@@ -88426,12 +89034,12 @@ var Protocol = class {
88426
89034
  }
88427
89035
  } catch {
88428
89036
  }
88429
- return new Promise((resolve60, reject) => {
89037
+ return new Promise((resolve66, reject) => {
88430
89038
  if (signal.aborted) {
88431
89039
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
88432
89040
  return;
88433
89041
  }
88434
- const timeoutId = setTimeout(resolve60, interval);
89042
+ const timeoutId = setTimeout(resolve66, interval);
88435
89043
  signal.addEventListener("abort", () => {
88436
89044
  clearTimeout(timeoutId);
88437
89045
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -89221,7 +89829,7 @@ var Server = class extends Protocol {
89221
89829
  };
89222
89830
 
89223
89831
  // src/mcp/server.ts
89224
- var path85 = __toESM(require("path"));
89832
+ var path90 = __toESM(require("path"));
89225
89833
 
89226
89834
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
89227
89835
  var import_node_process3 = __toESM(require("node:process"), 1);
@@ -89304,12 +89912,12 @@ var StdioServerTransport = class {
89304
89912
  this.onclose?.();
89305
89913
  }
89306
89914
  send(message) {
89307
- return new Promise((resolve60) => {
89915
+ return new Promise((resolve66) => {
89308
89916
  const json = serializeMessage(message);
89309
89917
  if (this._stdout.write(json)) {
89310
- resolve60();
89918
+ resolve66();
89311
89919
  } else {
89312
- this._stdout.once("drain", resolve60);
89920
+ this._stdout.once("drain", resolve66);
89313
89921
  }
89314
89922
  });
89315
89923
  }
@@ -89319,6 +89927,379 @@ var StdioServerTransport = class {
89319
89927
  init_package_version();
89320
89928
  init_telemetry_config();
89321
89929
  init_runs();
89930
+
89931
+ // src/core/lines.ts
89932
+ var path89 = __toESM(require("path"));
89933
+
89934
+ // src/harness/line-create.ts
89935
+ var fs77 = __toESM(require("fs"));
89936
+ var path88 = __toESM(require("path"));
89937
+ init_paths();
89938
+ init_schema2();
89939
+ var LINE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
89940
+ function renderSkeleton(templatePath, tokens) {
89941
+ let content = fs77.readFileSync(templatePath, "utf8");
89942
+ for (const [token, value] of Object.entries(tokens)) {
89943
+ content = content.replace(new RegExp(token, "g"), value);
89944
+ }
89945
+ return content;
89946
+ }
89947
+ function createLine(repoPath, options) {
89948
+ const resolved = path88.resolve(repoPath);
89949
+ if (!harnessExists(resolved)) {
89950
+ throw new Error('No .har/ harness found. Run "har onboard" first.');
89951
+ }
89952
+ const id = options.id.trim();
89953
+ if (!LINE_ID_PATTERN.test(id)) {
89954
+ throw new Error(
89955
+ `Invalid line id "${id}". Use lowercase letters, digits, dots, dashes (e.g. "onboarding-line").`
89956
+ );
89957
+ }
89958
+ const lineDir = path88.join(resolved, LOCAL_LINES_DIR, id);
89959
+ const lineDirRel2 = `${LOCAL_LINES_DIR}/${id}`;
89960
+ if (fs77.existsSync(lineDir) && !options.force) {
89961
+ throw new Error(`Line already exists: ${lineDirRel2}/. Use --force to overwrite.`);
89962
+ }
89963
+ const stationIds = options.stations?.length ? options.stations : ["S1", "S2"];
89964
+ const title = options.title ?? `${id} factory line`;
89965
+ const gateStageId = `${id}-gate`;
89966
+ const withGateStage = options.gateStage ?? true;
89967
+ const program = LineProgramSchema.parse({
89968
+ contractVersion: LINE_CONTRACT_VERSION,
89969
+ id,
89970
+ title,
89971
+ description: options.description ?? 'Describe the program: what it produces, and what "done" means at the last station.',
89972
+ skills: [{ id: "factory-line", role: "orchestrator", install: "repo:.claude/skills/factory-line" }],
89973
+ mcp: [],
89974
+ plugins: [],
89975
+ stations: stationIds.map((stationId, i) => ({
89976
+ id: stationId,
89977
+ title: `Station ${i + 1}`,
89978
+ description: "What this station produces.",
89979
+ work: { source: "none", ids: [], optional: true }
89980
+ })),
89981
+ gate: {
89982
+ cumulative: true,
89983
+ optInEnv: options.optInEnv ?? null,
89984
+ stages: withGateStage ? [{ id: gateStageId, fromStation: stationIds[0], tier: "full" }] : []
89985
+ },
89986
+ extraStages: withGateStage ? [
89987
+ {
89988
+ id: gateStageId,
89989
+ kind: "test",
89990
+ tier: "full",
89991
+ description: `Line-owned gate for ${id} \u2014 registered, never on verify`
89992
+ }
89993
+ ] : [],
89994
+ handoff: {
89995
+ autonomousShip: false,
89996
+ waitFor: "human review before merge or release"
89997
+ },
89998
+ prototypeNotes: []
89999
+ });
90000
+ const manifest = LineManifestSchema.parse({
90001
+ kind: LINE_BUNDLE_KIND,
90002
+ id,
90003
+ title,
90004
+ program: "line.json",
90005
+ files: withGateStage ? [{ src: `stages/${gateStageId}.sh`, dest: `.har/stages/${gateStageId}.sh`, executable: true }] : [],
90006
+ stages: withGateStage ? [
90007
+ {
90008
+ id: gateStageId,
90009
+ kind: "test",
90010
+ description: `Line-owned gate for ${id} \u2014 registered, never on verify`,
90011
+ script: `stages/${gateStageId}.sh`,
90012
+ requiresAgentId: true,
90013
+ artifacts: [
90014
+ {
90015
+ path: `.har/artifacts/${gateStageId}`,
90016
+ kind: "directory",
90017
+ description: `Artifacts for the ${gateStageId} stage`
90018
+ }
90019
+ ],
90020
+ tier: "full"
90021
+ }
90022
+ ] : [],
90023
+ nextSteps: [
90024
+ `Edit ${lineDirRel2}/line.json \u2014 name the stations and bind tracker work`,
90025
+ ...withGateStage ? [`Edit ${lineDirRel2}/stages/${gateStageId}.sh \u2014 replace the TODO with the real gate`] : [],
90026
+ `har line status ${id}`,
90027
+ `har line gate ${stationIds[0]} --line ${id}`
90028
+ ],
90029
+ docsPath: `${lineDirRel2}/README.md`
90030
+ });
90031
+ const filesWritten = [];
90032
+ const write = (rel, content, executable = false) => {
90033
+ const abs = path88.join(lineDir, rel);
90034
+ fs77.mkdirSync(path88.dirname(abs), { recursive: true });
90035
+ fs77.writeFileSync(abs, content);
90036
+ if (executable) fs77.chmodSync(abs, 493);
90037
+ filesWritten.push(`${lineDirRel2}/${rel}`);
90038
+ };
90039
+ write("line.manifest.json", `${JSON.stringify(manifest, null, 2)}
90040
+ `);
90041
+ write("line.json", `${JSON.stringify(program, null, 2)}
90042
+ `);
90043
+ if (withGateStage) {
90044
+ const stageScript = renderSkeleton(
90045
+ path88.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
90046
+ {
90047
+ __STAGE_ID__: gateStageId,
90048
+ __STAGE_KIND__: "test",
90049
+ __STAGE_DESCRIPTION__: `Line-owned gate for ${id} (not on verify)`
90050
+ }
90051
+ );
90052
+ write(`stages/${gateStageId}.sh`, stageScript, true);
90053
+ }
90054
+ write("README.md", renderLineReadme(id, title, stationIds, gateStageId, withGateStage));
90055
+ return {
90056
+ lineId: id,
90057
+ lineDir,
90058
+ filesWritten,
90059
+ nextSteps: [
90060
+ `Edit ${lineDirRel2}/line.json \u2014 stations, work binding, gate tags`,
90061
+ ...withGateStage ? [`Edit ${lineDirRel2}/stages/${gateStageId}.sh \u2014 replace the TODO with the real gate`] : [],
90062
+ `Install it: har line add ${id} (re-run with --force after changes)`,
90063
+ `Installing writes .har/ADAPT-PROMPT-line-${id}.md \u2014 a ready adaptation prompt for your coding agent`,
90064
+ "Publishing later (npm/git) needs zero format changes \u2014 see the README"
90065
+ ]
90066
+ };
90067
+ }
90068
+ function renderLineReadme(id, title, stationIds, gateStageId, withGateStage) {
90069
+ return `# ${title}
90070
+
90071
+ A HAR **factory line**: an ordered program of stations plus a cumulative gate.
90072
+
90073
+ A line is not a verification plugin. Installing it registers stages but never
90074
+ adds them to \`verificationStages\` \u2014 default \`har env verify --full\` stays
90075
+ exactly as fast as it was. Line gate stages run on demand:
90076
+
90077
+ \`\`\`bash
90078
+ har line gate ${stationIds[0]} --line ${id}
90079
+ \`\`\`
90080
+
90081
+ If a check should gate *every* verify, it belongs in a verification plugin
90082
+ (\`har plugin create\` / \`har env add-plugin\`), not here.
90083
+
90084
+ ## Layout
90085
+
90086
+ \`\`\`text
90087
+ ${id}/
90088
+ \u251C\u2500\u2500 line.manifest.json # kind: line \u2014 what \`har line add\` applies
90089
+ \u251C\u2500\u2500 line.json # the program: stations, gate, handoff
90090
+ ${withGateStage ? `\u251C\u2500\u2500 stages/${gateStageId}.sh # extra gate stage \u2014 registered, NOT on verify
90091
+ ` : ""}\u2514\u2500\u2500 README.md
90092
+ \`\`\`
90093
+
90094
+ ## Stations
90095
+
90096
+ ${stationIds.map((s, i) => `${i + 1}. \`${s}\``).join("\n")}
90097
+
90098
+ ## The ratchet
90099
+
90100
+ \`gate.stages[].fromStation\` tags a stage as required at that station **and
90101
+ every later one**. Adding a station must never drop an earlier station's
90102
+ stages \u2014 that is what \`gate.cumulative: true\` means, and it is contract.
90103
+
90104
+ ## Install channels
90105
+
90106
+ \`\`\`bash
90107
+ har line add ${id} # this repo (.har/lines/${id})
90108
+ har line add ./${id} # local path
90109
+ har line add github:acme/${id} # git
90110
+ har line add @acme/${id} # npm
90111
+ \`\`\`
90112
+
90113
+ Publishing needs no format change: add a \`package.json\` and push.
90114
+ `;
90115
+ }
90116
+
90117
+ // src/core/lines.ts
90118
+ init_stages();
90119
+ init_slot_registry();
90120
+ init_runs();
90121
+ function listInstalledLineIds(repoPath) {
90122
+ const ledger = readLineLedger(repoPath);
90123
+ const fromLedger = ledger?.lines.map((l) => l.id) ?? [];
90124
+ const onDisk = listLocalLineIds(repoPath).filter(
90125
+ (id) => readInstalledLineProgram(repoPath, id) !== null
90126
+ );
90127
+ return [.../* @__PURE__ */ new Set([...fromLedger, ...onDisk])].sort();
90128
+ }
90129
+ function cumulativeGateStages(program, stationId) {
90130
+ const stationIndex = program.stations.findIndex((s) => s.id === stationId);
90131
+ if (stationIndex < 0) {
90132
+ throw new Error(
90133
+ `Unknown station "${stationId}". Stations: ${program.stations.map((s) => s.id).join(", ")}`
90134
+ );
90135
+ }
90136
+ const indexOf = new Map(program.stations.map((s, i) => [s.id, i]));
90137
+ return program.gate.stages.filter((stage) => {
90138
+ const from2 = indexOf.get(stage.fromStation);
90139
+ return from2 !== void 0 && from2 <= stationIndex;
90140
+ });
90141
+ }
90142
+ function latestRunStatusByStage(repoPath, stageIds) {
90143
+ const statuses = /* @__PURE__ */ new Map();
90144
+ for (const stageId of stageIds) {
90145
+ const [latest] = listRuns(repoPath, { stageId, limit: 1 });
90146
+ if (latest) statuses.set(stageId, latest.status);
90147
+ }
90148
+ return statuses;
90149
+ }
90150
+ function getLineStatus(repoPath, lineId) {
90151
+ const resolved = path89.resolve(repoPath);
90152
+ const program = readInstalledLineProgram(resolved, lineId);
90153
+ if (!program) {
90154
+ throw new Error(
90155
+ `Line not installed: ${lineId}. Install one with "har line add <spec>" or scaffold with "har line create <id>".`
90156
+ );
90157
+ }
90158
+ const ledger = readLineLedger(resolved);
90159
+ const entry = ledger?.lines.find((l) => l.id === lineId);
90160
+ const registry2 = readStageRegistry(resolved);
90161
+ const registeredIds = new Set(registry2.stages.map((s) => s.id));
90162
+ const verificationStages = registry2.verificationStages ?? [];
90163
+ const allGateStageIds = [...new Set(program.gate.stages.map((s) => s.id))];
90164
+ const runStatuses = latestRunStatusByStage(resolved, allGateStageIds);
90165
+ const stations = program.stations.map((station, index) => {
90166
+ const required2 = cumulativeGateStages(program, station.id);
90167
+ const requiredStageIds = [...new Set(required2.map((s) => s.id))];
90168
+ const missingStageIds = requiredStageIds.filter((id) => !registeredIds.has(id));
90169
+ const passedStageIds = requiredStageIds.filter((id) => runStatuses.get(id) === "pass");
90170
+ const neverRunStageIds = requiredStageIds.filter((id) => !runStatuses.has(id));
90171
+ return {
90172
+ id: station.id,
90173
+ title: station.title,
90174
+ index,
90175
+ requiredStageIds,
90176
+ passedStageIds,
90177
+ neverRunStageIds,
90178
+ missingStageIds,
90179
+ green: missingStageIds.length === 0 && passedStageIds.length === requiredStageIds.length,
90180
+ work: station.work
90181
+ };
90182
+ });
90183
+ let currentStationId;
90184
+ for (const station of stations) {
90185
+ if (station.green) currentStationId = station.id;
90186
+ else break;
90187
+ }
90188
+ const nextStationId = stations.find((s) => !s.green)?.id;
90189
+ const lineStageIds = entry?.stageIds ?? [];
90190
+ const verifyLeaks = lineStageIds.filter((id) => verificationStages.includes(id));
90191
+ const warnings = [];
90192
+ if (verifyLeaks.length > 0) {
90193
+ warnings.push(
90194
+ `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.`
90195
+ );
90196
+ }
90197
+ for (const station of stations) {
90198
+ for (const missing of station.missingStageIds) {
90199
+ warnings.push(`Gate stage "${missing}" (station ${station.id}) is not registered in .har/stages.json`);
90200
+ }
90201
+ }
90202
+ const slotsInFlight = listSlotRegistryEntries(resolved).map((slot) => ({
90203
+ agentId: slot.agentId,
90204
+ branch: slot.branch,
90205
+ workUnitId: slot.workUnitId,
90206
+ workDir: slot.workDir
90207
+ }));
90208
+ return {
90209
+ lineId,
90210
+ title: program.title,
90211
+ programPath: entry?.programPath ?? `.har/lines/${lineId}/line.json`,
90212
+ installed: true,
90213
+ source: entry?.source,
90214
+ stations,
90215
+ currentStationId,
90216
+ nextStationId,
90217
+ optInEnv: program.gate.optInEnv,
90218
+ registeredStageIds: lineStageIds,
90219
+ verifyLeaks,
90220
+ slotsInFlight,
90221
+ handoffAutonomousShip: false,
90222
+ warnings: [...new Set(warnings)]
90223
+ };
90224
+ }
90225
+ function getAllLineStatuses(repoPath) {
90226
+ return listInstalledLineIds(repoPath).map((id) => getLineStatus(repoPath, id));
90227
+ }
90228
+ function resolveLineId(repoPath, lineId) {
90229
+ if (lineId) return lineId;
90230
+ const ids = listInstalledLineIds(repoPath);
90231
+ if (ids.length === 0) {
90232
+ throw new Error('No factory line installed. Run "har line create <id>" or "har line add <spec>".');
90233
+ }
90234
+ if (ids.length > 1) {
90235
+ throw new Error(`Multiple lines installed (${ids.join(", ")}). Pass --line <id>.`);
90236
+ }
90237
+ return ids[0];
90238
+ }
90239
+ async function runLineGate(options) {
90240
+ const repoPath = path89.resolve(options.repoPath);
90241
+ const lineId = resolveLineId(repoPath, options.lineId);
90242
+ const program = readInstalledLineProgram(repoPath, lineId);
90243
+ if (!program) {
90244
+ throw new Error(`Line not installed: ${lineId}`);
90245
+ }
90246
+ const required2 = cumulativeGateStages(program, options.station);
90247
+ if (program.gate.optInEnv && !options.force && process.env[program.gate.optInEnv] !== "1") {
90248
+ return {
90249
+ lineId,
90250
+ station: options.station,
90251
+ stages: required2.map((stage) => ({
90252
+ stageId: stage.id,
90253
+ fromStation: stage.fromStation,
90254
+ status: "skipped",
90255
+ reason: `${program.gate.optInEnv} is not "1"`
90256
+ })),
90257
+ pass: true,
90258
+ skipped: true,
90259
+ skipReason: `Gate is opt-in: set ${program.gate.optInEnv}=1 (or pass --force) to run it.`
90260
+ };
90261
+ }
90262
+ const registry2 = readStageRegistry(repoPath);
90263
+ const registeredIds = new Set(registry2.stages.map((s) => s.id));
90264
+ const results = [];
90265
+ let pass = true;
90266
+ for (const stage of required2) {
90267
+ if (!registeredIds.has(stage.id)) {
90268
+ results.push({
90269
+ stageId: stage.id,
90270
+ fromStation: stage.fromStation,
90271
+ status: "skipped",
90272
+ reason: "stage is not registered in .har/stages.json"
90273
+ });
90274
+ pass = false;
90275
+ continue;
90276
+ }
90277
+ const result = await runStage({
90278
+ repoPath,
90279
+ stageId: stage.id,
90280
+ agentId: options.agentId,
90281
+ trigger: "cli"
90282
+ });
90283
+ results.push({
90284
+ stageId: stage.id,
90285
+ fromStation: stage.fromStation,
90286
+ status: result.status,
90287
+ durationMs: result.durationMs
90288
+ });
90289
+ if (result.status !== "pass") {
90290
+ pass = false;
90291
+ }
90292
+ }
90293
+ return { lineId, station: options.station, stages: results, pass, skipped: false };
90294
+ }
90295
+ function createLineBundle(repoPath, options) {
90296
+ return createLine(repoPath, options);
90297
+ }
90298
+ function addLine(repoPath, spec, options = {}) {
90299
+ return applyLine(repoPath, spec, options);
90300
+ }
90301
+
90302
+ // src/mcp/server.ts
89322
90303
  init_work_units();
89323
90304
  init_manifest();
89324
90305
 
@@ -89532,6 +90513,32 @@ var ControlUpOutputSchema = external_exports.object({
89532
90513
  failed: external_exports.number().int(),
89533
90514
  apiReady: external_exports.boolean()
89534
90515
  });
90516
+ var CreateLineInputSchema = external_exports.object({
90517
+ repo: external_exports.string().default("."),
90518
+ id: external_exports.string().min(1).describe("Line id (lowercase slug, e.g. onboarding-line)"),
90519
+ title: external_exports.string().min(1).optional().describe("Human-readable line title"),
90520
+ description: external_exports.string().min(1).max(2e3).optional(),
90521
+ stations: external_exports.array(external_exports.string().min(1)).optional().describe("Station ids in order (default: S1, S2)"),
90522
+ gateStage: external_exports.boolean().default(true).describe("Scaffold one registered-but-off-verify gate stage"),
90523
+ optInEnv: external_exports.string().min(1).optional().describe('Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'),
90524
+ force: external_exports.boolean().default(false)
90525
+ });
90526
+ var AddLineInputSchema = external_exports.object({
90527
+ repo: external_exports.string().default("."),
90528
+ line: external_exports.string().min(1).describe("Line id, local path (./line), npm package (@org/pkg), or git URL (github:org/repo)"),
90529
+ force: external_exports.boolean().default(false).describe("Overwrite existing line files and stage entries")
90530
+ });
90531
+ var LineStatusInputSchema = external_exports.object({
90532
+ repo: external_exports.string().default("."),
90533
+ line: external_exports.string().min(1).optional().describe("Line id (default: every installed line)")
90534
+ });
90535
+ var RunLineGateInputSchema = external_exports.object({
90536
+ repo: external_exports.string().default("."),
90537
+ station: external_exports.string().min(1).describe("Station id whose cumulative gate should run"),
90538
+ line: external_exports.string().min(1).optional().describe("Line id when more than one is installed"),
90539
+ agentId: external_exports.number().int().optional().describe("Agent slot to run the stages in"),
90540
+ force: external_exports.boolean().default(false).describe("Run even when the program declares an opt-in env var that is not set")
90541
+ });
89535
90542
 
89536
90543
  // src/mcp/server.ts
89537
90544
  var HAR_MCP_TOOLS = [
@@ -89780,6 +90787,73 @@ var HAR_MCP_TOOLS = [
89780
90787
  repo: repoJsonProperty,
89781
90788
  detach: { type: "boolean", description: "Run the container in detached mode (default true)" }
89782
90789
  })
90790
+ },
90791
+ {
90792
+ name: "har_line_create",
90793
+ 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.",
90794
+ inputSchema: objectJsonSchema(
90795
+ {
90796
+ repo: repoJsonProperty,
90797
+ id: { type: "string", description: "Line id (lowercase slug, e.g. onboarding-line)" },
90798
+ title: { type: "string", description: "Human-readable line title" },
90799
+ description: { type: "string", description: "One-paragraph description of the program" },
90800
+ stations: {
90801
+ type: "array",
90802
+ items: { type: "string" },
90803
+ description: "Station ids in order (default: S1, S2)"
90804
+ },
90805
+ gateStage: {
90806
+ type: "boolean",
90807
+ description: "Scaffold one registered-but-off-verify gate stage (default true)"
90808
+ },
90809
+ optInEnv: {
90810
+ type: "string",
90811
+ description: 'Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'
90812
+ },
90813
+ force: { type: "boolean", description: "Overwrite an existing line" }
90814
+ },
90815
+ ["id"]
90816
+ )
90817
+ },
90818
+ {
90819
+ name: "har_add_line",
90820
+ 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.",
90821
+ inputSchema: objectJsonSchema(
90822
+ {
90823
+ repo: repoJsonProperty,
90824
+ line: {
90825
+ type: "string",
90826
+ description: "Line id, local path (./line), npm package (@org/pkg), or git URL"
90827
+ },
90828
+ force: { type: "boolean", description: "Overwrite existing line files and stage entries" }
90829
+ },
90830
+ ["line"]
90831
+ )
90832
+ },
90833
+ {
90834
+ name: "har_line_status",
90835
+ 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.",
90836
+ inputSchema: objectJsonSchema({
90837
+ repo: repoJsonProperty,
90838
+ line: { type: "string", description: "Line id (default: every installed line)" }
90839
+ })
90840
+ },
90841
+ {
90842
+ name: "har_run_line_gate",
90843
+ 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.",
90844
+ inputSchema: objectJsonSchema(
90845
+ {
90846
+ repo: repoJsonProperty,
90847
+ station: { type: "string", description: "Station id whose cumulative gate should run" },
90848
+ line: { type: "string", description: "Line id when more than one is installed" },
90849
+ agentId: agentIdJsonProperty,
90850
+ force: {
90851
+ type: "boolean",
90852
+ description: "Run even when the program declares an opt-in env var that is not set"
90853
+ }
90854
+ },
90855
+ ["station"]
90856
+ )
89783
90857
  }
89784
90858
  ];
89785
90859
  function jsonContent(data) {
@@ -90045,7 +91119,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
90045
91119
  const input = ControlUpInputSchema.parse({ ...args, repo });
90046
91120
  const result = await startControlAndSync({
90047
91121
  detach: input.detach,
90048
- cwd: path85.resolve(input.repo)
91122
+ cwd: path90.resolve(input.repo)
90049
91123
  });
90050
91124
  if (result.code !== 0) {
90051
91125
  return {
@@ -90067,6 +91141,59 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
90067
91141
  })
90068
91142
  );
90069
91143
  }
91144
+ case "har_line_create": {
91145
+ const input = CreateLineInputSchema.parse({ ...args, repo });
91146
+ const result = createLineBundle(repo, {
91147
+ id: input.id,
91148
+ title: input.title,
91149
+ description: input.description,
91150
+ stations: input.stations,
91151
+ gateStage: input.gateStage,
91152
+ optInEnv: input.optInEnv,
91153
+ force: input.force
91154
+ });
91155
+ return jsonContent({
91156
+ lineId: result.lineId,
91157
+ filesWritten: result.filesWritten,
91158
+ nextSteps: result.nextSteps
91159
+ });
91160
+ }
91161
+ case "har_add_line": {
91162
+ const input = AddLineInputSchema.parse({ ...args, repo });
91163
+ const result = addLine(repo, input.line, { force: input.force, spec: input.line });
91164
+ return jsonContent({
91165
+ lineId: result.lineId,
91166
+ title: result.title,
91167
+ stationIds: result.stationIds,
91168
+ firstGatedStationId: result.firstGatedStationId,
91169
+ stageIds: result.stageIds,
91170
+ filesWritten: result.filesWritten,
91171
+ warnings: result.warnings,
91172
+ nextSteps: result.nextSteps,
91173
+ programPath: result.programPath,
91174
+ docsPath: result.docsPath,
91175
+ source: result.source,
91176
+ adaptPromptPath: result.adaptPromptPath,
91177
+ verificationStagesUnchanged: true
91178
+ });
91179
+ }
91180
+ case "har_line_status": {
91181
+ const input = LineStatusInputSchema.parse({ ...args, repo });
91182
+ const statuses = input.line ? [getLineStatus(repo, input.line)] : getAllLineStatuses(repo);
91183
+ return jsonContent(input.line ? statuses[0] : { lines: statuses });
91184
+ }
91185
+ case "har_run_line_gate": {
91186
+ const input = RunLineGateInputSchema.parse({ ...args, repo });
91187
+ const agentId = input.agentId === void 0 ? void 0 : validateAgentId(input.agentId, repo);
91188
+ const result = await runLineGate({
91189
+ repoPath: repo,
91190
+ lineId: input.line,
91191
+ station: input.station,
91192
+ agentId,
91193
+ force: input.force
91194
+ });
91195
+ return jsonContent(result);
91196
+ }
90070
91197
  default:
90071
91198
  throw new Error(`Unknown tool: ${toolName}`);
90072
91199
  }
@@ -90109,11 +91236,11 @@ var mcpCommand = {
90109
91236
  };
90110
91237
 
90111
91238
  // src/cli/commands/onboard.ts
90112
- var path87 = __toESM(require("path"));
91239
+ var path92 = __toESM(require("path"));
90113
91240
  init_stages();
90114
91241
 
90115
91242
  // src/core/onboarding.ts
90116
- var path86 = __toESM(require("path"));
91243
+ var path91 = __toESM(require("path"));
90117
91244
  init_control_config();
90118
91245
  init_control_sync();
90119
91246
  init_otel_hooks();
@@ -90257,7 +91384,7 @@ async function defaultEnsureControl(options) {
90257
91384
  return { started: true, apiUrl: result.apiUrl };
90258
91385
  }
90259
91386
  async function runOnboarding(options, deps = {}) {
90260
- const repoPath = path86.resolve(options.repoPath);
91387
+ const repoPath = path91.resolve(options.repoPath);
90261
91388
  const applyTelemetry = deps.applyTelemetry ?? applyOnboardingTelemetry;
90262
91389
  const ensureControl = deps.ensureControl ?? defaultEnsureControl;
90263
91390
  const init = deps.initHarness ?? initHarness;
@@ -90455,7 +91582,7 @@ async function promptChoices(args, docker) {
90455
91582
  };
90456
91583
  }
90457
91584
  const pluginChoices = listPluginChoices();
90458
- const repoPath = path87.resolve(args.repo);
91585
+ const repoPath = path92.resolve(args.repo);
90459
91586
  const alreadyPresent = harnessExists(repoPath);
90460
91587
  const needProfile = !alreadyPresent && !args.skipInit && args.profile === void 0;
90461
91588
  const needAgentSlots = shouldConfigureSlots && flaggedSlots === void 0;
@@ -90576,7 +91703,7 @@ function printSummary(result) {
90576
91703
  console.error("");
90577
91704
  }
90578
91705
  async function handleOnboard(argv) {
90579
- const repoPath = path87.resolve(argv.repo);
91706
+ const repoPath = path92.resolve(argv.repo);
90580
91707
  header("har onboard");
90581
91708
  info(`Repository: ${repoPath}`);
90582
91709
  try {
@@ -90693,24 +91820,288 @@ var onboardCommand = {
90693
91820
  handler: (argv) => handleOnboard(argv)
90694
91821
  };
90695
91822
 
91823
+ // src/cli/commands/line.ts
91824
+ var path93 = __toESM(require("path"));
91825
+ init_logging();
91826
+ function handleLineCreate(argv) {
91827
+ if (!argv.id) {
91828
+ error('Missing line id. Usage: har line create <id> [--stations S1,S2] [--title "..."]');
91829
+ return finishCommand(1);
91830
+ }
91831
+ const repoPath = path93.resolve(argv.repo);
91832
+ header("har line create");
91833
+ info(`Repository: ${repoPath}`);
91834
+ info(`Line: ${argv.id}`);
91835
+ try {
91836
+ const result = createLineBundle(repoPath, {
91837
+ id: argv.id,
91838
+ title: argv.title,
91839
+ description: argv.description,
91840
+ stations: argv.stations,
91841
+ gateStage: argv.gateStage,
91842
+ optInEnv: argv.optInEnv,
91843
+ force: argv.force
91844
+ });
91845
+ divider();
91846
+ success(`Factory line scaffolded: .har/lines/${result.lineId}/`);
91847
+ for (const file of result.filesWritten) {
91848
+ info(` + ${file}`);
91849
+ }
91850
+ console.error("");
91851
+ console.error(" Next steps:");
91852
+ for (const step of result.nextSteps) {
91853
+ console.error(` ${step}`);
91854
+ }
91855
+ console.error("");
91856
+ console.error(` Docs: .har/lines/${result.lineId}/README.md`);
91857
+ console.error("");
91858
+ } catch (err) {
91859
+ error(err.message);
91860
+ return finishCommand(1);
91861
+ }
91862
+ }
91863
+ function handleLineAdd(argv) {
91864
+ if (!argv.spec) {
91865
+ error("Missing line spec. Usage: har line add <id|path|npm|git>");
91866
+ return finishCommand(1);
91867
+ }
91868
+ const repoPath = path93.resolve(argv.repo);
91869
+ header("har line add");
91870
+ info(`Repository: ${repoPath}`);
91871
+ info(`Line: ${argv.spec}`);
91872
+ try {
91873
+ const result = addLine(repoPath, argv.spec, { force: argv.force, spec: argv.spec });
91874
+ divider();
91875
+ console.error("");
91876
+ console.error(" verificationStages unchanged \u2014 line gate stages are opt-in.");
91877
+ console.error(
91878
+ ` Run the gate with: har line gate ${result.firstGatedStationId} --line ${result.lineId}`
91879
+ );
91880
+ console.error("");
91881
+ console.error(" Next steps:");
91882
+ for (const step of result.nextSteps) {
91883
+ console.error(` ${step}`);
91884
+ }
91885
+ console.error("");
91886
+ } catch (err) {
91887
+ error(err.message);
91888
+ return finishCommand(1);
91889
+ }
91890
+ }
91891
+ function renderStatus(status) {
91892
+ header(`Line: ${status.lineId} \u2014 ${status.title}`);
91893
+ info(`Program: ${status.programPath}${status.source ? ` (source: ${status.source})` : ""}`);
91894
+ info(
91895
+ status.optInEnv ? `Gate: opt-in via ${status.optInEnv}=1` : "Gate: on demand (har line gate <station>)"
91896
+ );
91897
+ info(
91898
+ status.registeredStageIds.length > 0 ? `Registered stages (off verify): ${status.registeredStageIds.join(", ")}` : "Registered stages (off verify): none"
91899
+ );
91900
+ console.error("");
91901
+ for (const station of status.stations) {
91902
+ const marker = station.green ? "\u2713" : station.id === status.nextStationId ? "\u25B6" : "\xB7";
91903
+ const required2 = station.requiredStageIds.length > 0 ? `${station.passedStageIds.length}/${station.requiredStageIds.length} gate stages green` : "no gate stages";
91904
+ console.error(` ${marker} ${station.id} ${station.title} \u2014 ${required2}`);
91905
+ if (station.missingStageIds.length > 0) {
91906
+ console.error(` missing (not registered): ${station.missingStageIds.join(", ")}`);
91907
+ }
91908
+ const pending = station.requiredStageIds.filter((id) => !station.passedStageIds.includes(id));
91909
+ if (pending.length > 0 && station.missingStageIds.length === 0) {
91910
+ console.error(` pending: ${pending.join(", ")}`);
91911
+ }
91912
+ }
91913
+ console.error("");
91914
+ if (status.slotsInFlight.length > 0) {
91915
+ console.error(" Slots in flight:");
91916
+ for (const slot of status.slotsInFlight) {
91917
+ const work = slot.workUnitId ? ` work=${slot.workUnitId}` : "";
91918
+ console.error(` agent ${slot.agentId}: ${slot.branch ?? slot.workDir}${work}`);
91919
+ }
91920
+ console.error("");
91921
+ }
91922
+ for (const warning of status.warnings) {
91923
+ warn(` \u26A0 ${warning}`);
91924
+ }
91925
+ console.error(
91926
+ status.nextStationId ? ` Next station: ${status.nextStationId}` : " All stations green \u2014 hand off for human review (autonomousShip: false)."
91927
+ );
91928
+ console.error("");
91929
+ }
91930
+ function handleLineStatus(argv) {
91931
+ const repoPath = path93.resolve(argv.repo);
91932
+ try {
91933
+ const statuses = argv.id ? [getLineStatus(repoPath, argv.id)] : getAllLineStatuses(repoPath);
91934
+ if (argv.json) {
91935
+ console.log(JSON.stringify(argv.id ? statuses[0] : statuses, null, 2));
91936
+ return;
91937
+ }
91938
+ if (statuses.length === 0) {
91939
+ info("No factory line installed.");
91940
+ info("Scaffold one: har line create <id> \u2022 Install one: har line add <spec>");
91941
+ return;
91942
+ }
91943
+ for (const status of statuses) {
91944
+ renderStatus(status);
91945
+ }
91946
+ } catch (err) {
91947
+ error(err.message);
91948
+ return finishCommand(1);
91949
+ }
91950
+ }
91951
+ async function handleLineGate(argv) {
91952
+ if (!argv.station) {
91953
+ error("Missing station. Usage: har line gate <station> [--line <id>] [--agent <slot>]");
91954
+ return finishCommand(1);
91955
+ }
91956
+ const repoPath = path93.resolve(argv.repo);
91957
+ try {
91958
+ const result = await runLineGate({
91959
+ repoPath,
91960
+ lineId: argv.line,
91961
+ station: argv.station,
91962
+ agentId: argv.agent,
91963
+ force: argv.force
91964
+ });
91965
+ if (argv.json) {
91966
+ console.log(JSON.stringify(result, null, 2));
91967
+ return finishCommand(result.pass ? 0 : 1);
91968
+ }
91969
+ header(`har line gate ${result.station} (${result.lineId})`);
91970
+ if (result.skipped) {
91971
+ info(result.skipReason ?? "Gate skipped.");
91972
+ for (const stage of result.stages) {
91973
+ info(` \xB7 ${stage.stageId} (from ${stage.fromStation}) \u2014 skipped`);
91974
+ }
91975
+ return;
91976
+ }
91977
+ for (const stage of result.stages) {
91978
+ const mark = stage.status === "pass" ? "\u2713" : stage.status === "skipped" ? "\xB7" : "\u2717";
91979
+ const detail = stage.reason ? ` \u2014 ${stage.reason}` : "";
91980
+ const ms = stage.durationMs !== void 0 ? ` (${Math.round(stage.durationMs / 100) / 10}s)` : "";
91981
+ console.error(` ${mark} ${stage.stageId} from ${stage.fromStation} ${stage.status}${ms}${detail}`);
91982
+ }
91983
+ divider();
91984
+ if (result.pass) {
91985
+ success(`Gate passed for station ${result.station}`);
91986
+ } else {
91987
+ error(`Gate failed for station ${result.station}`);
91988
+ }
91989
+ return finishCommand(result.pass ? 0 : 1);
91990
+ } catch (err) {
91991
+ error(err.message);
91992
+ return finishCommand(1);
91993
+ }
91994
+ }
91995
+ function handleLineList(argv) {
91996
+ const repoPath = path93.resolve(argv.repo);
91997
+ const installed = new Set(listInstalledLineIds(repoPath));
91998
+ for (const id of listBundledLineIds()) {
91999
+ console.log(`${id} (bundled)`);
92000
+ }
92001
+ for (const id of listLocalLineIds(repoPath)) {
92002
+ console.log(`${id} (local: .har/lines/${id})${installed.has(id) ? " [installed]" : ""}`);
92003
+ }
92004
+ }
92005
+ function parseStations(value) {
92006
+ if (typeof value !== "string" || value.trim() === "") return void 0;
92007
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
92008
+ }
92009
+ var lineCommand = {
92010
+ command: "line <subcommand>",
92011
+ describe: "Factory lines: multi-station programs with a cumulative gate that never joins verificationStages",
92012
+ builder: (yargs) => yargs.command(
92013
+ "create [id]",
92014
+ "Scaffold a project-owned line at .har/lines/<id>/ (manifest, program, gate stage, README)",
92015
+ (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", {
92016
+ type: "string",
92017
+ describe: "Comma-separated station ids in order (default: S1,S2)"
92018
+ }).option("gate-stage", {
92019
+ type: "boolean",
92020
+ default: true,
92021
+ describe: "Scaffold one registered-but-off-verify gate stage"
92022
+ }).option("opt-in-env", {
92023
+ type: "string",
92024
+ describe: 'Env var that must be "1" for the gate to run (e.g. HAR_FIXTURE_E2E)'
92025
+ }).option("force", { type: "boolean", default: false, describe: "Overwrite an existing line" }),
92026
+ (argv) => handleLineCreate({
92027
+ id: argv.id,
92028
+ repo: argv.repo,
92029
+ title: argv.title,
92030
+ description: argv.description,
92031
+ stations: parseStations(argv.stations),
92032
+ gateStage: argv["gate-stage"],
92033
+ optInEnv: argv["opt-in-env"],
92034
+ force: argv.force
92035
+ })
92036
+ ).command(
92037
+ "add [spec]",
92038
+ "Install a line bundle (local id, path, npm package, or git URL) \u2014 never touches verificationStages",
92039
+ (y) => y.positional("spec", {
92040
+ type: "string",
92041
+ describe: "Line id, path (./line), npm package (@org/pkg), or git URL (github:org/repo)"
92042
+ }).option("repo", { type: "string", default: ".", describe: "Path to the repository" }).option("force", {
92043
+ type: "boolean",
92044
+ default: false,
92045
+ describe: "Overwrite existing line files and stage entries"
92046
+ }),
92047
+ (argv) => handleLineAdd({
92048
+ spec: argv.spec,
92049
+ repo: argv.repo,
92050
+ force: argv.force
92051
+ })
92052
+ ).command(
92053
+ "status [id]",
92054
+ "Show stations, cumulative gate progress from run records, and slots in flight",
92055
+ (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" }),
92056
+ (argv) => handleLineStatus({
92057
+ id: argv.id,
92058
+ repo: argv.repo,
92059
+ json: argv.json
92060
+ })
92061
+ ).command(
92062
+ "gate [station]",
92063
+ "Run the cumulative gate for a station (does NOT run verify and does not widen it)",
92064
+ (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", {
92065
+ type: "boolean",
92066
+ default: false,
92067
+ describe: "Run even when the program declares an opt-in env var that is not set"
92068
+ }).option("json", { type: "boolean", default: false, describe: "Emit structured JSON" }),
92069
+ (argv) => handleLineGate({
92070
+ station: argv.station,
92071
+ repo: argv.repo,
92072
+ line: argv.line,
92073
+ agent: argv.agent,
92074
+ force: argv.force,
92075
+ json: argv.json
92076
+ })
92077
+ ).command(
92078
+ "list",
92079
+ "List lines available to this repository (bundled and local)",
92080
+ (y) => y.option("repo", { type: "string", default: ".", describe: "Path to the repository" }),
92081
+ (argv) => handleLineList({ repo: argv.repo })
92082
+ ).demandCommand(1, "Please specify a subcommand: create, add, status, gate, list"),
92083
+ handler: () => {
92084
+ }
92085
+ };
92086
+
90696
92087
  // src/cli/commands/plugin.ts
90697
- var path89 = __toESM(require("path"));
92088
+ var path95 = __toESM(require("path"));
90698
92089
 
90699
92090
  // src/harness/plugin-create.ts
90700
- var fs75 = __toESM(require("fs"));
90701
- var path88 = __toESM(require("path"));
92091
+ var fs78 = __toESM(require("fs"));
92092
+ var path94 = __toESM(require("path"));
90702
92093
  init_paths();
90703
92094
  init_schema2();
90704
92095
  var PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
90705
- function renderSkeleton(templatePath, tokens) {
90706
- let content = fs75.readFileSync(templatePath, "utf8");
92096
+ function renderSkeleton2(templatePath, tokens) {
92097
+ let content = fs78.readFileSync(templatePath, "utf8");
90707
92098
  for (const [token, value] of Object.entries(tokens)) {
90708
92099
  content = content.replace(new RegExp(token, "g"), value);
90709
92100
  }
90710
92101
  return content;
90711
92102
  }
90712
92103
  function createLocalPlugin(repoPath, options) {
90713
- const resolved = path88.resolve(repoPath);
92104
+ const resolved = path94.resolve(repoPath);
90714
92105
  if (!harnessExists(resolved)) {
90715
92106
  throw new Error('No .har/ harness found. Run "har onboard" first.');
90716
92107
  }
@@ -90726,9 +92117,9 @@ function createLocalPlugin(repoPath, options) {
90726
92117
  `Invalid stage kind "${kind}". Available: ${HarnessStageKindSchema.options.join(", ")}`
90727
92118
  );
90728
92119
  }
90729
- const pluginDir = path88.join(resolved, LOCAL_PLUGINS_DIR, id);
92120
+ const pluginDir = path94.join(resolved, LOCAL_PLUGINS_DIR, id);
90730
92121
  const pluginDirRel = `${LOCAL_PLUGINS_DIR}/${id}`;
90731
- if (fs75.existsSync(pluginDir) && !options.force) {
92122
+ if (fs78.existsSync(pluginDir) && !options.force) {
90732
92123
  throw new Error(`Plugin already exists: ${pluginDirRel}/. Use --force to overwrite.`);
90733
92124
  }
90734
92125
  const description = options.description ?? `Custom ${kind} stage (local plugin: ${id})`;
@@ -90770,16 +92161,16 @@ function createLocalPlugin(repoPath, options) {
90770
92161
  });
90771
92162
  const filesWritten = [];
90772
92163
  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);
92164
+ const abs = path94.join(pluginDir, rel);
92165
+ fs78.mkdirSync(path94.dirname(abs), { recursive: true });
92166
+ fs78.writeFileSync(abs, content);
92167
+ if (executable) fs78.chmodSync(abs, 493);
90777
92168
  filesWritten.push(`${pluginDirRel}/${rel}`);
90778
92169
  };
90779
92170
  write("template.manifest.json", `${JSON.stringify(manifest, null, 2)}
90780
92171
  `);
90781
- const stageScript = renderSkeleton(
90782
- path88.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
92172
+ const stageScript = renderSkeleton2(
92173
+ path94.join(resolveTemplatesDir(), "plugins", "custom-stage-skeleton.sh"),
90783
92174
  {
90784
92175
  __STAGE_ID__: id,
90785
92176
  __STAGE_KIND__: kind,
@@ -90787,8 +92178,8 @@ function createLocalPlugin(repoPath, options) {
90787
92178
  }
90788
92179
  );
90789
92180
  write(`stages/${id}.sh`, stageScript, true);
90790
- const readme = renderSkeleton(
90791
- path88.join(resolveTemplatesDir(), "plugins", "local-plugin-skeleton", "README.md"),
92181
+ const readme = renderSkeleton2(
92182
+ path94.join(resolveTemplatesDir(), "plugins", "local-plugin-skeleton", "README.md"),
90792
92183
  { __PLUGIN_ID__: id }
90793
92184
  );
90794
92185
  write("README.md", readme);
@@ -90820,7 +92211,7 @@ async function handlePluginCreate(argv) {
90820
92211
  error('Missing plugin id. Usage: har plugin create <id> [--kind test] [--description "..."]');
90821
92212
  return finishCommand(1);
90822
92213
  }
90823
- const repoPath = path89.resolve(argv.repo);
92214
+ const repoPath = path95.resolve(argv.repo);
90824
92215
  header("har plugin create");
90825
92216
  info(`Repository: ${repoPath}`);
90826
92217
  info(`Plugin: ${argv.id}`);
@@ -90851,7 +92242,7 @@ async function handlePluginCreate(argv) {
90851
92242
  }
90852
92243
  }
90853
92244
  function handlePluginList(argv) {
90854
- const repoPath = path89.resolve(argv.repo);
92245
+ const repoPath = path95.resolve(argv.repo);
90855
92246
  for (const id of listPluginIds()) {
90856
92247
  console.log(`${id} (bundled)`);
90857
92248
  }
@@ -91018,7 +92409,7 @@ var preferencesCommand = {
91018
92409
  };
91019
92410
 
91020
92411
  // src/cli/commands/telemetry.ts
91021
- var path90 = __toESM(require("path"));
92412
+ var path96 = __toESM(require("path"));
91022
92413
  init_control_config();
91023
92414
  init_control_sync();
91024
92415
  init_manifest();
@@ -91111,7 +92502,7 @@ async function handleOff() {
91111
92502
  info("Mission Control will not auto-start. Existing usage rows are kept.");
91112
92503
  }
91113
92504
  async function handleWriteEnv(argv) {
91114
- const repoPath = path90.resolve(argv.repo);
92505
+ const repoPath = path96.resolve(argv.repo);
91115
92506
  const harnessRoot = resolveHarnessRoot(repoPath);
91116
92507
  const session = readSlotRegistry(harnessRoot, argv.agentId);
91117
92508
  const workDir = argv.workDir ?? session?.workDir ?? repoPath;
@@ -91123,7 +92514,7 @@ async function handleWriteEnv(argv) {
91123
92514
  suffix,
91124
92515
  createdAt: session?.createdAt
91125
92516
  });
91126
- const envFile = argv.envFile ?? path90.join(workDir, `.env.agent.${argv.agentId}`);
92517
+ const envFile = argv.envFile ?? path96.join(workDir, `.env.agent.${argv.agentId}`);
91127
92518
  const attrs = {
91128
92519
  sessionKey,
91129
92520
  agentId: argv.agentId,
@@ -91139,7 +92530,7 @@ async function handleWriteEnv(argv) {
91139
92530
  success(`Wrote session attribution to ${envFile}`);
91140
92531
  }
91141
92532
  function handlePrintEnv(argv) {
91142
- const repoPath = path90.resolve(argv.repo);
92533
+ const repoPath = path96.resolve(argv.repo);
91143
92534
  const workDir = argv.workDir ?? repoPath;
91144
92535
  const sessionKey = buildSessionKey({
91145
92536
  branch: argv.branch,
@@ -91262,7 +92653,7 @@ var telemetryCommand = {
91262
92653
  // src/cli/index.ts
91263
92654
  async function runCli() {
91264
92655
  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();
92656
+ 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
92657
  await syncDirtyRepos();
91267
92658
  }
91268
92659