hunter-harness 0.2.26 → 0.2.28

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.
Files changed (2) hide show
  1. package/dist/bin.js +402 -191
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1233,7 +1233,7 @@ async function assertNoSymlinks(root, relativePath) {
1233
1233
  async function withRetry(action, options) {
1234
1234
  const attempts = options.attempts ?? 3;
1235
1235
  const sleep = options.sleep ?? (async (milliseconds) => {
1236
- await new Promise((resolve9) => setTimeout(resolve9, milliseconds));
1236
+ await new Promise((resolve10) => setTimeout(resolve10, milliseconds));
1237
1237
  });
1238
1238
  let lastError;
1239
1239
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
@@ -1684,6 +1684,9 @@ function classifyFile(input) {
1684
1684
  if (under(path, ".harness/knowledge/")) {
1685
1685
  return USER_DIFF;
1686
1686
  }
1687
+ if (under(path, ".harness/rules/")) {
1688
+ return USER_DIFF;
1689
+ }
1687
1690
  if (/^\.harness\/archive\/[^/]+\/reports\/final\/summary-data\.json$/u.test(path)) {
1688
1691
  return GENERATED_REVIEWABLE;
1689
1692
  }
@@ -1696,7 +1699,7 @@ function classifyFile(input) {
1696
1699
  if (under(path, ".harness/reports/")) {
1697
1700
  return REPORT_CACHE;
1698
1701
  }
1699
- if (under(path, ".harness/state/") || under(path, ".harness/rules/")) {
1702
+ if (under(path, ".harness/state/")) {
1700
1703
  return INTERNAL_STATE;
1701
1704
  }
1702
1705
  if (under(path, ".harness/generated/") || under(path, ".harness/cache/")) {
@@ -1730,9 +1733,9 @@ function decideUpdate(policy, dirty) {
1730
1733
  }
1731
1734
 
1732
1735
  // ../core/dist/project/initialize.js
1733
- import { createHash as createHash4 } from "node:crypto";
1734
- import { readFile as readFile4, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
1735
- import { basename, join as join5, resolve as resolve4 } from "node:path";
1736
+ import { createHash as createHash5 } from "node:crypto";
1737
+ import { readFile as readFile5, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
1738
+ import { basename as basename2, join as join6, resolve as resolve5 } from "node:path";
1736
1739
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
1737
1740
 
1738
1741
  // ../core/dist/transaction/transaction.js
@@ -2247,10 +2250,178 @@ function managedTargetsFor(adapter, bundle, context) {
2247
2250
  return records.sort((left, right) => left.target_path.localeCompare(right.target_path));
2248
2251
  }
2249
2252
 
2250
- // ../core/dist/project/profile-bundle.js
2253
+ // ../core/dist/project/project-rules.js
2251
2254
  import { createHash as createHash3 } from "node:crypto";
2252
- import { readFile as readFile3, readdir as readdir2 } from "node:fs/promises";
2253
- import { join as join4, resolve as resolve3 } from "node:path";
2255
+ import { mkdir as mkdir4, readFile as readFile3, readdir as readdir2, rename as rename3, unlink, writeFile as writeFile3 } from "node:fs/promises";
2256
+ import { basename, dirname as dirname3, extname, join as join4, resolve as resolve3 } from "node:path";
2257
+ var RULES_ROOT = ".harness/rules";
2258
+ var RECEIPT_PATH = ".harness/state/local/rule-projections.json";
2259
+ var CODEX_BLOCK_ID = "hunter-harness-project-rules";
2260
+ var MANAGED_NAMES = /* @__PURE__ */ new Set([
2261
+ "harness-general.md",
2262
+ "harness-general.mdc",
2263
+ "harness-profile-java.md",
2264
+ "harness-profile-java.mdc"
2265
+ ]);
2266
+ function sha256(content) {
2267
+ return createHash3("sha256").update(content).digest("hex");
2268
+ }
2269
+ async function optionalText(path) {
2270
+ try {
2271
+ return await readFile3(path, "utf8");
2272
+ } catch (error) {
2273
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
2274
+ return null;
2275
+ throw error;
2276
+ }
2277
+ }
2278
+ async function readReceipt(root) {
2279
+ try {
2280
+ const parsed = JSON.parse(await readFile3(join4(root, RECEIPT_PATH), "utf8"));
2281
+ if (parsed.schema_version === 1 && parsed.targets && parsed.source_hashes)
2282
+ return parsed;
2283
+ } catch {
2284
+ }
2285
+ return { schema_version: 1, source_hashes: {}, targets: {} };
2286
+ }
2287
+ async function markdownFiles(root) {
2288
+ try {
2289
+ return (await readdir2(root, { withFileTypes: true })).filter((entry) => entry.isFile() && [".md", ".mdc"].includes(extname(entry.name).toLowerCase())).map((entry) => entry.name).filter((name) => !MANAGED_NAMES.has(name)).sort();
2290
+ } catch (error) {
2291
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
2292
+ return [];
2293
+ throw error;
2294
+ }
2295
+ }
2296
+ function targetsFor(name, agents, surface) {
2297
+ const stem = basename(name, extname(name));
2298
+ const targets = [];
2299
+ if (agents.includes("claude-code"))
2300
+ targets.push(`.claude/rules/${stem}.md`);
2301
+ if (agents.includes("cursor"))
2302
+ targets.push(`.cursor/rules/${stem}.mdc`);
2303
+ if (agents.includes("codebuddy") && surface !== "cli") {
2304
+ targets.push(`.codebuddy/.rules/${stem}.mdc`);
2305
+ }
2306
+ if (agents.includes("codebuddy") && surface !== "ide") {
2307
+ targets.push(`.codebuddy/rules/${stem}.md`);
2308
+ }
2309
+ return targets;
2310
+ }
2311
+ async function atomicWrite(path, content) {
2312
+ await mkdir4(dirname3(path), { recursive: true });
2313
+ const temporary = `${path}.${process.pid}.tmp`;
2314
+ await writeFile3(temporary, content, "utf8");
2315
+ await rename3(temporary, path);
2316
+ }
2317
+ async function synchronizeProjectRules(projectRoot, agents, surface = "both") {
2318
+ const root = resolve3(projectRoot);
2319
+ const canonicalRoot = join4(root, RULES_ROOT);
2320
+ const result = {
2321
+ migrated: [],
2322
+ written: [],
2323
+ removed: [],
2324
+ unchanged: [],
2325
+ conflicts: []
2326
+ };
2327
+ await mkdir4(canonicalRoot, { recursive: true });
2328
+ if ((await markdownFiles(canonicalRoot)).length === 0) {
2329
+ for (const name of await markdownFiles(join4(root, ".claude", "rules"))) {
2330
+ const destination = join4(canonicalRoot, `${basename(name, extname(name))}.md`);
2331
+ if (await optionalText(destination) !== null)
2332
+ continue;
2333
+ const content = await readFile3(join4(root, ".claude", "rules", name), "utf8");
2334
+ await atomicWrite(destination, content);
2335
+ result.migrated.push(`${RULES_ROOT}/${basename(destination)}`);
2336
+ }
2337
+ }
2338
+ const previous = await readReceipt(root);
2339
+ const next = { schema_version: 1, source_hashes: {}, targets: {} };
2340
+ const desired = /* @__PURE__ */ new Map();
2341
+ for (const name of await markdownFiles(canonicalRoot)) {
2342
+ const sourcePath = `${RULES_ROOT}/${name}`;
2343
+ const content = await readFile3(join4(canonicalRoot, name), "utf8");
2344
+ next.source_hashes[sourcePath] = sha256(content);
2345
+ for (const target of targetsFor(name, agents, surface))
2346
+ desired.set(target, content);
2347
+ }
2348
+ for (const [target, content] of desired) {
2349
+ const path = join4(root, target);
2350
+ const current = await optionalText(path);
2351
+ const incomingHash = sha256(content);
2352
+ if (current === content) {
2353
+ result.unchanged.push(target);
2354
+ next.targets[target] = incomingHash;
2355
+ } else if (current === null || previous.targets[target] === sha256(current)) {
2356
+ await atomicWrite(path, content);
2357
+ result.written.push(target);
2358
+ next.targets[target] = incomingHash;
2359
+ } else {
2360
+ result.conflicts.push(target);
2361
+ next.targets[target] = previous.targets[target] ?? sha256(current);
2362
+ }
2363
+ }
2364
+ for (const [target, trustedHash] of Object.entries(previous.targets)) {
2365
+ if (target === "AGENTS.md")
2366
+ continue;
2367
+ if (desired.has(target))
2368
+ continue;
2369
+ const path = join4(root, target);
2370
+ const current = await optionalText(path);
2371
+ if (current === null)
2372
+ continue;
2373
+ if (sha256(current) === trustedHash) {
2374
+ await unlink(path);
2375
+ result.removed.push(target);
2376
+ } else {
2377
+ result.conflicts.push(target);
2378
+ next.targets[target] = trustedHash;
2379
+ }
2380
+ }
2381
+ if (agents.includes("codex")) {
2382
+ const rules = Object.keys(next.source_hashes).sort();
2383
+ const body = [
2384
+ "Before project work, read and follow these shared project rules:",
2385
+ ...rules.map((path) => `- \`${path}\``)
2386
+ ].join("\n");
2387
+ const agentsPath = join4(root, "AGENTS.md");
2388
+ const current = await optionalText(agentsPath) ?? "";
2389
+ const updated = rules.length > 0 ? upsertManagedBlockById(current, CODEX_BLOCK_ID, body) : removeManagedBlockById(current, CODEX_BLOCK_ID);
2390
+ const target = "AGENTS.md";
2391
+ const semanticallyEqual = updated.replace(/\r\n/g, "\n").trimEnd() === current.replace(/\r\n/g, "\n").trimEnd();
2392
+ if (semanticallyEqual)
2393
+ result.unchanged.push(target);
2394
+ else {
2395
+ await atomicWrite(agentsPath, updated);
2396
+ result.written.push(target);
2397
+ }
2398
+ if (rules.length > 0) {
2399
+ next.targets[target] = sha256(semanticallyEqual ? current : updated);
2400
+ }
2401
+ } else if (Object.prototype.hasOwnProperty.call(previous.targets, "AGENTS.md")) {
2402
+ const agentsPath = join4(root, "AGENTS.md");
2403
+ const current = await optionalText(agentsPath);
2404
+ if (current !== null) {
2405
+ const updated = removeManagedBlockById(current, CODEX_BLOCK_ID);
2406
+ if (updated !== current) {
2407
+ await atomicWrite(agentsPath, updated);
2408
+ result.written.push("AGENTS.md");
2409
+ }
2410
+ }
2411
+ }
2412
+ await atomicWrite(join4(root, RECEIPT_PATH), JSON.stringify(next, null, 2) + "\n");
2413
+ result.migrated.sort();
2414
+ result.written.sort();
2415
+ result.removed.sort();
2416
+ result.unchanged.sort();
2417
+ result.conflicts = [...new Set(result.conflicts)].sort();
2418
+ return result;
2419
+ }
2420
+
2421
+ // ../core/dist/project/profile-bundle.js
2422
+ import { createHash as createHash4 } from "node:crypto";
2423
+ import { readFile as readFile4, readdir as readdir3 } from "node:fs/promises";
2424
+ import { join as join5, resolve as resolve4 } from "node:path";
2254
2425
  var AdapterBundleError = class extends Error {
2255
2426
  code;
2256
2427
  exitCode = 7;
@@ -2276,15 +2447,15 @@ function validateRelativeBundlePath2(path) {
2276
2447
  }
2277
2448
  var bundleCache = /* @__PURE__ */ new Map();
2278
2449
  async function loadAgentBundle(resourcesRoot, profile, agent) {
2279
- const cacheKey = `${resolve3(resourcesRoot)}:${profile}:${agent}`;
2450
+ const cacheKey = `${resolve4(resourcesRoot)}:${profile}:${agent}`;
2280
2451
  const cached = bundleCache.get(cacheKey);
2281
2452
  if (cached) {
2282
2453
  return { manifest: cached.manifest, files: new Map(cached.files) };
2283
2454
  }
2284
- const manifestPath = join4(resourcesRoot, "harness", "manifests", profile, `${agent}.json`);
2455
+ const manifestPath = join5(resourcesRoot, "harness", "manifests", profile, `${agent}.json`);
2285
2456
  let manifestText;
2286
2457
  try {
2287
- manifestText = await readFile3(manifestPath, "utf8");
2458
+ manifestText = await readFile4(manifestPath, "utf8");
2288
2459
  } catch (error) {
2289
2460
  if (isEnoent(error)) {
2290
2461
  throw new AdapterBundleError("ADAPTER_BUNDLE_MISSING", `offline Harness Bundle manifest missing: ${profile}/${agent}`);
@@ -2312,14 +2483,14 @@ async function loadAgentBundle(resourcesRoot, profile, agent) {
2312
2483
  }
2313
2484
  let bytes;
2314
2485
  try {
2315
- bytes = await readFile3(join4(resourcesRoot, "harness", "bundles", profile, agent, item2.path));
2486
+ bytes = await readFile4(join5(resourcesRoot, "harness", "bundles", profile, agent, item2.path));
2316
2487
  } catch (error) {
2317
2488
  if (isEnoent(error)) {
2318
2489
  throw new AdapterBundleError("ADAPTER_BUNDLE_MISSING", `offline Harness Bundle file missing: ${profile}/${agent}/${item2.path}`);
2319
2490
  }
2320
2491
  throw error;
2321
2492
  }
2322
- if (createHash3("sha256").update(bytes).digest("hex") !== item2.sha256) {
2493
+ if (createHash4("sha256").update(bytes).digest("hex") !== item2.sha256) {
2323
2494
  throw new AdapterBundleError("ADAPTER_BUNDLE_INVALID", `Harness Bundle hash mismatch: ${profile}/${agent}/${item2.path}`);
2324
2495
  }
2325
2496
  files.set(item2.path, bytes);
@@ -2384,10 +2555,10 @@ function parseMigrationManifest(raw) {
2384
2555
  };
2385
2556
  }
2386
2557
  async function loadMigrationManifests(resourcesRoot) {
2387
- const migrationsRoot = join4(resourcesRoot, "harness", "migrations");
2558
+ const migrationsRoot = join5(resourcesRoot, "harness", "migrations");
2388
2559
  let versionDirs;
2389
2560
  try {
2390
- versionDirs = await readdir2(migrationsRoot);
2561
+ versionDirs = await readdir3(migrationsRoot);
2391
2562
  } catch (error) {
2392
2563
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2393
2564
  return [];
@@ -2398,7 +2569,7 @@ async function loadMigrationManifests(resourcesRoot) {
2398
2569
  for (const versionDir of versionDirs) {
2399
2570
  let files;
2400
2571
  try {
2401
- files = await readdir2(join4(migrationsRoot, versionDir));
2572
+ files = await readdir3(join5(migrationsRoot, versionDir));
2402
2573
  } catch (error) {
2403
2574
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2404
2575
  continue;
@@ -2408,7 +2579,7 @@ async function loadMigrationManifests(resourcesRoot) {
2408
2579
  for (const file of files) {
2409
2580
  if (!file.endsWith(".json"))
2410
2581
  continue;
2411
- manifests.push(parseMigrationManifest(JSON.parse(await readFile3(join4(migrationsRoot, versionDir, file), "utf8"))));
2582
+ manifests.push(parseMigrationManifest(JSON.parse(await readFile4(join5(migrationsRoot, versionDir, file), "utf8"))));
2412
2583
  }
2413
2584
  }
2414
2585
  return manifests;
@@ -2440,14 +2611,14 @@ function uuidV7(now = Date.now()) {
2440
2611
  var CONTEXT_INDEX_PATH = ".harness/context-index.json";
2441
2612
  async function fileHex(path) {
2442
2613
  try {
2443
- const data = await readFile4(path);
2444
- return createHash4("sha256").update(data).digest("hex");
2614
+ const data = await readFile5(path);
2615
+ return createHash5("sha256").update(data).digest("hex");
2445
2616
  } catch {
2446
2617
  return null;
2447
2618
  }
2448
2619
  }
2449
2620
  async function readExistingSkillBundles(root) {
2450
- const text = await readOptional(join5(root, CONTEXT_INDEX_PATH));
2621
+ const text = await readOptional(join6(root, CONTEXT_INDEX_PATH));
2451
2622
  if (text === "")
2452
2623
  return /* @__PURE__ */ new Map();
2453
2624
  try {
@@ -2467,7 +2638,7 @@ var TargetCollisionError = class extends Error {
2467
2638
  }
2468
2639
  };
2469
2640
  function hex(bytes) {
2470
- return createHash4("sha256").update(bytes).digest("hex");
2641
+ return createHash5("sha256").update(bytes).digest("hex");
2471
2642
  }
2472
2643
  function bytesEqual(left, right) {
2473
2644
  if (left.byteLength !== right.byteLength)
@@ -2480,7 +2651,7 @@ function bytesEqual(left, right) {
2480
2651
  }
2481
2652
  async function readOptional(path) {
2482
2653
  try {
2483
- return await readFile4(path, "utf8");
2654
+ return await readFile5(path, "utf8");
2484
2655
  } catch (error) {
2485
2656
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2486
2657
  return "";
@@ -2500,12 +2671,12 @@ async function exists2(path) {
2500
2671
  }
2501
2672
  }
2502
2673
  async function existingProjectConfig(root) {
2503
- const path = join5(root, ".harness", "project.yaml");
2674
+ const path = join6(root, ".harness", "project.yaml");
2504
2675
  const content = await readOptional(path);
2505
2676
  return content === "" ? null : projectConfigSchema.parse(parseYaml2(content));
2506
2677
  }
2507
2678
  async function operationFor(root, path, content) {
2508
- return await exists2(join5(root, path)) ? { operation: "modify", path, content } : { operation: "add", path, content };
2679
+ return await exists2(join6(root, path)) ? { operation: "modify", path, content } : { operation: "add", path, content };
2509
2680
  }
2510
2681
  var INSTALLED_BUNDLE_PATH = ".harness/state/local/installed-harness-bundle.json";
2511
2682
  function mergeOwnedTargets(owned) {
@@ -2541,7 +2712,7 @@ function mergeOwnedTargets(owned) {
2541
2712
  });
2542
2713
  }
2543
2714
  async function initializeProject(options) {
2544
- const root = resolve4(options.projectRoot);
2715
+ const root = resolve5(options.projectRoot);
2545
2716
  const config = initConfigSchema.parse(options.config);
2546
2717
  const existing = await existingProjectConfig(root);
2547
2718
  const profile = config.profile;
@@ -2584,7 +2755,7 @@ async function initializeProject(options) {
2584
2755
  const projectConfig = projectConfigSchema.parse({
2585
2756
  harness: { name: "hunter-harness", schema_version: 1 },
2586
2757
  project: {
2587
- name: existing?.project.name ?? basename(root),
2758
+ name: existing?.project.name ?? basename2(root),
2588
2759
  root: ".",
2589
2760
  local_project_key: existing?.project.local_project_key ?? uuidV7(),
2590
2761
  project_id: config.project_id ?? existing?.project.project_id ?? null,
@@ -2606,7 +2777,7 @@ async function initializeProject(options) {
2606
2777
  files: {}
2607
2778
  });
2608
2779
  const managedBlocks = [];
2609
- let agentsContent = await readOptional(join5(root, "AGENTS.md"));
2780
+ let agentsContent = await readOptional(join6(root, "AGENTS.md"));
2610
2781
  agentsContent = upsertManagedBlockById(agentsContent, AGENTS_CORE_BLOCK_ID, AGENTS_MANAGED_BLOCK_CONTENT);
2611
2782
  managedBlocks.push({
2612
2783
  owner: "shared",
@@ -2643,7 +2814,7 @@ async function initializeProject(options) {
2643
2814
  ["AGENTS.md", agentsContent]
2644
2815
  ]);
2645
2816
  if (enabledAgents.includes("claude-code")) {
2646
- let claudeContent = await readOptional(join5(root, "CLAUDE.md"));
2817
+ let claudeContent = await readOptional(join6(root, "CLAUDE.md"));
2647
2818
  claudeContent = upsertManagedBlockById(claudeContent, CLAUDE_BLOCK_ID, CLAUDE_MANAGED_BLOCK_CONTENT);
2648
2819
  files.set("CLAUDE.md", claudeContent);
2649
2820
  managedBlocks.push({
@@ -2654,7 +2825,7 @@ async function initializeProject(options) {
2654
2825
  });
2655
2826
  }
2656
2827
  if (enabledAgents.includes("codebuddy")) {
2657
- let codebuddyContent = await readOptional(join5(root, "CODEBUDDY.md"));
2828
+ let codebuddyContent = await readOptional(join6(root, "CODEBUDDY.md"));
2658
2829
  codebuddyContent = upsertManagedBlockById(codebuddyContent, CODEBUDDY_BLOCK_ID, CODEBUDDY_MANAGED_BLOCK_CONTENT);
2659
2830
  files.set("CODEBUDDY.md", codebuddyContent);
2660
2831
  managedBlocks.push({
@@ -2691,6 +2862,7 @@ async function initializeProject(options) {
2691
2862
  const writeOperations = await Promise.all([...files.entries()].map(async ([path, content]) => operationFor(root, path, content)));
2692
2863
  await runTransaction(root, writeOperations, { kind: "init" });
2693
2864
  await enrichContextIndexWithVerification(root, enabledAgents, profile, options.resourcesRoot, adapterContext);
2865
+ await synchronizeProjectRules(root, enabledAgents, surface);
2694
2866
  }
2695
2867
  return {
2696
2868
  projectConfig,
@@ -2700,8 +2872,8 @@ async function initializeProject(options) {
2700
2872
  };
2701
2873
  }
2702
2874
  async function enrichContextIndexWithVerification(root, agents, profile, resourcesRoot, adapterContext) {
2703
- const contextPath = join5(root, CONTEXT_INDEX_PATH);
2704
- const existing = await readOptional(join5(contextPath));
2875
+ const contextPath = join6(root, CONTEXT_INDEX_PATH);
2876
+ const existing = await readOptional(join6(contextPath));
2705
2877
  if (existing === "")
2706
2878
  return;
2707
2879
  let parsed;
@@ -2726,7 +2898,7 @@ async function enrichContextIndexWithVerification(root, agents, profile, resourc
2726
2898
  const entries = [];
2727
2899
  for (const target of targets) {
2728
2900
  const rel = target.target_path.replace(/\\/g, "/");
2729
- const actual = await fileHex(join5(root, target.target_path));
2901
+ const actual = await fileHex(join6(root, target.target_path));
2730
2902
  entries.push({ relpath: rel, sha256: actual ?? "" });
2731
2903
  if (actual === null) {
2732
2904
  mismatches.push({ relpath: rel, expected: target.sha256, actual: "<missing>" });
@@ -2743,19 +2915,19 @@ async function enrichContextIndexWithVerification(root, agents, profile, resourc
2743
2915
  entry.verificationStatus = newStatus;
2744
2916
  entry.mismatchDetails = mismatches;
2745
2917
  }
2746
- await writeFile3(contextPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
2918
+ await writeFile4(contextPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
2747
2919
  }
2748
2920
 
2749
2921
  // ../core/dist/project/refresh.js
2750
- import { createHash as createHash5 } from "node:crypto";
2751
- import { readFile as readFile5, readdir as readdir3, rmdir } from "node:fs/promises";
2752
- import { dirname as dirname3, join as join6, resolve as resolve5 } from "node:path";
2922
+ import { createHash as createHash6 } from "node:crypto";
2923
+ import { readFile as readFile6, readdir as readdir4, rmdir } from "node:fs/promises";
2924
+ import { dirname as dirname4, join as join7, resolve as resolve6 } from "node:path";
2753
2925
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
2754
2926
  var INSTALLED_STATE_PATH = ".harness/state/local/installed-harness-bundle.json";
2755
2927
  var CONTEXT_INDEX_PATH2 = ".harness/context-index.json";
2756
2928
  async function fileHex2(path) {
2757
2929
  try {
2758
- return createHash5("sha256").update(await readFile5(path)).digest("hex");
2930
+ return createHash6("sha256").update(await readFile6(path)).digest("hex");
2759
2931
  } catch (error) {
2760
2932
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2761
2933
  return null;
@@ -2765,7 +2937,7 @@ async function fileHex2(path) {
2765
2937
  }
2766
2938
  async function readOptionalText(path) {
2767
2939
  try {
2768
- return await readFile5(path, "utf8");
2940
+ return await readFile6(path, "utf8");
2769
2941
  } catch (error) {
2770
2942
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2771
2943
  return "";
@@ -2774,7 +2946,7 @@ async function readOptionalText(path) {
2774
2946
  }
2775
2947
  }
2776
2948
  async function readInstalledState(root) {
2777
- const content = await readOptionalText(join6(root, INSTALLED_STATE_PATH));
2949
+ const content = await readOptionalText(join7(root, INSTALLED_STATE_PATH));
2778
2950
  if (content === "") {
2779
2951
  return {
2780
2952
  profile: null,
@@ -2844,7 +3016,7 @@ async function readInstalledState(root) {
2844
3016
  };
2845
3017
  }
2846
3018
  async function readInstalledAgentConfiguration(projectRoot) {
2847
- const installed = await readInstalledState(resolve5(projectRoot));
3019
+ const installed = await readInstalledState(resolve6(projectRoot));
2848
3020
  return {
2849
3021
  agents: installed.adapters,
2850
3022
  profiles: Object.fromEntries(installed.adapters.map((agent) => [
@@ -2854,7 +3026,7 @@ async function readInstalledAgentConfiguration(projectRoot) {
2854
3026
  };
2855
3027
  }
2856
3028
  async function readContextIndexBundleHash(root) {
2857
- const content = await readOptionalText(join6(root, CONTEXT_INDEX_PATH2));
3029
+ const content = await readOptionalText(join7(root, CONTEXT_INDEX_PATH2));
2858
3030
  if (content === "")
2859
3031
  return null;
2860
3032
  try {
@@ -2866,13 +3038,13 @@ async function readContextIndexBundleHash(root) {
2866
3038
  }
2867
3039
  }
2868
3040
  async function pruneEmptyParentDirs(root, deletedPaths, boundaryPaths) {
2869
- const boundaries = new Set(boundaryPaths.map((path) => join6(root, path)));
3041
+ const boundaries = new Set(boundaryPaths.map((path) => join7(root, path)));
2870
3042
  for (const deleted of deletedPaths) {
2871
- let dir = dirname3(join6(root, deleted));
3043
+ let dir = dirname4(join7(root, deleted));
2872
3044
  while (dir.startsWith(root) && !boundaries.has(dir)) {
2873
3045
  let entries;
2874
3046
  try {
2875
- entries = await readdir3(dir);
3047
+ entries = await readdir4(dir);
2876
3048
  } catch {
2877
3049
  break;
2878
3050
  }
@@ -2883,7 +3055,7 @@ async function pruneEmptyParentDirs(root, deletedPaths, boundaryPaths) {
2883
3055
  } catch {
2884
3056
  break;
2885
3057
  }
2886
- dir = dirname3(dir);
3058
+ dir = dirname4(dir);
2887
3059
  }
2888
3060
  }
2889
3061
  }
@@ -2910,7 +3082,7 @@ function sortByTarget(items) {
2910
3082
  return [...items].sort((left, right) => left.target_path.localeCompare(right.target_path));
2911
3083
  }
2912
3084
  async function reconcileContextIndex(root, agents, profiles, manifests, codebuddySurface2, verifications) {
2913
- const existing = await readOptionalText(join6(root, CONTEXT_INDEX_PATH2));
3085
+ const existing = await readOptionalText(join7(root, CONTEXT_INDEX_PATH2));
2914
3086
  let codebase = {
2915
3087
  map: ".harness/codebase/map",
2916
3088
  status: "missing"
@@ -2963,7 +3135,7 @@ async function reconcileContextIndex(root, agents, profiles, manifests, codebudd
2963
3135
  }
2964
3136
  async function projectTransitionOperation(root, agents, profiles, codebuddySurface2) {
2965
3137
  const path = ".harness/project.yaml";
2966
- const content = await readOptionalText(join6(root, path));
3138
+ const content = await readOptionalText(join7(root, path));
2967
3139
  if (content === "")
2968
3140
  return null;
2969
3141
  const project = parseYaml3(content);
@@ -2998,11 +3170,11 @@ function mergeTargets(targets) {
2998
3170
  }).sort((left, right) => left.target_path.localeCompare(right.target_path));
2999
3171
  }
3000
3172
  async function reconcileMarkdownBlock(root, fileName, blockId, content, remove, ops, conflicts, preserved) {
3001
- const original = await readOptionalText(join6(root, fileName));
3173
+ const original = await readOptionalText(join7(root, fileName));
3002
3174
  const synthetic = {
3003
3175
  source_path: fileName,
3004
3176
  target_path: fileName,
3005
- sha256: createHash5("sha256").update(content).digest("hex"),
3177
+ sha256: createHash6("sha256").update(content).digest("hex"),
3006
3178
  bytes: new TextEncoder().encode(content)
3007
3179
  };
3008
3180
  let next;
@@ -3017,7 +3189,7 @@ async function reconcileMarkdownBlock(root, fileName, blockId, content, remove,
3017
3189
  next = refreshed.content;
3018
3190
  }
3019
3191
  } catch {
3020
- const current = original === "" ? null : createHash5("sha256").update(original).digest("hex");
3192
+ const current = original === "" ? null : createHash6("sha256").update(original).digest("hex");
3021
3193
  preserved.push(item(synthetic, "preserve", "MALFORMED_MANAGED_BLOCK", current, synthetic.sha256));
3022
3194
  conflicts.push(conflict(synthetic, "MALFORMED_MANAGED_BLOCK", current, synthetic.sha256));
3023
3195
  return;
@@ -3037,7 +3209,7 @@ function stateWithoutInstalledAt(value) {
3037
3209
  return copy;
3038
3210
  }
3039
3211
  async function refreshProject(options) {
3040
- const root = resolve5(options.projectRoot);
3212
+ const root = resolve6(options.projectRoot);
3041
3213
  const installed = await readInstalledState(root);
3042
3214
  const oldAgents = installed.adapters.length > 0 ? installed.adapters : ["claude-code"];
3043
3215
  const selectedAgents = sortHarnessAgents(options.agents);
@@ -3121,7 +3293,7 @@ async function refreshProject(options) {
3121
3293
  const newStateFiles = installed.files.filter((entry) => entry.owner === "shared" || !selectedSet.has(entry.owner));
3122
3294
  for (const target of newManaged) {
3123
3295
  const incoming = target.sha256;
3124
- const current = await fileHex2(join6(root, target.target_path));
3296
+ const current = await fileHex2(join7(root, target.target_path));
3125
3297
  if (current === null) {
3126
3298
  applied.push(item(target, "add", "MISSING_TARGET", null, incoming));
3127
3299
  ops.push({ operation: "add", path: target.target_path, content: target.bytes });
@@ -3149,7 +3321,7 @@ async function refreshProject(options) {
3149
3321
  }
3150
3322
  }
3151
3323
  for (const target of oldOnly) {
3152
- const current = await fileHex2(join6(root, target.target_path));
3324
+ const current = await fileHex2(join7(root, target.target_path));
3153
3325
  if (current === null) {
3154
3326
  continue;
3155
3327
  }
@@ -3192,7 +3364,7 @@ async function refreshProject(options) {
3192
3364
  const verifyEntries = [];
3193
3365
  for (const target of verifyTargets) {
3194
3366
  const rel = target.target_path.replace(/\\/g, "/");
3195
- const actual = await fileHex2(join6(root, target.target_path));
3367
+ const actual = await fileHex2(join7(root, target.target_path));
3196
3368
  verifyEntries.push({ relpath: rel, sha256: actual ?? "" });
3197
3369
  if (actual === null) {
3198
3370
  verifyMismatches.push({ relpath: rel, expected: target.sha256, actual: "<missing>" });
@@ -3225,19 +3397,19 @@ async function refreshProject(options) {
3225
3397
  owner: "shared",
3226
3398
  target_path: "AGENTS.md",
3227
3399
  block_id: AGENTS_CORE_BLOCK_ID,
3228
- content_sha256: createHash5("sha256").update(AGENTS_MANAGED_BLOCK_CONTENT).digest("hex")
3400
+ content_sha256: createHash6("sha256").update(AGENTS_MANAGED_BLOCK_CONTENT).digest("hex")
3229
3401
  },
3230
3402
  ...selectedSet.has("claude-code") ? [{
3231
3403
  owner: "claude-code",
3232
3404
  target_path: "CLAUDE.md",
3233
3405
  block_id: CLAUDE_BLOCK_ID,
3234
- content_sha256: createHash5("sha256").update(CLAUDE_MANAGED_BLOCK_CONTENT).digest("hex")
3406
+ content_sha256: createHash6("sha256").update(CLAUDE_MANAGED_BLOCK_CONTENT).digest("hex")
3235
3407
  }] : [],
3236
3408
  ...selectedSet.has("codebuddy") ? [{
3237
3409
  owner: "codebuddy",
3238
3410
  target_path: "CODEBUDDY.md",
3239
3411
  block_id: CODEBUDDY_BLOCK_ID,
3240
- content_sha256: createHash5("sha256").update(CODEBUDDY_MANAGED_BLOCK_CONTENT).digest("hex")
3412
+ content_sha256: createHash6("sha256").update(CODEBUDDY_MANAGED_BLOCK_CONTENT).digest("hex")
3241
3413
  }] : []
3242
3414
  ].sort((left, right) => left.target_path.localeCompare(right.target_path) || left.block_id.localeCompare(right.block_id));
3243
3415
  const filesByTarget = new Map(newStateFiles.map((entry) => [entry.target_path, entry]));
@@ -3253,7 +3425,7 @@ async function refreshProject(options) {
3253
3425
  files: [...filesByTarget.values()].sort((left, right) => left.target_path.localeCompare(right.target_path) || left.source_path.localeCompare(right.source_path)),
3254
3426
  managed_blocks: managedBlocks
3255
3427
  };
3256
- const existingState = await readOptionalText(join6(root, INSTALLED_STATE_PATH));
3428
+ const existingState = await readOptionalText(join7(root, INSTALLED_STATE_PATH));
3257
3429
  let existingParsed = null;
3258
3430
  try {
3259
3431
  existingParsed = existingState === "" ? null : JSON.parse(existingState);
@@ -3268,6 +3440,7 @@ async function refreshProject(options) {
3268
3440
  }
3269
3441
  if (!options.dryRun) {
3270
3442
  await runTransaction(root, ops, { kind: "refresh" });
3443
+ await synchronizeProjectRules(root, agents, codebuddySurface2);
3271
3444
  await pruneEmptyParentDirs(root, removed.map((item2) => item2.target_path), getAdapters(selectedAgents).flatMap((adapter) => adapter.pruneBoundaries({
3272
3445
  profile: profiles.get(adapter.name) ?? profile,
3273
3446
  codebuddySurface: codebuddySurface2
@@ -3307,7 +3480,7 @@ function buildMarkerCoreHash(text) {
3307
3480
  }
3308
3481
  }
3309
3482
  async function collectFreshness(options) {
3310
- const root = resolve5(options.projectRoot);
3483
+ const root = resolve6(options.projectRoot);
3311
3484
  const installed = await readInstalledState(root);
3312
3485
  const codebuddySurface2 = options.codebuddySurface ?? "both";
3313
3486
  const agents = [];
@@ -3352,18 +3525,18 @@ async function collectFreshness(options) {
3352
3525
  identity.adapterHash = sha256Bytes(canonicalJson(targets.map((target) => ({ path: target.target_path.replace(/\\/g, "/"), sha256: target.sha256 })).sort((a, b) => a.path.localeCompare(b.path))));
3353
3526
  const installedProjection = await Promise.all(targets.map(async (target) => ({
3354
3527
  path: target.target_path.replace(/\\/g, "/"),
3355
- sha256: await fileHex2(join6(root, target.target_path))
3528
+ sha256: await fileHex2(join7(root, target.target_path))
3356
3529
  })));
3357
3530
  identity.installedAdapterHash = sha256Bytes(canonicalJson(installedProjection.sort((a, b) => a.path.localeCompare(b.path))));
3358
3531
  const markerTarget = targets.find((target) => target.target_path.replace(/\\/g, "/").endsWith(`/${BUILD_MARKER_BUNDLE_PATH}`) || target.target_path === BUILD_MARKER_BUNDLE_PATH);
3359
3532
  if (markerTarget !== void 0) {
3360
- identity.installedCoreHash = buildMarkerCoreHash(await readOptionalText(join6(root, markerTarget.target_path)));
3533
+ identity.installedCoreHash = buildMarkerCoreHash(await readOptionalText(join7(root, markerTarget.target_path)));
3361
3534
  }
3362
3535
  const mismatchDetails = [];
3363
3536
  const contentEntries = [];
3364
3537
  for (const target of targets) {
3365
3538
  const rel = target.target_path.replace(/\\/g, "/");
3366
- const actual = await fileHex2(join6(root, target.target_path));
3539
+ const actual = await fileHex2(join7(root, target.target_path));
3367
3540
  contentEntries.push({ relpath: rel, sha256: actual ?? "" });
3368
3541
  if (actual === null) {
3369
3542
  mismatchDetails.push({ relpath: rel, expected: target.sha256, actual: "<missing>" });
@@ -3386,7 +3559,7 @@ async function collectFreshness(options) {
3386
3559
  const drifted = [];
3387
3560
  const missing = [];
3388
3561
  for (const target of targets) {
3389
- const current = await fileHex2(join6(root, target.target_path));
3562
+ const current = await fileHex2(join7(root, target.target_path));
3390
3563
  if (current === null) {
3391
3564
  missing.push(target.target_path);
3392
3565
  } else if (current !== target.sha256) {
@@ -3750,8 +3923,8 @@ function generateProposalPreview(input, scanOptions = {}) {
3750
3923
  }
3751
3924
 
3752
3925
  // ../core/dist/push/credentials.js
3753
- import { readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
3754
- import { join as join7 } from "node:path";
3926
+ import { readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
3927
+ import { join as join8 } from "node:path";
3755
3928
  import { parse as parseYaml4, stringify as stringifyYaml3 } from "yaml";
3756
3929
  var CREDENTIALS_LOCAL_RELATIVE = ".harness/credentials.local.yaml";
3757
3930
  var CREDENTIALS_GITIGNORE_LINES = [
@@ -3821,7 +3994,7 @@ function mergeLocalCredentials(existing, patch) {
3821
3994
  }
3822
3995
  async function readLocalCredentials(projectRoot) {
3823
3996
  try {
3824
- const raw = await readFile6(join7(projectRoot, CREDENTIALS_LOCAL_RELATIVE), "utf8");
3997
+ const raw = await readFile7(join8(projectRoot, CREDENTIALS_LOCAL_RELATIVE), "utf8");
3825
3998
  return parseLocalCredentials(parseYaml4(raw));
3826
3999
  } catch (error) {
3827
4000
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -3832,13 +4005,13 @@ async function readLocalCredentials(projectRoot) {
3832
4005
  }
3833
4006
  async function writeLocalCredentials(projectRoot, credentials) {
3834
4007
  const normalized = validateLocalCredentials(credentials);
3835
- await writeFile4(join7(projectRoot, CREDENTIALS_LOCAL_RELATIVE), stringifyYaml3(normalized, { sortMapEntries: true }) + "\n", "utf8");
4008
+ await writeFile5(join8(projectRoot, CREDENTIALS_LOCAL_RELATIVE), stringifyYaml3(normalized, { sortMapEntries: true }) + "\n", "utf8");
3836
4009
  }
3837
4010
  async function ensureCredentialsGitignore(projectRoot) {
3838
- const gitignorePath = join7(projectRoot, ".gitignore");
4011
+ const gitignorePath = join8(projectRoot, ".gitignore");
3839
4012
  let content = "";
3840
4013
  try {
3841
- content = await readFile6(gitignorePath, "utf8");
4014
+ content = await readFile7(gitignorePath, "utf8");
3842
4015
  } catch (error) {
3843
4016
  if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
3844
4017
  throw error;
@@ -3861,7 +4034,7 @@ async function ensureCredentialsGitignore(projectRoot) {
3861
4034
  return;
3862
4035
  }
3863
4036
  const suffix = content.length === 0 || content.endsWith("\n") ? "" : "\n";
3864
- await writeFile4(gitignorePath, content + suffix + missing.join("\n") + "\n", "utf8");
4037
+ await writeFile5(gitignorePath, content + suffix + missing.join("\n") + "\n", "utf8");
3865
4038
  }
3866
4039
  function resolvePushAuth(input) {
3867
4040
  const tokenEnv = input.tokenEnv ?? input.projectTokenEnv;
@@ -3889,28 +4062,28 @@ function resolvePushAuth(input) {
3889
4062
  }
3890
4063
 
3891
4064
  // ../core/dist/push/push.js
3892
- import { lstat as lstat3, readFile as readFile10, readdir as readdir4, rm as rm5 } from "node:fs/promises";
3893
- import { join as join11, relative, resolve as resolve7 } from "node:path";
4065
+ import { lstat as lstat3, readFile as readFile11, readdir as readdir5, rm as rm5 } from "node:fs/promises";
4066
+ import { join as join12, relative, resolve as resolve8 } from "node:path";
3894
4067
  import { parse as parseYaml5, stringify as stringifyYaml4 } from "yaml";
3895
4068
 
3896
4069
  // ../core/dist/state/baseline.js
3897
- import { readFile as readFile7 } from "node:fs/promises";
3898
- import { join as join8 } from "node:path";
4070
+ import { readFile as readFile8 } from "node:fs/promises";
4071
+ import { join as join9 } from "node:path";
3899
4072
  async function readBaseline(projectRoot) {
3900
- const content = await readFile7(join8(stateLayout(projectRoot).baseline, "manifest.json"), "utf8");
4073
+ const content = await readFile8(join9(stateLayout(projectRoot).baseline, "manifest.json"), "utf8");
3901
4074
  return baselineManifestSchema.parse(JSON.parse(content));
3902
4075
  }
3903
4076
 
3904
4077
  // ../core/dist/state/locks.js
3905
4078
  import { randomUUID as randomUUID3 } from "node:crypto";
3906
- import { readFile as readFile8, rename as rename3, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
3907
- import { join as join9 } from "node:path";
4079
+ import { readFile as readFile9, rename as rename4, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
4080
+ import { join as join10 } from "node:path";
3908
4081
  async function readLock(path) {
3909
- return JSON.parse(await readFile8(path, "utf8"));
4082
+ return JSON.parse(await readFile9(path, "utf8"));
3910
4083
  }
3911
4084
  async function acquireProtocolLock(projectRoot, operation, options = {}) {
3912
4085
  const layout = await ensureStateLayout(projectRoot);
3913
- const lockPath = join9(layout.locks, "protocol.lock");
4086
+ const lockPath = join10(layout.locks, "protocol.lock");
3914
4087
  const now = options.now ?? Date.now();
3915
4088
  const staleAfterMs = options.staleAfterMs ?? 15 * 60 * 1e3;
3916
4089
  const record = {
@@ -3923,7 +4096,7 @@ async function acquireProtocolLock(projectRoot, operation, options = {}) {
3923
4096
  };
3924
4097
  for (let attempt = 0; attempt < 2; attempt += 1) {
3925
4098
  try {
3926
- await writeFile5(lockPath, JSON.stringify(record, null, 2), { flag: "wx" });
4099
+ await writeFile6(lockPath, JSON.stringify(record, null, 2), { flag: "wx" });
3927
4100
  return {
3928
4101
  path: lockPath,
3929
4102
  operation,
@@ -3945,15 +4118,15 @@ async function acquireProtocolLock(projectRoot, operation, options = {}) {
3945
4118
  if (now - current.heartbeat_at_ms <= staleAfterMs) {
3946
4119
  throw new Error("protocol lock is active for operation " + current.operation, { cause: error });
3947
4120
  }
3948
- await rename3(lockPath, lockPath + ".stale-" + randomUUID3());
4121
+ await rename4(lockPath, lockPath + ".stale-" + randomUUID3());
3949
4122
  }
3950
4123
  }
3951
4124
  throw new Error("unable to acquire protocol lock");
3952
4125
  }
3953
4126
 
3954
4127
  // ../core/dist/sync/synchronize.js
3955
- import { lstat as lstat2, readFile as readFile9, rm as rm4 } from "node:fs/promises";
3956
- import { join as join10, resolve as resolve6 } from "node:path";
4128
+ import { lstat as lstat2, readFile as readFile10, rm as rm4 } from "node:fs/promises";
4129
+ import { join as join11, resolve as resolve7 } from "node:path";
3957
4130
 
3958
4131
  // ../core/dist/update/conflicts.js
3959
4132
  function operationTargetPath(operation) {
@@ -4235,8 +4408,8 @@ async function pathExists(path) {
4235
4408
  }
4236
4409
  }
4237
4410
  async function optionalContent(root, path) {
4238
- const full = join10(root, path);
4239
- return await pathExists(full) ? readFile9(full, "utf8") : null;
4411
+ const full = join11(root, path);
4412
+ return await pathExists(full) ? readFile10(full, "utf8") : null;
4240
4413
  }
4241
4414
  function manifestPayloadHash(manifest) {
4242
4415
  const payload = { ...manifest };
@@ -4248,10 +4421,10 @@ async function loadBlob(root, client, artifactId, operation, requestId, dryRun)
4248
4421
  return null;
4249
4422
  }
4250
4423
  const hash = operation.content_sha256;
4251
- const cacheRoot = join10(root, ".harness", "cache", "server-artifacts", artifactId);
4252
- const cachePath = join10(cacheRoot, "blobs", hash.replace(":", "_"));
4424
+ const cacheRoot = join11(root, ".harness", "cache", "server-artifacts", artifactId);
4425
+ const cachePath = join11(cacheRoot, "blobs", hash.replace(":", "_"));
4253
4426
  if (await pathExists(cachePath) && await sha256File(cachePath) === hash) {
4254
- return readFile9(cachePath, "utf8");
4427
+ return readFile10(cachePath, "utf8");
4255
4428
  }
4256
4429
  const bytes = await client.downloadArtifactBlob(artifactId, hash, requestId);
4257
4430
  if (bytes.byteLength !== operation.size_bytes || sha256Bytes(bytes) !== hash) {
@@ -4356,7 +4529,7 @@ async function planSingleArtifact(root, baseline, manifest, client, requestId, d
4356
4529
  });
4357
4530
  }
4358
4531
  async function saveConflictReport(root, requestId, manifest, plan) {
4359
- const reportPath = join10(root, ".harness", "reports", "conflicts-" + requestId + ".json");
4532
+ const reportPath = join11(root, ".harness", "reports", "conflicts-" + requestId + ".json");
4360
4533
  await atomicWriteJson(reportPath, {
4361
4534
  schema_version: 1,
4362
4535
  request_id: requestId,
@@ -4367,7 +4540,7 @@ async function saveConflictReport(root, requestId, manifest, plan) {
4367
4540
  });
4368
4541
  }
4369
4542
  async function synchronizeArtifacts(options, initialBaseline) {
4370
- const root = resolve6(options.projectRoot);
4543
+ const root = resolve7(options.projectRoot);
4371
4544
  const projectId = options.project.project.project_id;
4372
4545
  if (projectId === null) {
4373
4546
  throw new Error("PROJECT_NOT_BOUND");
@@ -4474,7 +4647,7 @@ async function synchronizeArtifacts(options, initialBaseline) {
4474
4647
  }
4475
4648
  continue;
4476
4649
  }
4477
- await atomicWriteJson(join10(root, ".harness", "cache", "server-artifacts", manifest.artifact_id, "manifest.json"), manifest);
4650
+ await atomicWriteJson(join11(root, ".harness", "cache", "server-artifacts", manifest.artifact_id, "manifest.json"), manifest);
4478
4651
  const lock = await acquireProtocolLock(root, "update", { requestId: options.requestId });
4479
4652
  try {
4480
4653
  const nextBaseline = applyBaselineUpdates(baseline, plan);
@@ -4544,7 +4717,7 @@ function baselineEntryFromOperation(manifest, operation, localContent) {
4544
4717
  };
4545
4718
  }
4546
4719
  async function advanceBaselineFromArtifact(options, baseline) {
4547
- const root = resolve6(options.projectRoot);
4720
+ const root = resolve7(options.projectRoot);
4548
4721
  if (options.manifest.project_version === null) {
4549
4722
  throw new Error("artifact manifest missing project_version");
4550
4723
  }
@@ -4621,7 +4794,8 @@ var PushWorkflowError = class extends Error {
4621
4794
  };
4622
4795
  var SHARED_MANAGED_ROOTS = [
4623
4796
  ".harness/knowledge",
4624
- ".harness/codebase"
4797
+ ".harness/codebase",
4798
+ ".harness/rules"
4625
4799
  ];
4626
4800
  var SHARED_MANAGED_FILES = [
4627
4801
  "AGENTS.md",
@@ -4644,8 +4818,8 @@ async function walkFiles(root, current, output) {
4644
4818
  if (!await exists3(current)) {
4645
4819
  return;
4646
4820
  }
4647
- for (const item2 of await readdir4(current, { withFileTypes: true })) {
4648
- const path = join11(current, item2.name);
4821
+ for (const item2 of await readdir5(current, { withFileTypes: true })) {
4822
+ const path = join12(current, item2.name);
4649
4823
  if (item2.isSymbolicLink()) {
4650
4824
  throw new PushWorkflowError("symlink is not pushable", 6, "UNSAFE_SYMLINK");
4651
4825
  }
@@ -4657,16 +4831,16 @@ async function walkFiles(root, current, output) {
4657
4831
  }
4658
4832
  }
4659
4833
  async function walkArchiveSummaries(root, output) {
4660
- const archiveRoot = join11(root, ".harness", "archive");
4834
+ const archiveRoot = join12(root, ".harness", "archive");
4661
4835
  if (!await exists3(archiveRoot))
4662
4836
  return;
4663
- for (const item2 of await readdir4(archiveRoot, { withFileTypes: true })) {
4837
+ for (const item2 of await readdir5(archiveRoot, { withFileTypes: true })) {
4664
4838
  if (item2.isSymbolicLink()) {
4665
4839
  throw new PushWorkflowError("symlink is not pushable", 6, "UNSAFE_SYMLINK");
4666
4840
  }
4667
4841
  if (!item2.isDirectory())
4668
4842
  continue;
4669
- const summaryPath = join11(archiveRoot, item2.name, "reports", "final", "summary-data.json");
4843
+ const summaryPath = join12(archiveRoot, item2.name, "reports", "final", "summary-data.json");
4670
4844
  try {
4671
4845
  const stats = await lstat3(summaryPath);
4672
4846
  if (stats.isSymbolicLink()) {
@@ -4694,9 +4868,9 @@ function enabledHarnessAgents(project) {
4694
4868
  async function walkHarnessEntries(root, directory, output) {
4695
4869
  if (!await exists3(directory))
4696
4870
  return;
4697
- for (const item2 of await readdir4(directory, { withFileTypes: true })) {
4871
+ for (const item2 of await readdir5(directory, { withFileTypes: true })) {
4698
4872
  if (item2.name.startsWith("harness-")) {
4699
- const path = join11(directory, item2.name);
4873
+ const path = join12(directory, item2.name);
4700
4874
  if (item2.isDirectory()) {
4701
4875
  await walkFiles(root, path, output);
4702
4876
  } else if (item2.isFile()) {
@@ -4706,7 +4880,7 @@ async function walkHarnessEntries(root, directory, output) {
4706
4880
  }
4707
4881
  }
4708
4882
  async function managedFiles(projectRoot, project) {
4709
- const root = resolve7(projectRoot);
4883
+ const root = resolve8(projectRoot);
4710
4884
  const paths = [];
4711
4885
  const adapters = getAdapters(enabledHarnessAgents(project));
4712
4886
  const managedFiles2 = [
@@ -4715,26 +4889,26 @@ async function managedFiles(projectRoot, project) {
4715
4889
  ...adapters.some((adapter) => adapter.name === "codebuddy") ? ["CODEBUDDY.md"] : []
4716
4890
  ];
4717
4891
  for (const path of managedFiles2) {
4718
- if (await exists3(join11(root, path))) {
4892
+ if (await exists3(join12(root, path))) {
4719
4893
  paths.push(path);
4720
4894
  }
4721
4895
  }
4722
4896
  for (const path of SHARED_MANAGED_ROOTS) {
4723
- await walkFiles(root, join11(root, path), paths);
4897
+ await walkFiles(root, join12(root, path), paths);
4724
4898
  }
4725
4899
  await walkArchiveSummaries(root, paths);
4726
4900
  for (const adapter of adapters) {
4727
4901
  if (adapter.rulesRoot !== null) {
4728
- await walkFiles(root, join11(root, adapter.rulesRoot), paths);
4902
+ await walkHarnessEntries(root, join12(root, adapter.rulesRoot), paths);
4729
4903
  }
4730
- await walkHarnessEntries(root, join11(root, adapter.skillsRoot), paths);
4904
+ await walkHarnessEntries(root, join12(root, adapter.skillsRoot), paths);
4731
4905
  if (adapter.agentsRoot !== null) {
4732
- await walkHarnessEntries(root, join11(root, adapter.agentsRoot), paths);
4906
+ await walkHarnessEntries(root, join12(root, adapter.agentsRoot), paths);
4733
4907
  }
4734
4908
  }
4735
4909
  const result = {};
4736
4910
  for (const path of [...new Set(paths)].sort()) {
4737
- result[path] = await readFile10(join11(root, path), "utf8");
4911
+ result[path] = await readFile11(join12(root, path), "utf8");
4738
4912
  }
4739
4913
  return result;
4740
4914
  }
@@ -4743,14 +4917,14 @@ function proposalBaseline(baseline) {
4743
4917
  }
4744
4918
  async function readProject(root) {
4745
4919
  try {
4746
- return projectConfigSchema.parse(parseYaml5(await readFile10(join11(root, ".harness", "project.yaml"), "utf8")));
4920
+ return projectConfigSchema.parse(parseYaml5(await readFile11(join12(root, ".harness", "project.yaml"), "utf8")));
4747
4921
  } catch {
4748
4922
  throw new PushWorkflowError("project configuration is missing or invalid", 3, "PROJECT_CONFIG_INVALID");
4749
4923
  }
4750
4924
  }
4751
4925
  async function readOptionalJson(path) {
4752
4926
  try {
4753
- return JSON.parse(await readFile10(path, "utf8"));
4927
+ return JSON.parse(await readFile11(path, "utf8"));
4754
4928
  } catch (error) {
4755
4929
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
4756
4930
  return null;
@@ -4762,7 +4936,7 @@ async function clientIdFor(root, explicit) {
4762
4936
  if (explicit !== void 0) {
4763
4937
  return explicit;
4764
4938
  }
4765
- const path = join11(root, ".harness", "state", "local", "client.json");
4939
+ const path = join12(root, ".harness", "state", "local", "client.json");
4766
4940
  const existing = await readOptionalJson(path);
4767
4941
  if (typeof existing?.client_id === "string" && /^cli_[A-Za-z0-9_-]+$/.test(existing.client_id)) {
4768
4942
  return existing.client_id;
@@ -4958,7 +5132,7 @@ async function bindProject(root, project, baseline, projectId) {
4958
5132
  return { project: nextProject, baseline: nextBaseline };
4959
5133
  }
4960
5134
  async function pushProject(options) {
4961
- const root = resolve7(options.projectRoot);
5135
+ const root = resolve8(options.projectRoot);
4962
5136
  let project = await readProject(root);
4963
5137
  let baseline = await readBaseline(root);
4964
5138
  const profile = parseHarnessProfile(project.project.profiles[0]);
@@ -5024,7 +5198,7 @@ async function pushProject(options) {
5024
5198
  if (options.confirmProposal !== void 0 && !await options.confirmProposal(preview)) {
5025
5199
  return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
5026
5200
  }
5027
- const workflowPath = join11(root, ".harness", "state", "local", "push-workflow.json");
5201
+ const workflowPath = join12(root, ".harness", "state", "local", "push-workflow.json");
5028
5202
  const priorWorkflow = await readOptionalJson(workflowPath);
5029
5203
  const provisionalRequestId = priorWorkflow?.local_project_key === project.project.local_project_key ? priorWorkflow.request_id : uuidV7();
5030
5204
  const lock = await acquireProtocolLock(root, "push", { requestId: provisionalRequestId });
@@ -5179,7 +5353,7 @@ async function pushProject(options) {
5179
5353
  pushWarning = "BASELINE_ADVANCE_DEFERRED";
5180
5354
  }
5181
5355
  }
5182
- await atomicWriteJson(join11(root, ".harness", "state", "local", "push-results", finalized.proposal_id + ".json"), {
5356
+ await atomicWriteJson(join12(root, ".harness", "state", "local", "push-results", finalized.proposal_id + ".json"), {
5183
5357
  schema_version: 1,
5184
5358
  request_id: requestId,
5185
5359
  project_id: projectId,
@@ -5224,14 +5398,14 @@ async function pushProject(options) {
5224
5398
  }
5225
5399
 
5226
5400
  // ../core/dist/state/cleanup.js
5227
- import { readFile as readFile11, readdir as readdir5, rm as rm6 } from "node:fs/promises";
5228
- import { join as join12 } from "node:path";
5401
+ import { readFile as readFile12, readdir as readdir6, rm as rm6 } from "node:fs/promises";
5402
+ import { join as join13 } from "node:path";
5229
5403
  function isSafeEntryName(name) {
5230
5404
  return name.length > 0 && !name.includes("/") && !name.includes("\\") && name !== "." && name !== ".." && !name.includes("\0");
5231
5405
  }
5232
5406
  async function listDir(path) {
5233
5407
  try {
5234
- return await readdir5(path);
5408
+ return await readdir6(path);
5235
5409
  } catch (error) {
5236
5410
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
5237
5411
  return [];
@@ -5249,7 +5423,7 @@ async function cleanupProject(options) {
5249
5423
  continue;
5250
5424
  let journal;
5251
5425
  try {
5252
- journal = JSON.parse(await readFile11(join12(layout.transactions, name, "journal.json"), "utf8"));
5426
+ journal = JSON.parse(await readFile12(join13(layout.transactions, name, "journal.json"), "utf8"));
5253
5427
  } catch {
5254
5428
  continue;
5255
5429
  }
@@ -5267,7 +5441,7 @@ async function cleanupProject(options) {
5267
5441
  continue;
5268
5442
  pruned.push(entry.id);
5269
5443
  if (!options.dryRun) {
5270
- await rm6(join12(layout.transactions, entry.id), { recursive: true, force: true });
5444
+ await rm6(join13(layout.transactions, entry.id), { recursive: true, force: true });
5271
5445
  }
5272
5446
  }
5273
5447
  }
@@ -5276,7 +5450,7 @@ async function cleanupProject(options) {
5276
5450
  continue;
5277
5451
  removedCache.push(name);
5278
5452
  if (!options.dryRun) {
5279
- await rm6(join12(layout.serverArtifacts, name), { recursive: true, force: true });
5453
+ await rm6(join13(layout.serverArtifacts, name), { recursive: true, force: true });
5280
5454
  }
5281
5455
  }
5282
5456
  return {
@@ -5336,10 +5510,10 @@ var SKILL_TARGET_AGENTS = Object.freeze([
5336
5510
  ]);
5337
5511
 
5338
5512
  // ../core/dist/transaction/recovery.js
5339
- import { readFile as readFile12, readdir as readdir6, rm as rm7, stat as stat3 } from "node:fs/promises";
5340
- import { join as join13 } from "node:path";
5513
+ import { readFile as readFile13, readdir as readdir7, rm as rm7, stat as stat3 } from "node:fs/promises";
5514
+ import { join as join14 } from "node:path";
5341
5515
  async function recoverTransaction(projectRoot, transactionId) {
5342
- const journal = JSON.parse(await readFile12(join13(stateLayout(projectRoot).transactions, transactionId, "journal.json"), "utf8"));
5516
+ const journal = JSON.parse(await readFile13(join14(stateLayout(projectRoot).transactions, transactionId, "journal.json"), "utf8"));
5343
5517
  if (journal.state === "committed") {
5344
5518
  return { transactionId, status: "committed" };
5345
5519
  }
@@ -5356,7 +5530,7 @@ async function listTransactions(projectRoot) {
5356
5530
  const root = stateLayout(projectRoot).transactions;
5357
5531
  let names;
5358
5532
  try {
5359
- names = await readdir6(root);
5533
+ names = await readdir7(root);
5360
5534
  } catch (error) {
5361
5535
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
5362
5536
  return [];
@@ -5366,7 +5540,7 @@ async function listTransactions(projectRoot) {
5366
5540
  const transactions = [];
5367
5541
  for (const transactionId of names) {
5368
5542
  try {
5369
- const journal = JSON.parse(await readFile12(join13(root, transactionId, "journal.json"), "utf8"));
5543
+ const journal = JSON.parse(await readFile13(join14(root, transactionId, "journal.json"), "utf8"));
5370
5544
  transactions.push({
5371
5545
  transactionId,
5372
5546
  kind: journal.kind,
@@ -5397,11 +5571,11 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
5397
5571
  if (latest === void 0) {
5398
5572
  throw new Error("no committed update transaction is available for rollback");
5399
5573
  }
5400
- const transactionRoot = join13(stateLayout(projectRoot).transactions, latest.transactionId);
5401
- const journal = JSON.parse(await readFile12(join13(transactionRoot, "journal.json"), "utf8"));
5402
- const after = JSON.parse(await readFile12(join13(transactionRoot, "after", "manifest.json"), "utf8"));
5574
+ const transactionRoot = join14(stateLayout(projectRoot).transactions, latest.transactionId);
5575
+ const journal = JSON.parse(await readFile13(join14(transactionRoot, "journal.json"), "utf8"));
5576
+ const after = JSON.parse(await readFile13(join14(transactionRoot, "after", "manifest.json"), "utf8"));
5403
5577
  for (const entry of after) {
5404
- const target = join13(projectRoot, entry.path);
5578
+ const target = join14(projectRoot, entry.path);
5405
5579
  const exists6 = await pathExists2(target);
5406
5580
  if (exists6 !== entry.exists || exists6 && await sha256File(target) !== entry.hash) {
5407
5581
  throw new Error("cannot rollback dirty path: " + entry.path);
@@ -5414,10 +5588,10 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
5414
5588
  continue;
5415
5589
  }
5416
5590
  seen.add(snapshot.path);
5417
- const target = join13(projectRoot, snapshot.path);
5591
+ const target = join14(projectRoot, snapshot.path);
5418
5592
  const exists6 = await pathExists2(target);
5419
5593
  if (snapshot.existed && snapshot.snapshot_name !== null) {
5420
- const content = await readFile12(join13(transactionRoot, "before", snapshot.snapshot_name));
5594
+ const content = await readFile13(join14(transactionRoot, "before", snapshot.snapshot_name));
5421
5595
  operations.push({
5422
5596
  operation: exists6 ? "modify" : "add",
5423
5597
  path: snapshot.path,
@@ -5446,7 +5620,7 @@ async function cleanupOldTransactions(projectRoot, now = /* @__PURE__ */ new Dat
5446
5620
  if (!removable) {
5447
5621
  continue;
5448
5622
  }
5449
- await rm7(join13(stateLayout(projectRoot).transactions, item2.transactionId), {
5623
+ await rm7(join14(stateLayout(projectRoot).transactions, item2.transactionId), {
5450
5624
  recursive: true,
5451
5625
  force: true
5452
5626
  });
@@ -5456,8 +5630,8 @@ async function cleanupOldTransactions(projectRoot, now = /* @__PURE__ */ new Dat
5456
5630
  }
5457
5631
 
5458
5632
  // ../core/dist/update/update.js
5459
- import { readFile as readFile13 } from "node:fs/promises";
5460
- import { join as join14, resolve as resolve8 } from "node:path";
5633
+ import { readFile as readFile14 } from "node:fs/promises";
5634
+ import { join as join15, resolve as resolve9 } from "node:path";
5461
5635
  import { parse as parseYaml8 } from "yaml";
5462
5636
  var UpdateWorkflowError = class extends Error {
5463
5637
  exitCode;
@@ -5470,10 +5644,10 @@ var UpdateWorkflowError = class extends Error {
5470
5644
  }
5471
5645
  };
5472
5646
  async function updateProject(options) {
5473
- const root = resolve8(options.projectRoot);
5647
+ const root = resolve9(options.projectRoot);
5474
5648
  let project;
5475
5649
  try {
5476
- project = projectConfigSchema.parse(parseYaml8(await readFile13(join14(root, ".harness", "project.yaml"), "utf8")));
5650
+ project = projectConfigSchema.parse(parseYaml8(await readFile14(join15(root, ".harness", "project.yaml"), "utf8")));
5477
5651
  } catch {
5478
5652
  throw new UpdateWorkflowError("project configuration is invalid", 3, "PROJECT_CONFIG_INVALID");
5479
5653
  }
@@ -5523,6 +5697,13 @@ async function updateProject(options) {
5523
5697
  ...options.confirmConflictStrategy === void 0 ? {} : { confirmConflictStrategy: options.confirmConflictStrategy },
5524
5698
  ...options.transactionOptions === void 0 ? {} : { transactionOptions: options.transactionOptions }
5525
5699
  }, baseline);
5700
+ if (!options.dryRun) {
5701
+ const agents = project.adapters.enabled.flatMap((agent) => {
5702
+ const parsed = harnessAgentSchema.safeParse(agent);
5703
+ return parsed.success ? [parsed.data] : [];
5704
+ });
5705
+ await synchronizeProjectRules(root, agents, project.adapter_options?.codebuddy?.surface ?? "both");
5706
+ }
5526
5707
  return result;
5527
5708
  } catch (error) {
5528
5709
  if (error instanceof UpdateWorkflowError) {
@@ -5548,8 +5729,8 @@ async function updateProject(options) {
5548
5729
  }
5549
5730
 
5550
5731
  // src/config/init-config.ts
5551
- import { isAbsolute as isAbsolute2, join as join15 } from "node:path";
5552
- import { readFile as readFile14 } from "node:fs/promises";
5732
+ import { isAbsolute as isAbsolute2, join as join16 } from "node:path";
5733
+ import { readFile as readFile15 } from "node:fs/promises";
5553
5734
  var InitConfigurationError = class extends Error {
5554
5735
  exitCode;
5555
5736
  code;
@@ -5632,9 +5813,9 @@ function hasOwn(record, key) {
5632
5813
  async function resolveInitConfig(cwd, flags, prompts = {}, warnings = []) {
5633
5814
  let fileConfig = {};
5634
5815
  if (flags.config !== void 0) {
5635
- const path = isAbsolute2(flags.config) ? flags.config : join15(cwd, flags.config);
5816
+ const path = isAbsolute2(flags.config) ? flags.config : join16(cwd, flags.config);
5636
5817
  try {
5637
- fileConfig = JSON.parse(await readFile14(path, "utf8"));
5818
+ fileConfig = JSON.parse(await readFile15(path, "utf8"));
5638
5819
  } catch (error) {
5639
5820
  throw new InitConfigurationError(
5640
5821
  "unable to read init config: " + (error instanceof Error ? error.message : String(error)),
@@ -5716,8 +5897,8 @@ function serializeCliResult(result) {
5716
5897
  }
5717
5898
 
5718
5899
  // src/config/codebuddy-setup.ts
5719
- import { mkdir as mkdir4, readFile as readFile15, readdir as readdir7, stat as stat4, writeFile as writeFile6 } from "node:fs/promises";
5720
- import { basename as basename2, dirname as dirname4, extname, join as join16 } from "node:path";
5900
+ import { mkdir as mkdir5, readFile as readFile16, readdir as readdir8, stat as stat4, writeFile as writeFile7 } from "node:fs/promises";
5901
+ import { basename as basename3, dirname as dirname5, extname as extname2, join as join17 } from "node:path";
5721
5902
  var MANAGED_RULE_NAMES = /* @__PURE__ */ new Set([
5722
5903
  "harness-general.md",
5723
5904
  "harness-general.mdc",
@@ -5736,35 +5917,59 @@ async function exists4(path) {
5736
5917
  }
5737
5918
  async function readJsonObject(path) {
5738
5919
  try {
5739
- const parsed = JSON.parse(await readFile15(path, "utf8"));
5920
+ const parsed = JSON.parse(await readFile16(path, "utf8"));
5740
5921
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
5741
5922
  } catch (error) {
5742
5923
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
5743
5924
  return null;
5744
5925
  }
5745
5926
  }
5746
- async function inspectCodeBuddySetup(projectRoot) {
5747
- const rulesRoot = join16(projectRoot, ".claude", "rules");
5748
- let claudeRules = [];
5927
+ async function inspectCodeBuddySetup(projectRoot, surface = "both") {
5928
+ const rulesRoot = join17(projectRoot, ".claude", "rules");
5929
+ let ruleNames = [];
5749
5930
  try {
5750
- claudeRules = (await readdir7(rulesRoot, { withFileTypes: true })).filter((entry) => entry.isFile() && [".md", ".mdc"].includes(extname(entry.name).toLowerCase())).map((entry) => entry.name).filter((name) => !MANAGED_RULE_NAMES.has(name)).sort();
5931
+ ruleNames = (await readdir8(rulesRoot, { withFileTypes: true })).filter((entry) => entry.isFile() && [".md", ".mdc"].includes(extname2(entry.name).toLowerCase())).map((entry) => entry.name).filter((name) => !MANAGED_RULE_NAMES.has(name)).sort();
5751
5932
  } catch (error) {
5752
5933
  if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
5753
5934
  }
5754
- const mcp = await readJsonObject(join16(projectRoot, ".mcp.json"));
5935
+ const claudeRules = [];
5936
+ const currentClaudeRules = [];
5937
+ const conflictingClaudeRules = [];
5938
+ for (const name of ruleNames) {
5939
+ const sourceContent = await readFile16(join17(rulesRoot, name), "utf8");
5940
+ const targetContents = await Promise.all(
5941
+ destinationTargets(projectRoot, surface, name).map(async (target) => {
5942
+ try {
5943
+ return await readFile16(target, "utf8");
5944
+ } catch (error) {
5945
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
5946
+ throw error;
5947
+ }
5948
+ })
5949
+ );
5950
+ if (targetContents.some((content) => content === null)) claudeRules.push(name);
5951
+ if (targetContents.some((content) => content !== null && content !== sourceContent)) {
5952
+ conflictingClaudeRules.push(name);
5953
+ } else if (targetContents.length > 0 && targetContents.every((content) => content === sourceContent)) {
5954
+ currentClaudeRules.push(name);
5955
+ }
5956
+ }
5957
+ const mcp = await readJsonObject(join17(projectRoot, ".mcp.json"));
5755
5958
  const servers = mcp?.mcpServers;
5756
5959
  const configured = servers !== null && typeof servers === "object" && !Array.isArray(servers) && Object.prototype.hasOwnProperty.call(servers, "codegraph");
5757
5960
  return {
5758
5961
  claudeRules,
5759
- hasCodeGraphIndex: await exists4(join16(projectRoot, ".codegraph")),
5962
+ currentClaudeRules,
5963
+ conflictingClaudeRules,
5964
+ hasCodeGraphIndex: await exists4(join17(projectRoot, ".codegraph")),
5760
5965
  codeGraphConfigured: configured
5761
5966
  };
5762
5967
  }
5763
5968
  function destinationTargets(root, surface, name) {
5764
- const stem = basename2(name, extname(name));
5969
+ const stem = basename3(name, extname2(name));
5765
5970
  const targets = [];
5766
- if (surface !== "cli") targets.push(join16(root, ".codebuddy", ".rules", `${stem}.mdc`));
5767
- if (surface !== "ide") targets.push(join16(root, ".codebuddy", "rules", `${stem}.md`));
5971
+ if (surface !== "cli") targets.push(join17(root, ".codebuddy", ".rules", `${stem}.mdc`));
5972
+ if (surface !== "ide") targets.push(join17(root, ".codebuddy", "rules", `${stem}.md`));
5768
5973
  return targets;
5769
5974
  }
5770
5975
  async function applyCodeBuddySetup(options) {
@@ -5776,10 +5981,10 @@ async function applyCodeBuddySetup(options) {
5776
5981
  warnings: []
5777
5982
  };
5778
5983
  if (options.syncClaudeRules) {
5779
- const plan = await inspectCodeBuddySetup(options.projectRoot);
5984
+ const plan = await inspectCodeBuddySetup(options.projectRoot, options.surface);
5780
5985
  for (const name of plan.claudeRules) {
5781
- const source = join16(options.projectRoot, ".claude", "rules", name);
5782
- const content = await readFile15(source, "utf8");
5986
+ const source = join17(options.projectRoot, ".claude", "rules", name);
5987
+ const content = await readFile16(source, "utf8");
5783
5988
  if (SENSITIVE_ASSIGNMENT.test(content) || PRIVATE_KEY_BLOCK.test(content)) {
5784
5989
  result.skippedSensitive.push(name);
5785
5990
  continue;
@@ -5789,14 +5994,14 @@ async function applyCodeBuddySetup(options) {
5789
5994
  result.preserved.push(target);
5790
5995
  continue;
5791
5996
  }
5792
- await mkdir4(dirname4(target), { recursive: true });
5793
- await writeFile6(target, content, { encoding: "utf8", flag: "wx" });
5997
+ await mkdir5(dirname5(target), { recursive: true });
5998
+ await writeFile7(target, content, { encoding: "utf8", flag: "wx" });
5794
5999
  result.copied.push(target);
5795
6000
  }
5796
6001
  }
5797
6002
  }
5798
6003
  if (options.configureCodeGraph) {
5799
- const path = join16(options.projectRoot, ".mcp.json");
6004
+ const path = join17(options.projectRoot, ".mcp.json");
5800
6005
  const current = await readJsonObject(path);
5801
6006
  if (current === null) {
5802
6007
  result.warnings.push(".mcp.json \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\u5E76\u8DF3\u8FC7 CodeGraph MCP \u914D\u7F6E");
@@ -5811,7 +6016,7 @@ async function applyCodeBuddySetup(options) {
5811
6016
  codegraph: { command: "codegraph", args: ["serve", "--mcp"] }
5812
6017
  }
5813
6018
  };
5814
- await writeFile6(path, JSON.stringify(next, null, 2) + "\n", "utf8");
6019
+ await writeFile7(path, JSON.stringify(next, null, 2) + "\n", "utf8");
5815
6020
  result.mcpUpdated = true;
5816
6021
  }
5817
6022
  }
@@ -5820,13 +6025,13 @@ async function applyCodeBuddySetup(options) {
5820
6025
  }
5821
6026
 
5822
6027
  // src/commands/refresh.ts
5823
- import { readFile as readFile16 } from "node:fs/promises";
5824
- import { join as join17 } from "node:path";
6028
+ import { readFile as readFile17 } from "node:fs/promises";
6029
+ import { join as join18 } from "node:path";
5825
6030
  import { parse as parseYaml9 } from "yaml";
5826
6031
  async function detectProject(root) {
5827
6032
  let content;
5828
6033
  try {
5829
- content = await readFile16(join17(root, ".harness", "project.yaml"), "utf8");
6034
+ content = await readFile17(join18(root, ".harness", "project.yaml"), "utf8");
5830
6035
  } catch (error) {
5831
6036
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
5832
6037
  return { status: "absent" };
@@ -6055,7 +6260,13 @@ function agentMenuLines(installedProfiles) {
6055
6260
  }
6056
6261
  async function configureCodeBuddyExtras(agents, surface, options, dependencies) {
6057
6262
  if (!agents.includes("codebuddy")) return;
6058
- const plan = await inspectCodeBuddySetup(dependencies.cwd);
6263
+ const plan = await inspectCodeBuddySetup(dependencies.cwd, surface);
6264
+ if (plan.conflictingClaudeRules.length > 0) {
6265
+ dependencies.stderr(
6266
+ `\u4EE5\u4E0B CodeBuddy \u89C4\u5219\u4E0E Claude \u6E90\u89C4\u5219\u5185\u5BB9\u4E0D\u540C\uFF0C\u5DF2\u4FDD\u7559\u76EE\u6807\u6587\u4EF6\uFF1A${plan.conflictingClaudeRules.join(", ")}
6267
+ `
6268
+ );
6269
+ }
6059
6270
  let syncClaudeRules = false;
6060
6271
  let configureCodeGraph = false;
6061
6272
  if (plan.claudeRules.length > 0) {
@@ -6654,7 +6865,7 @@ async function runUpdate(options, dependencies) {
6654
6865
 
6655
6866
  // src/commands/recovery.ts
6656
6867
  import { stat as stat5 } from "node:fs/promises";
6657
- import { join as join18 } from "node:path";
6868
+ import { join as join19 } from "node:path";
6658
6869
  async function exists5(path) {
6659
6870
  try {
6660
6871
  await stat5(path);
@@ -6714,7 +6925,7 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
6714
6925
  return 5;
6715
6926
  }
6716
6927
  }
6717
- const initialized = await exists5(join18(dependencies.cwd, ".harness", "project.yaml"));
6928
+ const initialized = await exists5(join19(dependencies.cwd, ".harness", "project.yaml"));
6718
6929
  if (!initialized || options.nonInteractive === true || explicitConfigure(options)) {
6719
6930
  return null;
6720
6931
  }
@@ -6757,8 +6968,8 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
6757
6968
  }
6758
6969
 
6759
6970
  // src/workflow-data/resolve.ts
6760
- import { cp, mkdir as mkdir5, readdir as readdir8, readFile as readFile17, rm as rm8, stat as stat6 } from "node:fs/promises";
6761
- import { dirname as dirname5, join as join19, relative as relative2 } from "node:path";
6971
+ import { cp, mkdir as mkdir6, readdir as readdir9, readFile as readFile18, rm as rm8, stat as stat6 } from "node:fs/promises";
6972
+ import { dirname as dirname6, join as join20, relative as relative2 } from "node:path";
6762
6973
  import { fileURLToPath } from "node:url";
6763
6974
  var WorkflowDataResolutionError = class extends Error {
6764
6975
  code;
@@ -6792,17 +7003,17 @@ function workflowPackageName(family, env) {
6792
7003
  return `${scope}/workflow-${family}`;
6793
7004
  }
6794
7005
  async function listFilesRecursive(root, base = root) {
6795
- const entries = await readdir8(root, { withFileTypes: true });
7006
+ const entries = await readdir9(root, { withFileTypes: true });
6796
7007
  const files = [];
6797
7008
  for (const entry of entries) {
6798
- const absolute = join19(root, entry.name);
7009
+ const absolute = join20(root, entry.name);
6799
7010
  if (entry.isDirectory()) {
6800
7011
  files.push(...await listFilesRecursive(absolute, base));
6801
7012
  continue;
6802
7013
  }
6803
7014
  if (!entry.isFile()) continue;
6804
7015
  const rel = relative2(base, absolute).replaceAll("\\", "/");
6805
- files.push({ path: rel, content: await readFile17(absolute, "utf8") });
7016
+ files.push({ path: rel, content: await readFile18(absolute, "utf8") });
6806
7017
  }
6807
7018
  return files;
6808
7019
  }
@@ -6815,7 +7026,7 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
6815
7026
  const manifest = await readWorkflowFamilyManifest(resourcesRoot);
6816
7027
  const expected = manifest.content_sha256;
6817
7028
  if (typeof expected !== "string" || expected.length === 0) return;
6818
- const harnessRoot = join19(resourcesRoot, "harness");
7029
+ const harnessRoot = join20(resourcesRoot, "harness");
6819
7030
  if (!await pathExists3(harnessRoot)) {
6820
7031
  throw new WorkflowDataResolutionError(
6821
7032
  "\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u7F3A\u5C11 harness/\uFF0C\u65E0\u6CD5\u6821\u9A8C SHA-256",
@@ -6834,40 +7045,40 @@ async function verifyWorkflowPackageIntegrity(resourcesRoot) {
6834
7045
  }
6835
7046
  }
6836
7047
  async function monorepoResourcesRoot() {
6837
- const here = dirname5(fileURLToPath(import.meta.url));
7048
+ const here = dirname6(fileURLToPath(import.meta.url));
6838
7049
  const candidates = [
6839
7050
  // TypeScript source: packages/cli/src/workflow-data -> packages/workflow-data-harness.
6840
- join19(here, "../../../workflow-data-harness"),
7051
+ join20(here, "../../../workflow-data-harness"),
6841
7052
  // Bundled CLI: packages/cli/dist -> packages/workflow-data-harness.
6842
- join19(here, "../../workflow-data-harness"),
7053
+ join20(here, "../../workflow-data-harness"),
6843
7054
  // Test/build layouts that preserve additional source directory levels.
6844
- join19(here, "../../../../packages/workflow-data-harness")
7055
+ join20(here, "../../../../packages/workflow-data-harness")
6845
7056
  ];
6846
7057
  for (const candidate of candidates) {
6847
- if (await pathExists3(join19(candidate, "harness", "manifests"))) return candidate;
7058
+ if (await pathExists3(join20(candidate, "harness", "manifests"))) return candidate;
6848
7059
  }
6849
7060
  return null;
6850
7061
  }
6851
7062
  async function siblingWorkflowPackage(cwd) {
6852
- const here = dirname5(fileURLToPath(import.meta.url));
7063
+ const here = dirname6(fileURLToPath(import.meta.url));
6853
7064
  const candidates = [
6854
- join19(cwd, "node_modules", "@hunter-harness", "workflow-harness"),
7065
+ join20(cwd, "node_modules", "@hunter-harness", "workflow-harness"),
6855
7066
  // Published layout: node_modules/hunter-harness/dist/bin.js alongside the
6856
7067
  // scoped workflow package in node_modules/@hunter-harness/workflow-harness.
6857
- join19(here, "..", "..", "@hunter-harness", "workflow-harness"),
7068
+ join20(here, "..", "..", "@hunter-harness", "workflow-harness"),
6858
7069
  // Monorepo bundled layout: packages/cli/dist/bin.js.
6859
- join19(here, "..", "..", "workflow-data-harness")
7070
+ join20(here, "..", "..", "workflow-data-harness")
6860
7071
  ];
6861
7072
  for (const candidate of candidates) {
6862
- if (await pathExists3(join19(candidate, "harness", "manifests"))) return candidate;
7073
+ if (await pathExists3(join20(candidate, "harness", "manifests"))) return candidate;
6863
7074
  }
6864
7075
  return null;
6865
7076
  }
6866
7077
  async function readCachedNpmPackageVersion(cacheRoot) {
6867
- const manifestPath = join19(cacheRoot, "package.json");
7078
+ const manifestPath = join20(cacheRoot, "package.json");
6868
7079
  if (!await pathExists3(manifestPath)) return null;
6869
7080
  try {
6870
- const pkg = JSON.parse(await readFile17(manifestPath, "utf8"));
7081
+ const pkg = JSON.parse(await readFile18(manifestPath, "utf8"));
6871
7082
  return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : null;
6872
7083
  } catch {
6873
7084
  return null;
@@ -6889,13 +7100,13 @@ async function latestWorkflowCacheIsStale(cacheRoot, packageName, resolveManifes
6889
7100
  }
6890
7101
  }
6891
7102
  async function materializeWorkflowPackageCache(cacheRoot, packageSpec, extract) {
6892
- await mkdir5(cacheRoot, { recursive: true });
6893
- const staging = join19(cacheRoot, ".extract");
7103
+ await mkdir6(cacheRoot, { recursive: true });
7104
+ const staging = join20(cacheRoot, ".extract");
6894
7105
  await rm8(staging, { recursive: true, force: true });
6895
- await mkdir5(staging, { recursive: true });
7106
+ await mkdir6(staging, { recursive: true });
6896
7107
  await extract(packageSpec, staging);
6897
- const packageDir = await pathExists3(join19(staging, "harness", "manifests")) ? staging : join19(staging, "package");
6898
- if (!await pathExists3(join19(packageDir, "harness", "manifests"))) {
7108
+ const packageDir = await pathExists3(join20(staging, "harness", "manifests")) ? staging : join20(staging, "package");
7109
+ if (!await pathExists3(join20(packageDir, "harness", "manifests"))) {
6899
7110
  throw new WorkflowDataResolutionError(
6900
7111
  "\u5DE5\u4F5C\u6D41\u6570\u636E\u5305\u5185\u5BB9\u65E0\u6548\uFF1A\u7F3A\u5C11 harness/manifests",
6901
7112
  "WORKFLOW_DATA_INVALID",
@@ -6924,8 +7135,8 @@ async function resolveWorkflowResourcesRoot(options, argv = []) {
6924
7135
  const packageName = workflowPackageName(family, options.env);
6925
7136
  const packageSpec = version === "latest" ? packageName : `${packageName}@${version}`;
6926
7137
  const cacheKey = packageSpec.replace("/", "+");
6927
- const cacheRoot = join19(options.cwd, ".harness", "cache", "workflow-packages", cacheKey);
6928
- if (await pathExists3(join19(cacheRoot, "harness", "manifests"))) {
7138
+ const cacheRoot = join20(options.cwd, ".harness", "cache", "workflow-packages", cacheKey);
7139
+ if (await pathExists3(join20(cacheRoot, "harness", "manifests"))) {
6929
7140
  if (version === "latest") {
6930
7141
  const stale = await latestWorkflowCacheIsStale(cacheRoot, packageName, options.pacoteManifest);
6931
7142
  if (stale) {
@@ -6971,9 +7182,9 @@ function describeWorkflowDataFetchFailure(error, packageSpec) {
6971
7182
  return `\u65E0\u6CD5\u83B7\u53D6\u5DE5\u4F5C\u6D41\u6570\u636E\u5305 ${packageSpec}\uFF1A\u672C\u5730\u7F13\u5B58\u4E0D\u5B58\u5728\u4E14\u83B7\u53D6\u5931\u8D25\u3002\u53EF${hintRoot}\u3002\u539F\u56E0\uFF1A${code !== "" ? code + " " : ""}${detail}`;
6972
7183
  }
6973
7184
  async function readWorkflowFamilyManifest(resourcesRoot) {
6974
- const manifestPath = join19(resourcesRoot, "hunter-workflow-family.json");
7185
+ const manifestPath = join20(resourcesRoot, "hunter-workflow-family.json");
6975
7186
  if (!await pathExists3(manifestPath)) return {};
6976
- return JSON.parse(await readFile17(manifestPath, "utf8"));
7187
+ return JSON.parse(await readFile18(manifestPath, "utf8"));
6977
7188
  }
6978
7189
 
6979
7190
  // src/bin.ts