@osfactory/har 0.45.0 → 0.46.1
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 +887 -685
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -553,8 +553,8 @@ var init_parseUtil = __esm({
|
|
|
553
553
|
init_errors();
|
|
554
554
|
init_en();
|
|
555
555
|
makeIssue = (params) => {
|
|
556
|
-
const { data, path:
|
|
557
|
-
const fullPath = [...
|
|
556
|
+
const { data, path: path62, errorMaps, issueData } = params;
|
|
557
|
+
const fullPath = [...path62, ...issueData.path || []];
|
|
558
558
|
const fullIssue = {
|
|
559
559
|
...issueData,
|
|
560
560
|
path: fullPath
|
|
@@ -862,11 +862,11 @@ var init_types = __esm({
|
|
|
862
862
|
init_parseUtil();
|
|
863
863
|
init_util();
|
|
864
864
|
ParseInputLazyPath = class {
|
|
865
|
-
constructor(parent, value,
|
|
865
|
+
constructor(parent, value, path62, key) {
|
|
866
866
|
this._cachedPath = [];
|
|
867
867
|
this.parent = parent;
|
|
868
868
|
this.data = value;
|
|
869
|
-
this._path =
|
|
869
|
+
this._path = path62;
|
|
870
870
|
this._key = key;
|
|
871
871
|
}
|
|
872
872
|
get path() {
|
|
@@ -32051,6 +32051,7 @@ async function buildPortalPayload(repoPath, controlApiUrl, since, full = false)
|
|
|
32051
32051
|
const workUnits = listWorkUnits(harnessRoot);
|
|
32052
32052
|
const attempts = listWorkAttempts(harnessRoot);
|
|
32053
32053
|
const validationBindings = listValidationBindings(harnessRoot);
|
|
32054
|
+
const userEmail = resolvePortalUserEmail();
|
|
32054
32055
|
const { usage: usage2, events, maxSyncedAt } = await collectPortalTelemetry(
|
|
32055
32056
|
repoPath,
|
|
32056
32057
|
status.slots,
|
|
@@ -32069,13 +32070,16 @@ async function buildPortalPayload(repoPath, controlApiUrl, since, full = false)
|
|
|
32069
32070
|
...stagesRegistry ? { stagesRegistry } : {},
|
|
32070
32071
|
slots: status.slots,
|
|
32071
32072
|
generatedAt: status.generatedAt,
|
|
32072
|
-
...validations.length > 0 ? {
|
|
32073
|
+
...validations.length > 0 ? {
|
|
32074
|
+
validations: userEmail ? validations.map((v2) => ({ ...v2, userEmail })) : validations
|
|
32075
|
+
} : {},
|
|
32073
32076
|
...workUnits.length > 0 ? { workUnits } : {},
|
|
32074
32077
|
...attempts.length > 0 ? { attempts } : {},
|
|
32075
32078
|
...validationBindings.length > 0 ? { validationBindings } : {},
|
|
32076
32079
|
...usage2.length > 0 ? { usage: usage2 } : {}
|
|
32077
32080
|
};
|
|
32078
|
-
|
|
32081
|
+
const runsPayload = userEmail ? runs.map((run2) => ({ ...run2, userEmail })) : runs;
|
|
32082
|
+
return { syncBody, runs: runsPayload, events, maxSyncedAt };
|
|
32079
32083
|
}
|
|
32080
32084
|
async function isControlApiReachable(apiUrl = getControlApiUrl()) {
|
|
32081
32085
|
try {
|
|
@@ -32518,32 +32522,32 @@ __export(otel_hooks_exports, {
|
|
|
32518
32522
|
});
|
|
32519
32523
|
function getOtelHooksHome() {
|
|
32520
32524
|
if (process.env.HAR_OTEL_HOOKS_HOME) {
|
|
32521
|
-
return
|
|
32525
|
+
return path47.resolve(process.env.HAR_OTEL_HOOKS_HOME);
|
|
32522
32526
|
}
|
|
32523
|
-
return
|
|
32527
|
+
return path47.join(os15.homedir(), ".har", "otel-hooks");
|
|
32524
32528
|
}
|
|
32525
32529
|
function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
|
|
32526
|
-
return
|
|
32530
|
+
return path47.join(hooksHome, "otel_config.json");
|
|
32527
32531
|
}
|
|
32528
32532
|
function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
|
|
32529
|
-
return
|
|
32533
|
+
return path47.join(hooksHome, "run-otel-hook.sh");
|
|
32530
32534
|
}
|
|
32531
32535
|
function getOtelHooksStateDir(hooksHome = getOtelHooksHome()) {
|
|
32532
|
-
return
|
|
32536
|
+
return path47.join(hooksHome, "state");
|
|
32533
32537
|
}
|
|
32534
32538
|
function localOtelHookBin(hooksHome) {
|
|
32535
|
-
return
|
|
32539
|
+
return path47.join(hooksHome, "node_modules", ".bin", "otel-hook");
|
|
32536
32540
|
}
|
|
32537
32541
|
function installedPackageVersion(hooksHome) {
|
|
32538
32542
|
try {
|
|
32539
|
-
const pkgPath =
|
|
32543
|
+
const pkgPath = path47.join(
|
|
32540
32544
|
hooksHome,
|
|
32541
32545
|
"node_modules",
|
|
32542
32546
|
"@osfactory",
|
|
32543
32547
|
"otel-hook",
|
|
32544
32548
|
"package.json"
|
|
32545
32549
|
);
|
|
32546
|
-
const pkg = JSON.parse(
|
|
32550
|
+
const pkg = JSON.parse(fs41.readFileSync(pkgPath, "utf8"));
|
|
32547
32551
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
32548
32552
|
} catch {
|
|
32549
32553
|
return null;
|
|
@@ -32594,15 +32598,15 @@ function buildOtelHooksConfig(options) {
|
|
|
32594
32598
|
};
|
|
32595
32599
|
}
|
|
32596
32600
|
function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
|
|
32597
|
-
|
|
32601
|
+
fs41.mkdirSync(hooksHome, { recursive: true });
|
|
32598
32602
|
const configPath = getOtelHooksConfigPath(hooksHome);
|
|
32599
|
-
|
|
32603
|
+
fs41.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
|
|
32600
32604
|
`);
|
|
32601
32605
|
return configPath;
|
|
32602
32606
|
}
|
|
32603
32607
|
function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
|
|
32604
|
-
|
|
32605
|
-
|
|
32608
|
+
fs41.mkdirSync(hooksHome, { recursive: true });
|
|
32609
|
+
fs41.mkdirSync(getOtelHooksStateDir(hooksHome), { recursive: true });
|
|
32606
32610
|
const wrapperPath = getOtelHooksWrapperPath(hooksHome);
|
|
32607
32611
|
const configPath = getOtelHooksConfigPath(hooksHome);
|
|
32608
32612
|
const stateDir = getOtelHooksStateDir(hooksHome);
|
|
@@ -32614,9 +32618,9 @@ exec "${otelHookCommand}" run \\
|
|
|
32614
32618
|
--state-dir "${stateDir}" \\
|
|
32615
32619
|
"$@"
|
|
32616
32620
|
`;
|
|
32617
|
-
|
|
32621
|
+
fs41.writeFileSync(wrapperPath, script, { mode: 493 });
|
|
32618
32622
|
try {
|
|
32619
|
-
|
|
32623
|
+
fs41.chmodSync(wrapperPath, 493);
|
|
32620
32624
|
} catch {
|
|
32621
32625
|
}
|
|
32622
32626
|
return wrapperPath;
|
|
@@ -32629,11 +32633,11 @@ function commandExists2(command2) {
|
|
|
32629
32633
|
}
|
|
32630
32634
|
function resolveOtelHookCommand(hooksHome) {
|
|
32631
32635
|
const local = localOtelHookBin(hooksHome);
|
|
32632
|
-
if (
|
|
32636
|
+
if (fs41.existsSync(local)) return local;
|
|
32633
32637
|
return null;
|
|
32634
32638
|
}
|
|
32635
32639
|
function tryInstallPackage(hooksHome) {
|
|
32636
|
-
|
|
32640
|
+
fs41.mkdirSync(hooksHome, { recursive: true });
|
|
32637
32641
|
if (!commandExists2("npm")) {
|
|
32638
32642
|
return {
|
|
32639
32643
|
ok: false,
|
|
@@ -32645,7 +32649,7 @@ function tryInstallPackage(hooksHome) {
|
|
|
32645
32649
|
["install", "--prefix", hooksHome, "--no-fund", "--no-audit", OTEL_HOOKS_PACKAGE],
|
|
32646
32650
|
{ encoding: "utf8", timeout: 18e4 }
|
|
32647
32651
|
);
|
|
32648
|
-
if (result.status === 0 &&
|
|
32652
|
+
if (result.status === 0 && fs41.existsSync(localOtelHookBin(hooksHome))) {
|
|
32649
32653
|
return { ok: true, detail: `npm install --prefix ${hooksHome} ${OTEL_HOOKS_PACKAGE}` };
|
|
32650
32654
|
}
|
|
32651
32655
|
return {
|
|
@@ -32671,14 +32675,14 @@ function uninstallLegacyPythonHooks(hooksHome) {
|
|
|
32671
32675
|
}
|
|
32672
32676
|
function removeLegacyPythonArtifacts(hooksHome) {
|
|
32673
32677
|
const legacyPaths = [
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32678
|
+
path47.join(hooksHome, ".venv"),
|
|
32679
|
+
path47.join(hooksHome, "otel_config.yaml"),
|
|
32680
|
+
path47.join(hooksHome, "venv")
|
|
32677
32681
|
];
|
|
32678
32682
|
for (const target of legacyPaths) {
|
|
32679
32683
|
try {
|
|
32680
|
-
if (
|
|
32681
|
-
|
|
32684
|
+
if (fs41.existsSync(target)) {
|
|
32685
|
+
fs41.rmSync(target, { recursive: true, force: true });
|
|
32682
32686
|
}
|
|
32683
32687
|
} catch {
|
|
32684
32688
|
}
|
|
@@ -32719,10 +32723,10 @@ function runSetupProvider(providerId, otelHookCommand, wrapperPath) {
|
|
|
32719
32723
|
};
|
|
32720
32724
|
}
|
|
32721
32725
|
function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
32722
|
-
if (!
|
|
32726
|
+
if (!fs41.existsSync(filePath)) return false;
|
|
32723
32727
|
let raw;
|
|
32724
32728
|
try {
|
|
32725
|
-
raw =
|
|
32729
|
+
raw = fs41.readFileSync(filePath, "utf8");
|
|
32726
32730
|
} catch {
|
|
32727
32731
|
return false;
|
|
32728
32732
|
}
|
|
@@ -32769,15 +32773,15 @@ function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
|
|
|
32769
32773
|
return changed;
|
|
32770
32774
|
};
|
|
32771
32775
|
if (!walk(parsed)) return false;
|
|
32772
|
-
|
|
32776
|
+
fs41.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
32773
32777
|
`);
|
|
32774
32778
|
return true;
|
|
32775
32779
|
}
|
|
32776
32780
|
function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
|
|
32777
|
-
if (!
|
|
32781
|
+
if (!fs41.existsSync(filePath)) return false;
|
|
32778
32782
|
let raw;
|
|
32779
32783
|
try {
|
|
32780
|
-
raw =
|
|
32784
|
+
raw = fs41.readFileSync(filePath, "utf8");
|
|
32781
32785
|
} catch {
|
|
32782
32786
|
return false;
|
|
32783
32787
|
}
|
|
@@ -32812,21 +32816,21 @@ function pruneLegacyCursorHookEvents(filePath, wrapperPath) {
|
|
|
32812
32816
|
}
|
|
32813
32817
|
}
|
|
32814
32818
|
if (!changed) return false;
|
|
32815
|
-
|
|
32819
|
+
fs41.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
32816
32820
|
`);
|
|
32817
32821
|
return true;
|
|
32818
32822
|
}
|
|
32819
32823
|
function rewriteKnownHookRegistrations(wrapperPath) {
|
|
32820
32824
|
const home = os15.homedir();
|
|
32821
32825
|
const candidates = [
|
|
32822
|
-
|
|
32823
|
-
|
|
32824
|
-
|
|
32826
|
+
path47.join(home, ".cursor", "hooks.json"),
|
|
32827
|
+
path47.join(home, ".claude", "settings.json"),
|
|
32828
|
+
path47.join(home, ".codex", "hooks.json")
|
|
32825
32829
|
];
|
|
32826
32830
|
for (const filePath of candidates) {
|
|
32827
32831
|
try {
|
|
32828
32832
|
rewriteHookCommandsToWrapper(filePath, wrapperPath);
|
|
32829
|
-
if (filePath.endsWith(`${
|
|
32833
|
+
if (filePath.endsWith(`${path47.sep}.cursor${path47.sep}hooks.json`)) {
|
|
32830
32834
|
pruneLegacyCursorHookEvents(filePath, wrapperPath);
|
|
32831
32835
|
}
|
|
32832
32836
|
} catch {
|
|
@@ -32842,7 +32846,7 @@ function ensureOtelHooks(options) {
|
|
|
32842
32846
|
const hooksHome = getOtelHooksHome();
|
|
32843
32847
|
const errors = [];
|
|
32844
32848
|
const providersSetup = [];
|
|
32845
|
-
|
|
32849
|
+
fs41.mkdirSync(hooksHome, { recursive: true });
|
|
32846
32850
|
let otelHookCommand = resolveOtelHookCommand(hooksHome);
|
|
32847
32851
|
const needsInstall = !otelHookCommand || installedPackageVersion(hooksHome) !== pinnedVersion();
|
|
32848
32852
|
if (needsInstall) {
|
|
@@ -32936,14 +32940,14 @@ function ensureOtelHooks(options) {
|
|
|
32936
32940
|
function disableOtelHooksExport() {
|
|
32937
32941
|
return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
|
|
32938
32942
|
}
|
|
32939
|
-
var import_child_process10,
|
|
32943
|
+
var import_child_process10, fs41, os15, path47, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_PROVIDERS, OTEL_HOOKS_AGENTS, LEGACY_CURSOR_ONLY_HOOK_EVENTS;
|
|
32940
32944
|
var init_otel_hooks = __esm({
|
|
32941
32945
|
"src/core/otel-hooks.ts"() {
|
|
32942
32946
|
"use strict";
|
|
32943
32947
|
import_child_process10 = require("child_process");
|
|
32944
|
-
|
|
32948
|
+
fs41 = __toESM(require("fs"));
|
|
32945
32949
|
os15 = __toESM(require("os"));
|
|
32946
|
-
|
|
32950
|
+
path47 = __toESM(require("path"));
|
|
32947
32951
|
init_control_config();
|
|
32948
32952
|
init_telemetry_config();
|
|
32949
32953
|
OTEL_HOOKS_PACKAGE = "@osfactory/otel-hook@0.1.2";
|
|
@@ -34059,15 +34063,15 @@ var require_route = __commonJS({
|
|
|
34059
34063
|
};
|
|
34060
34064
|
}
|
|
34061
34065
|
function wrapConversion(toModel, graph) {
|
|
34062
|
-
const
|
|
34066
|
+
const path62 = [graph[toModel].parent, toModel];
|
|
34063
34067
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
34064
34068
|
let cur = graph[toModel].parent;
|
|
34065
34069
|
while (graph[cur].parent) {
|
|
34066
|
-
|
|
34070
|
+
path62.unshift(graph[cur].parent);
|
|
34067
34071
|
fn = link(conversions[graph[cur].parent][cur], fn);
|
|
34068
34072
|
cur = graph[cur].parent;
|
|
34069
34073
|
}
|
|
34070
|
-
fn.conversion =
|
|
34074
|
+
fn.conversion = path62;
|
|
34071
34075
|
return fn;
|
|
34072
34076
|
}
|
|
34073
34077
|
module2.exports = function(fromModel) {
|
|
@@ -39997,7 +40001,7 @@ var require_iso2022 = __commonJS({
|
|
|
39997
40001
|
// node_modules/chardet/index.js
|
|
39998
40002
|
var require_chardet = __commonJS({
|
|
39999
40003
|
"node_modules/chardet/index.js"(exports2, module2) {
|
|
40000
|
-
var
|
|
40004
|
+
var fs48 = require("fs");
|
|
40001
40005
|
var utf8 = require_utf8();
|
|
40002
40006
|
var unicode = require_unicode();
|
|
40003
40007
|
var mbcs = require_mbcs();
|
|
@@ -40071,28 +40075,28 @@ var require_chardet = __commonJS({
|
|
|
40071
40075
|
var fd;
|
|
40072
40076
|
var handler = function(err, buffer) {
|
|
40073
40077
|
if (fd) {
|
|
40074
|
-
|
|
40078
|
+
fs48.closeSync(fd);
|
|
40075
40079
|
}
|
|
40076
40080
|
if (err) return cb(err, null);
|
|
40077
40081
|
cb(null, self.detect(buffer, opts));
|
|
40078
40082
|
};
|
|
40079
40083
|
if (opts && opts.sampleSize) {
|
|
40080
|
-
fd =
|
|
40081
|
-
|
|
40084
|
+
fd = fs48.openSync(filepath, "r"), sample = Buffer.allocUnsafe(opts.sampleSize);
|
|
40085
|
+
fs48.read(fd, sample, 0, opts.sampleSize, null, function(err) {
|
|
40082
40086
|
handler(err, sample);
|
|
40083
40087
|
});
|
|
40084
40088
|
return;
|
|
40085
40089
|
}
|
|
40086
|
-
|
|
40090
|
+
fs48.readFile(filepath, handler);
|
|
40087
40091
|
};
|
|
40088
40092
|
module2.exports.detectFileSync = function(filepath, opts) {
|
|
40089
40093
|
if (opts && opts.sampleSize) {
|
|
40090
|
-
var fd =
|
|
40091
|
-
|
|
40092
|
-
|
|
40094
|
+
var fd = fs48.openSync(filepath, "r"), sample2 = Buffer.allocUnsafe(opts.sampleSize);
|
|
40095
|
+
fs48.readSync(fd, sample2, 0, opts.sampleSize);
|
|
40096
|
+
fs48.closeSync(fd);
|
|
40093
40097
|
return self.detect(sample2, opts);
|
|
40094
40098
|
}
|
|
40095
|
-
return self.detect(
|
|
40099
|
+
return self.detect(fs48.readFileSync(filepath), opts);
|
|
40096
40100
|
};
|
|
40097
40101
|
module2.exports.detectAll = function(buffer, opts) {
|
|
40098
40102
|
if (typeof opts !== "object") {
|
|
@@ -43557,16 +43561,16 @@ var require_os_tmpdir = __commonJS({
|
|
|
43557
43561
|
var isWindows = process.platform === "win32";
|
|
43558
43562
|
var trailingSlashRe = isWindows ? /[^:]\\$/ : /.\/$/;
|
|
43559
43563
|
module2.exports = function() {
|
|
43560
|
-
var
|
|
43564
|
+
var path62;
|
|
43561
43565
|
if (isWindows) {
|
|
43562
|
-
|
|
43566
|
+
path62 = process.env.TEMP || process.env.TMP || (process.env.SystemRoot || process.env.windir) + "\\temp";
|
|
43563
43567
|
} else {
|
|
43564
|
-
|
|
43568
|
+
path62 = process.env.TMPDIR || process.env.TMP || process.env.TEMP || "/tmp";
|
|
43565
43569
|
}
|
|
43566
|
-
if (trailingSlashRe.test(
|
|
43567
|
-
|
|
43570
|
+
if (trailingSlashRe.test(path62)) {
|
|
43571
|
+
path62 = path62.slice(0, -1);
|
|
43568
43572
|
}
|
|
43569
|
-
return
|
|
43573
|
+
return path62;
|
|
43570
43574
|
};
|
|
43571
43575
|
}
|
|
43572
43576
|
});
|
|
@@ -43574,8 +43578,8 @@ var require_os_tmpdir = __commonJS({
|
|
|
43574
43578
|
// node_modules/tmp/lib/tmp.js
|
|
43575
43579
|
var require_tmp = __commonJS({
|
|
43576
43580
|
"node_modules/tmp/lib/tmp.js"(exports2, module2) {
|
|
43577
|
-
var
|
|
43578
|
-
var
|
|
43581
|
+
var fs48 = require("fs");
|
|
43582
|
+
var path62 = require("path");
|
|
43579
43583
|
var crypto6 = require("crypto");
|
|
43580
43584
|
var osTmpDir = require_os_tmpdir();
|
|
43581
43585
|
var _c2 = process.binding("constants");
|
|
@@ -43617,7 +43621,7 @@ var require_tmp = __commonJS({
|
|
|
43617
43621
|
}
|
|
43618
43622
|
function _generateTmpName(opts) {
|
|
43619
43623
|
if (opts.name) {
|
|
43620
|
-
return
|
|
43624
|
+
return path62.join(opts.dir || tmpDir, opts.name);
|
|
43621
43625
|
}
|
|
43622
43626
|
if (opts.template) {
|
|
43623
43627
|
return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
|
|
@@ -43628,7 +43632,7 @@ var require_tmp = __commonJS({
|
|
|
43628
43632
|
_randomChars(12),
|
|
43629
43633
|
opts.postfix || ""
|
|
43630
43634
|
].join("");
|
|
43631
|
-
return
|
|
43635
|
+
return path62.join(opts.dir || tmpDir, name);
|
|
43632
43636
|
}
|
|
43633
43637
|
function tmpName(options, callback) {
|
|
43634
43638
|
var args = _parseArguments(options, callback), opts = args[0], cb = args[1], tries = opts.name ? 1 : opts.tries || DEFAULT_TRIES;
|
|
@@ -43638,7 +43642,7 @@ var require_tmp = __commonJS({
|
|
|
43638
43642
|
return cb(new Error("Invalid template provided"));
|
|
43639
43643
|
(function _getUniqueName() {
|
|
43640
43644
|
const name = _generateTmpName(opts);
|
|
43641
|
-
|
|
43645
|
+
fs48.stat(name, function(err) {
|
|
43642
43646
|
if (!err) {
|
|
43643
43647
|
if (tries-- > 0) return _getUniqueName();
|
|
43644
43648
|
return cb(new Error("Could not get a unique tmp filename, max tries reached " + name));
|
|
@@ -43656,7 +43660,7 @@ var require_tmp = __commonJS({
|
|
|
43656
43660
|
do {
|
|
43657
43661
|
const name = _generateTmpName(opts);
|
|
43658
43662
|
try {
|
|
43659
|
-
|
|
43663
|
+
fs48.statSync(name);
|
|
43660
43664
|
} catch (e) {
|
|
43661
43665
|
return name;
|
|
43662
43666
|
}
|
|
@@ -43668,13 +43672,13 @@ var require_tmp = __commonJS({
|
|
|
43668
43672
|
opts.postfix = _isUndefined(opts.postfix) ? ".tmp" : opts.postfix;
|
|
43669
43673
|
tmpName(opts, function _tmpNameCreated(err, name) {
|
|
43670
43674
|
if (err) return cb(err);
|
|
43671
|
-
|
|
43675
|
+
fs48.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) {
|
|
43672
43676
|
if (err2) return cb(err2);
|
|
43673
43677
|
if (opts.discardDescriptor) {
|
|
43674
|
-
return
|
|
43678
|
+
return fs48.close(fd, function _discardCallback(err3) {
|
|
43675
43679
|
if (err3) {
|
|
43676
43680
|
try {
|
|
43677
|
-
|
|
43681
|
+
fs48.unlinkSync(name);
|
|
43678
43682
|
} catch (e) {
|
|
43679
43683
|
if (!isENOENT(e)) {
|
|
43680
43684
|
err3 = e;
|
|
@@ -43697,9 +43701,9 @@ var require_tmp = __commonJS({
|
|
|
43697
43701
|
opts.postfix = opts.postfix || ".tmp";
|
|
43698
43702
|
const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
|
|
43699
43703
|
const name = tmpNameSync(opts);
|
|
43700
|
-
var fd =
|
|
43704
|
+
var fd = fs48.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
|
|
43701
43705
|
if (opts.discardDescriptor) {
|
|
43702
|
-
|
|
43706
|
+
fs48.closeSync(fd);
|
|
43703
43707
|
fd = void 0;
|
|
43704
43708
|
}
|
|
43705
43709
|
return {
|
|
@@ -43711,9 +43715,9 @@ var require_tmp = __commonJS({
|
|
|
43711
43715
|
function _rmdirRecursiveSync(root) {
|
|
43712
43716
|
const dirs = [root];
|
|
43713
43717
|
do {
|
|
43714
|
-
var dir2 = dirs.pop(), deferred = false, files =
|
|
43718
|
+
var dir2 = dirs.pop(), deferred = false, files = fs48.readdirSync(dir2);
|
|
43715
43719
|
for (var i = 0, length = files.length; i < length; i++) {
|
|
43716
|
-
var file2 =
|
|
43720
|
+
var file2 = path62.join(dir2, files[i]), stat = fs48.lstatSync(file2);
|
|
43717
43721
|
if (stat.isDirectory()) {
|
|
43718
43722
|
if (!deferred) {
|
|
43719
43723
|
deferred = true;
|
|
@@ -43721,11 +43725,11 @@ var require_tmp = __commonJS({
|
|
|
43721
43725
|
}
|
|
43722
43726
|
dirs.push(file2);
|
|
43723
43727
|
} else {
|
|
43724
|
-
|
|
43728
|
+
fs48.unlinkSync(file2);
|
|
43725
43729
|
}
|
|
43726
43730
|
}
|
|
43727
43731
|
if (!deferred) {
|
|
43728
|
-
|
|
43732
|
+
fs48.rmdirSync(dir2);
|
|
43729
43733
|
}
|
|
43730
43734
|
} while (dirs.length !== 0);
|
|
43731
43735
|
}
|
|
@@ -43733,7 +43737,7 @@ var require_tmp = __commonJS({
|
|
|
43733
43737
|
var args = _parseArguments(options, callback), opts = args[0], cb = args[1];
|
|
43734
43738
|
tmpName(opts, function _tmpNameCreated(err, name) {
|
|
43735
43739
|
if (err) return cb(err);
|
|
43736
|
-
|
|
43740
|
+
fs48.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) {
|
|
43737
43741
|
if (err2) return cb(err2);
|
|
43738
43742
|
cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
|
|
43739
43743
|
});
|
|
@@ -43742,7 +43746,7 @@ var require_tmp = __commonJS({
|
|
|
43742
43746
|
function dirSync(options) {
|
|
43743
43747
|
var args = _parseArguments(options), opts = args[0];
|
|
43744
43748
|
const name = tmpNameSync(opts);
|
|
43745
|
-
|
|
43749
|
+
fs48.mkdirSync(name, opts.mode || DIR_MODE);
|
|
43746
43750
|
return {
|
|
43747
43751
|
name,
|
|
43748
43752
|
removeCallback: _prepareTmpDirRemoveCallback(name, opts)
|
|
@@ -43752,7 +43756,7 @@ var require_tmp = __commonJS({
|
|
|
43752
43756
|
const removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
|
|
43753
43757
|
try {
|
|
43754
43758
|
if (0 <= fdPath[0]) {
|
|
43755
|
-
|
|
43759
|
+
fs48.closeSync(fdPath[0]);
|
|
43756
43760
|
}
|
|
43757
43761
|
} catch (e) {
|
|
43758
43762
|
if (!isEBADF(e) && !isENOENT(e)) {
|
|
@@ -43760,7 +43764,7 @@ var require_tmp = __commonJS({
|
|
|
43760
43764
|
}
|
|
43761
43765
|
}
|
|
43762
43766
|
try {
|
|
43763
|
-
|
|
43767
|
+
fs48.unlinkSync(fdPath[1]);
|
|
43764
43768
|
} catch (e) {
|
|
43765
43769
|
if (!isENOENT(e)) {
|
|
43766
43770
|
throw e;
|
|
@@ -43773,7 +43777,7 @@ var require_tmp = __commonJS({
|
|
|
43773
43777
|
return removeCallback;
|
|
43774
43778
|
}
|
|
43775
43779
|
function _prepareTmpDirRemoveCallback(name, opts) {
|
|
43776
|
-
const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync :
|
|
43780
|
+
const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs48.rmdirSync.bind(fs48);
|
|
43777
43781
|
const removeCallback = _prepareRemoveCallback(removeFunction, name);
|
|
43778
43782
|
if (!opts.keep) {
|
|
43779
43783
|
_removeObjects.unshift(removeCallback);
|
|
@@ -56906,8 +56910,8 @@ var require_utils = __commonJS({
|
|
|
56906
56910
|
}
|
|
56907
56911
|
return ind;
|
|
56908
56912
|
}
|
|
56909
|
-
function removeDotSegments(
|
|
56910
|
-
let input =
|
|
56913
|
+
function removeDotSegments(path62) {
|
|
56914
|
+
let input = path62;
|
|
56911
56915
|
const output = [];
|
|
56912
56916
|
let nextSlash = -1;
|
|
56913
56917
|
let len = 0;
|
|
@@ -57159,8 +57163,8 @@ var require_schemes = __commonJS({
|
|
|
57159
57163
|
wsComponent.secure = void 0;
|
|
57160
57164
|
}
|
|
57161
57165
|
if (wsComponent.resourceName) {
|
|
57162
|
-
const [
|
|
57163
|
-
wsComponent.path =
|
|
57166
|
+
const [path62, query] = wsComponent.resourceName.split("?");
|
|
57167
|
+
wsComponent.path = path62 && path62 !== "/" ? path62 : void 0;
|
|
57164
57168
|
wsComponent.query = query;
|
|
57165
57169
|
wsComponent.resourceName = void 0;
|
|
57166
57170
|
}
|
|
@@ -60553,12 +60557,12 @@ var require_dist = __commonJS({
|
|
|
60553
60557
|
throw new Error(`Unknown format "${name}"`);
|
|
60554
60558
|
return f;
|
|
60555
60559
|
};
|
|
60556
|
-
function addFormats(ajv, list,
|
|
60560
|
+
function addFormats(ajv, list, fs48, exportName) {
|
|
60557
60561
|
var _a2;
|
|
60558
60562
|
var _b2;
|
|
60559
60563
|
(_a2 = (_b2 = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
60560
60564
|
for (const f of list)
|
|
60561
|
-
ajv.addFormat(f,
|
|
60565
|
+
ajv.addFormat(f, fs48[f]);
|
|
60562
60566
|
}
|
|
60563
60567
|
module2.exports = exports2 = formatsPlugin;
|
|
60564
60568
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -61842,11 +61846,11 @@ var parser = new YargsParser({
|
|
|
61842
61846
|
resolve: import_path2.resolve,
|
|
61843
61847
|
// TODO: figure out a way to combine ESM and CJS coverage, such that
|
|
61844
61848
|
// we can exercise all the lines below:
|
|
61845
|
-
require: (
|
|
61849
|
+
require: (path62) => {
|
|
61846
61850
|
if (typeof require !== "undefined") {
|
|
61847
|
-
return require(
|
|
61848
|
-
} else if (
|
|
61849
|
-
return JSON.parse((0, import_fs2.readFileSync)(
|
|
61851
|
+
return require(path62);
|
|
61852
|
+
} else if (path62.match(/\.json$/)) {
|
|
61853
|
+
return JSON.parse((0, import_fs2.readFileSync)(path62, "utf8"));
|
|
61850
61854
|
} else {
|
|
61851
61855
|
throw Error("only .json config files are supported in ESM");
|
|
61852
61856
|
}
|
|
@@ -65755,7 +65759,7 @@ function handleRemove(argv) {
|
|
|
65755
65759
|
}
|
|
65756
65760
|
|
|
65757
65761
|
// src/cli/commands/env.ts
|
|
65758
|
-
var
|
|
65762
|
+
var path51 = __toESM(require("path"));
|
|
65759
65763
|
|
|
65760
65764
|
// src/cli/finish-command.ts
|
|
65761
65765
|
init_sync_context();
|
|
@@ -65765,8 +65769,8 @@ async function finishCommand(code) {
|
|
|
65765
65769
|
}
|
|
65766
65770
|
|
|
65767
65771
|
// src/core/harness.ts
|
|
65768
|
-
var
|
|
65769
|
-
var
|
|
65772
|
+
var fs37 = __toESM(require("fs"));
|
|
65773
|
+
var path42 = __toESM(require("path"));
|
|
65770
65774
|
|
|
65771
65775
|
// src/harness/generator.ts
|
|
65772
65776
|
var fs28 = __toESM(require("fs"));
|
|
@@ -65890,9 +65894,485 @@ function finalizeHarness(repoPath, adaptationSummary, stack) {
|
|
|
65890
65894
|
success("Harness adaptation complete.");
|
|
65891
65895
|
}
|
|
65892
65896
|
|
|
65893
|
-
// src/harness/
|
|
65897
|
+
// src/harness/agent-md.ts
|
|
65898
|
+
var fs30 = __toESM(require("fs"));
|
|
65899
|
+
var path36 = __toESM(require("path"));
|
|
65900
|
+
init_file_ops();
|
|
65901
|
+
init_logging();
|
|
65902
|
+
init_manifest();
|
|
65903
|
+
|
|
65904
|
+
// src/harness/instruction-files.ts
|
|
65894
65905
|
var fs29 = __toESM(require("fs"));
|
|
65906
|
+
var os13 = __toESM(require("os"));
|
|
65895
65907
|
var path35 = __toESM(require("path"));
|
|
65908
|
+
var readline2 = __toESM(require("readline"));
|
|
65909
|
+
init_file_ops();
|
|
65910
|
+
init_logging();
|
|
65911
|
+
var AGENTS_MD = "AGENTS.md";
|
|
65912
|
+
var LEGACY_AGENT_MD = "AGENT.md";
|
|
65913
|
+
var CLAUDE_MD = "CLAUDE.md";
|
|
65914
|
+
var HAR_SECTION_START = "<!-- har:agent-environment:start -->";
|
|
65915
|
+
var HAR_SECTION_END = "<!-- har:agent-environment:end -->";
|
|
65916
|
+
var CLAUDE_HAR_SECTION_START = "<!-- har:claude-pointer:start -->";
|
|
65917
|
+
var CLAUDE_HAR_SECTION_END = "<!-- har:claude-pointer:end -->";
|
|
65918
|
+
var AgentsMdShrinkError = class extends Error {
|
|
65919
|
+
constructor(message) {
|
|
65920
|
+
super(message);
|
|
65921
|
+
this.name = "AgentsMdShrinkError";
|
|
65922
|
+
}
|
|
65923
|
+
};
|
|
65924
|
+
function escapeRegExp(value) {
|
|
65925
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
65926
|
+
}
|
|
65927
|
+
function countNonEmptyLines(text) {
|
|
65928
|
+
return text.split("\n").filter((line) => line.trim().length > 0).length;
|
|
65929
|
+
}
|
|
65930
|
+
function stripClaudePointerFromAgentsMd(content) {
|
|
65931
|
+
if (!content.includes(CLAUDE_HAR_SECTION_START)) return content;
|
|
65932
|
+
const pattern = new RegExp(
|
|
65933
|
+
`${escapeRegExp(CLAUDE_HAR_SECTION_START)}[\\s\\S]*?${escapeRegExp(CLAUDE_HAR_SECTION_END)}\\n?`,
|
|
65934
|
+
"g"
|
|
65935
|
+
);
|
|
65936
|
+
return content.replace(pattern, "").replace(/\n{3,}/g, "\n\n");
|
|
65937
|
+
}
|
|
65938
|
+
function extractOutsideHarSection(content) {
|
|
65939
|
+
const sanitized = stripClaudePointerFromAgentsMd(content);
|
|
65940
|
+
return removeCompleteMarkedBlocks(sanitized, HAR_SECTION_START, HAR_SECTION_END).trim();
|
|
65941
|
+
}
|
|
65942
|
+
function findLastCompleteMarkedBlock(content, startMarker, endMarker) {
|
|
65943
|
+
let last = null;
|
|
65944
|
+
let searchFrom = 0;
|
|
65945
|
+
while (searchFrom < content.length) {
|
|
65946
|
+
const start = content.indexOf(startMarker, searchFrom);
|
|
65947
|
+
if (start === -1) break;
|
|
65948
|
+
const end = content.indexOf(endMarker, start + startMarker.length);
|
|
65949
|
+
if (end === -1) break;
|
|
65950
|
+
const nestedStart = content.indexOf(startMarker, start + startMarker.length);
|
|
65951
|
+
if (nestedStart !== -1 && nestedStart < end) {
|
|
65952
|
+
searchFrom = start + startMarker.length;
|
|
65953
|
+
continue;
|
|
65954
|
+
}
|
|
65955
|
+
last = { start, end: end + endMarker.length };
|
|
65956
|
+
searchFrom = end + endMarker.length;
|
|
65957
|
+
}
|
|
65958
|
+
return last;
|
|
65959
|
+
}
|
|
65960
|
+
function removeCompleteMarkedBlocks(content, startMarker, endMarker) {
|
|
65961
|
+
let result = content;
|
|
65962
|
+
let block = findLastCompleteMarkedBlock(result, startMarker, endMarker);
|
|
65963
|
+
while (block) {
|
|
65964
|
+
result = result.slice(0, block.start) + result.slice(block.end);
|
|
65965
|
+
block = findLastCompleteMarkedBlock(result, startMarker, endMarker);
|
|
65966
|
+
}
|
|
65967
|
+
return result;
|
|
65968
|
+
}
|
|
65969
|
+
function checkOutsideContentPreserved(before, after, options) {
|
|
65970
|
+
const outsideBefore = extractOutsideHarSection(before);
|
|
65971
|
+
const outsideAfter = extractOutsideHarSection(after);
|
|
65972
|
+
const beforeLines = countNonEmptyLines(outsideBefore);
|
|
65973
|
+
const afterLines = countNonEmptyLines(outsideAfter);
|
|
65974
|
+
const minRatio = options.minOutsidePreservationRatio ?? 0.9;
|
|
65975
|
+
if (beforeLines >= 5 && afterLines < beforeLines * minRatio) {
|
|
65976
|
+
return "shrink";
|
|
65977
|
+
}
|
|
65978
|
+
return "ok";
|
|
65979
|
+
}
|
|
65980
|
+
function outsideContentShrinkMessage(beforeLines, afterLines) {
|
|
65981
|
+
return `Refusing to update ${AGENTS_MD}: refresh would drop project-specific content outside har:agent-environment markers (${beforeLines} \u2192 ${afterLines} non-empty lines). Keep custom sections outside those markers, or merge manually before --finalize.`;
|
|
65982
|
+
}
|
|
65983
|
+
function detectInstructionFiles(repoPath) {
|
|
65984
|
+
return {
|
|
65985
|
+
agentsMd: fs29.existsSync(path35.join(repoPath, AGENTS_MD)),
|
|
65986
|
+
agentMd: fs29.existsSync(path35.join(repoPath, LEGACY_AGENT_MD)),
|
|
65987
|
+
claudeMd: fs29.existsSync(path35.join(repoPath, CLAUDE_MD)),
|
|
65988
|
+
cursorDir: fs29.existsSync(path35.join(repoPath, ".cursor")),
|
|
65989
|
+
claudeDir: fs29.existsSync(path35.join(repoPath, ".claude")),
|
|
65990
|
+
codexHome: fs29.existsSync(path35.join(os13.homedir(), ".codex"))
|
|
65991
|
+
};
|
|
65992
|
+
}
|
|
65993
|
+
function formatDetectionReport(detection) {
|
|
65994
|
+
const lines2 = ["Detected:"];
|
|
65995
|
+
lines2.push(` - ${AGENTS_MD} (${detection.agentsMd ? "exists" : "missing"})`);
|
|
65996
|
+
if (detection.agentMd) {
|
|
65997
|
+
lines2.push(` - ${LEGACY_AGENT_MD} (legacy \u2014 will migrate into ${AGENTS_MD})`);
|
|
65998
|
+
}
|
|
65999
|
+
lines2.push(` - ${CLAUDE_MD} (${detection.claudeMd ? "exists" : "missing"})`);
|
|
66000
|
+
lines2.push(` - .cursor/ (${detection.cursorDir ? "exists" : "missing"})`);
|
|
66001
|
+
lines2.push(` - .claude/ (${detection.claudeDir ? "exists" : "missing"})`);
|
|
66002
|
+
lines2.push(` - ~/.codex (${detection.codexHome ? "exists" : "missing"})`);
|
|
66003
|
+
return lines2.join("\n");
|
|
66004
|
+
}
|
|
66005
|
+
function buildInstallPlan(detection, targets, options = {}) {
|
|
66006
|
+
const writeAgentsMd = options.writeAgentsMd !== false;
|
|
66007
|
+
const skillsEnabled = options.skillsEnabled !== false;
|
|
66008
|
+
const cursorExplicit = options.cursorRule;
|
|
66009
|
+
const wantsCursor = cursorExplicit === true || cursorExplicit !== false && (targets.includes("cursor") || detection.cursorDir);
|
|
66010
|
+
return {
|
|
66011
|
+
agentsMd: writeAgentsMd,
|
|
66012
|
+
migrateLegacyAgentMd: writeAgentsMd && detection.agentMd,
|
|
66013
|
+
claudeMd: skillsEnabled && targets.includes("claude"),
|
|
66014
|
+
cursorRule: wantsCursor,
|
|
66015
|
+
skills: skillsEnabled ? targets : []
|
|
66016
|
+
};
|
|
66017
|
+
}
|
|
66018
|
+
function formatInstallPlan(plan, detection) {
|
|
66019
|
+
const mark = (on) => on ? "[x]" : "[ ]";
|
|
66020
|
+
const lines2 = ["After you confirm, HAR will:"];
|
|
66021
|
+
const agentsAction = detection.agentsMd ? `Update ${AGENTS_MD} \u2014 refresh HAR / agent environment section` : `Create ${AGENTS_MD} \u2014 shared HAR workflow (Codex + cross-tool)`;
|
|
66022
|
+
lines2.push(` ${mark(plan.agentsMd)} ${agentsAction}`);
|
|
66023
|
+
if (plan.migrateLegacyAgentMd) {
|
|
66024
|
+
lines2.push(` ${mark(true)} Migrate ${LEGACY_AGENT_MD} \u2192 ${AGENTS_MD}, then remove legacy file`);
|
|
66025
|
+
}
|
|
66026
|
+
lines2.push(
|
|
66027
|
+
` ${mark(plan.claudeMd)} Claude \u2014 ensure ${CLAUDE_MD} points at ${AGENTS_MD}; install .claude/skills if confirmed`
|
|
66028
|
+
);
|
|
66029
|
+
lines2.push(
|
|
66030
|
+
` ${mark(plan.cursorRule)} Cursor \u2014 install/refresh .cursor/rules/har-workflow.mdc + commands if confirmed`
|
|
66031
|
+
);
|
|
66032
|
+
lines2.push(
|
|
66033
|
+
` ${mark(plan.skills.includes("codex"))} Codex \u2014 global ~/.codex/prompts (skills); project contract is ${AGENTS_MD}`
|
|
66034
|
+
);
|
|
66035
|
+
return lines2.join("\n");
|
|
66036
|
+
}
|
|
66037
|
+
function loadHarAgentsSectionFromTemplate() {
|
|
66038
|
+
const templatePath = resolveTemplateFile("AGENTS.md.template");
|
|
66039
|
+
if (!templatePath) {
|
|
66040
|
+
throw new Error("AGENTS.md.template not found. Run npm run build.");
|
|
66041
|
+
}
|
|
66042
|
+
const content = fs29.readFileSync(templatePath, "utf8");
|
|
66043
|
+
const start = content.indexOf(HAR_SECTION_START);
|
|
66044
|
+
const end = content.indexOf(HAR_SECTION_END);
|
|
66045
|
+
if (start === -1 || end === -1 || end < start) {
|
|
66046
|
+
throw new Error("AGENTS.md.template is missing har:agent-environment markers.");
|
|
66047
|
+
}
|
|
66048
|
+
return content.slice(start, end + HAR_SECTION_END.length);
|
|
66049
|
+
}
|
|
66050
|
+
function loadAgentsMdTemplate() {
|
|
66051
|
+
const templatePath = resolveTemplateFile("AGENTS.md.template");
|
|
66052
|
+
if (!templatePath) {
|
|
66053
|
+
throw new Error("AGENTS.md.template not found. Run npm run build.");
|
|
66054
|
+
}
|
|
66055
|
+
return fs29.readFileSync(templatePath, "utf8");
|
|
66056
|
+
}
|
|
66057
|
+
function upsertMarkedBlock(content, block, startMarker, endMarker) {
|
|
66058
|
+
const existingBlock = findLastCompleteMarkedBlock(content, startMarker, endMarker);
|
|
66059
|
+
if (existingBlock) {
|
|
66060
|
+
const newContent = content.slice(0, existingBlock.start) + block + content.slice(existingBlock.end);
|
|
66061
|
+
return { content: newContent, action: "updated" };
|
|
66062
|
+
}
|
|
66063
|
+
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
66064
|
+
return { content: `${content}${suffix}
|
|
66065
|
+
${block}
|
|
66066
|
+
`, action: "appended" };
|
|
66067
|
+
}
|
|
66068
|
+
function mergeAgentsMdContent(existing, incoming) {
|
|
66069
|
+
const sanitizedExisting = stripClaudePointerFromAgentsMd(existing);
|
|
66070
|
+
const sanitizedIncoming = stripClaudePointerFromAgentsMd(incoming);
|
|
66071
|
+
let harSection = loadHarAgentsSectionFromTemplate();
|
|
66072
|
+
if (sanitizedIncoming.includes(HAR_SECTION_START)) {
|
|
66073
|
+
const start = sanitizedIncoming.indexOf(HAR_SECTION_START);
|
|
66074
|
+
const end = sanitizedIncoming.indexOf(HAR_SECTION_END, start + HAR_SECTION_START.length);
|
|
66075
|
+
if (end !== -1) {
|
|
66076
|
+
harSection = sanitizedIncoming.slice(start, end + HAR_SECTION_END.length);
|
|
66077
|
+
}
|
|
66078
|
+
}
|
|
66079
|
+
const { content } = upsertMarkedBlock(
|
|
66080
|
+
sanitizedExisting,
|
|
66081
|
+
harSection,
|
|
66082
|
+
HAR_SECTION_START,
|
|
66083
|
+
HAR_SECTION_END
|
|
66084
|
+
);
|
|
66085
|
+
return content;
|
|
66086
|
+
}
|
|
66087
|
+
function upsertAgentsMdHarSection(repoPath, options = {}) {
|
|
66088
|
+
const dest = path35.join(repoPath, AGENTS_MD);
|
|
66089
|
+
const section = loadHarAgentsSectionFromTemplate();
|
|
66090
|
+
if (!fs29.existsSync(dest)) {
|
|
66091
|
+
writeFileSafe(dest, loadAgentsMdTemplate());
|
|
66092
|
+
return "created";
|
|
66093
|
+
}
|
|
66094
|
+
const existing = fs29.readFileSync(dest, "utf8");
|
|
66095
|
+
const sanitized = stripClaudePointerFromAgentsMd(existing);
|
|
66096
|
+
const { content, action } = upsertMarkedBlock(
|
|
66097
|
+
sanitized,
|
|
66098
|
+
section,
|
|
66099
|
+
HAR_SECTION_START,
|
|
66100
|
+
HAR_SECTION_END
|
|
66101
|
+
);
|
|
66102
|
+
if (checkOutsideContentPreserved(existing, content, options) === "shrink") {
|
|
66103
|
+
const beforeLines = countNonEmptyLines(extractOutsideHarSection(existing));
|
|
66104
|
+
const afterLines = countNonEmptyLines(extractOutsideHarSection(content));
|
|
66105
|
+
const message = outsideContentShrinkMessage(beforeLines, afterLines);
|
|
66106
|
+
if (options.rejectSignificantShrink) {
|
|
66107
|
+
throw new AgentsMdShrinkError(message);
|
|
66108
|
+
}
|
|
66109
|
+
warn(message);
|
|
66110
|
+
return "skipped";
|
|
66111
|
+
}
|
|
66112
|
+
if (content === existing) {
|
|
66113
|
+
return "skipped";
|
|
66114
|
+
}
|
|
66115
|
+
writeFileSafe(dest, content);
|
|
66116
|
+
return action;
|
|
66117
|
+
}
|
|
66118
|
+
function migrateLegacyAgentMd(repoPath) {
|
|
66119
|
+
const legacyPath = path35.join(repoPath, LEGACY_AGENT_MD);
|
|
66120
|
+
const agentsPath = path35.join(repoPath, AGENTS_MD);
|
|
66121
|
+
if (!fs29.existsSync(legacyPath)) return false;
|
|
66122
|
+
const legacy = fs29.readFileSync(legacyPath, "utf8");
|
|
66123
|
+
if (!fs29.existsSync(agentsPath)) {
|
|
66124
|
+
writeFileSafe(agentsPath, legacy);
|
|
66125
|
+
upsertAgentsMdHarSection(repoPath);
|
|
66126
|
+
} else {
|
|
66127
|
+
const agents = fs29.readFileSync(agentsPath, "utf8");
|
|
66128
|
+
const legacyTrimmed = legacy.trim();
|
|
66129
|
+
if (legacyTrimmed.length > 0 && !agents.includes(legacyTrimmed)) {
|
|
66130
|
+
const suffix = agents.endsWith("\n") ? "" : "\n";
|
|
66131
|
+
writeFileSafe(agentsPath, `${agents}${suffix}
|
|
66132
|
+
${legacyTrimmed}
|
|
66133
|
+
`);
|
|
66134
|
+
}
|
|
66135
|
+
upsertAgentsMdHarSection(repoPath);
|
|
66136
|
+
}
|
|
66137
|
+
fs29.unlinkSync(legacyPath);
|
|
66138
|
+
info(`Migrated ${LEGACY_AGENT_MD} \u2192 ${AGENTS_MD} (removed legacy file)`);
|
|
66139
|
+
return true;
|
|
66140
|
+
}
|
|
66141
|
+
function loadClaudeMdTemplate(projectName) {
|
|
66142
|
+
const templatePath = resolveTemplateFile("CLAUDE.md.template");
|
|
66143
|
+
if (!templatePath) {
|
|
66144
|
+
throw new Error("CLAUDE.md.template not found. Run npm run build.");
|
|
66145
|
+
}
|
|
66146
|
+
const displayName = projectName.replace(/_/g, " ");
|
|
66147
|
+
return fs29.readFileSync(templatePath, "utf8").replace(/__PROJECT_DISPLAY_NAME__/g, displayName);
|
|
66148
|
+
}
|
|
66149
|
+
function isThinClaudePointer(content) {
|
|
66150
|
+
const trimmed = content.trim();
|
|
66151
|
+
if (trimmed.length > 600) return false;
|
|
66152
|
+
return /AGENTS\.md|AGENT\.md/.test(trimmed) && (trimmed.includes(".har/README.md") || trimmed.includes("har-workflow"));
|
|
66153
|
+
}
|
|
66154
|
+
var CLAUDE_POINTER_BLOCK = `${CLAUDE_HAR_SECTION_START}
|
|
66155
|
+
## HAR
|
|
66156
|
+
|
|
66157
|
+
This repository uses a \`.har/\` harness. Read [AGENTS.md](./AGENTS.md) and
|
|
66158
|
+
[\`.har/README.md\`](./.har/README.md) before making changes.
|
|
66159
|
+
${CLAUDE_HAR_SECTION_END}`;
|
|
66160
|
+
function ensureClaudeMdPointer(repoPath, options = {}) {
|
|
66161
|
+
const dest = path35.join(repoPath, CLAUDE_MD);
|
|
66162
|
+
const projectName = path35.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
|
|
66163
|
+
const thin = loadClaudeMdTemplate(projectName);
|
|
66164
|
+
if (!fs29.existsSync(dest)) {
|
|
66165
|
+
writeFileSafe(dest, thin);
|
|
66166
|
+
return "created";
|
|
66167
|
+
}
|
|
66168
|
+
const existing = fs29.readFileSync(dest, "utf8");
|
|
66169
|
+
if (isThinClaudePointer(existing) || options.force) {
|
|
66170
|
+
writeFileSafe(dest, thin);
|
|
66171
|
+
return "updated";
|
|
66172
|
+
}
|
|
66173
|
+
if (existing.includes(CLAUDE_HAR_SECTION_START) || /\[AGENTS\.md\]/.test(existing)) {
|
|
66174
|
+
if (existing.includes(CLAUDE_HAR_SECTION_START)) {
|
|
66175
|
+
const { content: content2, action: action2 } = upsertMarkedBlock(
|
|
66176
|
+
existing,
|
|
66177
|
+
CLAUDE_POINTER_BLOCK,
|
|
66178
|
+
CLAUDE_HAR_SECTION_START,
|
|
66179
|
+
CLAUDE_HAR_SECTION_END
|
|
66180
|
+
);
|
|
66181
|
+
writeFileSafe(dest, content2);
|
|
66182
|
+
return action2;
|
|
66183
|
+
}
|
|
66184
|
+
return "skipped";
|
|
66185
|
+
}
|
|
66186
|
+
const { content, action } = upsertMarkedBlock(
|
|
66187
|
+
existing,
|
|
66188
|
+
CLAUDE_POINTER_BLOCK,
|
|
66189
|
+
CLAUDE_HAR_SECTION_START,
|
|
66190
|
+
CLAUDE_HAR_SECTION_END
|
|
66191
|
+
);
|
|
66192
|
+
writeFileSafe(dest, content);
|
|
66193
|
+
return action;
|
|
66194
|
+
}
|
|
66195
|
+
async function askYesNo2(question, defaultYes = true) {
|
|
66196
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
66197
|
+
return new Promise((resolve47) => {
|
|
66198
|
+
process.stderr.write(`${question} `);
|
|
66199
|
+
rl.once("line", (answer) => {
|
|
66200
|
+
rl.close();
|
|
66201
|
+
const trimmed = answer.trim();
|
|
66202
|
+
if (trimmed === "") {
|
|
66203
|
+
resolve47(defaultYes);
|
|
66204
|
+
return;
|
|
66205
|
+
}
|
|
66206
|
+
resolve47(/^y(es)?$/i.test(trimmed));
|
|
66207
|
+
});
|
|
66208
|
+
});
|
|
66209
|
+
}
|
|
66210
|
+
async function handleInstructionFiles(options) {
|
|
66211
|
+
const { repoPath, mode } = options;
|
|
66212
|
+
const writeAgentsMd = options.writeAgentsMd !== false;
|
|
66213
|
+
const skillsEnabled = options.enabled !== false;
|
|
66214
|
+
const detection = detectInstructionFiles(repoPath);
|
|
66215
|
+
let targets;
|
|
66216
|
+
if (!skillsEnabled) {
|
|
66217
|
+
targets = [];
|
|
66218
|
+
} else if (typeof options.agents === "string") {
|
|
66219
|
+
targets = parseAgentTargets(options.agents);
|
|
66220
|
+
} else {
|
|
66221
|
+
targets = detectAgentTargets(repoPath);
|
|
66222
|
+
}
|
|
66223
|
+
if (skillsEnabled && targets.length === 0 && mode === "init" && typeof options.agents !== "string" && !options.autoYes) {
|
|
66224
|
+
targets = [...AGENT_SKILL_TARGETS];
|
|
66225
|
+
}
|
|
66226
|
+
let plan = buildInstallPlan(detection, targets, {
|
|
66227
|
+
writeAgentsMd,
|
|
66228
|
+
cursorRule: options.cursorRule,
|
|
66229
|
+
skillsEnabled
|
|
66230
|
+
});
|
|
66231
|
+
divider();
|
|
66232
|
+
process.stderr.write(`${formatDetectionReport(detection)}
|
|
66233
|
+
|
|
66234
|
+
`);
|
|
66235
|
+
process.stderr.write(`${formatInstallPlan(plan, detection)}
|
|
66236
|
+
`);
|
|
66237
|
+
divider();
|
|
66238
|
+
const shouldPrompt = !options.autoYes && skillsEnabled && typeof options.agents !== "string" && !(mode === "maintain" && targets.length > 0 && !detection.agentMd);
|
|
66239
|
+
if (shouldPrompt && targets.length > 0) {
|
|
66240
|
+
const accepted = await askYesNo2(
|
|
66241
|
+
`Install HAR agent instructions for: ${targets.join(", ")}? [Y/n]`
|
|
66242
|
+
);
|
|
66243
|
+
if (!accepted) {
|
|
66244
|
+
info("Skipped agent instruction adapters (AGENTS.md will still be updated)");
|
|
66245
|
+
targets = [];
|
|
66246
|
+
plan = buildInstallPlan(detection, targets, {
|
|
66247
|
+
writeAgentsMd,
|
|
66248
|
+
cursorRule: options.cursorRule === true ? true : false,
|
|
66249
|
+
skillsEnabled: false
|
|
66250
|
+
});
|
|
66251
|
+
}
|
|
66252
|
+
}
|
|
66253
|
+
let migratedLegacy = false;
|
|
66254
|
+
let agentsMdAction = null;
|
|
66255
|
+
let claudeMdAction = null;
|
|
66256
|
+
if (writeAgentsMd) {
|
|
66257
|
+
if (detection.agentMd) {
|
|
66258
|
+
migratedLegacy = migrateLegacyAgentMd(repoPath);
|
|
66259
|
+
}
|
|
66260
|
+
agentsMdAction = upsertAgentsMdHarSection(repoPath, {
|
|
66261
|
+
rejectSignificantShrink: options.finalize === true
|
|
66262
|
+
});
|
|
66263
|
+
if (agentsMdAction === "created") {
|
|
66264
|
+
success(`Wrote ${AGENTS_MD}`);
|
|
66265
|
+
} else if (agentsMdAction === "updated" || agentsMdAction === "appended") {
|
|
66266
|
+
info(
|
|
66267
|
+
`${agentsMdAction === "updated" ? "Refreshed" : "Appended"} HAR section in ${AGENTS_MD}`
|
|
66268
|
+
);
|
|
66269
|
+
} else if (agentsMdAction === "skipped" && options.finalize) {
|
|
66270
|
+
warn(
|
|
66271
|
+
`${AGENTS_MD} left unchanged \u2014 project-specific content outside har:agent-environment markers was preserved.`
|
|
66272
|
+
);
|
|
66273
|
+
}
|
|
66274
|
+
} else {
|
|
66275
|
+
agentsMdAction = "skipped";
|
|
66276
|
+
}
|
|
66277
|
+
if (plan.claudeMd) {
|
|
66278
|
+
claudeMdAction = ensureClaudeMdPointer(repoPath);
|
|
66279
|
+
if (claudeMdAction === "created") {
|
|
66280
|
+
success(`Wrote ${CLAUDE_MD} (pointer \u2192 ${AGENTS_MD})`);
|
|
66281
|
+
} else if (claudeMdAction === "updated" || claudeMdAction === "appended") {
|
|
66282
|
+
info(
|
|
66283
|
+
`${claudeMdAction === "updated" ? "Updated" : "Appended"} ${CLAUDE_MD} pointer \u2192 ${AGENTS_MD}`
|
|
66284
|
+
);
|
|
66285
|
+
}
|
|
66286
|
+
} else {
|
|
66287
|
+
claudeMdAction = "skipped";
|
|
66288
|
+
}
|
|
66289
|
+
return {
|
|
66290
|
+
detection,
|
|
66291
|
+
plan,
|
|
66292
|
+
targets,
|
|
66293
|
+
agentsMdAction,
|
|
66294
|
+
migratedLegacy,
|
|
66295
|
+
claudeMdAction
|
|
66296
|
+
};
|
|
66297
|
+
}
|
|
66298
|
+
|
|
66299
|
+
// src/harness/agent-md.ts
|
|
66300
|
+
var AGENTS_MD_PROPOSAL = "AGENTS.md.proposed";
|
|
66301
|
+
var AGENTS_MD_PROPOSAL_META = "AGENTS.md.proposed.meta.json";
|
|
66302
|
+
var AGENT_MD_PROPOSAL = "AGENT.md.proposed";
|
|
66303
|
+
var AGENT_MD_PROPOSAL_META = "AGENT.md.proposed.meta.json";
|
|
66304
|
+
function readAgentMdProposal(repoPath) {
|
|
66305
|
+
const harnessDir = getHarnessDir(repoPath);
|
|
66306
|
+
const proposalPath = path36.join(harnessDir, AGENTS_MD_PROPOSAL);
|
|
66307
|
+
const metaPath = path36.join(harnessDir, AGENTS_MD_PROPOSAL_META);
|
|
66308
|
+
const legacyProposalPath = path36.join(harnessDir, AGENT_MD_PROPOSAL);
|
|
66309
|
+
const legacyMetaPath = path36.join(harnessDir, AGENT_MD_PROPOSAL_META);
|
|
66310
|
+
const resolvedProposal = fs30.existsSync(proposalPath) ? proposalPath : fs30.existsSync(legacyProposalPath) ? legacyProposalPath : null;
|
|
66311
|
+
if (!resolvedProposal) return null;
|
|
66312
|
+
const resolvedMeta = resolvedProposal === proposalPath ? metaPath : legacyMetaPath;
|
|
66313
|
+
const content = fs30.readFileSync(resolvedProposal, "utf8");
|
|
66314
|
+
let rationale = "";
|
|
66315
|
+
let createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
66316
|
+
if (fs30.existsSync(resolvedMeta)) {
|
|
66317
|
+
try {
|
|
66318
|
+
const meta = JSON.parse(fs30.readFileSync(resolvedMeta, "utf8"));
|
|
66319
|
+
rationale = meta.rationale ?? "";
|
|
66320
|
+
createdAt = meta.createdAt ?? createdAt;
|
|
66321
|
+
} catch {
|
|
66322
|
+
rationale = "";
|
|
66323
|
+
}
|
|
66324
|
+
}
|
|
66325
|
+
return { content, rationale, createdAt };
|
|
66326
|
+
}
|
|
66327
|
+
function clearAgentMdProposal(repoPath) {
|
|
66328
|
+
const harnessDir = getHarnessDir(repoPath);
|
|
66329
|
+
for (const file of [
|
|
66330
|
+
AGENTS_MD_PROPOSAL,
|
|
66331
|
+
AGENTS_MD_PROPOSAL_META,
|
|
66332
|
+
AGENT_MD_PROPOSAL,
|
|
66333
|
+
AGENT_MD_PROPOSAL_META
|
|
66334
|
+
]) {
|
|
66335
|
+
const p = path36.join(harnessDir, file);
|
|
66336
|
+
if (fs30.existsSync(p)) fs30.unlinkSync(p);
|
|
66337
|
+
}
|
|
66338
|
+
}
|
|
66339
|
+
function wouldProposalShrinkExisting(existing, proposal) {
|
|
66340
|
+
const outsideExisting = extractOutsideHarSection(existing);
|
|
66341
|
+
const outsideMerged = extractOutsideHarSection(mergeAgentsMdContent(existing, proposal));
|
|
66342
|
+
const existingLines = outsideExisting.split("\n").filter((l) => l.trim()).length;
|
|
66343
|
+
const mergedLines = outsideMerged.split("\n").filter((l) => l.trim()).length;
|
|
66344
|
+
return existingLines >= 5 && mergedLines < existingLines * 0.9;
|
|
66345
|
+
}
|
|
66346
|
+
function applyAgentMdProposalMerge(repoPath, options = {}) {
|
|
66347
|
+
const proposal = readAgentMdProposal(repoPath);
|
|
66348
|
+
if (!proposal) return false;
|
|
66349
|
+
const agentsMdPath = path36.join(repoPath, AGENTS_MD);
|
|
66350
|
+
if (fs30.existsSync(agentsMdPath)) {
|
|
66351
|
+
const existing = fs30.readFileSync(agentsMdPath, "utf8");
|
|
66352
|
+
if (wouldProposalShrinkExisting(existing, proposal.content)) {
|
|
66353
|
+
const message = `${AGENTS_MD} proposal would drop project-specific content outside har:agent-environment markers \u2014 merge custom sections from .har/${AGENTS_MD_PROPOSAL} manually, then delete the proposal.`;
|
|
66354
|
+
if (options.rejectSignificantShrink) {
|
|
66355
|
+
throw new AgentsMdShrinkError(message);
|
|
66356
|
+
}
|
|
66357
|
+
warn(message);
|
|
66358
|
+
return false;
|
|
66359
|
+
}
|
|
66360
|
+
writeFileSafe(agentsMdPath, mergeAgentsMdContent(existing, proposal.content));
|
|
66361
|
+
} else {
|
|
66362
|
+
writeFileSafe(agentsMdPath, proposal.content);
|
|
66363
|
+
}
|
|
66364
|
+
clearAgentMdProposal(repoPath);
|
|
66365
|
+
info(`Merged ${AGENTS_MD} proposal into repo root (preserved content outside managed markers)`);
|
|
66366
|
+
return true;
|
|
66367
|
+
}
|
|
66368
|
+
function finalizeAgentsMdInstructionFiles(repoPath) {
|
|
66369
|
+
applyAgentMdProposalMerge(repoPath, { rejectSignificantShrink: true });
|
|
66370
|
+
upsertAgentsMdHarSection(repoPath, { rejectSignificantShrink: true });
|
|
66371
|
+
}
|
|
66372
|
+
|
|
66373
|
+
// src/harness/validator.ts
|
|
66374
|
+
var fs31 = __toESM(require("fs"));
|
|
66375
|
+
var path37 = __toESM(require("path"));
|
|
65896
66376
|
init_shell();
|
|
65897
66377
|
init_manifest();
|
|
65898
66378
|
init_stages();
|
|
@@ -65947,22 +66427,22 @@ var SHELL_SCRIPTS = [
|
|
|
65947
66427
|
function validateHarness(repoPath) {
|
|
65948
66428
|
const harnessDir = getHarnessDir(repoPath);
|
|
65949
66429
|
const issues = [];
|
|
65950
|
-
if (!
|
|
66430
|
+
if (!fs31.existsSync(harnessDir)) {
|
|
65951
66431
|
return {
|
|
65952
66432
|
pass: false,
|
|
65953
66433
|
issues: [{ file: ".har", message: "Harness directory not found", severity: "error" }]
|
|
65954
66434
|
};
|
|
65955
66435
|
}
|
|
65956
66436
|
for (const file of getRequiredFiles(repoPath)) {
|
|
65957
|
-
const filePath =
|
|
65958
|
-
if (!
|
|
66437
|
+
const filePath = path37.join(harnessDir, file);
|
|
66438
|
+
if (!fs31.existsSync(filePath)) {
|
|
65959
66439
|
issues.push({ file, message: "Required file missing", severity: "error" });
|
|
65960
66440
|
}
|
|
65961
66441
|
}
|
|
65962
66442
|
for (const script of SHELL_SCRIPTS) {
|
|
65963
|
-
const scriptPath =
|
|
65964
|
-
if (!
|
|
65965
|
-
const stat =
|
|
66443
|
+
const scriptPath = path37.join(harnessDir, script);
|
|
66444
|
+
if (!fs31.existsSync(scriptPath)) continue;
|
|
66445
|
+
const stat = fs31.statSync(scriptPath);
|
|
65966
66446
|
if (!(stat.mode & 73)) {
|
|
65967
66447
|
issues.push({ file: script, message: "Script is not executable", severity: "warning" });
|
|
65968
66448
|
}
|
|
@@ -65977,9 +66457,9 @@ function validateHarness(repoPath) {
|
|
|
65977
66457
|
}
|
|
65978
66458
|
const manifest = readManifest(repoPath);
|
|
65979
66459
|
const profile = manifest?.profile ?? "default";
|
|
65980
|
-
const ecosystemPath =
|
|
65981
|
-
if (profile !== "cli" &&
|
|
65982
|
-
const content =
|
|
66460
|
+
const ecosystemPath = path37.join(harnessDir, "ecosystem.agent.template.cjs");
|
|
66461
|
+
if (profile !== "cli" && fs31.existsSync(ecosystemPath)) {
|
|
66462
|
+
const content = fs31.readFileSync(ecosystemPath, "utf8");
|
|
65983
66463
|
if (!content.includes("module.exports")) {
|
|
65984
66464
|
issues.push({
|
|
65985
66465
|
file: "ecosystem.agent.template.cjs",
|
|
@@ -65988,9 +66468,9 @@ function validateHarness(repoPath) {
|
|
|
65988
66468
|
});
|
|
65989
66469
|
}
|
|
65990
66470
|
}
|
|
65991
|
-
const harnessEnvPath =
|
|
65992
|
-
if (
|
|
65993
|
-
const content =
|
|
66471
|
+
const harnessEnvPath = path37.join(harnessDir, "harness.env");
|
|
66472
|
+
if (fs31.existsSync(harnessEnvPath)) {
|
|
66473
|
+
const content = fs31.readFileSync(harnessEnvPath, "utf8");
|
|
65994
66474
|
if (content.includes("TODO: set migrate command")) {
|
|
65995
66475
|
issues.push({ file: "harness.env", message: "Migrate command still has TODO", severity: "warning" });
|
|
65996
66476
|
}
|
|
@@ -65998,17 +66478,17 @@ function validateHarness(repoPath) {
|
|
|
65998
66478
|
issues.push({ file: "harness.env", message: "Seed command still has TODO", severity: "warning" });
|
|
65999
66479
|
}
|
|
66000
66480
|
}
|
|
66001
|
-
const verifyPath =
|
|
66002
|
-
if (
|
|
66003
|
-
const content =
|
|
66481
|
+
const verifyPath = path37.join(harnessDir, "verify.sh");
|
|
66482
|
+
if (fs31.existsSync(verifyPath)) {
|
|
66483
|
+
const content = fs31.readFileSync(verifyPath, "utf8");
|
|
66004
66484
|
if (content.includes("echo 'TODO:")) {
|
|
66005
66485
|
issues.push({ file: "verify.sh", message: "Verification steps still have TODO placeholders", severity: "warning" });
|
|
66006
66486
|
}
|
|
66007
66487
|
}
|
|
66008
|
-
const stagesPath =
|
|
66009
|
-
if (
|
|
66488
|
+
const stagesPath = path37.join(harnessDir, "stages.json");
|
|
66489
|
+
if (fs31.existsSync(stagesPath)) {
|
|
66010
66490
|
try {
|
|
66011
|
-
JSON.parse(
|
|
66491
|
+
JSON.parse(fs31.readFileSync(stagesPath, "utf8"));
|
|
66012
66492
|
const registry2 = readStageRegistry(repoPath);
|
|
66013
66493
|
if (registry2.stages.length === 0) {
|
|
66014
66494
|
issues.push({ file: "stages.json", message: "No harness stages declared", severity: "warning" });
|
|
@@ -66023,9 +66503,9 @@ function validateHarness(repoPath) {
|
|
|
66023
66503
|
} else {
|
|
66024
66504
|
issues.push({ file: "stages.json", message: "Stage registry missing", severity: "warning" });
|
|
66025
66505
|
}
|
|
66026
|
-
const readmePath =
|
|
66027
|
-
if (
|
|
66028
|
-
const content =
|
|
66506
|
+
const readmePath = path37.join(harnessDir, "README.md");
|
|
66507
|
+
if (fs31.existsSync(readmePath)) {
|
|
66508
|
+
const content = fs31.readFileSync(readmePath, "utf8");
|
|
66029
66509
|
if (!content.includes(".har")) {
|
|
66030
66510
|
issues.push({ file: "README.md", message: "README should document .har/ paths", severity: "warning" });
|
|
66031
66511
|
}
|
|
@@ -66039,8 +66519,8 @@ function validateHarness(repoPath) {
|
|
|
66039
66519
|
async function smokeTestHarness(repoPath) {
|
|
66040
66520
|
const harnessDir = getHarnessDir(repoPath);
|
|
66041
66521
|
const issues = [];
|
|
66042
|
-
const setupScript =
|
|
66043
|
-
if (
|
|
66522
|
+
const setupScript = path37.join(harnessDir, "setup-infra.sh");
|
|
66523
|
+
if (fs31.existsSync(setupScript)) {
|
|
66044
66524
|
const result = run(`bash "${setupScript}"`, { cwd: repoPath });
|
|
66045
66525
|
if (result.code !== 0) {
|
|
66046
66526
|
issues.push({
|
|
@@ -66055,8 +66535,8 @@ async function smokeTestHarness(repoPath) {
|
|
|
66055
66535
|
}
|
|
66056
66536
|
|
|
66057
66537
|
// src/harness/drift.ts
|
|
66058
|
-
var
|
|
66059
|
-
var
|
|
66538
|
+
var fs32 = __toESM(require("fs"));
|
|
66539
|
+
var path38 = __toESM(require("path"));
|
|
66060
66540
|
init_env();
|
|
66061
66541
|
init_manifest();
|
|
66062
66542
|
init_stages();
|
|
@@ -66127,16 +66607,16 @@ function substituteProjectName(content, projectName) {
|
|
|
66127
66607
|
return content.replace(/__PROJECT_NAME__/g, projectName).replace(/template___PROJECT_NAME__/g, `template_${projectName}`);
|
|
66128
66608
|
}
|
|
66129
66609
|
function listBoilerplateFiles(boilerplateDir) {
|
|
66130
|
-
if (!
|
|
66131
|
-
return
|
|
66610
|
+
if (!fs32.existsSync(boilerplateDir)) return [];
|
|
66611
|
+
return fs32.readdirSync(boilerplateDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
|
|
66132
66612
|
}
|
|
66133
66613
|
function compareHarnessToTemplate(repoPath) {
|
|
66134
|
-
const resolved =
|
|
66614
|
+
const resolved = path38.resolve(repoPath);
|
|
66135
66615
|
const manifest = readManifest(resolved);
|
|
66136
66616
|
const profile = manifest?.profile ?? "default";
|
|
66137
66617
|
const harnessDir = getHarnessDir(resolved);
|
|
66138
|
-
const projectName =
|
|
66139
|
-
const boilerplateDir =
|
|
66618
|
+
const projectName = path38.basename(resolved).toLowerCase().replace(/[^a-z0-9]/g, "_");
|
|
66619
|
+
const boilerplateDir = path38.join(resolveTemplatesDir(), PROFILE_DIRS2[profile]);
|
|
66140
66620
|
const templateFiles = listBoilerplateFiles(boilerplateDir);
|
|
66141
66621
|
const missing = [];
|
|
66142
66622
|
const checksumMismatch = [];
|
|
@@ -66144,17 +66624,17 @@ function compareHarnessToTemplate(repoPath) {
|
|
|
66144
66624
|
const unchanged = [];
|
|
66145
66625
|
for (const file of templateFiles) {
|
|
66146
66626
|
const harnessFile = harnessFileForTemplate(file);
|
|
66147
|
-
const templatePath =
|
|
66148
|
-
const harnessPath =
|
|
66149
|
-
let templateContent =
|
|
66627
|
+
const templatePath = path38.join(boilerplateDir, file);
|
|
66628
|
+
const harnessPath = path38.join(harnessDir, harnessFile);
|
|
66629
|
+
let templateContent = fs32.readFileSync(templatePath, "utf8");
|
|
66150
66630
|
if (file === "harness.env") {
|
|
66151
66631
|
templateContent = substituteProjectName(templateContent, projectName);
|
|
66152
66632
|
}
|
|
66153
|
-
if (!
|
|
66633
|
+
if (!fs32.existsSync(harnessPath)) {
|
|
66154
66634
|
missing.push(harnessFile);
|
|
66155
66635
|
continue;
|
|
66156
66636
|
}
|
|
66157
|
-
const harnessChecksum = computeFileChecksum(
|
|
66637
|
+
const harnessChecksum = computeFileChecksum(fs32.readFileSync(harnessPath, "utf8"));
|
|
66158
66638
|
const templateChecksum = computeFileChecksum(templateContent);
|
|
66159
66639
|
if (harnessChecksum === templateChecksum) {
|
|
66160
66640
|
unchanged.push(harnessFile);
|
|
@@ -66162,10 +66642,10 @@ function compareHarnessToTemplate(repoPath) {
|
|
|
66162
66642
|
checksumMismatch.push(harnessFile);
|
|
66163
66643
|
}
|
|
66164
66644
|
}
|
|
66165
|
-
if (
|
|
66166
|
-
for (const file of
|
|
66167
|
-
const full =
|
|
66168
|
-
if (!
|
|
66645
|
+
if (fs32.existsSync(harnessDir)) {
|
|
66646
|
+
for (const file of fs32.readdirSync(harnessDir)) {
|
|
66647
|
+
const full = path38.join(harnessDir, file);
|
|
66648
|
+
if (!fs32.statSync(full).isFile()) continue;
|
|
66169
66649
|
if (file === "manifest.json" || file.startsWith("ADAPT-PROMPT")) continue;
|
|
66170
66650
|
if (profile === "cli" && CLI_EXPECTED_ABSENT.has(file)) {
|
|
66171
66651
|
extra.push(file);
|
|
@@ -66196,29 +66676,29 @@ function compareHarnessToTemplate(repoPath) {
|
|
|
66196
66676
|
}
|
|
66197
66677
|
|
|
66198
66678
|
// src/harness/maintain-bundle.ts
|
|
66199
|
-
var
|
|
66679
|
+
var fs36 = __toESM(require("fs"));
|
|
66200
66680
|
var os14 = __toESM(require("os"));
|
|
66201
|
-
var
|
|
66681
|
+
var path41 = __toESM(require("path"));
|
|
66202
66682
|
init_file_ops();
|
|
66203
66683
|
init_shell();
|
|
66204
66684
|
init_manifest();
|
|
66205
66685
|
|
|
66206
66686
|
// src/harness/plugin-drift.ts
|
|
66207
|
-
var
|
|
66208
|
-
var
|
|
66687
|
+
var fs35 = __toESM(require("fs"));
|
|
66688
|
+
var path40 = __toESM(require("path"));
|
|
66209
66689
|
init_manifest();
|
|
66210
66690
|
|
|
66211
66691
|
// src/harness/plugins.ts
|
|
66212
|
-
var
|
|
66213
|
-
var
|
|
66692
|
+
var fs34 = __toESM(require("fs"));
|
|
66693
|
+
var path39 = __toESM(require("path"));
|
|
66214
66694
|
init_zod();
|
|
66215
66695
|
init_logging();
|
|
66216
66696
|
|
|
66217
66697
|
// src/harness/parser.ts
|
|
66218
|
-
var
|
|
66698
|
+
var fs33 = __toESM(require("fs"));
|
|
66219
66699
|
init_manifest();
|
|
66220
66700
|
function harnessExists(repoPath) {
|
|
66221
|
-
return
|
|
66701
|
+
return fs33.existsSync(`${repoPath}/.har/setup-infra.sh`);
|
|
66222
66702
|
}
|
|
66223
66703
|
|
|
66224
66704
|
// src/harness/plugins.ts
|
|
@@ -66243,16 +66723,16 @@ var PluginManifestSchema = external_exports.object({
|
|
|
66243
66723
|
docsPath: external_exports.string().min(1)
|
|
66244
66724
|
});
|
|
66245
66725
|
function resolvePluginDir(pluginId) {
|
|
66246
|
-
const dir =
|
|
66247
|
-
if (!
|
|
66726
|
+
const dir = path39.join(resolveTemplatesDir(), "plugins", pluginId);
|
|
66727
|
+
if (!fs34.existsSync(dir)) {
|
|
66248
66728
|
throw new Error(`Plugin not found: ${pluginId}. Run npm run build.`);
|
|
66249
66729
|
}
|
|
66250
66730
|
return dir;
|
|
66251
66731
|
}
|
|
66252
66732
|
function readPluginManifest(pluginId) {
|
|
66253
|
-
const manifestPath =
|
|
66733
|
+
const manifestPath = path39.join(resolvePluginDir(pluginId), "template.manifest.json");
|
|
66254
66734
|
const parsed = PluginManifestSchema.safeParse(
|
|
66255
|
-
JSON.parse(
|
|
66735
|
+
JSON.parse(fs34.readFileSync(manifestPath, "utf8"))
|
|
66256
66736
|
);
|
|
66257
66737
|
if (!parsed.success) {
|
|
66258
66738
|
throw new Error(`Invalid plugin manifest for ${pluginId}: ${parsed.error.message}`);
|
|
@@ -66263,40 +66743,40 @@ function readPluginManifest(pluginId) {
|
|
|
66263
66743
|
return parsed.data;
|
|
66264
66744
|
}
|
|
66265
66745
|
function ensureParentDir(filePath) {
|
|
66266
|
-
const parent =
|
|
66267
|
-
if (!
|
|
66268
|
-
|
|
66746
|
+
const parent = path39.dirname(filePath);
|
|
66747
|
+
if (!fs34.existsSync(parent)) {
|
|
66748
|
+
fs34.mkdirSync(parent, { recursive: true });
|
|
66269
66749
|
}
|
|
66270
66750
|
}
|
|
66271
66751
|
function copyPluginFile(pluginDir, file, repoPath, force) {
|
|
66272
|
-
const srcPath =
|
|
66273
|
-
const destPath =
|
|
66274
|
-
if (!
|
|
66752
|
+
const srcPath = path39.join(pluginDir, file.src);
|
|
66753
|
+
const destPath = path39.join(repoPath, file.dest);
|
|
66754
|
+
if (!fs34.existsSync(srcPath)) {
|
|
66275
66755
|
throw new Error(`Plugin file missing: ${file.src}`);
|
|
66276
66756
|
}
|
|
66277
|
-
if (
|
|
66757
|
+
if (fs34.existsSync(destPath) && !force) {
|
|
66278
66758
|
throw new Error(
|
|
66279
66759
|
`File already exists: ${file.dest}. Use --force to overwrite or remove it first.`
|
|
66280
66760
|
);
|
|
66281
66761
|
}
|
|
66282
66762
|
ensureParentDir(destPath);
|
|
66283
|
-
|
|
66763
|
+
fs34.copyFileSync(srcPath, destPath);
|
|
66284
66764
|
if (file.executable) {
|
|
66285
|
-
|
|
66765
|
+
fs34.chmodSync(destPath, 493);
|
|
66286
66766
|
}
|
|
66287
66767
|
return { written: true, path: file.dest };
|
|
66288
66768
|
}
|
|
66289
66769
|
function mergePackageJson(repoPath, pluginDir, fragmentRelPath, warnings) {
|
|
66290
|
-
const packagePath =
|
|
66291
|
-
if (!
|
|
66770
|
+
const packagePath = path39.join(repoPath, "package.json");
|
|
66771
|
+
if (!fs34.existsSync(packagePath)) {
|
|
66292
66772
|
throw new Error("No package.json in repo root. Add one before applying this plugin.");
|
|
66293
66773
|
}
|
|
66294
|
-
const fragmentPath =
|
|
66295
|
-
if (!
|
|
66774
|
+
const fragmentPath = path39.join(pluginDir, fragmentRelPath);
|
|
66775
|
+
if (!fs34.existsSync(fragmentPath)) {
|
|
66296
66776
|
throw new Error(`Package fragment missing: ${fragmentRelPath}`);
|
|
66297
66777
|
}
|
|
66298
|
-
const pkg = JSON.parse(
|
|
66299
|
-
const fragment = JSON.parse(
|
|
66778
|
+
const pkg = JSON.parse(fs34.readFileSync(packagePath, "utf8"));
|
|
66779
|
+
const fragment = JSON.parse(fs34.readFileSync(fragmentPath, "utf8"));
|
|
66300
66780
|
for (const section of ["scripts", "devDependencies"]) {
|
|
66301
66781
|
const existing = pkg[section] ?? {};
|
|
66302
66782
|
const incoming = fragment[section] ?? {};
|
|
@@ -66309,7 +66789,7 @@ function mergePackageJson(repoPath, pluginDir, fragmentRelPath, warnings) {
|
|
|
66309
66789
|
}
|
|
66310
66790
|
pkg[section] = existing;
|
|
66311
66791
|
}
|
|
66312
|
-
|
|
66792
|
+
fs34.writeFileSync(packagePath, JSON.stringify(pkg, null, 2) + "\n");
|
|
66313
66793
|
}
|
|
66314
66794
|
function patchStageRegistry(repoPath, manifest, force) {
|
|
66315
66795
|
const registry2 = readStageRegistry(repoPath);
|
|
@@ -66349,8 +66829,8 @@ function assertHarnessPresent(repoPath) {
|
|
|
66349
66829
|
}
|
|
66350
66830
|
function assertStageNotPresent(repoPath, stageId, force) {
|
|
66351
66831
|
if (force) return;
|
|
66352
|
-
const scriptPath =
|
|
66353
|
-
if (
|
|
66832
|
+
const scriptPath = path39.join(repoPath, ".har", "stages", `${stageId}.sh`);
|
|
66833
|
+
if (fs34.existsSync(scriptPath)) {
|
|
66354
66834
|
throw new Error(
|
|
66355
66835
|
`Stage script already exists: .har/stages/${stageId}.sh. Use --force to overwrite.`
|
|
66356
66836
|
);
|
|
@@ -66363,7 +66843,7 @@ function assertStageNotPresent(repoPath, stageId, force) {
|
|
|
66363
66843
|
}
|
|
66364
66844
|
}
|
|
66365
66845
|
function applyPlugin(repoPath, pluginId, options = {}) {
|
|
66366
|
-
const resolved =
|
|
66846
|
+
const resolved = path39.resolve(repoPath);
|
|
66367
66847
|
const force = options.force ?? false;
|
|
66368
66848
|
const warnings = [];
|
|
66369
66849
|
const filesWritten = [];
|
|
@@ -66382,7 +66862,7 @@ function applyPlugin(repoPath, pluginId, options = {}) {
|
|
|
66382
66862
|
if (file.skipFlag === "skipCi" && options.skipCi) {
|
|
66383
66863
|
continue;
|
|
66384
66864
|
}
|
|
66385
|
-
if (
|
|
66865
|
+
if (fs34.existsSync(path39.join(resolved, file.dest)) && !force) {
|
|
66386
66866
|
warnings.push(`Skipped optional file (exists): ${file.dest}`);
|
|
66387
66867
|
continue;
|
|
66388
66868
|
}
|
|
@@ -66417,18 +66897,18 @@ function applyPlugin(repoPath, pluginId, options = {}) {
|
|
|
66417
66897
|
};
|
|
66418
66898
|
}
|
|
66419
66899
|
function listPluginIds() {
|
|
66420
|
-
const root =
|
|
66421
|
-
if (!
|
|
66422
|
-
return
|
|
66900
|
+
const root = path39.join(resolveTemplatesDir(), "plugins");
|
|
66901
|
+
if (!fs34.existsSync(root)) return [...PLUGIN_IDS];
|
|
66902
|
+
return fs34.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => PLUGIN_IDS.includes(name));
|
|
66423
66903
|
}
|
|
66424
66904
|
|
|
66425
66905
|
// src/harness/plugin-drift.ts
|
|
66426
66906
|
init_stages();
|
|
66427
66907
|
var PACKAGE_SECTIONS = ["scripts", "devDependencies"];
|
|
66428
66908
|
function detectInstalledPlugins(repoPath) {
|
|
66429
|
-
const resolved =
|
|
66430
|
-
const registryPath =
|
|
66431
|
-
if (!
|
|
66909
|
+
const resolved = path40.resolve(repoPath);
|
|
66910
|
+
const registryPath = path40.join(resolved, ".har", "stages.json");
|
|
66911
|
+
if (!fs35.existsSync(registryPath)) return [];
|
|
66432
66912
|
const registry2 = readStageRegistry(resolved);
|
|
66433
66913
|
const stageIds = new Set(registry2.stages.map((stage) => stage.id));
|
|
66434
66914
|
const installed = [];
|
|
@@ -66441,17 +66921,17 @@ function detectInstalledPlugins(repoPath) {
|
|
|
66441
66921
|
return installed;
|
|
66442
66922
|
}
|
|
66443
66923
|
function pluginTemplatePath(pluginId, srcRel) {
|
|
66444
|
-
return
|
|
66924
|
+
return path40.join(resolveTemplatesDir(), "plugins", pluginId, srcRel);
|
|
66445
66925
|
}
|
|
66446
66926
|
function readBundledPluginFile(pluginId, srcRel) {
|
|
66447
|
-
return
|
|
66927
|
+
return fs35.readFileSync(pluginTemplatePath(pluginId, srcRel), "utf8");
|
|
66448
66928
|
}
|
|
66449
66929
|
function listPluginDestPaths(pluginId, repoPath) {
|
|
66450
66930
|
const manifest = readPluginManifest(pluginId);
|
|
66451
66931
|
const paths = manifest.files.map((file) => file.dest);
|
|
66452
66932
|
if (manifest.optionalFiles) {
|
|
66453
66933
|
for (const file of manifest.optionalFiles) {
|
|
66454
|
-
if (
|
|
66934
|
+
if (fs35.existsSync(path40.join(repoPath, file.dest))) {
|
|
66455
66935
|
paths.push(file.dest);
|
|
66456
66936
|
}
|
|
66457
66937
|
}
|
|
@@ -66510,9 +66990,9 @@ function readTemplatePluginFile(pluginId, dest) {
|
|
|
66510
66990
|
return null;
|
|
66511
66991
|
}
|
|
66512
66992
|
function readInstalledPluginFile(repoPath, pluginId, dest) {
|
|
66513
|
-
const installedPath =
|
|
66514
|
-
if (!
|
|
66515
|
-
const content =
|
|
66993
|
+
const installedPath = path40.join(path40.resolve(repoPath), dest);
|
|
66994
|
+
if (!fs35.existsSync(installedPath)) return null;
|
|
66995
|
+
const content = fs35.readFileSync(installedPath, "utf8");
|
|
66516
66996
|
const manifest = readPluginManifest(pluginId);
|
|
66517
66997
|
const fragmentRel = manifest.merge?.[dest];
|
|
66518
66998
|
if (fragmentRel) {
|
|
@@ -66531,7 +67011,7 @@ function pluginActionHint(pluginId, dest, kind) {
|
|
|
66531
67011
|
}
|
|
66532
67012
|
function comparePluginToTemplate(repoPath, pluginId) {
|
|
66533
67013
|
const manifest = readPluginManifest(pluginId);
|
|
66534
|
-
const resolved =
|
|
67014
|
+
const resolved = path40.resolve(repoPath);
|
|
66535
67015
|
const destPaths = listPluginDestPaths(pluginId, resolved);
|
|
66536
67016
|
const missing = [];
|
|
66537
67017
|
const checksumMismatch = [];
|
|
@@ -66546,8 +67026,8 @@ function comparePluginToTemplate(repoPath, pluginId) {
|
|
|
66546
67026
|
}
|
|
66547
67027
|
if (dest === "package.json") {
|
|
66548
67028
|
const fragmentRel = manifest.merge?.[dest];
|
|
66549
|
-
const installedPath =
|
|
66550
|
-
const installedRaw =
|
|
67029
|
+
const installedPath = path40.join(resolved, dest);
|
|
67030
|
+
const installedRaw = fs35.readFileSync(installedPath, "utf8");
|
|
66551
67031
|
if (fragmentRel && pluginKeysMatch(installedRaw, readBundledPluginFile(pluginId, fragmentRel))) {
|
|
66552
67032
|
unchanged.push(dest);
|
|
66553
67033
|
} else if (computeFileChecksum(installedContent) === computeFileChecksum(templateContent)) {
|
|
@@ -66605,293 +67085,6 @@ function buildPluginDriftActions(repoPath, pluginDrift) {
|
|
|
66605
67085
|
);
|
|
66606
67086
|
}
|
|
66607
67087
|
|
|
66608
|
-
// src/harness/instruction-files.ts
|
|
66609
|
-
var fs34 = __toESM(require("fs"));
|
|
66610
|
-
var os13 = __toESM(require("os"));
|
|
66611
|
-
var path39 = __toESM(require("path"));
|
|
66612
|
-
var readline2 = __toESM(require("readline"));
|
|
66613
|
-
init_file_ops();
|
|
66614
|
-
init_logging();
|
|
66615
|
-
var AGENTS_MD = "AGENTS.md";
|
|
66616
|
-
var LEGACY_AGENT_MD = "AGENT.md";
|
|
66617
|
-
var CLAUDE_MD = "CLAUDE.md";
|
|
66618
|
-
var HAR_SECTION_START = "<!-- har:agent-environment:start -->";
|
|
66619
|
-
var HAR_SECTION_END = "<!-- har:agent-environment:end -->";
|
|
66620
|
-
var CLAUDE_HAR_SECTION_START = "<!-- har:claude-pointer:start -->";
|
|
66621
|
-
var CLAUDE_HAR_SECTION_END = "<!-- har:claude-pointer:end -->";
|
|
66622
|
-
function escapeRegExp(value) {
|
|
66623
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66624
|
-
}
|
|
66625
|
-
function detectInstructionFiles(repoPath) {
|
|
66626
|
-
return {
|
|
66627
|
-
agentsMd: fs34.existsSync(path39.join(repoPath, AGENTS_MD)),
|
|
66628
|
-
agentMd: fs34.existsSync(path39.join(repoPath, LEGACY_AGENT_MD)),
|
|
66629
|
-
claudeMd: fs34.existsSync(path39.join(repoPath, CLAUDE_MD)),
|
|
66630
|
-
cursorDir: fs34.existsSync(path39.join(repoPath, ".cursor")),
|
|
66631
|
-
claudeDir: fs34.existsSync(path39.join(repoPath, ".claude")),
|
|
66632
|
-
codexHome: fs34.existsSync(path39.join(os13.homedir(), ".codex"))
|
|
66633
|
-
};
|
|
66634
|
-
}
|
|
66635
|
-
function formatDetectionReport(detection) {
|
|
66636
|
-
const lines2 = ["Detected:"];
|
|
66637
|
-
lines2.push(` - ${AGENTS_MD} (${detection.agentsMd ? "exists" : "missing"})`);
|
|
66638
|
-
if (detection.agentMd) {
|
|
66639
|
-
lines2.push(` - ${LEGACY_AGENT_MD} (legacy \u2014 will migrate into ${AGENTS_MD})`);
|
|
66640
|
-
}
|
|
66641
|
-
lines2.push(` - ${CLAUDE_MD} (${detection.claudeMd ? "exists" : "missing"})`);
|
|
66642
|
-
lines2.push(` - .cursor/ (${detection.cursorDir ? "exists" : "missing"})`);
|
|
66643
|
-
lines2.push(` - .claude/ (${detection.claudeDir ? "exists" : "missing"})`);
|
|
66644
|
-
lines2.push(` - ~/.codex (${detection.codexHome ? "exists" : "missing"})`);
|
|
66645
|
-
return lines2.join("\n");
|
|
66646
|
-
}
|
|
66647
|
-
function buildInstallPlan(detection, targets, options = {}) {
|
|
66648
|
-
const writeAgentsMd = options.writeAgentsMd !== false;
|
|
66649
|
-
const skillsEnabled = options.skillsEnabled !== false;
|
|
66650
|
-
const cursorExplicit = options.cursorRule;
|
|
66651
|
-
const wantsCursor = cursorExplicit === true || cursorExplicit !== false && (targets.includes("cursor") || detection.cursorDir);
|
|
66652
|
-
return {
|
|
66653
|
-
agentsMd: writeAgentsMd,
|
|
66654
|
-
migrateLegacyAgentMd: writeAgentsMd && detection.agentMd,
|
|
66655
|
-
claudeMd: skillsEnabled && targets.includes("claude"),
|
|
66656
|
-
cursorRule: wantsCursor,
|
|
66657
|
-
skills: skillsEnabled ? targets : []
|
|
66658
|
-
};
|
|
66659
|
-
}
|
|
66660
|
-
function formatInstallPlan(plan, detection) {
|
|
66661
|
-
const mark = (on) => on ? "[x]" : "[ ]";
|
|
66662
|
-
const lines2 = ["After you confirm, HAR will:"];
|
|
66663
|
-
const agentsAction = detection.agentsMd ? `Update ${AGENTS_MD} \u2014 refresh HAR / agent environment section` : `Create ${AGENTS_MD} \u2014 shared HAR workflow (Codex + cross-tool)`;
|
|
66664
|
-
lines2.push(` ${mark(plan.agentsMd)} ${agentsAction}`);
|
|
66665
|
-
if (plan.migrateLegacyAgentMd) {
|
|
66666
|
-
lines2.push(` ${mark(true)} Migrate ${LEGACY_AGENT_MD} \u2192 ${AGENTS_MD}, then remove legacy file`);
|
|
66667
|
-
}
|
|
66668
|
-
lines2.push(
|
|
66669
|
-
` ${mark(plan.claudeMd)} Claude \u2014 ensure ${CLAUDE_MD} points at ${AGENTS_MD}; install .claude/skills if confirmed`
|
|
66670
|
-
);
|
|
66671
|
-
lines2.push(
|
|
66672
|
-
` ${mark(plan.cursorRule)} Cursor \u2014 install/refresh .cursor/rules/har-workflow.mdc + commands if confirmed`
|
|
66673
|
-
);
|
|
66674
|
-
lines2.push(
|
|
66675
|
-
` ${mark(plan.skills.includes("codex"))} Codex \u2014 global ~/.codex/prompts (skills); project contract is ${AGENTS_MD}`
|
|
66676
|
-
);
|
|
66677
|
-
return lines2.join("\n");
|
|
66678
|
-
}
|
|
66679
|
-
function loadHarAgentsSectionFromTemplate() {
|
|
66680
|
-
const templatePath = resolveTemplateFile("AGENTS.md.template");
|
|
66681
|
-
if (!templatePath) {
|
|
66682
|
-
throw new Error("AGENTS.md.template not found. Run npm run build.");
|
|
66683
|
-
}
|
|
66684
|
-
const content = fs34.readFileSync(templatePath, "utf8");
|
|
66685
|
-
const start = content.indexOf(HAR_SECTION_START);
|
|
66686
|
-
const end = content.indexOf(HAR_SECTION_END);
|
|
66687
|
-
if (start === -1 || end === -1 || end < start) {
|
|
66688
|
-
throw new Error("AGENTS.md.template is missing har:agent-environment markers.");
|
|
66689
|
-
}
|
|
66690
|
-
return content.slice(start, end + HAR_SECTION_END.length);
|
|
66691
|
-
}
|
|
66692
|
-
function loadAgentsMdTemplate() {
|
|
66693
|
-
const templatePath = resolveTemplateFile("AGENTS.md.template");
|
|
66694
|
-
if (!templatePath) {
|
|
66695
|
-
throw new Error("AGENTS.md.template not found. Run npm run build.");
|
|
66696
|
-
}
|
|
66697
|
-
return fs34.readFileSync(templatePath, "utf8");
|
|
66698
|
-
}
|
|
66699
|
-
function upsertMarkedBlock(content, block, startMarker, endMarker) {
|
|
66700
|
-
if (content.includes(startMarker)) {
|
|
66701
|
-
const pattern = new RegExp(
|
|
66702
|
-
`${escapeRegExp(startMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}`
|
|
66703
|
-
);
|
|
66704
|
-
return { content: content.replace(pattern, block), action: "updated" };
|
|
66705
|
-
}
|
|
66706
|
-
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
66707
|
-
return { content: `${content}${suffix}
|
|
66708
|
-
${block}
|
|
66709
|
-
`, action: "appended" };
|
|
66710
|
-
}
|
|
66711
|
-
function upsertAgentsMdHarSection(repoPath) {
|
|
66712
|
-
const dest = path39.join(repoPath, AGENTS_MD);
|
|
66713
|
-
const section = loadHarAgentsSectionFromTemplate();
|
|
66714
|
-
if (!fs34.existsSync(dest)) {
|
|
66715
|
-
writeFileSafe(dest, loadAgentsMdTemplate());
|
|
66716
|
-
return "created";
|
|
66717
|
-
}
|
|
66718
|
-
const existing = fs34.readFileSync(dest, "utf8");
|
|
66719
|
-
const { content, action } = upsertMarkedBlock(
|
|
66720
|
-
existing,
|
|
66721
|
-
section,
|
|
66722
|
-
HAR_SECTION_START,
|
|
66723
|
-
HAR_SECTION_END
|
|
66724
|
-
);
|
|
66725
|
-
writeFileSafe(dest, content);
|
|
66726
|
-
return action;
|
|
66727
|
-
}
|
|
66728
|
-
function migrateLegacyAgentMd(repoPath) {
|
|
66729
|
-
const legacyPath = path39.join(repoPath, LEGACY_AGENT_MD);
|
|
66730
|
-
const agentsPath = path39.join(repoPath, AGENTS_MD);
|
|
66731
|
-
if (!fs34.existsSync(legacyPath)) return false;
|
|
66732
|
-
const legacy = fs34.readFileSync(legacyPath, "utf8");
|
|
66733
|
-
if (!fs34.existsSync(agentsPath)) {
|
|
66734
|
-
writeFileSafe(agentsPath, legacy);
|
|
66735
|
-
upsertAgentsMdHarSection(repoPath);
|
|
66736
|
-
} else {
|
|
66737
|
-
upsertAgentsMdHarSection(repoPath);
|
|
66738
|
-
}
|
|
66739
|
-
fs34.unlinkSync(legacyPath);
|
|
66740
|
-
info(`Migrated ${LEGACY_AGENT_MD} \u2192 ${AGENTS_MD} (removed legacy file)`);
|
|
66741
|
-
return true;
|
|
66742
|
-
}
|
|
66743
|
-
function loadClaudeMdTemplate(projectName) {
|
|
66744
|
-
const templatePath = resolveTemplateFile("CLAUDE.md.template");
|
|
66745
|
-
if (!templatePath) {
|
|
66746
|
-
throw new Error("CLAUDE.md.template not found. Run npm run build.");
|
|
66747
|
-
}
|
|
66748
|
-
const displayName = projectName.replace(/_/g, " ");
|
|
66749
|
-
return fs34.readFileSync(templatePath, "utf8").replace(/__PROJECT_DISPLAY_NAME__/g, displayName);
|
|
66750
|
-
}
|
|
66751
|
-
function isThinClaudePointer(content) {
|
|
66752
|
-
const trimmed = content.trim();
|
|
66753
|
-
if (trimmed.length > 600) return false;
|
|
66754
|
-
return /AGENTS\.md|AGENT\.md/.test(trimmed) && (trimmed.includes(".har/README.md") || trimmed.includes("har-workflow"));
|
|
66755
|
-
}
|
|
66756
|
-
var CLAUDE_POINTER_BLOCK = `${CLAUDE_HAR_SECTION_START}
|
|
66757
|
-
## HAR
|
|
66758
|
-
|
|
66759
|
-
This repository uses a \`.har/\` harness. Read [AGENTS.md](./AGENTS.md) and
|
|
66760
|
-
[\`.har/README.md\`](./.har/README.md) before making changes.
|
|
66761
|
-
${CLAUDE_HAR_SECTION_END}`;
|
|
66762
|
-
function ensureClaudeMdPointer(repoPath, options = {}) {
|
|
66763
|
-
const dest = path39.join(repoPath, CLAUDE_MD);
|
|
66764
|
-
const projectName = path39.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
|
|
66765
|
-
const thin = loadClaudeMdTemplate(projectName);
|
|
66766
|
-
if (!fs34.existsSync(dest)) {
|
|
66767
|
-
writeFileSafe(dest, thin);
|
|
66768
|
-
return "created";
|
|
66769
|
-
}
|
|
66770
|
-
const existing = fs34.readFileSync(dest, "utf8");
|
|
66771
|
-
if (isThinClaudePointer(existing) || options.force) {
|
|
66772
|
-
writeFileSafe(dest, thin);
|
|
66773
|
-
return "updated";
|
|
66774
|
-
}
|
|
66775
|
-
if (existing.includes(CLAUDE_HAR_SECTION_START) || /\[AGENTS\.md\]/.test(existing)) {
|
|
66776
|
-
if (existing.includes(CLAUDE_HAR_SECTION_START)) {
|
|
66777
|
-
const { content: content2, action: action2 } = upsertMarkedBlock(
|
|
66778
|
-
existing,
|
|
66779
|
-
CLAUDE_POINTER_BLOCK,
|
|
66780
|
-
CLAUDE_HAR_SECTION_START,
|
|
66781
|
-
CLAUDE_HAR_SECTION_END
|
|
66782
|
-
);
|
|
66783
|
-
writeFileSafe(dest, content2);
|
|
66784
|
-
return action2;
|
|
66785
|
-
}
|
|
66786
|
-
return "skipped";
|
|
66787
|
-
}
|
|
66788
|
-
const { content, action } = upsertMarkedBlock(
|
|
66789
|
-
existing,
|
|
66790
|
-
CLAUDE_POINTER_BLOCK,
|
|
66791
|
-
CLAUDE_HAR_SECTION_START,
|
|
66792
|
-
CLAUDE_HAR_SECTION_END
|
|
66793
|
-
);
|
|
66794
|
-
writeFileSafe(dest, content);
|
|
66795
|
-
return action;
|
|
66796
|
-
}
|
|
66797
|
-
async function askYesNo2(question, defaultYes = true) {
|
|
66798
|
-
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
66799
|
-
return new Promise((resolve47) => {
|
|
66800
|
-
process.stderr.write(`${question} `);
|
|
66801
|
-
rl.once("line", (answer) => {
|
|
66802
|
-
rl.close();
|
|
66803
|
-
const trimmed = answer.trim();
|
|
66804
|
-
if (trimmed === "") {
|
|
66805
|
-
resolve47(defaultYes);
|
|
66806
|
-
return;
|
|
66807
|
-
}
|
|
66808
|
-
resolve47(/^y(es)?$/i.test(trimmed));
|
|
66809
|
-
});
|
|
66810
|
-
});
|
|
66811
|
-
}
|
|
66812
|
-
async function handleInstructionFiles(options) {
|
|
66813
|
-
const { repoPath, mode } = options;
|
|
66814
|
-
const writeAgentsMd = options.writeAgentsMd !== false;
|
|
66815
|
-
const skillsEnabled = options.enabled !== false;
|
|
66816
|
-
const detection = detectInstructionFiles(repoPath);
|
|
66817
|
-
let targets;
|
|
66818
|
-
if (!skillsEnabled) {
|
|
66819
|
-
targets = [];
|
|
66820
|
-
} else if (typeof options.agents === "string") {
|
|
66821
|
-
targets = parseAgentTargets(options.agents);
|
|
66822
|
-
} else {
|
|
66823
|
-
targets = detectAgentTargets(repoPath);
|
|
66824
|
-
}
|
|
66825
|
-
if (skillsEnabled && targets.length === 0 && mode === "init" && typeof options.agents !== "string" && !options.autoYes) {
|
|
66826
|
-
targets = [...AGENT_SKILL_TARGETS];
|
|
66827
|
-
}
|
|
66828
|
-
let plan = buildInstallPlan(detection, targets, {
|
|
66829
|
-
writeAgentsMd,
|
|
66830
|
-
cursorRule: options.cursorRule,
|
|
66831
|
-
skillsEnabled
|
|
66832
|
-
});
|
|
66833
|
-
divider();
|
|
66834
|
-
process.stderr.write(`${formatDetectionReport(detection)}
|
|
66835
|
-
|
|
66836
|
-
`);
|
|
66837
|
-
process.stderr.write(`${formatInstallPlan(plan, detection)}
|
|
66838
|
-
`);
|
|
66839
|
-
divider();
|
|
66840
|
-
const shouldPrompt = !options.autoYes && skillsEnabled && typeof options.agents !== "string" && !(mode === "maintain" && targets.length > 0 && !detection.agentMd);
|
|
66841
|
-
if (shouldPrompt && targets.length > 0) {
|
|
66842
|
-
const accepted = await askYesNo2(
|
|
66843
|
-
`Install HAR agent instructions for: ${targets.join(", ")}? [Y/n]`
|
|
66844
|
-
);
|
|
66845
|
-
if (!accepted) {
|
|
66846
|
-
info("Skipped agent instruction adapters (AGENTS.md will still be updated)");
|
|
66847
|
-
targets = [];
|
|
66848
|
-
plan = buildInstallPlan(detection, targets, {
|
|
66849
|
-
writeAgentsMd,
|
|
66850
|
-
cursorRule: options.cursorRule === true ? true : false,
|
|
66851
|
-
skillsEnabled: false
|
|
66852
|
-
});
|
|
66853
|
-
}
|
|
66854
|
-
}
|
|
66855
|
-
let migratedLegacy = false;
|
|
66856
|
-
let agentsMdAction = null;
|
|
66857
|
-
let claudeMdAction = null;
|
|
66858
|
-
if (writeAgentsMd) {
|
|
66859
|
-
if (detection.agentMd) {
|
|
66860
|
-
migratedLegacy = migrateLegacyAgentMd(repoPath);
|
|
66861
|
-
}
|
|
66862
|
-
agentsMdAction = upsertAgentsMdHarSection(repoPath);
|
|
66863
|
-
if (agentsMdAction === "created") {
|
|
66864
|
-
success(`Wrote ${AGENTS_MD}`);
|
|
66865
|
-
} else if (agentsMdAction === "updated" || agentsMdAction === "appended") {
|
|
66866
|
-
info(
|
|
66867
|
-
`${agentsMdAction === "updated" ? "Refreshed" : "Appended"} HAR section in ${AGENTS_MD}`
|
|
66868
|
-
);
|
|
66869
|
-
}
|
|
66870
|
-
} else {
|
|
66871
|
-
agentsMdAction = "skipped";
|
|
66872
|
-
}
|
|
66873
|
-
if (plan.claudeMd) {
|
|
66874
|
-
claudeMdAction = ensureClaudeMdPointer(repoPath);
|
|
66875
|
-
if (claudeMdAction === "created") {
|
|
66876
|
-
success(`Wrote ${CLAUDE_MD} (pointer \u2192 ${AGENTS_MD})`);
|
|
66877
|
-
} else if (claudeMdAction === "updated" || claudeMdAction === "appended") {
|
|
66878
|
-
info(
|
|
66879
|
-
`${claudeMdAction === "updated" ? "Updated" : "Appended"} ${CLAUDE_MD} pointer \u2192 ${AGENTS_MD}`
|
|
66880
|
-
);
|
|
66881
|
-
}
|
|
66882
|
-
} else {
|
|
66883
|
-
claudeMdAction = "skipped";
|
|
66884
|
-
}
|
|
66885
|
-
return {
|
|
66886
|
-
detection,
|
|
66887
|
-
plan,
|
|
66888
|
-
targets,
|
|
66889
|
-
agentsMdAction,
|
|
66890
|
-
migratedLegacy,
|
|
66891
|
-
claudeMdAction
|
|
66892
|
-
};
|
|
66893
|
-
}
|
|
66894
|
-
|
|
66895
67088
|
// src/harness/maintain-bundle.ts
|
|
66896
67089
|
var MAINTAIN_DIR = "maintain";
|
|
66897
67090
|
var PROFILE_DIRS3 = {
|
|
@@ -66903,14 +67096,14 @@ function substituteProjectName2(content, projectName) {
|
|
|
66903
67096
|
return content.replace(/__PROJECT_NAME__/g, projectName).replace(/template___PROJECT_NAME__/g, `template_${projectName}`);
|
|
66904
67097
|
}
|
|
66905
67098
|
function projectNameFromRepo(repoPath) {
|
|
66906
|
-
return
|
|
67099
|
+
return path41.basename(repoPath).toLowerCase().replace(/[^a-z0-9]/g, "_");
|
|
66907
67100
|
}
|
|
66908
67101
|
function boilerplateDirForProfile(profile) {
|
|
66909
|
-
return
|
|
67102
|
+
return path41.join(resolveTemplatesDir(), PROFILE_DIRS3[profile]);
|
|
66910
67103
|
}
|
|
66911
67104
|
function readBundledTemplateContent(repoPath, profile, file) {
|
|
66912
|
-
const templatePath =
|
|
66913
|
-
let content =
|
|
67105
|
+
const templatePath = path41.join(boilerplateDirForProfile(profile), templateFileForHarness(file));
|
|
67106
|
+
let content = fs36.readFileSync(templatePath, "utf8");
|
|
66914
67107
|
if (file === "harness.env") {
|
|
66915
67108
|
content = substituteProjectName2(content, projectNameFromRepo(repoPath));
|
|
66916
67109
|
}
|
|
@@ -66941,12 +67134,12 @@ function actionHint(file, kind) {
|
|
|
66941
67134
|
return `Read maintain/diffs/${file}.diff and merge into .har/${file}.`;
|
|
66942
67135
|
}
|
|
66943
67136
|
function createUnifiedDiff(installedContent, templateContent, installedLabel, templateLabel) {
|
|
66944
|
-
const tmpDir =
|
|
67137
|
+
const tmpDir = fs36.mkdtempSync(path41.join(os14.tmpdir(), "har-maintain-diff-"));
|
|
66945
67138
|
try {
|
|
66946
|
-
const installedPath =
|
|
66947
|
-
const templatePath =
|
|
66948
|
-
|
|
66949
|
-
|
|
67139
|
+
const installedPath = path41.join(tmpDir, "installed");
|
|
67140
|
+
const templatePath = path41.join(tmpDir, "template");
|
|
67141
|
+
fs36.writeFileSync(installedPath, installedContent);
|
|
67142
|
+
fs36.writeFileSync(templatePath, templateContent);
|
|
66950
67143
|
const result = run(`diff -u "${installedPath}" "${templatePath}"`);
|
|
66951
67144
|
const body = result.stdout.trim();
|
|
66952
67145
|
if (!body) return "";
|
|
@@ -66955,7 +67148,7 @@ function createUnifiedDiff(installedContent, templateContent, installedLabel, te
|
|
|
66955
67148
|
${body.split("\n").slice(2).join("\n")}
|
|
66956
67149
|
`;
|
|
66957
67150
|
} finally {
|
|
66958
|
-
|
|
67151
|
+
fs36.rmSync(tmpDir, { recursive: true, force: true });
|
|
66959
67152
|
}
|
|
66960
67153
|
}
|
|
66961
67154
|
function buildActions(repoPath, profile, drift) {
|
|
@@ -66970,7 +67163,7 @@ function buildActions(repoPath, profile, drift) {
|
|
|
66970
67163
|
});
|
|
66971
67164
|
}
|
|
66972
67165
|
for (const file of drift.checksumMismatch) {
|
|
66973
|
-
const installedPath =
|
|
67166
|
+
const installedPath = path41.join(harnessDir, file);
|
|
66974
67167
|
const installedRel = `maintain/installed/${file}`;
|
|
66975
67168
|
const templateRel = `maintain/templates/${file}`;
|
|
66976
67169
|
const diffRel = `maintain/diffs/${file}.diff`;
|
|
@@ -66978,7 +67171,7 @@ function buildActions(repoPath, profile, drift) {
|
|
|
66978
67171
|
file,
|
|
66979
67172
|
kind: "drift",
|
|
66980
67173
|
template: templateRel,
|
|
66981
|
-
installed:
|
|
67174
|
+
installed: fs36.existsSync(installedPath) ? installedRel : void 0,
|
|
66982
67175
|
diff: diffRel,
|
|
66983
67176
|
hint: actionHint(file, "drift")
|
|
66984
67177
|
});
|
|
@@ -67097,6 +67290,10 @@ function buildReadme(report) {
|
|
|
67097
67290
|
lines2.push("", "## Agent instruction files (generator 0.5.0)", "");
|
|
67098
67291
|
lines2.push(
|
|
67099
67292
|
"Repo-root agent docs are **outside** `.har/` checksum drift. Apply while adapting:",
|
|
67293
|
+
"",
|
|
67294
|
+
"**Merge contract:** `har env maintain --finalize` refreshes only the content between",
|
|
67295
|
+
"`<!-- har:agent-environment:start/end -->` in `AGENTS.md`. Custom `## Project` sections and",
|
|
67296
|
+
"other guidance **outside** those markers must survive finalize \u2014 relocate them if needed.",
|
|
67100
67297
|
""
|
|
67101
67298
|
);
|
|
67102
67299
|
for (const note of report.instructionFiles) {
|
|
@@ -67126,28 +67323,28 @@ function buildReadme(report) {
|
|
|
67126
67323
|
}
|
|
67127
67324
|
function writeBundleArtifacts(repoPath, profile, drift, report) {
|
|
67128
67325
|
const harnessDir = getHarnessDir(repoPath);
|
|
67129
|
-
const bundleDir =
|
|
67130
|
-
if (
|
|
67131
|
-
|
|
67326
|
+
const bundleDir = path41.join(harnessDir, MAINTAIN_DIR);
|
|
67327
|
+
if (fs36.existsSync(bundleDir)) {
|
|
67328
|
+
fs36.rmSync(bundleDir, { recursive: true, force: true });
|
|
67132
67329
|
}
|
|
67133
67330
|
const dirs = [
|
|
67134
67331
|
bundleDir,
|
|
67135
|
-
|
|
67136
|
-
|
|
67137
|
-
|
|
67138
|
-
|
|
67332
|
+
path41.join(bundleDir, "templates"),
|
|
67333
|
+
path41.join(bundleDir, "installed"),
|
|
67334
|
+
path41.join(bundleDir, "diffs"),
|
|
67335
|
+
path41.join(bundleDir, "stale")
|
|
67139
67336
|
];
|
|
67140
67337
|
for (const dir of dirs) {
|
|
67141
|
-
|
|
67338
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
67142
67339
|
}
|
|
67143
67340
|
const affectedFiles = [...drift.missing, ...drift.checksumMismatch];
|
|
67144
67341
|
for (const file of affectedFiles) {
|
|
67145
67342
|
const templateContent = readBundledTemplateContent(repoPath, profile, file);
|
|
67146
|
-
writeFileSafe(
|
|
67147
|
-
const harnessPath =
|
|
67148
|
-
if (
|
|
67149
|
-
const installedContent =
|
|
67150
|
-
writeFileSafe(
|
|
67343
|
+
writeFileSafe(path41.join(bundleDir, "templates", file), templateContent);
|
|
67344
|
+
const harnessPath = path41.join(harnessDir, file);
|
|
67345
|
+
if (fs36.existsSync(harnessPath)) {
|
|
67346
|
+
const installedContent = fs36.readFileSync(harnessPath, "utf8");
|
|
67347
|
+
writeFileSafe(path41.join(bundleDir, "installed", file), installedContent);
|
|
67151
67348
|
const diff = createUnifiedDiff(
|
|
67152
67349
|
installedContent,
|
|
67153
67350
|
templateContent,
|
|
@@ -67155,14 +67352,14 @@ function writeBundleArtifacts(repoPath, profile, drift, report) {
|
|
|
67155
67352
|
`templates/${file}`
|
|
67156
67353
|
);
|
|
67157
67354
|
if (diff) {
|
|
67158
|
-
writeFileSafe(
|
|
67355
|
+
writeFileSafe(path41.join(bundleDir, "diffs", `${file}.diff`), diff);
|
|
67159
67356
|
}
|
|
67160
67357
|
}
|
|
67161
67358
|
}
|
|
67162
|
-
writeFileSafe(
|
|
67163
|
-
writeFileSafe(
|
|
67359
|
+
writeFileSafe(path41.join(bundleDir, "README.md"), buildReadme(report));
|
|
67360
|
+
writeFileSafe(path41.join(bundleDir, "drift-report.json"), JSON.stringify(report, null, 2) + "\n");
|
|
67164
67361
|
writeFileSafe(
|
|
67165
|
-
|
|
67362
|
+
path41.join(bundleDir, "validation.json"),
|
|
67166
67363
|
JSON.stringify(report.validation, null, 2) + "\n"
|
|
67167
67364
|
);
|
|
67168
67365
|
if (report.stale.length > 0) {
|
|
@@ -67174,34 +67371,34 @@ function writeBundleArtifacts(repoPath, profile, drift, report) {
|
|
|
67174
67371
|
...report.stale.map((s) => `- **${s.file}** \u2014 ${s.hint}`),
|
|
67175
67372
|
""
|
|
67176
67373
|
];
|
|
67177
|
-
writeFileSafe(
|
|
67374
|
+
writeFileSafe(path41.join(bundleDir, "stale", "MANIFEST.md"), staleLines.join("\n"));
|
|
67178
67375
|
}
|
|
67179
67376
|
return bundleDir;
|
|
67180
67377
|
}
|
|
67181
67378
|
function ensureParentDir2(filePath) {
|
|
67182
|
-
const parent =
|
|
67183
|
-
if (!
|
|
67184
|
-
|
|
67379
|
+
const parent = path41.dirname(filePath);
|
|
67380
|
+
if (!fs36.existsSync(parent)) {
|
|
67381
|
+
fs36.mkdirSync(parent, { recursive: true });
|
|
67185
67382
|
}
|
|
67186
67383
|
}
|
|
67187
67384
|
function writePluginBundleArtifacts(repoPath, bundleDir, report) {
|
|
67188
67385
|
if (report.pluginActions.length === 0) return;
|
|
67189
67386
|
for (const action of report.pluginActions) {
|
|
67190
|
-
const pluginRoot =
|
|
67191
|
-
const templatesDir =
|
|
67192
|
-
const installedDir =
|
|
67193
|
-
const diffsDir =
|
|
67387
|
+
const pluginRoot = path41.join(bundleDir, "plugins", action.pluginId);
|
|
67388
|
+
const templatesDir = path41.join(pluginRoot, "templates");
|
|
67389
|
+
const installedDir = path41.join(pluginRoot, "installed");
|
|
67390
|
+
const diffsDir = path41.join(pluginRoot, "diffs");
|
|
67194
67391
|
for (const dir of [templatesDir, installedDir, diffsDir]) {
|
|
67195
|
-
|
|
67392
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
67196
67393
|
}
|
|
67197
67394
|
const templateContent = readTemplatePluginFile(action.pluginId, action.file);
|
|
67198
67395
|
if (templateContent === null) continue;
|
|
67199
|
-
const templatePath =
|
|
67396
|
+
const templatePath = path41.join(templatesDir, action.file);
|
|
67200
67397
|
ensureParentDir2(templatePath);
|
|
67201
67398
|
writeFileSafe(templatePath, templateContent);
|
|
67202
67399
|
const installedContent = readInstalledPluginFile(repoPath, action.pluginId, action.file);
|
|
67203
67400
|
if (installedContent !== null) {
|
|
67204
|
-
const installedPath =
|
|
67401
|
+
const installedPath = path41.join(installedDir, action.file);
|
|
67205
67402
|
ensureParentDir2(installedPath);
|
|
67206
67403
|
writeFileSafe(installedPath, installedContent);
|
|
67207
67404
|
const diff = createUnifiedDiff(
|
|
@@ -67211,7 +67408,7 @@ function writePluginBundleArtifacts(repoPath, bundleDir, report) {
|
|
|
67211
67408
|
`templates/${action.file}`
|
|
67212
67409
|
);
|
|
67213
67410
|
if (diff) {
|
|
67214
|
-
const diffPath =
|
|
67411
|
+
const diffPath = path41.join(diffsDir, `${action.file}.diff`);
|
|
67215
67412
|
ensureParentDir2(diffPath);
|
|
67216
67413
|
writeFileSafe(diffPath, diff);
|
|
67217
67414
|
}
|
|
@@ -67247,9 +67444,9 @@ function buildMaintainBundle(repoPath, validation2, drift) {
|
|
|
67247
67444
|
return { bundleDir, report };
|
|
67248
67445
|
}
|
|
67249
67446
|
function removeMaintainBundle(repoPath) {
|
|
67250
|
-
const bundleDir =
|
|
67251
|
-
if (
|
|
67252
|
-
|
|
67447
|
+
const bundleDir = path41.join(getHarnessDir(repoPath), MAINTAIN_DIR);
|
|
67448
|
+
if (fs36.existsSync(bundleDir)) {
|
|
67449
|
+
fs36.rmSync(bundleDir, { recursive: true, force: true });
|
|
67253
67450
|
}
|
|
67254
67451
|
}
|
|
67255
67452
|
function formatMaintainBundlePromptSection(report) {
|
|
@@ -67338,11 +67535,11 @@ init_manifest();
|
|
|
67338
67535
|
init_stages();
|
|
67339
67536
|
function listHarnessScripts(repoPath) {
|
|
67340
67537
|
const harnessDir = getHarnessDir(repoPath);
|
|
67341
|
-
if (!
|
|
67342
|
-
return
|
|
67538
|
+
if (!fs37.existsSync(harnessDir)) return [];
|
|
67539
|
+
return fs37.readdirSync(harnessDir).filter((name) => name.endsWith(".sh")).sort();
|
|
67343
67540
|
}
|
|
67344
67541
|
function describeProject(repoPath) {
|
|
67345
|
-
const resolved =
|
|
67542
|
+
const resolved = path42.resolve(repoPath);
|
|
67346
67543
|
const manifest = readManifest(resolved);
|
|
67347
67544
|
const present = harnessExists(resolved);
|
|
67348
67545
|
return {
|
|
@@ -67362,8 +67559,8 @@ function describeProject(repoPath) {
|
|
|
67362
67559
|
};
|
|
67363
67560
|
}
|
|
67364
67561
|
async function initHarness(options) {
|
|
67365
|
-
const repoPath =
|
|
67366
|
-
if (!
|
|
67562
|
+
const repoPath = path42.resolve(options.repoPath);
|
|
67563
|
+
if (!fs37.existsSync(repoPath)) {
|
|
67367
67564
|
throw new Error(`Path not found: ${repoPath}`);
|
|
67368
67565
|
}
|
|
67369
67566
|
const scaffold = scaffoldHarnessBoilerplate(repoPath, {
|
|
@@ -67383,9 +67580,9 @@ async function initHarness(options) {
|
|
|
67383
67580
|
};
|
|
67384
67581
|
}
|
|
67385
67582
|
async function maintainHarness(options) {
|
|
67386
|
-
const repoPath =
|
|
67583
|
+
const repoPath = path42.resolve(options.repoPath);
|
|
67387
67584
|
const harnessDir = getHarnessDir(repoPath);
|
|
67388
|
-
if (!
|
|
67585
|
+
if (!fs37.existsSync(harnessDir)) {
|
|
67389
67586
|
throw new Error('No .har/ found. Run "har env init" first.');
|
|
67390
67587
|
}
|
|
67391
67588
|
const drift = compareHarnessToTemplate(repoPath);
|
|
@@ -67395,6 +67592,7 @@ async function maintainHarness(options) {
|
|
|
67395
67592
|
throw new Error("Cannot finalize: harness validation has errors. Fix them first.");
|
|
67396
67593
|
}
|
|
67397
67594
|
syncAgentSlotsToHarnessEnv(repoPath);
|
|
67595
|
+
finalizeAgentsMdInstructionFiles(repoPath);
|
|
67398
67596
|
const existing = readManifest(repoPath);
|
|
67399
67597
|
finalizeHarness(
|
|
67400
67598
|
repoPath,
|
|
@@ -67419,13 +67617,13 @@ function addPlugin(repoPath, pluginId, options = {}) {
|
|
|
67419
67617
|
}
|
|
67420
67618
|
|
|
67421
67619
|
// src/harness/custom-stage.ts
|
|
67422
|
-
var
|
|
67423
|
-
var
|
|
67620
|
+
var fs38 = __toESM(require("fs"));
|
|
67621
|
+
var path43 = __toESM(require("path"));
|
|
67424
67622
|
init_schema2();
|
|
67425
67623
|
init_stages();
|
|
67426
67624
|
var STAGE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
67427
67625
|
function addCustomStage(repoPath, options) {
|
|
67428
|
-
const resolved =
|
|
67626
|
+
const resolved = path43.resolve(repoPath);
|
|
67429
67627
|
if (!harnessExists(resolved)) {
|
|
67430
67628
|
throw new Error('No .har/ harness found. Run "har env init" first.');
|
|
67431
67629
|
}
|
|
@@ -67462,20 +67660,20 @@ function addCustomStage(repoPath, options) {
|
|
|
67462
67660
|
if (options.command) {
|
|
67463
67661
|
stage.command = options.command;
|
|
67464
67662
|
} else {
|
|
67465
|
-
const scriptRel =
|
|
67466
|
-
const scriptAbs =
|
|
67467
|
-
if (
|
|
67663
|
+
const scriptRel = path43.join("stages", `${id}.sh`);
|
|
67664
|
+
const scriptAbs = path43.join(resolved, ".har", scriptRel);
|
|
67665
|
+
if (fs38.existsSync(scriptAbs) && !options.force) {
|
|
67468
67666
|
throw new Error(`Stage script already exists: .har/${scriptRel}. Use --force to overwrite.`);
|
|
67469
67667
|
}
|
|
67470
|
-
const skeletonPath =
|
|
67668
|
+
const skeletonPath = path43.join(
|
|
67471
67669
|
resolveTemplatesDir(),
|
|
67472
67670
|
"plugins",
|
|
67473
67671
|
"custom-stage-skeleton.sh"
|
|
67474
67672
|
);
|
|
67475
|
-
const skeleton =
|
|
67476
|
-
|
|
67477
|
-
|
|
67478
|
-
|
|
67673
|
+
const skeleton = fs38.readFileSync(skeletonPath, "utf8").replace(/__STAGE_ID__/g, id).replace(/__STAGE_KIND__/g, kind).replace(/__STAGE_DESCRIPTION__/g, stage.description);
|
|
67674
|
+
fs38.mkdirSync(path43.dirname(scriptAbs), { recursive: true });
|
|
67675
|
+
fs38.writeFileSync(scriptAbs, skeleton);
|
|
67676
|
+
fs38.chmodSync(scriptAbs, 493);
|
|
67479
67677
|
filesWritten.push(`.har/${scriptRel}`);
|
|
67480
67678
|
stage.script = scriptRel;
|
|
67481
67679
|
stage.artifacts = [
|
|
@@ -67523,8 +67721,8 @@ function addCustomStage(repoPath, options) {
|
|
|
67523
67721
|
}
|
|
67524
67722
|
|
|
67525
67723
|
// src/harness/adaptation-prompt.ts
|
|
67526
|
-
var
|
|
67527
|
-
var
|
|
67724
|
+
var fs39 = __toESM(require("fs"));
|
|
67725
|
+
var path44 = __toESM(require("path"));
|
|
67528
67726
|
var readline3 = __toESM(require("readline"));
|
|
67529
67727
|
|
|
67530
67728
|
// src/utils/clipboard.ts
|
|
@@ -67614,7 +67812,7 @@ function loadTemplate(name) {
|
|
|
67614
67812
|
if (!filePath) {
|
|
67615
67813
|
throw new Error(`Adaptation prompt template not found: ${name}. Run npm run build.`);
|
|
67616
67814
|
}
|
|
67617
|
-
return
|
|
67815
|
+
return fs39.readFileSync(filePath, "utf8");
|
|
67618
67816
|
}
|
|
67619
67817
|
function applyProfilePlaceholders(content, profile) {
|
|
67620
67818
|
return content.replace(/\{\{PROFILE\}\}/g, profile).replace(/\{\{PROFILE_HINT\}\}/g, PROFILE_HINTS[profile]);
|
|
@@ -67636,7 +67834,7 @@ function buildMaintainAdaptationPrompt(_repoPath, bundleReport) {
|
|
|
67636
67834
|
}
|
|
67637
67835
|
function writeAdaptationPrompt(repoPath, content) {
|
|
67638
67836
|
const harnessDir = getHarnessDir(repoPath);
|
|
67639
|
-
const filePath =
|
|
67837
|
+
const filePath = path44.join(harnessDir, ADAPTATION_PROMPT_FILE);
|
|
67640
67838
|
writeFileSafe(filePath, content);
|
|
67641
67839
|
return filePath;
|
|
67642
67840
|
}
|
|
@@ -67691,27 +67889,27 @@ async function offerAdaptationPromptClipboard(content, options = {}) {
|
|
|
67691
67889
|
}
|
|
67692
67890
|
|
|
67693
67891
|
// src/harness/cursor-rule.ts
|
|
67694
|
-
var
|
|
67695
|
-
var
|
|
67892
|
+
var fs40 = __toESM(require("fs"));
|
|
67893
|
+
var path45 = __toESM(require("path"));
|
|
67696
67894
|
var readline4 = __toESM(require("readline"));
|
|
67697
67895
|
init_file_ops();
|
|
67698
67896
|
init_logging();
|
|
67699
67897
|
var CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/har-workflow.mdc";
|
|
67700
67898
|
function getCursorRulePath(repoPath) {
|
|
67701
|
-
return
|
|
67899
|
+
return path45.join(repoPath, CURSOR_RULE_RELATIVE_PATH);
|
|
67702
67900
|
}
|
|
67703
67901
|
function isCursorWorkspace(repoPath) {
|
|
67704
|
-
return
|
|
67902
|
+
return fs40.existsSync(path45.join(repoPath, ".cursor"));
|
|
67705
67903
|
}
|
|
67706
67904
|
function cursorRuleExists(repoPath) {
|
|
67707
|
-
return
|
|
67905
|
+
return fs40.existsSync(getCursorRulePath(repoPath));
|
|
67708
67906
|
}
|
|
67709
67907
|
function scaffoldCursorRule(repoPath) {
|
|
67710
67908
|
const templatePath = resolveTemplateFile("cursor-rule.mdc.template");
|
|
67711
67909
|
if (!templatePath) {
|
|
67712
67910
|
throw new Error("Cursor rule template not found (cursor-rule.mdc.template). Run npm run build.");
|
|
67713
67911
|
}
|
|
67714
|
-
const content =
|
|
67912
|
+
const content = fs40.readFileSync(templatePath, "utf8");
|
|
67715
67913
|
writeFileSafe(getCursorRulePath(repoPath), content);
|
|
67716
67914
|
}
|
|
67717
67915
|
async function handleCursorRule(options) {
|
|
@@ -67801,7 +67999,7 @@ init_control_config();
|
|
|
67801
67999
|
|
|
67802
68000
|
// src/core/control-lifecycle.ts
|
|
67803
68001
|
var import_child_process9 = require("child_process");
|
|
67804
|
-
var
|
|
68002
|
+
var path46 = __toESM(require("path"));
|
|
67805
68003
|
|
|
67806
68004
|
// src/core/control-image.ts
|
|
67807
68005
|
var DEFAULT_CONTROL_IMAGE = "theosfactory/har-control";
|
|
@@ -67827,10 +68025,10 @@ var CONTROL_DATA_VOLUME = "har_control_data";
|
|
|
67827
68025
|
var CONTROL_CONTAINER_PORT = 3847;
|
|
67828
68026
|
var CONTROL_DATA_DB_URL = "file:/data/har_control.db";
|
|
67829
68027
|
function resolveControlDir() {
|
|
67830
|
-
return
|
|
68028
|
+
return path46.resolve(__dirname, "..", "control");
|
|
67831
68029
|
}
|
|
67832
68030
|
function resolveControlBuildContext() {
|
|
67833
|
-
return
|
|
68031
|
+
return path46.resolve(resolveControlDir(), "..");
|
|
67834
68032
|
}
|
|
67835
68033
|
function buildDockerRunArgs(options) {
|
|
67836
68034
|
const name = options.containerName ?? CONTROL_CONTAINER_NAME;
|
|
@@ -67886,7 +68084,7 @@ async function startMissionControl(options) {
|
|
|
67886
68084
|
"-t",
|
|
67887
68085
|
imageRef,
|
|
67888
68086
|
"-f",
|
|
67889
|
-
|
|
68087
|
+
path46.join(resolveControlDir(), "Dockerfile"),
|
|
67890
68088
|
resolveControlBuildContext()
|
|
67891
68089
|
]);
|
|
67892
68090
|
if (buildCode !== 0) {
|
|
@@ -68033,7 +68231,7 @@ async function ensureTelemetryInfrastructure(options) {
|
|
|
68033
68231
|
// src/core/run-service.ts
|
|
68034
68232
|
init_telemetry_config();
|
|
68035
68233
|
init_telemetry_env();
|
|
68036
|
-
var
|
|
68234
|
+
var path48 = __toESM(require("path"));
|
|
68037
68235
|
init_local_executor();
|
|
68038
68236
|
function extractShellOutput(result) {
|
|
68039
68237
|
const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
|
|
@@ -68236,12 +68434,12 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
|
|
|
68236
68434
|
suffix: session.suffix,
|
|
68237
68435
|
createdAt: session.createdAt
|
|
68238
68436
|
});
|
|
68239
|
-
const envFile =
|
|
68437
|
+
const envFile = path48.join(session.workDir, `.env.agent.${options.agentId}`);
|
|
68240
68438
|
try {
|
|
68241
68439
|
appendTelemetryEnvToFile(envFile, {
|
|
68242
68440
|
sessionKey,
|
|
68243
68441
|
agentId: options.agentId,
|
|
68244
|
-
repoPath:
|
|
68442
|
+
repoPath: path48.resolve(options.repoPath),
|
|
68245
68443
|
workDir: session.workDir,
|
|
68246
68444
|
branch: session.branch,
|
|
68247
68445
|
suffix: session.suffix,
|
|
@@ -68479,9 +68677,9 @@ init_slot_status();
|
|
|
68479
68677
|
var readline5 = __toESM(require("readline"));
|
|
68480
68678
|
|
|
68481
68679
|
// src/core/hooks.ts
|
|
68482
|
-
var
|
|
68680
|
+
var fs42 = __toESM(require("fs"));
|
|
68483
68681
|
var os16 = __toESM(require("os"));
|
|
68484
|
-
var
|
|
68682
|
+
var path49 = __toESM(require("path"));
|
|
68485
68683
|
init_shell();
|
|
68486
68684
|
init_schema2();
|
|
68487
68685
|
init_stages();
|
|
@@ -68512,7 +68710,7 @@ function resolveCheckoutRoot(cwd) {
|
|
|
68512
68710
|
function resolveHooksDir(checkoutDir) {
|
|
68513
68711
|
const hooksPath = tryGit3(checkoutDir, "rev-parse --git-path hooks");
|
|
68514
68712
|
if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
|
|
68515
|
-
return
|
|
68713
|
+
return path49.resolve(checkoutDir, hooksPath);
|
|
68516
68714
|
}
|
|
68517
68715
|
function getConfiguredHooksPath(checkoutDir) {
|
|
68518
68716
|
return tryGit3(checkoutDir, "config core.hooksPath") || void 0;
|
|
@@ -68520,9 +68718,9 @@ function getConfiguredHooksPath(checkoutDir) {
|
|
|
68520
68718
|
function defaultHarInvocation() {
|
|
68521
68719
|
const entry = process.argv[1];
|
|
68522
68720
|
if (entry && entry.endsWith(".js")) {
|
|
68523
|
-
return `"${process.execPath}" "${
|
|
68721
|
+
return `"${process.execPath}" "${path49.resolve(entry)}"`;
|
|
68524
68722
|
}
|
|
68525
|
-
if (entry) return `"${
|
|
68723
|
+
if (entry) return `"${path49.resolve(entry)}"`;
|
|
68526
68724
|
return "har";
|
|
68527
68725
|
}
|
|
68528
68726
|
function buildHookScript(harInvocation, subcommand, failOpenNotice) {
|
|
@@ -68543,40 +68741,40 @@ exit 0
|
|
|
68543
68741
|
`;
|
|
68544
68742
|
}
|
|
68545
68743
|
function upsertMarkedBlock2(filePath, block) {
|
|
68546
|
-
if (!
|
|
68547
|
-
|
|
68744
|
+
if (!fs42.existsSync(filePath)) {
|
|
68745
|
+
fs42.writeFileSync(filePath, `#!/bin/sh
|
|
68548
68746
|
${block}
|
|
68549
68747
|
`, { mode: 493 });
|
|
68550
68748
|
return "created";
|
|
68551
68749
|
}
|
|
68552
|
-
const content =
|
|
68750
|
+
const content = fs42.readFileSync(filePath, "utf8");
|
|
68553
68751
|
if (content.includes(MARKER_START)) {
|
|
68554
68752
|
const pattern = new RegExp(
|
|
68555
68753
|
`${escapeRegExp2(MARKER_START)}[\\s\\S]*?${escapeRegExp2(MARKER_END)}`
|
|
68556
68754
|
);
|
|
68557
|
-
|
|
68558
|
-
|
|
68755
|
+
fs42.writeFileSync(filePath, content.replace(pattern, block));
|
|
68756
|
+
fs42.chmodSync(filePath, 493);
|
|
68559
68757
|
return "updated";
|
|
68560
68758
|
}
|
|
68561
68759
|
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
68562
|
-
|
|
68760
|
+
fs42.writeFileSync(filePath, `${content}${suffix}
|
|
68563
68761
|
${block}
|
|
68564
68762
|
`);
|
|
68565
|
-
|
|
68763
|
+
fs42.chmodSync(filePath, 493);
|
|
68566
68764
|
return "appended";
|
|
68567
68765
|
}
|
|
68568
68766
|
function removeMarkedBlock(filePath) {
|
|
68569
|
-
if (!
|
|
68570
|
-
const content =
|
|
68767
|
+
if (!fs42.existsSync(filePath)) return false;
|
|
68768
|
+
const content = fs42.readFileSync(filePath, "utf8");
|
|
68571
68769
|
if (!content.includes(MARKER_START)) return false;
|
|
68572
68770
|
const pattern = new RegExp(
|
|
68573
68771
|
`\\n?${escapeRegExp2(MARKER_START)}[\\s\\S]*?${escapeRegExp2(MARKER_END)}\\n?`
|
|
68574
68772
|
);
|
|
68575
68773
|
const stripped = content.replace(pattern, "\n");
|
|
68576
68774
|
if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
|
|
68577
|
-
|
|
68775
|
+
fs42.rmSync(filePath);
|
|
68578
68776
|
} else {
|
|
68579
|
-
|
|
68777
|
+
fs42.writeFileSync(filePath, stripped);
|
|
68580
68778
|
}
|
|
68581
68779
|
return true;
|
|
68582
68780
|
}
|
|
@@ -68598,20 +68796,20 @@ Or re-run with --force to write into that directory anyway.`
|
|
|
68598
68796
|
);
|
|
68599
68797
|
}
|
|
68600
68798
|
const hooksDir = resolveHooksDir(checkout);
|
|
68601
|
-
|
|
68799
|
+
fs42.mkdirSync(hooksDir, { recursive: true });
|
|
68602
68800
|
const invocation = options.harInvocation ?? defaultHarInvocation();
|
|
68603
|
-
|
|
68604
|
-
|
|
68801
|
+
fs42.writeFileSync(
|
|
68802
|
+
path49.join(hooksDir, "har-pre-commit"),
|
|
68605
68803
|
buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
|
|
68606
68804
|
{ mode: 493 }
|
|
68607
68805
|
);
|
|
68608
|
-
|
|
68609
|
-
|
|
68806
|
+
fs42.writeFileSync(
|
|
68807
|
+
path49.join(hooksDir, "har-post-commit"),
|
|
68610
68808
|
buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
|
|
68611
68809
|
{ mode: 493 }
|
|
68612
68810
|
);
|
|
68613
|
-
const preCommit = upsertMarkedBlock2(
|
|
68614
|
-
const postCommit = upsertMarkedBlock2(
|
|
68811
|
+
const preCommit = upsertMarkedBlock2(path49.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
|
|
68812
|
+
const postCommit = upsertMarkedBlock2(path49.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
|
|
68615
68813
|
ensureValidationsIgnored(checkout);
|
|
68616
68814
|
return { hooksDir, preCommit, postCommit };
|
|
68617
68815
|
}
|
|
@@ -68620,12 +68818,12 @@ function uninstallHooks(repoPath) {
|
|
|
68620
68818
|
if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
|
|
68621
68819
|
const hooksDir = resolveHooksDir(checkout);
|
|
68622
68820
|
let removed = false;
|
|
68623
|
-
removed = removeMarkedBlock(
|
|
68624
|
-
removed = removeMarkedBlock(
|
|
68821
|
+
removed = removeMarkedBlock(path49.join(hooksDir, "pre-commit")) || removed;
|
|
68822
|
+
removed = removeMarkedBlock(path49.join(hooksDir, "post-commit")) || removed;
|
|
68625
68823
|
for (const file of ["har-pre-commit", "har-post-commit"]) {
|
|
68626
|
-
const full =
|
|
68627
|
-
if (
|
|
68628
|
-
|
|
68824
|
+
const full = path49.join(hooksDir, file);
|
|
68825
|
+
if (fs42.existsSync(full)) {
|
|
68826
|
+
fs42.rmSync(full);
|
|
68629
68827
|
removed = true;
|
|
68630
68828
|
}
|
|
68631
68829
|
}
|
|
@@ -68644,10 +68842,10 @@ function isAgentWorktree(checkoutDir) {
|
|
|
68644
68842
|
if (branch && (/^har-agent-\d+$/.test(branch) || /-har-agent-\d+-[a-z0-9]+$/.test(branch))) {
|
|
68645
68843
|
return true;
|
|
68646
68844
|
}
|
|
68647
|
-
const worktreesRoot =
|
|
68648
|
-
const resolved =
|
|
68649
|
-
const name =
|
|
68650
|
-
return resolved.startsWith(`${worktreesRoot}${
|
|
68845
|
+
const worktreesRoot = path49.join(os16.homedir(), "worktrees");
|
|
68846
|
+
const resolved = path49.resolve(checkoutDir);
|
|
68847
|
+
const name = path49.basename(resolved);
|
|
68848
|
+
return resolved.startsWith(`${worktreesRoot}${path49.sep}`) && (/-agent-\d+$/.test(name) || /-har-agent-\d+-[a-z0-9]+$/.test(name));
|
|
68651
68849
|
}
|
|
68652
68850
|
function resolveEffectiveMode(checkoutDir, gate) {
|
|
68653
68851
|
if (!gate.enabled) return "off";
|
|
@@ -68660,15 +68858,15 @@ function getHooksStatus(repoPath) {
|
|
|
68660
68858
|
const hooksDir = resolveHooksDir(checkout);
|
|
68661
68859
|
const gate = getCommitGateConfig(checkout);
|
|
68662
68860
|
const hasBlock = (name) => {
|
|
68663
|
-
const file =
|
|
68664
|
-
return
|
|
68861
|
+
const file = path49.join(hooksDir, name);
|
|
68862
|
+
return fs42.existsSync(file) && fs42.readFileSync(file, "utf8").includes(MARKER_START);
|
|
68665
68863
|
};
|
|
68666
68864
|
return {
|
|
68667
68865
|
checkout,
|
|
68668
68866
|
hooksDir,
|
|
68669
68867
|
configuredHooksPath: getConfiguredHooksPath(checkout),
|
|
68670
|
-
preCommitInstalled: hasBlock("pre-commit") &&
|
|
68671
|
-
postCommitInstalled: hasBlock("post-commit") &&
|
|
68868
|
+
preCommitInstalled: hasBlock("pre-commit") && fs42.existsSync(path49.join(hooksDir, "har-pre-commit")),
|
|
68869
|
+
postCommitInstalled: hasBlock("post-commit") && fs42.existsSync(path49.join(hooksDir, "har-post-commit")),
|
|
68672
68870
|
gate,
|
|
68673
68871
|
effectiveMode: resolveEffectiveMode(checkout, gate)
|
|
68674
68872
|
};
|
|
@@ -68679,7 +68877,7 @@ function checkCommitGate(cwd) {
|
|
|
68679
68877
|
}
|
|
68680
68878
|
const checkout = resolveCheckoutRoot(cwd);
|
|
68681
68879
|
if (!checkout) return { exitCode: 0, messages: [] };
|
|
68682
|
-
if (!
|
|
68880
|
+
if (!fs42.existsSync(path49.join(checkout, ".har", "stages.json"))) {
|
|
68683
68881
|
return { exitCode: 0, messages: [] };
|
|
68684
68882
|
}
|
|
68685
68883
|
const gate = getCommitGateConfig(checkout);
|
|
@@ -68801,21 +68999,21 @@ async function handleCommitGateOnboarding(options) {
|
|
|
68801
68999
|
}
|
|
68802
69000
|
|
|
68803
69001
|
// src/core/onboarding-preferences.ts
|
|
68804
|
-
var
|
|
69002
|
+
var fs43 = __toESM(require("fs"));
|
|
68805
69003
|
var os17 = __toESM(require("os"));
|
|
68806
|
-
var
|
|
69004
|
+
var path50 = __toESM(require("path"));
|
|
68807
69005
|
init_schema2();
|
|
68808
69006
|
function getOnboardingPreferencesPath() {
|
|
68809
69007
|
if (process.env.HAR_PREFERENCES_PATH) {
|
|
68810
|
-
return
|
|
69008
|
+
return path50.resolve(process.env.HAR_PREFERENCES_PATH);
|
|
68811
69009
|
}
|
|
68812
|
-
return
|
|
69010
|
+
return path50.join(os17.homedir(), ".har", "preferences.json");
|
|
68813
69011
|
}
|
|
68814
69012
|
function readOnboardingPreferences() {
|
|
68815
69013
|
const preferencePath = getOnboardingPreferencesPath();
|
|
68816
69014
|
try {
|
|
68817
|
-
if (!
|
|
68818
|
-
return OnboardingPreferencesSchema.parse(JSON.parse(
|
|
69015
|
+
if (!fs43.existsSync(preferencePath)) return OnboardingPreferencesSchema.parse({});
|
|
69016
|
+
return OnboardingPreferencesSchema.parse(JSON.parse(fs43.readFileSync(preferencePath, "utf8")));
|
|
68819
69017
|
} catch {
|
|
68820
69018
|
return OnboardingPreferencesSchema.parse({});
|
|
68821
69019
|
}
|
|
@@ -68840,8 +69038,8 @@ function writeOnboardingPreferences(overrides) {
|
|
|
68840
69038
|
};
|
|
68841
69039
|
const parsed = OnboardingPreferencesSchema.parse(preference);
|
|
68842
69040
|
const preferencePath = getOnboardingPreferencesPath();
|
|
68843
|
-
|
|
68844
|
-
|
|
69041
|
+
fs43.mkdirSync(path50.dirname(preferencePath), { recursive: true });
|
|
69042
|
+
fs43.writeFileSync(preferencePath, `${JSON.stringify(parsed, null, 2)}
|
|
68845
69043
|
`);
|
|
68846
69044
|
return parsed;
|
|
68847
69045
|
}
|
|
@@ -69132,7 +69330,7 @@ var envCommand = {
|
|
|
69132
69330
|
}
|
|
69133
69331
|
};
|
|
69134
69332
|
async function handleInit(argv) {
|
|
69135
|
-
const repoPath =
|
|
69333
|
+
const repoPath = path51.resolve(argv.repo);
|
|
69136
69334
|
const onboarding = resolveOnboardingOptions(argv);
|
|
69137
69335
|
header("har env init");
|
|
69138
69336
|
info(`Repository: ${repoPath}`);
|
|
@@ -69178,7 +69376,7 @@ async function handleInit(argv) {
|
|
|
69178
69376
|
}
|
|
69179
69377
|
}
|
|
69180
69378
|
async function handleMaintain(argv) {
|
|
69181
|
-
const repoPath =
|
|
69379
|
+
const repoPath = path51.resolve(argv.repo);
|
|
69182
69380
|
const onboarding = resolveOnboardingOptions(argv);
|
|
69183
69381
|
header("har env maintain");
|
|
69184
69382
|
info(`Repository: ${repoPath}`);
|
|
@@ -69233,6 +69431,8 @@ async function handleMaintain(argv) {
|
|
|
69233
69431
|
mode: "maintain",
|
|
69234
69432
|
autoYes: argv.yes,
|
|
69235
69433
|
cursorRule: onboarding.cursorRule,
|
|
69434
|
+
writeAgentsMd: !argv.finalize,
|
|
69435
|
+
finalize: argv.finalize,
|
|
69236
69436
|
...onboarding.agentSkills
|
|
69237
69437
|
});
|
|
69238
69438
|
} catch (err) {
|
|
@@ -69284,7 +69484,9 @@ async function applyAgentIntegrations(options) {
|
|
|
69284
69484
|
enabled: options.enabled,
|
|
69285
69485
|
cursorRule: options.cursorRule,
|
|
69286
69486
|
autoYes: options.autoYes,
|
|
69287
|
-
mode: options.mode
|
|
69487
|
+
mode: options.mode,
|
|
69488
|
+
writeAgentsMd: options.writeAgentsMd,
|
|
69489
|
+
finalize: options.finalize
|
|
69288
69490
|
});
|
|
69289
69491
|
const cursorRuleFlag = options.cursorRule === false ? false : options.cursorRule === true || instruction.plan.cursorRule ? true : false;
|
|
69290
69492
|
await handleCursorRule({
|
|
@@ -69305,7 +69507,7 @@ async function applyAgentIntegrations(options) {
|
|
|
69305
69507
|
}
|
|
69306
69508
|
}
|
|
69307
69509
|
async function handleAddPlugin(argv) {
|
|
69308
|
-
const repoPath =
|
|
69510
|
+
const repoPath = path51.resolve(argv.repo);
|
|
69309
69511
|
const available = listPluginIds();
|
|
69310
69512
|
if (argv.list) {
|
|
69311
69513
|
for (const id of available) {
|
|
@@ -69343,7 +69545,7 @@ async function handleAddPlugin(argv) {
|
|
|
69343
69545
|
}
|
|
69344
69546
|
}
|
|
69345
69547
|
async function handleAddStage(argv) {
|
|
69346
|
-
const repoPath =
|
|
69548
|
+
const repoPath = path51.resolve(argv.repo);
|
|
69347
69549
|
const available = listPluginIds();
|
|
69348
69550
|
if (argv.list) {
|
|
69349
69551
|
warn("har env add-stage --list is deprecated; prefer: har env add-plugin --list");
|
|
@@ -69409,7 +69611,7 @@ async function handleAddStage(argv) {
|
|
|
69409
69611
|
});
|
|
69410
69612
|
}
|
|
69411
69613
|
async function handlePreflight(argv) {
|
|
69412
|
-
const repo =
|
|
69614
|
+
const repo = path51.resolve(argv.repo);
|
|
69413
69615
|
const agentId = validateAgentId(argv.id, repo);
|
|
69414
69616
|
const result = await preflightEnvironment({
|
|
69415
69617
|
repoPath: repo,
|
|
@@ -69425,7 +69627,7 @@ async function handlePreflight(argv) {
|
|
|
69425
69627
|
return finishCommand(result.code);
|
|
69426
69628
|
}
|
|
69427
69629
|
async function handleLaunch(argv) {
|
|
69428
|
-
const repo =
|
|
69630
|
+
const repo = path51.resolve(argv.repo);
|
|
69429
69631
|
const agentId = validateAgentId(argv.id, repo);
|
|
69430
69632
|
if (!argv.resume) {
|
|
69431
69633
|
const guard = checkLaunchGuard2(repo, agentId, {});
|
|
@@ -69480,7 +69682,7 @@ async function handleRecover(argv) {
|
|
|
69480
69682
|
});
|
|
69481
69683
|
}
|
|
69482
69684
|
async function handleVerify(argv) {
|
|
69483
|
-
const repo =
|
|
69685
|
+
const repo = path51.resolve(argv.repo);
|
|
69484
69686
|
const agentId = validateAgentId(argv.id, repo);
|
|
69485
69687
|
const result = await runVerification({
|
|
69486
69688
|
repoPath: repo,
|
|
@@ -69492,7 +69694,7 @@ async function handleVerify(argv) {
|
|
|
69492
69694
|
return finishCommand(result.code);
|
|
69493
69695
|
}
|
|
69494
69696
|
async function handleTeardown(argv) {
|
|
69495
|
-
const repo =
|
|
69697
|
+
const repo = path51.resolve(argv.repo);
|
|
69496
69698
|
const agentId = validateAgentId(argv.id, repo);
|
|
69497
69699
|
const result = await teardownEnvironment({
|
|
69498
69700
|
repoPath: repo,
|
|
@@ -69503,7 +69705,7 @@ async function handleTeardown(argv) {
|
|
|
69503
69705
|
return finishCommand(result.code);
|
|
69504
69706
|
}
|
|
69505
69707
|
async function handleComplete(argv) {
|
|
69506
|
-
const repo =
|
|
69708
|
+
const repo = path51.resolve(argv.repo);
|
|
69507
69709
|
const agentId = validateAgentId(argv.id, repo);
|
|
69508
69710
|
const result = await completeEnvironment({
|
|
69509
69711
|
repoPath: repo,
|
|
@@ -69524,7 +69726,7 @@ async function handleComplete(argv) {
|
|
|
69524
69726
|
return finishCommand(result.code);
|
|
69525
69727
|
}
|
|
69526
69728
|
async function handleStatus(argv) {
|
|
69527
|
-
const repoPath =
|
|
69729
|
+
const repoPath = path51.resolve(argv.repo);
|
|
69528
69730
|
if (argv.json) {
|
|
69529
69731
|
const status = collectEnvironmentStatus(repoPath);
|
|
69530
69732
|
const output = EnvironmentStatusSchema.parse(status);
|
|
@@ -69537,7 +69739,7 @@ async function handleStatus(argv) {
|
|
|
69537
69739
|
});
|
|
69538
69740
|
}
|
|
69539
69741
|
async function handleRunsList(argv) {
|
|
69540
|
-
const repoPath =
|
|
69742
|
+
const repoPath = path51.resolve(argv.repo);
|
|
69541
69743
|
const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
|
|
69542
69744
|
if (argv.json) {
|
|
69543
69745
|
process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
|
|
@@ -69554,7 +69756,7 @@ async function handleRunsList(argv) {
|
|
|
69554
69756
|
}
|
|
69555
69757
|
}
|
|
69556
69758
|
async function handleRunsGet(argv) {
|
|
69557
|
-
const run2 = getRun(
|
|
69759
|
+
const run2 = getRun(path51.resolve(argv.repo), argv.runId);
|
|
69558
69760
|
if (!run2) {
|
|
69559
69761
|
error(`Run not found: ${argv.runId}`);
|
|
69560
69762
|
return finishCommand(1);
|
|
@@ -69636,7 +69838,7 @@ function printNextSteps() {
|
|
|
69636
69838
|
}
|
|
69637
69839
|
|
|
69638
69840
|
// src/cli/commands/control.ts
|
|
69639
|
-
var
|
|
69841
|
+
var path55 = __toESM(require("path"));
|
|
69640
69842
|
|
|
69641
69843
|
// node_modules/@inquirer/core/dist/esm/lib/key.mjs
|
|
69642
69844
|
var isUpKey = (key) => (
|
|
@@ -71690,9 +71892,9 @@ var import_run_async = __toESM(require_run_async(), 1);
|
|
|
71690
71892
|
var import_mute_stream2 = __toESM(require_lib(), 1);
|
|
71691
71893
|
var import_ansi_escapes5 = __toESM(require_ansi_escapes(), 1);
|
|
71692
71894
|
var _ = {
|
|
71693
|
-
set: (obj,
|
|
71895
|
+
set: (obj, path62 = "", value) => {
|
|
71694
71896
|
let pointer = obj;
|
|
71695
|
-
|
|
71897
|
+
path62.split(".").forEach((key, index, arr) => {
|
|
71696
71898
|
if (key === "__proto__" || key === "constructor")
|
|
71697
71899
|
return;
|
|
71698
71900
|
if (index === arr.length - 1) {
|
|
@@ -71703,8 +71905,8 @@ var _ = {
|
|
|
71703
71905
|
pointer = pointer[key];
|
|
71704
71906
|
});
|
|
71705
71907
|
},
|
|
71706
|
-
get: (obj,
|
|
71707
|
-
const travel = (regexp) => String.prototype.split.call(
|
|
71908
|
+
get: (obj, path62 = "", defaultValue) => {
|
|
71909
|
+
const travel = (regexp) => String.prototype.split.call(path62, regexp).filter(Boolean).reduce(
|
|
71708
71910
|
// @ts-expect-error implicit any on res[key]
|
|
71709
71911
|
(res, key) => res !== null && res !== void 0 ? res[key] : res,
|
|
71710
71912
|
obj
|
|
@@ -71958,21 +72160,21 @@ init_control_port();
|
|
|
71958
72160
|
init_control_sync();
|
|
71959
72161
|
|
|
71960
72162
|
// src/core/control-sync-selection.ts
|
|
71961
|
-
var
|
|
72163
|
+
var fs44 = __toESM(require("fs"));
|
|
71962
72164
|
var os18 = __toESM(require("os"));
|
|
71963
|
-
var
|
|
72165
|
+
var path52 = __toESM(require("path"));
|
|
71964
72166
|
init_control_repo_path();
|
|
71965
72167
|
function getSelectionPath() {
|
|
71966
72168
|
if (process.env.HAR_CONTROL_SYNC_SELECTION_PATH) {
|
|
71967
|
-
return
|
|
72169
|
+
return path52.resolve(process.env.HAR_CONTROL_SYNC_SELECTION_PATH);
|
|
71968
72170
|
}
|
|
71969
|
-
return
|
|
72171
|
+
return path52.join(os18.homedir(), ".har", "sync-selection.json");
|
|
71970
72172
|
}
|
|
71971
72173
|
function readSyncSelection() {
|
|
71972
72174
|
const selectionPath = getSelectionPath();
|
|
71973
72175
|
try {
|
|
71974
|
-
if (!
|
|
71975
|
-
const parsed = JSON.parse(
|
|
72176
|
+
if (!fs44.existsSync(selectionPath)) return null;
|
|
72177
|
+
const parsed = JSON.parse(fs44.readFileSync(selectionPath, "utf8"));
|
|
71976
72178
|
if (!Array.isArray(parsed.repos)) return null;
|
|
71977
72179
|
return parsed.repos.map((repoPath) => canonicalizeControlRepoPath(repoPath));
|
|
71978
72180
|
} catch {
|
|
@@ -71982,8 +72184,8 @@ function readSyncSelection() {
|
|
|
71982
72184
|
function writeSyncSelection(repos) {
|
|
71983
72185
|
const selectionPath = getSelectionPath();
|
|
71984
72186
|
const canonical = Array.from(new Set(repos.map((repoPath) => canonicalizeControlRepoPath(repoPath))));
|
|
71985
|
-
|
|
71986
|
-
|
|
72187
|
+
fs44.mkdirSync(path52.dirname(selectionPath), { recursive: true });
|
|
72188
|
+
fs44.writeFileSync(selectionPath, JSON.stringify({ repos: canonical }, null, 2) + "\n");
|
|
71987
72189
|
}
|
|
71988
72190
|
function resolveSyncSelection(input) {
|
|
71989
72191
|
const { discovered, stored, forceSelect, isTTY } = input;
|
|
@@ -72090,8 +72292,8 @@ If your browser did not open, paste that URL.
|
|
|
72090
72292
|
}
|
|
72091
72293
|
|
|
72092
72294
|
// src/core/control-unregister.ts
|
|
72093
|
-
var
|
|
72094
|
-
var
|
|
72295
|
+
var fs45 = __toESM(require("fs"));
|
|
72296
|
+
var path53 = __toESM(require("path"));
|
|
72095
72297
|
init_control_repo_path();
|
|
72096
72298
|
init_control_registry();
|
|
72097
72299
|
init_control_config();
|
|
@@ -72101,9 +72303,9 @@ function listUnregisterWorktreeCandidates(repoPath) {
|
|
|
72101
72303
|
const harnessRoot = canonicalizeControlRepoPath(repoPath);
|
|
72102
72304
|
const byPath = /* @__PURE__ */ new Map();
|
|
72103
72305
|
const add = (partial2) => {
|
|
72104
|
-
const resolved =
|
|
72306
|
+
const resolved = path53.resolve(partial2.worktreePath);
|
|
72105
72307
|
if (!resolved || resolved === harnessRoot) return;
|
|
72106
|
-
const exists = partial2.exists ??
|
|
72308
|
+
const exists = partial2.exists ?? fs45.existsSync(resolved);
|
|
72107
72309
|
const prev = byPath.get(resolved);
|
|
72108
72310
|
if (prev) {
|
|
72109
72311
|
byPath.set(resolved, {
|
|
@@ -72217,28 +72419,28 @@ async function teardownWorktrees(repoPath, candidates, dryRun) {
|
|
|
72217
72419
|
trigger: "cli"
|
|
72218
72420
|
});
|
|
72219
72421
|
if (result.code !== 0) {
|
|
72220
|
-
if (
|
|
72221
|
-
|
|
72422
|
+
if (fs45.existsSync(candidate.worktreePath)) {
|
|
72423
|
+
fs45.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
72222
72424
|
}
|
|
72223
72425
|
}
|
|
72224
72426
|
outcomes.push({
|
|
72225
72427
|
path: candidate.worktreePath,
|
|
72226
72428
|
agentId: candidate.agentId,
|
|
72227
|
-
deleted: !
|
|
72228
|
-
error: result.code !== 0 &&
|
|
72429
|
+
deleted: !fs45.existsSync(candidate.worktreePath),
|
|
72430
|
+
error: result.code !== 0 && fs45.existsSync(candidate.worktreePath) ? result.stderr : void 0
|
|
72229
72431
|
});
|
|
72230
72432
|
} catch (err) {
|
|
72231
72433
|
const message = err instanceof Error ? err.message : String(err);
|
|
72232
72434
|
try {
|
|
72233
|
-
if (
|
|
72234
|
-
|
|
72435
|
+
if (fs45.existsSync(candidate.worktreePath)) {
|
|
72436
|
+
fs45.rmSync(candidate.worktreePath, { recursive: true, force: true });
|
|
72235
72437
|
}
|
|
72236
72438
|
} catch {
|
|
72237
72439
|
}
|
|
72238
72440
|
outcomes.push({
|
|
72239
72441
|
path: candidate.worktreePath,
|
|
72240
72442
|
agentId: candidate.agentId,
|
|
72241
|
-
deleted: !
|
|
72443
|
+
deleted: !fs45.existsSync(candidate.worktreePath),
|
|
72242
72444
|
error: message
|
|
72243
72445
|
});
|
|
72244
72446
|
}
|
|
@@ -72301,16 +72503,16 @@ init_control_config();
|
|
|
72301
72503
|
init_control_registry();
|
|
72302
72504
|
|
|
72303
72505
|
// src/core/harness-scrub.ts
|
|
72304
|
-
var
|
|
72305
|
-
var
|
|
72506
|
+
var fs46 = __toESM(require("fs"));
|
|
72507
|
+
var path54 = __toESM(require("path"));
|
|
72306
72508
|
var HARNESS_SCRUB_DIRS = ["runs", "validations", "state", "slots"];
|
|
72307
72509
|
function scrubLocalHarnessDirs(repoPath) {
|
|
72308
72510
|
const results = [];
|
|
72309
|
-
const resolved =
|
|
72310
|
-
if (!
|
|
72511
|
+
const resolved = path54.resolve(repoPath);
|
|
72512
|
+
if (!fs46.existsSync(resolved)) {
|
|
72311
72513
|
for (const directory of HARNESS_SCRUB_DIRS) {
|
|
72312
72514
|
results.push({
|
|
72313
|
-
path:
|
|
72515
|
+
path: path54.join(resolved, ".har", directory),
|
|
72314
72516
|
directory,
|
|
72315
72517
|
deleted: false,
|
|
72316
72518
|
error: "repository path does not exist"
|
|
@@ -72319,24 +72521,24 @@ function scrubLocalHarnessDirs(repoPath) {
|
|
|
72319
72521
|
return results;
|
|
72320
72522
|
}
|
|
72321
72523
|
for (const directory of HARNESS_SCRUB_DIRS) {
|
|
72322
|
-
const target =
|
|
72323
|
-
if (!
|
|
72524
|
+
const target = path54.join(resolved, ".har", directory);
|
|
72525
|
+
if (!fs46.existsSync(target)) {
|
|
72324
72526
|
results.push({ path: target, directory, deleted: true });
|
|
72325
72527
|
continue;
|
|
72326
72528
|
}
|
|
72327
72529
|
try {
|
|
72328
|
-
|
|
72530
|
+
fs46.rmSync(target, { recursive: true, force: true });
|
|
72329
72531
|
results.push({
|
|
72330
72532
|
path: target,
|
|
72331
72533
|
directory,
|
|
72332
|
-
deleted: !
|
|
72333
|
-
error:
|
|
72534
|
+
deleted: !fs46.existsSync(target),
|
|
72535
|
+
error: fs46.existsSync(target) ? "failed to remove directory" : void 0
|
|
72334
72536
|
});
|
|
72335
72537
|
} catch (err) {
|
|
72336
72538
|
results.push({
|
|
72337
72539
|
path: target,
|
|
72338
72540
|
directory,
|
|
72339
|
-
deleted: !
|
|
72541
|
+
deleted: !fs46.existsSync(target),
|
|
72340
72542
|
error: err instanceof Error ? err.message : String(err)
|
|
72341
72543
|
});
|
|
72342
72544
|
}
|
|
@@ -72347,7 +72549,7 @@ function scrubLocalHarnessForRepos(repoPaths) {
|
|
|
72347
72549
|
const results = [];
|
|
72348
72550
|
const seen = /* @__PURE__ */ new Set();
|
|
72349
72551
|
for (const repoPath of repoPaths) {
|
|
72350
|
-
const key =
|
|
72552
|
+
const key = path54.resolve(repoPath);
|
|
72351
72553
|
if (seen.has(key)) continue;
|
|
72352
72554
|
seen.add(key);
|
|
72353
72555
|
results.push(...scrubLocalHarnessDirs(key));
|
|
@@ -72603,7 +72805,7 @@ async function handleDown() {
|
|
|
72603
72805
|
return finishCommand(code);
|
|
72604
72806
|
}
|
|
72605
72807
|
async function handleRegister(argv) {
|
|
72606
|
-
const repoPath =
|
|
72808
|
+
const repoPath = path55.resolve(argv.repo);
|
|
72607
72809
|
header("har control register");
|
|
72608
72810
|
info(`Repository: ${repoPath}`);
|
|
72609
72811
|
try {
|
|
@@ -72695,7 +72897,7 @@ async function handleReset(argv) {
|
|
|
72695
72897
|
}
|
|
72696
72898
|
}
|
|
72697
72899
|
async function handleUnregister(argv) {
|
|
72698
|
-
const repoPath =
|
|
72900
|
+
const repoPath = path55.resolve(argv.repo);
|
|
72699
72901
|
const worktrees = listUnregisterWorktreeCandidates(repoPath);
|
|
72700
72902
|
try {
|
|
72701
72903
|
let deleteWorktrees = argv.deleteWorktrees;
|
|
@@ -72874,11 +73076,11 @@ async function handleLogin(argv) {
|
|
|
72874
73076
|
}
|
|
72875
73077
|
|
|
72876
73078
|
// src/cli/commands/hooks.ts
|
|
72877
|
-
var
|
|
73079
|
+
var path57 = __toESM(require("path"));
|
|
72878
73080
|
|
|
72879
73081
|
// src/core/claude-hooks.ts
|
|
72880
|
-
var
|
|
72881
|
-
var
|
|
73082
|
+
var fs47 = __toESM(require("fs"));
|
|
73083
|
+
var path56 = __toESM(require("path"));
|
|
72882
73084
|
var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
|
|
72883
73085
|
var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
|
|
72884
73086
|
var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
|
|
@@ -72889,8 +73091,8 @@ function isHarGuardEntry(entry) {
|
|
|
72889
73091
|
);
|
|
72890
73092
|
}
|
|
72891
73093
|
function readSettings(settingsPath) {
|
|
72892
|
-
if (!
|
|
72893
|
-
const raw =
|
|
73094
|
+
if (!fs47.existsSync(settingsPath)) return {};
|
|
73095
|
+
const raw = fs47.readFileSync(settingsPath, "utf8").trim();
|
|
72894
73096
|
if (raw === "") return {};
|
|
72895
73097
|
const parsed = JSON.parse(raw);
|
|
72896
73098
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -72899,19 +73101,19 @@ function readSettings(settingsPath) {
|
|
|
72899
73101
|
return parsed;
|
|
72900
73102
|
}
|
|
72901
73103
|
function writeSettings(settingsPath, settings) {
|
|
72902
|
-
|
|
72903
|
-
|
|
73104
|
+
fs47.mkdirSync(path56.dirname(settingsPath), { recursive: true });
|
|
73105
|
+
fs47.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
72904
73106
|
}
|
|
72905
73107
|
function installClaudeGuard(repoPath) {
|
|
72906
73108
|
const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
|
|
72907
73109
|
if (!templatePath) {
|
|
72908
73110
|
throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
|
|
72909
73111
|
}
|
|
72910
|
-
const guardScript =
|
|
72911
|
-
|
|
72912
|
-
|
|
72913
|
-
|
|
72914
|
-
const settingsPath =
|
|
73112
|
+
const guardScript = path56.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
73113
|
+
fs47.mkdirSync(path56.dirname(guardScript), { recursive: true });
|
|
73114
|
+
fs47.copyFileSync(templatePath, guardScript);
|
|
73115
|
+
fs47.chmodSync(guardScript, 493);
|
|
73116
|
+
const settingsPath = path56.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
72915
73117
|
const settings = readSettings(settingsPath);
|
|
72916
73118
|
const hooks = settings.hooks ?? {};
|
|
72917
73119
|
const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
|
|
@@ -72925,15 +73127,15 @@ function installClaudeGuard(repoPath) {
|
|
|
72925
73127
|
}
|
|
72926
73128
|
function uninstallClaudeGuard(repoPath) {
|
|
72927
73129
|
let removed = false;
|
|
72928
|
-
const guardScript =
|
|
72929
|
-
if (
|
|
72930
|
-
|
|
73130
|
+
const guardScript = path56.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
|
|
73131
|
+
if (fs47.existsSync(guardScript)) {
|
|
73132
|
+
fs47.rmSync(guardScript);
|
|
72931
73133
|
removed = true;
|
|
72932
|
-
const parent =
|
|
72933
|
-
if (
|
|
73134
|
+
const parent = path56.dirname(guardScript);
|
|
73135
|
+
if (fs47.existsSync(parent) && fs47.readdirSync(parent).length === 0) fs47.rmdirSync(parent);
|
|
72934
73136
|
}
|
|
72935
|
-
const settingsPath =
|
|
72936
|
-
if (
|
|
73137
|
+
const settingsPath = path56.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
73138
|
+
if (fs47.existsSync(settingsPath)) {
|
|
72937
73139
|
const settings = readSettings(settingsPath);
|
|
72938
73140
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
72939
73141
|
if (preToolUse) {
|
|
@@ -72958,8 +73160,8 @@ function uninstallClaudeGuard(repoPath) {
|
|
|
72958
73160
|
return { removed };
|
|
72959
73161
|
}
|
|
72960
73162
|
function claudeGuardInstalled(repoPath) {
|
|
72961
|
-
const settingsPath =
|
|
72962
|
-
if (!
|
|
73163
|
+
const settingsPath = path56.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
|
|
73164
|
+
if (!fs47.existsSync(settingsPath)) return false;
|
|
72963
73165
|
try {
|
|
72964
73166
|
const settings = readSettings(settingsPath);
|
|
72965
73167
|
return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
|
|
@@ -72976,7 +73178,7 @@ function repoOption(y) {
|
|
|
72976
73178
|
function handleInstall2(argv) {
|
|
72977
73179
|
try {
|
|
72978
73180
|
if (argv.claude) {
|
|
72979
|
-
const result2 = installClaudeGuard(
|
|
73181
|
+
const result2 = installClaudeGuard(path57.resolve(argv.repo));
|
|
72980
73182
|
success("Claude Code worktree guard installed.");
|
|
72981
73183
|
info(`Guard script: ${result2.guardScript}`);
|
|
72982
73184
|
info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
|
|
@@ -72984,7 +73186,7 @@ function handleInstall2(argv) {
|
|
|
72984
73186
|
info("Commit both files so the guard travels with the repo.");
|
|
72985
73187
|
return;
|
|
72986
73188
|
}
|
|
72987
|
-
const result = installHooks({ repoPath:
|
|
73189
|
+
const result = installHooks({ repoPath: path57.resolve(argv.repo), force: argv.force });
|
|
72988
73190
|
success(`Commit gate installed in ${result.hooksDir}`);
|
|
72989
73191
|
info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
|
|
72990
73192
|
info("Commits of unverified change batches will be blocked in agent worktrees.");
|
|
@@ -72997,7 +73199,7 @@ function handleInstall2(argv) {
|
|
|
72997
73199
|
function handleUninstall(argv) {
|
|
72998
73200
|
try {
|
|
72999
73201
|
if (argv.claude) {
|
|
73000
|
-
const result2 = uninstallClaudeGuard(
|
|
73202
|
+
const result2 = uninstallClaudeGuard(path57.resolve(argv.repo));
|
|
73001
73203
|
if (result2.removed) {
|
|
73002
73204
|
success("Claude Code worktree guard removed.");
|
|
73003
73205
|
} else {
|
|
@@ -73005,7 +73207,7 @@ function handleUninstall(argv) {
|
|
|
73005
73207
|
}
|
|
73006
73208
|
return;
|
|
73007
73209
|
}
|
|
73008
|
-
const result = uninstallHooks(
|
|
73210
|
+
const result = uninstallHooks(path57.resolve(argv.repo));
|
|
73009
73211
|
if (result.removed) {
|
|
73010
73212
|
success(`Commit gate removed from ${result.hooksDir}`);
|
|
73011
73213
|
} else {
|
|
@@ -73018,7 +73220,7 @@ function handleUninstall(argv) {
|
|
|
73018
73220
|
}
|
|
73019
73221
|
function handleStatus2(argv) {
|
|
73020
73222
|
try {
|
|
73021
|
-
const status = getHooksStatus(
|
|
73223
|
+
const status = getHooksStatus(path57.resolve(argv.repo));
|
|
73022
73224
|
if (argv.json) {
|
|
73023
73225
|
process.stdout.write(`${JSON.stringify(status, null, 2)}
|
|
73024
73226
|
`);
|
|
@@ -73036,7 +73238,7 @@ function handleStatus2(argv) {
|
|
|
73036
73238
|
);
|
|
73037
73239
|
info(`Effective: ${status.effectiveMode} (in this checkout)`);
|
|
73038
73240
|
info(
|
|
73039
|
-
`Claude guard: ${claudeGuardInstalled(
|
|
73241
|
+
`Claude guard: ${claudeGuardInstalled(path57.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
|
|
73040
73242
|
);
|
|
73041
73243
|
} catch (err) {
|
|
73042
73244
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -73297,10 +73499,10 @@ function assignProp(target, prop, value) {
|
|
|
73297
73499
|
configurable: true
|
|
73298
73500
|
});
|
|
73299
73501
|
}
|
|
73300
|
-
function getElementAtPath(obj,
|
|
73301
|
-
if (!
|
|
73502
|
+
function getElementAtPath(obj, path62) {
|
|
73503
|
+
if (!path62)
|
|
73302
73504
|
return obj;
|
|
73303
|
-
return
|
|
73505
|
+
return path62.reduce((acc, key) => acc?.[key], obj);
|
|
73304
73506
|
}
|
|
73305
73507
|
function promiseAllObject(promisesObj) {
|
|
73306
73508
|
const keys = Object.keys(promisesObj);
|
|
@@ -73620,11 +73822,11 @@ function aborted(x, startIndex = 0) {
|
|
|
73620
73822
|
}
|
|
73621
73823
|
return false;
|
|
73622
73824
|
}
|
|
73623
|
-
function prefixIssues(
|
|
73825
|
+
function prefixIssues(path62, issues) {
|
|
73624
73826
|
return issues.map((iss) => {
|
|
73625
73827
|
var _a2;
|
|
73626
73828
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
73627
|
-
iss.path.unshift(
|
|
73829
|
+
iss.path.unshift(path62);
|
|
73628
73830
|
return iss;
|
|
73629
73831
|
});
|
|
73630
73832
|
}
|
|
@@ -80098,7 +80300,7 @@ var Server = class extends Protocol {
|
|
|
80098
80300
|
};
|
|
80099
80301
|
|
|
80100
80302
|
// src/mcp/server.ts
|
|
80101
|
-
var
|
|
80303
|
+
var path58 = __toESM(require("path"));
|
|
80102
80304
|
|
|
80103
80305
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
80104
80306
|
var import_node_process3 = __toESM(require("node:process"), 1);
|
|
@@ -80741,7 +80943,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
|
|
|
80741
80943
|
const input = ControlUpInputSchema.parse({ ...args, repo });
|
|
80742
80944
|
const result = await startControlAndSync({
|
|
80743
80945
|
detach: input.detach,
|
|
80744
|
-
cwd:
|
|
80946
|
+
cwd: path58.resolve(input.repo)
|
|
80745
80947
|
});
|
|
80746
80948
|
if (result.code !== 0) {
|
|
80747
80949
|
return {
|
|
@@ -80805,11 +81007,11 @@ var mcpCommand = {
|
|
|
80805
81007
|
};
|
|
80806
81008
|
|
|
80807
81009
|
// src/cli/commands/onboard.ts
|
|
80808
|
-
var
|
|
81010
|
+
var path60 = __toESM(require("path"));
|
|
80809
81011
|
init_stages();
|
|
80810
81012
|
|
|
80811
81013
|
// src/core/onboarding.ts
|
|
80812
|
-
var
|
|
81014
|
+
var path59 = __toESM(require("path"));
|
|
80813
81015
|
init_control_config();
|
|
80814
81016
|
init_control_sync();
|
|
80815
81017
|
init_otel_hooks();
|
|
@@ -80944,7 +81146,7 @@ async function defaultEnsureControl(options) {
|
|
|
80944
81146
|
return { started: true, apiUrl: result.apiUrl };
|
|
80945
81147
|
}
|
|
80946
81148
|
async function runOnboarding(options, deps = {}) {
|
|
80947
|
-
const repoPath =
|
|
81149
|
+
const repoPath = path59.resolve(options.repoPath);
|
|
80948
81150
|
const applyTelemetry = deps.applyTelemetry ?? applyOnboardingTelemetry;
|
|
80949
81151
|
const ensureControl = deps.ensureControl ?? defaultEnsureControl;
|
|
80950
81152
|
const init = deps.initHarness ?? initHarness;
|
|
@@ -81132,7 +81334,7 @@ async function promptChoices(args) {
|
|
|
81132
81334
|
};
|
|
81133
81335
|
}
|
|
81134
81336
|
const pluginChoices = listPluginChoices();
|
|
81135
|
-
const repoPath =
|
|
81337
|
+
const repoPath = path60.resolve(args.repo);
|
|
81136
81338
|
const alreadyPresent = harnessExists(repoPath);
|
|
81137
81339
|
const needProfile = !alreadyPresent && !args.skipInit && args.profile === void 0;
|
|
81138
81340
|
const needAgentSlots = shouldConfigureSlots && flaggedSlots === void 0;
|
|
@@ -81249,7 +81451,7 @@ function printSummary(result) {
|
|
|
81249
81451
|
console.error("");
|
|
81250
81452
|
}
|
|
81251
81453
|
async function handleOnboard(argv) {
|
|
81252
|
-
const repoPath =
|
|
81454
|
+
const repoPath = path60.resolve(argv.repo);
|
|
81253
81455
|
header("har onboard");
|
|
81254
81456
|
info(`Repository: ${repoPath}`);
|
|
81255
81457
|
try {
|
|
@@ -81480,7 +81682,7 @@ var preferencesCommand = {
|
|
|
81480
81682
|
};
|
|
81481
81683
|
|
|
81482
81684
|
// src/cli/commands/telemetry.ts
|
|
81483
|
-
var
|
|
81685
|
+
var path61 = __toESM(require("path"));
|
|
81484
81686
|
init_control_config();
|
|
81485
81687
|
init_control_sync();
|
|
81486
81688
|
init_manifest();
|
|
@@ -81573,7 +81775,7 @@ async function handleOff() {
|
|
|
81573
81775
|
info("Mission Control will not auto-start. Existing usage rows are kept.");
|
|
81574
81776
|
}
|
|
81575
81777
|
async function handleWriteEnv(argv) {
|
|
81576
|
-
const repoPath =
|
|
81778
|
+
const repoPath = path61.resolve(argv.repo);
|
|
81577
81779
|
const harnessRoot = resolveHarnessRoot(repoPath);
|
|
81578
81780
|
const session = readSlotRegistry(harnessRoot, argv.agentId);
|
|
81579
81781
|
const workDir = argv.workDir ?? session?.workDir ?? repoPath;
|
|
@@ -81585,7 +81787,7 @@ async function handleWriteEnv(argv) {
|
|
|
81585
81787
|
suffix,
|
|
81586
81788
|
createdAt: session?.createdAt
|
|
81587
81789
|
});
|
|
81588
|
-
const envFile = argv.envFile ??
|
|
81790
|
+
const envFile = argv.envFile ?? path61.join(workDir, `.env.agent.${argv.agentId}`);
|
|
81589
81791
|
const attrs = {
|
|
81590
81792
|
sessionKey,
|
|
81591
81793
|
agentId: argv.agentId,
|
|
@@ -81601,7 +81803,7 @@ async function handleWriteEnv(argv) {
|
|
|
81601
81803
|
success(`Wrote session attribution to ${envFile}`);
|
|
81602
81804
|
}
|
|
81603
81805
|
function handlePrintEnv(argv) {
|
|
81604
|
-
const repoPath =
|
|
81806
|
+
const repoPath = path61.resolve(argv.repo);
|
|
81605
81807
|
const workDir = argv.workDir ?? repoPath;
|
|
81606
81808
|
const sessionKey = buildSessionKey({
|
|
81607
81809
|
branch: argv.branch,
|