hunter-harness 0.2.16 → 0.2.18

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 +135 -0
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1929,6 +1929,11 @@ function validateRelativeBundlePath(path) {
1929
1929
  throw new Error("invalid Harness Bundle path");
1930
1930
  }
1931
1931
  }
1932
+ function validateChangeId(changeId) {
1933
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(changeId) || changeId === "." || changeId === "..") {
1934
+ throw new Error(`invalid Harness change id: ${changeId}`);
1935
+ }
1936
+ }
1932
1937
  function ruleTarget(sourcePath, targetPath, content) {
1933
1938
  return {
1934
1939
  source_path: sourcePath,
@@ -1981,6 +1986,15 @@ function makeAdapter(spec) {
1981
1986
  agentsRoot: spec.agentsRoot,
1982
1987
  commandsRoot: spec.commandsRoot,
1983
1988
  supportsExecutableHooks: false,
1989
+ worktreeFor(changeId) {
1990
+ validateChangeId(changeId);
1991
+ return {
1992
+ root: spec.worktreeRoot,
1993
+ path: `${spec.worktreeRoot}/${changeId}`,
1994
+ branchPrefix: spec.branchPrefix,
1995
+ branch: `${spec.branchPrefix}${changeId}`
1996
+ };
1997
+ },
1984
1998
  projectInstructionTargets() {
1985
1999
  return ["AGENTS.md", ...spec.extraInstructionFiles];
1986
2000
  },
@@ -2032,6 +2046,8 @@ var ADAPTERS = {
2032
2046
  agentsRoot: ".claude/agents",
2033
2047
  commandsRoot: null,
2034
2048
  instructions: "CLAUDE.md",
2049
+ worktreeRoot: ".claude/worktrees",
2050
+ branchPrefix: "claude/",
2035
2051
  extraInstructionFiles: ["CLAUDE.md"],
2036
2052
  ruleExt: ".md"
2037
2053
  }),
@@ -2042,6 +2058,8 @@ var ADAPTERS = {
2042
2058
  agentsRoot: null,
2043
2059
  commandsRoot: null,
2044
2060
  instructions: "AGENTS.md",
2061
+ worktreeRoot: ".codex/worktrees",
2062
+ branchPrefix: "codex/",
2045
2063
  extraInstructionFiles: [],
2046
2064
  ruleExt: null
2047
2065
  }),
@@ -2052,6 +2070,8 @@ var ADAPTERS = {
2052
2070
  agentsRoot: null,
2053
2071
  commandsRoot: ".cursor/commands",
2054
2072
  instructions: "AGENTS.md",
2073
+ worktreeRoot: ".cursor/worktrees",
2074
+ branchPrefix: "cursor/",
2055
2075
  extraInstructionFiles: [],
2056
2076
  ruleExt: ".mdc"
2057
2077
  }),
@@ -2062,6 +2082,8 @@ var ADAPTERS = {
2062
2082
  agentsRoot: ".codebuddy/agents",
2063
2083
  commandsRoot: ".codebuddy/commands",
2064
2084
  instructions: "CODEBUDDY.md",
2085
+ worktreeRoot: ".codebuddy/worktrees",
2086
+ branchPrefix: "codebuddy/",
2065
2087
  extraInstructionFiles: ["CODEBUDDY.md"],
2066
2088
  ruleExt: null
2067
2089
  })
@@ -3003,6 +3025,111 @@ async function refreshProject(options) {
3003
3025
  conflicts: sortByTarget(conflicts)
3004
3026
  };
3005
3027
  }
3028
+ function freshnessEntry(agent, profile, status, identity) {
3029
+ return { agent, profile, status, identity, driftedFiles: [], missingFiles: [] };
3030
+ }
3031
+ var BUILD_MARKER_BUNDLE_PATH = ".harness-build.json";
3032
+ function buildMarkerCoreHash(text) {
3033
+ if (text === null || text === "")
3034
+ return null;
3035
+ try {
3036
+ const parsed = JSON.parse(text);
3037
+ return typeof parsed.coreHash === "string" && parsed.coreHash.length > 0 ? parsed.coreHash : null;
3038
+ } catch {
3039
+ return null;
3040
+ }
3041
+ }
3042
+ async function collectFreshness(options) {
3043
+ const root = resolve4(options.projectRoot);
3044
+ const installed = await readInstalledState(root);
3045
+ const codebuddySurface2 = options.codebuddySurface ?? "both";
3046
+ const agents = [];
3047
+ for (const agent of sortHarnessAgents(options.agents)) {
3048
+ const installedProfile = installed.profiles.get(agent) ?? installed.profile;
3049
+ const requestedProfile = options.profile ?? installedProfile ?? "general";
3050
+ const installedManifest = installed.manifests.find((entry) => entry.adapter === agent);
3051
+ const identity = {
3052
+ adapter: agent,
3053
+ bundleVersion: null,
3054
+ installedBundleVersion: installedManifest?.bundle_version ?? null,
3055
+ manifestHash: null,
3056
+ installedManifestHash: installedManifest?.bundle_manifest_hash ?? null,
3057
+ coreHash: null,
3058
+ installedCoreHash: null,
3059
+ adapterHash: null,
3060
+ installedAdapterHash: null
3061
+ };
3062
+ let officialHash = null;
3063
+ let officialVersion = null;
3064
+ let bundle;
3065
+ try {
3066
+ bundle = await loadAgentBundle(options.resourcesRoot, requestedProfile, agent);
3067
+ officialHash = sha256Bytes(canonicalJson(bundle.manifest.files));
3068
+ officialVersion = bundle.manifest.bundle_version;
3069
+ identity.bundleVersion = officialVersion;
3070
+ identity.manifestHash = officialHash;
3071
+ const markerBytes = bundle.files.get(BUILD_MARKER_BUNDLE_PATH);
3072
+ identity.coreHash = markerBytes === void 0 ? null : buildMarkerCoreHash(new TextDecoder().decode(markerBytes));
3073
+ } catch {
3074
+ bundle = null;
3075
+ }
3076
+ if (installed.schemaVersion === null || !installed.adapters.includes(agent) || installedProfile === null || installedProfile === void 0 || installedManifest === void 0 || bundle === null) {
3077
+ agents.push(freshnessEntry(agent, installedProfile ?? null, "UNVERIFIABLE", identity));
3078
+ continue;
3079
+ }
3080
+ const targets = managedTargetsFor(getAdapter(agent), bundle, {
3081
+ profile: installedProfile,
3082
+ codebuddySurface: codebuddySurface2
3083
+ });
3084
+ identity.adapterHash = sha256Bytes(canonicalJson(targets.map((target) => ({ path: target.target_path.replace(/\\/g, "/"), sha256: target.sha256 })).sort((a, b) => a.path.localeCompare(b.path))));
3085
+ const installedProjection = await Promise.all(targets.map(async (target) => ({
3086
+ path: target.target_path.replace(/\\/g, "/"),
3087
+ sha256: await fileHex(join6(root, target.target_path))
3088
+ })));
3089
+ identity.installedAdapterHash = sha256Bytes(canonicalJson(installedProjection.sort((a, b) => a.path.localeCompare(b.path))));
3090
+ const markerTarget = targets.find((target) => target.target_path.replace(/\\/g, "/").endsWith(`/${BUILD_MARKER_BUNDLE_PATH}`) || target.target_path === BUILD_MARKER_BUNDLE_PATH);
3091
+ if (markerTarget !== void 0) {
3092
+ identity.installedCoreHash = buildMarkerCoreHash(await readOptionalText(join6(root, markerTarget.target_path)));
3093
+ }
3094
+ if (requestedProfile !== installedProfile) {
3095
+ agents.push(freshnessEntry(agent, installedProfile, "PROFILE_MISMATCH", identity));
3096
+ continue;
3097
+ }
3098
+ if (installedManifest.bundle_manifest_hash !== officialHash || installedManifest.bundle_version !== officialVersion) {
3099
+ agents.push(freshnessEntry(agent, installedProfile, "VERSION_BEHIND", identity));
3100
+ continue;
3101
+ }
3102
+ const drifted = [];
3103
+ const missing = [];
3104
+ for (const target of targets) {
3105
+ const current = await fileHex(join6(root, target.target_path));
3106
+ if (current === null) {
3107
+ missing.push(target.target_path);
3108
+ } else if (current !== target.sha256) {
3109
+ drifted.push(target.target_path);
3110
+ }
3111
+ }
3112
+ if (missing.length > 0) {
3113
+ const entry = freshnessEntry(agent, installedProfile, "MISSING", identity);
3114
+ entry.missingFiles = missing.sort();
3115
+ entry.driftedFiles = drifted.sort();
3116
+ agents.push(entry);
3117
+ continue;
3118
+ }
3119
+ if (drifted.length > 0) {
3120
+ const entry = freshnessEntry(agent, installedProfile, "LOCALLY_MODIFIED", identity);
3121
+ entry.driftedFiles = drifted.sort();
3122
+ agents.push(entry);
3123
+ continue;
3124
+ }
3125
+ agents.push(freshnessEntry(agent, installedProfile, "CURRENT", identity));
3126
+ }
3127
+ return {
3128
+ schema_version: 1,
3129
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
3130
+ agents
3131
+ };
3132
+ }
3006
3133
 
3007
3134
  // ../core/dist/proposal/diff.js
3008
3135
  function operationPath(operation) {
@@ -5554,6 +5681,14 @@ async function runRefresh(options, dependencies) {
5554
5681
  forceManaged: options.forceManaged === true
5555
5682
  });
5556
5683
  const output = summarize(result);
5684
+ const freshness = await collectFreshness({
5685
+ projectRoot: dependencies.cwd,
5686
+ resourcesRoot: dependencies.resourcesRoot,
5687
+ ...targetProfile === void 0 ? {} : { profile: targetProfile },
5688
+ agents: targetAgents,
5689
+ codebuddySurface: codebuddySurface(detection.config, options.codebuddySurface)
5690
+ });
5691
+ output.freshness = freshness.agents;
5557
5692
  if (options.json === true) {
5558
5693
  dependencies.stdout(serializeCliResult({ ...output, request_id: requestId }));
5559
5694
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunter-harness",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "Local-first, server-governed agent harness",
5
5
  "license": "MIT",
6
6
  "type": "module",