hunter-harness 0.1.0 → 0.1.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.
Files changed (69) hide show
  1. package/dist/bin.js +127 -106
  2. package/package.json +2 -2
  3. package/resources/harness/general/.harness-build.json +1 -1
  4. package/resources/harness/general/harness-archive/SKILL.md +215 -215
  5. package/resources/harness/general/harness-codebase-map/SKILL.md +112 -112
  6. package/resources/harness/general/harness-codebase-map/templates/map-summary.md +3 -3
  7. package/resources/harness/general/harness-knowledge-ingest/SKILL.md +246 -246
  8. package/resources/harness/general/harness-knowledge-query/SKILL.md +164 -164
  9. package/resources/harness/general/harness-plan/SKILL.md +127 -127
  10. package/resources/harness/general/harness-review/SKILL.md +156 -156
  11. package/resources/harness/general/harness-run/SKILL.md +132 -132
  12. package/resources/harness/general/harness-submit/SKILL.md +159 -159
  13. package/resources/harness/general/harness-sync/SKILL.md +82 -82
  14. package/resources/harness/general/harness-test/SKILL.md +180 -180
  15. package/resources/harness/general/scripts/harness_deploy.py +580 -580
  16. package/resources/harness/java/.harness-build.json +1 -1
  17. package/resources/harness/java/harness-apidoc/SKILL.md +86 -86
  18. package/resources/harness/java/harness-archive/SKILL.md +215 -215
  19. package/resources/harness/java/harness-codebase-map/SKILL.md +112 -112
  20. package/resources/harness/java/harness-codebase-map/templates/map-summary.md +3 -3
  21. package/resources/harness/java/harness-knowledge-ingest/SKILL.md +246 -246
  22. package/resources/harness/java/harness-knowledge-query/SKILL.md +164 -164
  23. package/resources/harness/java/harness-package/SKILL.md +87 -87
  24. package/resources/harness/java/harness-plan/SKILL.md +127 -127
  25. package/resources/harness/java/harness-review/SKILL.md +156 -156
  26. package/resources/harness/java/harness-run/SKILL.md +148 -148
  27. package/resources/harness/java/harness-submit/SKILL.md +166 -166
  28. package/resources/harness/java/harness-sync/SKILL.md +82 -82
  29. package/resources/harness/java/harness-test/SKILL.md +192 -192
  30. package/resources/harness/java/scripts/harness_deploy.py +580 -580
  31. package/resources/harness/manifests/general.json +13 -33
  32. package/resources/harness/manifests/java.json +15 -35
  33. package/resources/bootstrap-ir/manifest.json +0 -19
  34. package/resources/bootstrap-ir/skills/harness-apidoc.yaml +0 -24
  35. package/resources/bootstrap-ir/skills/harness-archive.yaml +0 -24
  36. package/resources/bootstrap-ir/skills/harness-codebase-map.yaml +0 -24
  37. package/resources/bootstrap-ir/skills/harness-knowledge-ingest.yaml +0 -24
  38. package/resources/bootstrap-ir/skills/harness-package.yaml +0 -24
  39. package/resources/bootstrap-ir/skills/harness-plan.yaml +0 -24
  40. package/resources/bootstrap-ir/skills/harness-review.yaml +0 -24
  41. package/resources/bootstrap-ir/skills/harness-run.yaml +0 -24
  42. package/resources/bootstrap-ir/skills/harness-skill-optimizer.yaml +0 -28
  43. package/resources/bootstrap-ir/skills/harness-submit.yaml +0 -24
  44. package/resources/bootstrap-ir/skills/harness-sync.yaml +0 -24
  45. package/resources/bootstrap-ir/skills/harness-test.yaml +0 -24
  46. package/resources/bootstrap-ir/templates/claude-code-skill.md +0 -12
  47. package/resources/harness/general/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/archive/2026-01-10-ledger-reconciliation/reports/final/summary-data.json +0 -43
  48. package/resources/harness/general/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/archive/2026-02-14-ledger-snapshot-followup/reports/final/summary-data.json +0 -38
  49. package/resources/harness/general/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/archive/2026-03-05-webhook-contract/reports/final/summary-data.json +0 -36
  50. package/resources/harness/general/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/project.yaml +0 -1
  51. package/resources/harness/general/harness-knowledge-ingest/tests/test_harness_knowledge.py +0 -1792
  52. package/resources/harness/java/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/archive/2026-01-10-ledger-reconciliation/reports/final/summary-data.json +0 -43
  53. package/resources/harness/java/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/archive/2026-02-14-ledger-snapshot-followup/reports/final/summary-data.json +0 -38
  54. package/resources/harness/java/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/archive/2026-03-05-webhook-contract/reports/final/summary-data.json +0 -36
  55. package/resources/harness/java/harness-knowledge-ingest/tests/fixtures/mcp-eval-project/.harness/project.yaml +0 -1
  56. package/resources/harness/java/harness-knowledge-ingest/tests/test_harness_knowledge.py +0 -1792
  57. package/resources/manifest.json +0 -19
  58. package/resources/skills/harness-apidoc/SKILL.md +0 -50
  59. package/resources/skills/harness-archive/SKILL.md +0 -48
  60. package/resources/skills/harness-codebase-map/SKILL.md +0 -53
  61. package/resources/skills/harness-knowledge-ingest/SKILL.md +0 -48
  62. package/resources/skills/harness-package/SKILL.md +0 -48
  63. package/resources/skills/harness-plan/SKILL.md +0 -51
  64. package/resources/skills/harness-review/SKILL.md +0 -50
  65. package/resources/skills/harness-run/SKILL.md +0 -48
  66. package/resources/skills/harness-skill-optimizer/SKILL.md +0 -54
  67. package/resources/skills/harness-submit/SKILL.md +0 -47
  68. package/resources/skills/harness-sync/SKILL.md +0 -48
  69. package/resources/skills/harness-test/SKILL.md +0 -50
package/dist/bin.js CHANGED
@@ -1244,9 +1244,8 @@ function decideUpdate(policy, dirty) {
1244
1244
  }
1245
1245
 
1246
1246
  // ../core/dist/project/initialize.js
1247
- import { createHash as createHash2 } from "node:crypto";
1248
- import { readFile as readFile3, stat as stat2 } from "node:fs/promises";
1249
- import { basename, join as join4, resolve as resolve3 } from "node:path";
1247
+ import { readFile as readFile4, stat as stat2 } from "node:fs/promises";
1248
+ import { basename, join as join5, resolve as resolve3 } from "node:path";
1250
1249
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
1251
1250
 
1252
1251
  // ../core/dist/transaction/transaction.js
@@ -1473,6 +1472,61 @@ async function runTransaction(projectRoot, rawOperations, options = {}) {
1473
1472
  }
1474
1473
  }
1475
1474
 
1475
+ // ../core/dist/project/profile-bundle.js
1476
+ import { createHash as createHash2 } from "node:crypto";
1477
+ import { readFile as readFile3 } from "node:fs/promises";
1478
+ import { join as join4 } from "node:path";
1479
+ function isProfile(value) {
1480
+ return value === "general" || value === "java";
1481
+ }
1482
+ function validateRelativeBundlePath(path) {
1483
+ if (typeof path !== "string" || path.length === 0 || path.includes("\0") || path.startsWith("/") || path.startsWith("\\") || /^[A-Za-z]:/.test(path) || path.split("/").some((part) => part === "" || part === "." || part === "..")) {
1484
+ throw new Error("invalid Harness Bundle path");
1485
+ }
1486
+ }
1487
+ async function loadProfileBundle(resourcesRoot, profile) {
1488
+ const raw = JSON.parse(await readFile3(join4(resourcesRoot, "harness", "manifests", `${profile}.json`), "utf8"));
1489
+ if (raw.schema_version !== 1 || raw.profile !== profile || typeof raw.bundle_version !== "string" || raw.generator !== "harness_deploy.py" || !Array.isArray(raw.files)) {
1490
+ throw new Error(`invalid ${profile} Harness Bundle manifest`);
1491
+ }
1492
+ const files = /* @__PURE__ */ new Map();
1493
+ for (const item of raw.files) {
1494
+ validateRelativeBundlePath(item.path);
1495
+ if (typeof item.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(item.sha256) || files.has(item.path)) {
1496
+ throw new Error(`invalid ${profile} Harness Bundle manifest entry`);
1497
+ }
1498
+ const bytes = await readFile3(join4(resourcesRoot, "harness", profile, item.path));
1499
+ if (createHash2("sha256").update(bytes).digest("hex") !== item.sha256) {
1500
+ throw new Error(`Harness Bundle hash mismatch: ${item.path}`);
1501
+ }
1502
+ files.set(item.path, bytes);
1503
+ }
1504
+ return { manifest: raw, files };
1505
+ }
1506
+ async function managedBundleTargets(resourcesRoot, profile) {
1507
+ const bundle = await loadProfileBundle(resourcesRoot, profile);
1508
+ const targets = new Set(bundleTargetContents(bundle).keys());
1509
+ if (profile === "java")
1510
+ targets.add(".claude/rules/harness-profile-java.md");
1511
+ return targets;
1512
+ }
1513
+ function bundleTargetContents(bundle) {
1514
+ const contents = /* @__PURE__ */ new Map();
1515
+ for (const [path, bytes] of bundle.files) {
1516
+ const skillTarget = `.claude/skills/${path}`;
1517
+ contents.set(skillTarget, bytes);
1518
+ const agent = /^agents\/([^/]+\.md)$/.exec(path);
1519
+ if (agent?.[1] !== void 0) {
1520
+ const agentTarget = `.claude/agents/${agent[1]}`;
1521
+ contents.set(agentTarget, bytes);
1522
+ }
1523
+ }
1524
+ return contents;
1525
+ }
1526
+ function parseHarnessProfile(value) {
1527
+ return isProfile(value) ? value : null;
1528
+ }
1529
+
1476
1530
  // ../core/dist/project/uuid-v7.js
1477
1531
  import { randomBytes } from "node:crypto";
1478
1532
  function uuidV7(now = Date.now()) {
@@ -1498,7 +1552,7 @@ function uuidV7(now = Date.now()) {
1498
1552
  // ../core/dist/project/initialize.js
1499
1553
  async function readOptional(path) {
1500
1554
  try {
1501
- return await readFile3(path, "utf8");
1555
+ return await readFile4(path, "utf8");
1502
1556
  } catch (error) {
1503
1557
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1504
1558
  return "";
@@ -1518,53 +1572,32 @@ async function exists2(path) {
1518
1572
  }
1519
1573
  }
1520
1574
  async function existingProjectConfig(root) {
1521
- const path = join4(root, ".harness", "project.yaml");
1575
+ const path = join5(root, ".harness", "project.yaml");
1522
1576
  const content = await readOptional(path);
1523
1577
  return content === "" ? null : projectConfigSchema.parse(parseYaml2(content));
1524
1578
  }
1525
1579
  async function operationFor(root, path, content) {
1526
- return await exists2(join4(root, path)) ? { operation: "modify", path, content } : { operation: "add", path, content };
1527
- }
1528
- async function readProfileBundle(resourcesRoot, profile) {
1529
- const manifest = JSON.parse(await readFile3(join4(resourcesRoot, "harness", "manifests", `${profile}.json`), "utf8"));
1530
- if (manifest.schema_version !== 1 || manifest.profile !== profile) {
1531
- throw new Error(`invalid ${profile} Harness Bundle manifest`);
1532
- }
1533
- const files = /* @__PURE__ */ new Map();
1534
- for (const item of manifest.files) {
1535
- const bytes = await readFile3(join4(resourcesRoot, "harness", profile, item.path));
1536
- if (createHash2("sha256").update(bytes).digest("hex") !== item.sha256) {
1537
- throw new Error(`Harness Bundle hash mismatch: ${item.path}`);
1538
- }
1539
- files.set(item.path, bytes);
1540
- }
1541
- return { manifest, files };
1542
- }
1543
- function bundleTargets(bundleFiles) {
1544
- const targets = /* @__PURE__ */ new Map();
1545
- for (const [path, bytes] of bundleFiles) {
1546
- targets.set(`.claude/skills/${path}`, bytes);
1547
- const agent = /^agents\/([^/]+\.md)$/.exec(path);
1548
- if (agent?.[1] !== void 0)
1549
- targets.set(`.claude/agents/${agent[1]}`, bytes);
1550
- }
1551
- return targets;
1580
+ return await exists2(join5(root, path)) ? { operation: "modify", path, content } : { operation: "add", path, content };
1552
1581
  }
1553
1582
  var INSTALLED_BUNDLE_PATH = ".harness/state/local/installed-harness-bundle.json";
1554
- async function previousInstalledFiles(root) {
1555
- const content = await readOptional(join4(root, INSTALLED_BUNDLE_PATH));
1583
+ async function previousInstalledProfile(root) {
1584
+ const content = await readOptional(join5(root, INSTALLED_BUNDLE_PATH));
1556
1585
  if (content === "")
1557
- return [];
1558
- const parsed = JSON.parse(content);
1559
- return parsed.schema_version === 1 && Array.isArray(parsed.files) ? parsed.files : [];
1586
+ return null;
1587
+ try {
1588
+ const parsed = JSON.parse(content);
1589
+ return parsed.schema_version === 1 ? parseHarnessProfile(parsed.profile) : null;
1590
+ } catch {
1591
+ return null;
1592
+ }
1560
1593
  }
1561
1594
  async function initializeProject(options) {
1562
1595
  const root = resolve3(options.projectRoot);
1563
1596
  const config = initConfigSchema.parse(options.config);
1564
1597
  const existing = await existingProjectConfig(root);
1565
1598
  const profile = config.profile;
1566
- const bundle = await readProfileBundle(options.resourcesRoot, profile);
1567
- const bundleFiles = bundleTargets(bundle.files);
1599
+ const bundle = await loadProfileBundle(options.resourcesRoot, profile);
1600
+ const bundleFiles = bundleTargetContents(bundle);
1568
1601
  const bundleHash = sha256Bytes(canonicalJson(bundle.manifest.files));
1569
1602
  const projectConfig = projectConfigSchema.parse({
1570
1603
  harness: { name: "hunter-harness", schema_version: 1 },
@@ -1588,14 +1621,14 @@ async function initializeProject(options) {
1588
1621
  artifact_manifest_hash: null,
1589
1622
  files: {}
1590
1623
  });
1591
- const agentsContent = upsertManagedBlock(await readOptional(join4(root, "AGENTS.md")), [
1624
+ const agentsContent = upsertManagedBlock(await readOptional(join5(root, "AGENTS.md")), [
1592
1625
  "# Hunter Harness",
1593
1626
  "",
1594
1627
  "Use .harness/context-index.json to route rules, Knowledge, and codebase maps.",
1595
1628
  "Treat .claude/skills/harness-* as editable adapter working copies.",
1596
1629
  "Do not modify .harness/state or .harness/cache directly."
1597
1630
  ].join("\n"));
1598
- const claudeContent = upsertManagedBlock(await readOptional(join4(root, "CLAUDE.md")), [
1631
+ const claudeContent = upsertManagedBlock(await readOptional(join5(root, "CLAUDE.md")), [
1599
1632
  "@AGENTS.md",
1600
1633
  "",
1601
1634
  "# Hunter Harness",
@@ -1664,8 +1697,10 @@ async function initializeProject(options) {
1664
1697
  files: installedFileList.sort()
1665
1698
  };
1666
1699
  files.set(INSTALLED_BUNDLE_PATH, JSON.stringify(installedManifest, null, 2) + "\n");
1667
- const newManaged = new Set(installedManifest.files);
1668
- const deleteOperations = (await previousInstalledFiles(root)).filter((path) => !newManaged.has(path)).map((path) => ({ operation: "delete", path }));
1700
+ const newManaged = await managedBundleTargets(options.resourcesRoot, profile);
1701
+ const oldProfile = await previousInstalledProfile(root);
1702
+ const oldManaged = oldProfile === null ? /* @__PURE__ */ new Set() : await managedBundleTargets(options.resourcesRoot, oldProfile);
1703
+ const deleteOperations = [...oldManaged].filter((path) => !newManaged.has(path)).map((path) => ({ operation: "delete", path }));
1669
1704
  const paths = [...files.keys()].sort((left, right) => left.localeCompare(right));
1670
1705
  if (!options.dryRun) {
1671
1706
  const writeOperations = await Promise.all([...files.entries()].map(async ([path, content]) => operationFor(root, path, content)));
@@ -1977,28 +2012,28 @@ function generateProposalPreview(input, scanOptions = {}) {
1977
2012
  }
1978
2013
 
1979
2014
  // ../core/dist/push/push.js
1980
- import { lstat as lstat2, readFile as readFile6, readdir, rm as rm4 } from "node:fs/promises";
1981
- import { join as join7, relative, resolve as resolve4 } from "node:path";
2015
+ import { lstat as lstat2, readFile as readFile7, readdir, rm as rm4 } from "node:fs/promises";
2016
+ import { join as join8, relative, resolve as resolve4 } from "node:path";
1982
2017
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
1983
2018
 
1984
2019
  // ../core/dist/state/baseline.js
1985
- import { readFile as readFile4 } from "node:fs/promises";
1986
- import { join as join5 } from "node:path";
2020
+ import { readFile as readFile5 } from "node:fs/promises";
2021
+ import { join as join6 } from "node:path";
1987
2022
  async function readBaseline(projectRoot) {
1988
- const content = await readFile4(join5(stateLayout(projectRoot).baseline, "manifest.json"), "utf8");
2023
+ const content = await readFile5(join6(stateLayout(projectRoot).baseline, "manifest.json"), "utf8");
1989
2024
  return baselineManifestSchema.parse(JSON.parse(content));
1990
2025
  }
1991
2026
 
1992
2027
  // ../core/dist/state/locks.js
1993
2028
  import { randomUUID as randomUUID3 } from "node:crypto";
1994
- import { readFile as readFile5, rename as rename3, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
1995
- import { join as join6 } from "node:path";
2029
+ import { readFile as readFile6, rename as rename3, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
2030
+ import { join as join7 } from "node:path";
1996
2031
  async function readLock(path) {
1997
- return JSON.parse(await readFile5(path, "utf8"));
2032
+ return JSON.parse(await readFile6(path, "utf8"));
1998
2033
  }
1999
2034
  async function acquireProtocolLock(projectRoot, operation, options = {}) {
2000
2035
  const layout = await ensureStateLayout(projectRoot);
2001
- const lockPath = join6(layout.locks, "protocol.lock");
2036
+ const lockPath = join7(layout.locks, "protocol.lock");
2002
2037
  const now = options.now ?? Date.now();
2003
2038
  const staleAfterMs = options.staleAfterMs ?? 15 * 60 * 1e3;
2004
2039
  const record = {
@@ -2061,22 +2096,6 @@ var MANAGED_FILES = [
2061
2096
  ".harness/project.yaml",
2062
2097
  ".harness/context-index.json"
2063
2098
  ];
2064
- var INSTALLED_BUNDLE_PATH2 = ".harness/state/local/installed-harness-bundle.json";
2065
- async function installedBundlePaths(root) {
2066
- try {
2067
- const content = await readFile6(join7(root, INSTALLED_BUNDLE_PATH2), "utf8");
2068
- const parsed = JSON.parse(content);
2069
- if (parsed.schema_version === 1 && Array.isArray(parsed.files)) {
2070
- return new Set(parsed.files);
2071
- }
2072
- } catch (error) {
2073
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2074
- return /* @__PURE__ */ new Set();
2075
- }
2076
- throw error;
2077
- }
2078
- return /* @__PURE__ */ new Set();
2079
- }
2080
2099
  async function exists3(path) {
2081
2100
  try {
2082
2101
  await lstat2(path);
@@ -2093,7 +2112,7 @@ async function walkFiles(root, current, output) {
2093
2112
  return;
2094
2113
  }
2095
2114
  for (const item of await readdir(current, { withFileTypes: true })) {
2096
- const path = join7(current, item.name);
2115
+ const path = join8(current, item.name);
2097
2116
  if (item.isSymbolicLink()) {
2098
2117
  throw new PushWorkflowError("symlink is not pushable", 6, "UNSAFE_SYMLINK");
2099
2118
  }
@@ -2108,24 +2127,24 @@ async function managedFiles(projectRoot) {
2108
2127
  const root = resolve4(projectRoot);
2109
2128
  const paths = [];
2110
2129
  for (const path of MANAGED_FILES) {
2111
- if (await exists3(join7(root, path))) {
2130
+ if (await exists3(join8(root, path))) {
2112
2131
  paths.push(path);
2113
2132
  }
2114
2133
  }
2115
2134
  for (const path of MANAGED_ROOTS) {
2116
- await walkFiles(root, join7(root, path), paths);
2135
+ await walkFiles(root, join8(root, path), paths);
2117
2136
  }
2118
- const skillsRoot = join7(root, ".claude", "skills");
2137
+ const skillsRoot = join8(root, ".claude", "skills");
2119
2138
  if (await exists3(skillsRoot)) {
2120
2139
  for (const item of await readdir(skillsRoot, { withFileTypes: true })) {
2121
2140
  if (item.isDirectory() && item.name.startsWith("harness-")) {
2122
- await walkFiles(root, join7(skillsRoot, item.name), paths);
2141
+ await walkFiles(root, join8(skillsRoot, item.name), paths);
2123
2142
  }
2124
2143
  }
2125
2144
  }
2126
2145
  const result = {};
2127
2146
  for (const path of [...new Set(paths)].sort()) {
2128
- result[path] = await readFile6(join7(root, path), "utf8");
2147
+ result[path] = await readFile7(join8(root, path), "utf8");
2129
2148
  }
2130
2149
  return result;
2131
2150
  }
@@ -2134,14 +2153,14 @@ function proposalBaseline(baseline) {
2134
2153
  }
2135
2154
  async function readProject(root) {
2136
2155
  try {
2137
- return projectConfigSchema.parse(parseYaml3(await readFile6(join7(root, ".harness", "project.yaml"), "utf8")));
2156
+ return projectConfigSchema.parse(parseYaml3(await readFile7(join8(root, ".harness", "project.yaml"), "utf8")));
2138
2157
  } catch {
2139
2158
  throw new PushWorkflowError("project configuration is missing or invalid", 3, "PROJECT_CONFIG_INVALID");
2140
2159
  }
2141
2160
  }
2142
2161
  async function readOptionalJson(path) {
2143
2162
  try {
2144
- return JSON.parse(await readFile6(path, "utf8"));
2163
+ return JSON.parse(await readFile7(path, "utf8"));
2145
2164
  } catch (error) {
2146
2165
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2147
2166
  return null;
@@ -2153,7 +2172,7 @@ async function clientIdFor(root, explicit) {
2153
2172
  if (explicit !== void 0) {
2154
2173
  return explicit;
2155
2174
  }
2156
- const path = join7(root, ".harness", "state", "local", "client.json");
2175
+ const path = join8(root, ".harness", "state", "local", "client.json");
2157
2176
  const existing = await readOptionalJson(path);
2158
2177
  if (typeof existing?.client_id === "string" && /^cli_[A-Za-z0-9_-]+$/.test(existing.client_id)) {
2159
2178
  return existing.client_id;
@@ -2239,7 +2258,8 @@ async function pushProject(options) {
2239
2258
  const root = resolve4(options.projectRoot);
2240
2259
  let project = await readProject(root);
2241
2260
  let baseline = await readBaseline(root);
2242
- const installedPaths = await installedBundlePaths(root);
2261
+ const profile = parseHarnessProfile(project.project.profiles[0]);
2262
+ const installedPaths = profile === null ? /* @__PURE__ */ new Set() : await managedBundleTargets(options.resourcesRoot, profile);
2243
2263
  let preview = makePreview(baseline, await managedFiles(root), options.confirmedProjectLocal ?? [], installedPaths);
2244
2264
  if (preview.blocked) {
2245
2265
  throw new PushWorkflowError("sensitive information scan blocked the proposal", 6, "SENSITIVE_CONTENT_BLOCKED");
@@ -2271,7 +2291,7 @@ async function pushProject(options) {
2271
2291
  if (parsedServerUrl.protocol !== "https:") {
2272
2292
  throw new PushWorkflowError("server_url must use HTTPS", 3, "SERVER_URL_INVALID");
2273
2293
  }
2274
- const workflowPath = join7(root, ".harness", "state", "local", "push-workflow.json");
2294
+ const workflowPath = join8(root, ".harness", "state", "local", "push-workflow.json");
2275
2295
  const priorWorkflow = await readOptionalJson(workflowPath);
2276
2296
  const provisionalRequestId = priorWorkflow?.local_project_key === project.project.local_project_key ? priorWorkflow.request_id : uuidV7();
2277
2297
  const lock = await acquireProtocolLock(root, "push", { requestId: provisionalRequestId });
@@ -2362,7 +2382,7 @@ async function pushProject(options) {
2362
2382
  }
2363
2383
  }
2364
2384
  const finalized = await client.finalizeProposal(session.session_id, { schema_version: 1, manifest_sha256: proposalManifestHash }, requestId, workflow.finalize_idempotency_key);
2365
- await atomicWriteJson(join7(root, ".harness", "state", "local", "push-results", finalized.proposal_id + ".json"), {
2385
+ await atomicWriteJson(join8(root, ".harness", "state", "local", "push-results", finalized.proposal_id + ".json"), {
2366
2386
  schema_version: 1,
2367
2387
  request_id: requestId,
2368
2388
  project_id: projectId,
@@ -2431,10 +2451,10 @@ var AGENT_DESCRIPTORS = {
2431
2451
  var INSTALLABLE_AGENTS = Object.keys(AGENT_DESCRIPTORS).filter((agent) => AGENT_DESCRIPTORS[agent]?.installable === true);
2432
2452
 
2433
2453
  // ../core/dist/transaction/recovery.js
2434
- import { readFile as readFile7, readdir as readdir2, rm as rm5, stat as stat3 } from "node:fs/promises";
2435
- import { join as join8 } from "node:path";
2454
+ import { readFile as readFile8, readdir as readdir2, rm as rm5, stat as stat3 } from "node:fs/promises";
2455
+ import { join as join9 } from "node:path";
2436
2456
  async function recoverTransaction(projectRoot, transactionId) {
2437
- const journal = JSON.parse(await readFile7(join8(stateLayout(projectRoot).transactions, transactionId, "journal.json"), "utf8"));
2457
+ const journal = JSON.parse(await readFile8(join9(stateLayout(projectRoot).transactions, transactionId, "journal.json"), "utf8"));
2438
2458
  if (journal.state === "committed") {
2439
2459
  return { transactionId, status: "committed" };
2440
2460
  }
@@ -2461,7 +2481,7 @@ async function listTransactions(projectRoot) {
2461
2481
  const transactions = [];
2462
2482
  for (const transactionId of names) {
2463
2483
  try {
2464
- const journal = JSON.parse(await readFile7(join8(root, transactionId, "journal.json"), "utf8"));
2484
+ const journal = JSON.parse(await readFile8(join9(root, transactionId, "journal.json"), "utf8"));
2465
2485
  transactions.push({
2466
2486
  transactionId,
2467
2487
  kind: journal.kind,
@@ -2492,11 +2512,11 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
2492
2512
  if (latest === void 0) {
2493
2513
  throw new Error("no committed update transaction is available for rollback");
2494
2514
  }
2495
- const transactionRoot = join8(stateLayout(projectRoot).transactions, latest.transactionId);
2496
- const journal = JSON.parse(await readFile7(join8(transactionRoot, "journal.json"), "utf8"));
2497
- const after = JSON.parse(await readFile7(join8(transactionRoot, "after", "manifest.json"), "utf8"));
2515
+ const transactionRoot = join9(stateLayout(projectRoot).transactions, latest.transactionId);
2516
+ const journal = JSON.parse(await readFile8(join9(transactionRoot, "journal.json"), "utf8"));
2517
+ const after = JSON.parse(await readFile8(join9(transactionRoot, "after", "manifest.json"), "utf8"));
2498
2518
  for (const entry of after) {
2499
- const target = join8(projectRoot, entry.path);
2519
+ const target = join9(projectRoot, entry.path);
2500
2520
  const exists5 = await pathExists(target);
2501
2521
  if (exists5 !== entry.exists || exists5 && await sha256File(target) !== entry.hash) {
2502
2522
  throw new Error("cannot rollback dirty path: " + entry.path);
@@ -2509,10 +2529,10 @@ async function rollbackLatestCommittedUpdate(projectRoot) {
2509
2529
  continue;
2510
2530
  }
2511
2531
  seen.add(snapshot.path);
2512
- const target = join8(projectRoot, snapshot.path);
2532
+ const target = join9(projectRoot, snapshot.path);
2513
2533
  const exists5 = await pathExists(target);
2514
2534
  if (snapshot.existed && snapshot.snapshot_name !== null) {
2515
- const content = await readFile7(join8(transactionRoot, "before", snapshot.snapshot_name));
2535
+ const content = await readFile8(join9(transactionRoot, "before", snapshot.snapshot_name));
2516
2536
  operations.push({
2517
2537
  operation: exists5 ? "modify" : "add",
2518
2538
  path: snapshot.path,
@@ -2541,7 +2561,7 @@ async function cleanupOldTransactions(projectRoot, now = /* @__PURE__ */ new Dat
2541
2561
  if (!removable) {
2542
2562
  continue;
2543
2563
  }
2544
- await rm5(join8(stateLayout(projectRoot).transactions, item.transactionId), {
2564
+ await rm5(join9(stateLayout(projectRoot).transactions, item.transactionId), {
2545
2565
  recursive: true,
2546
2566
  force: true
2547
2567
  });
@@ -2573,8 +2593,8 @@ function managedBlockDirty(currentContent, managedBlockHash) {
2573
2593
  }
2574
2594
 
2575
2595
  // ../core/dist/update/update.js
2576
- import { lstat as lstat3, readFile as readFile8, rm as rm6 } from "node:fs/promises";
2577
- import { join as join9, resolve as resolve5 } from "node:path";
2596
+ import { lstat as lstat3, readFile as readFile9, rm as rm6 } from "node:fs/promises";
2597
+ import { join as join10, resolve as resolve5 } from "node:path";
2578
2598
  import { parse as parseYaml6 } from "yaml";
2579
2599
  var UpdateWorkflowError = class extends Error {
2580
2600
  exitCode;
@@ -2598,7 +2618,7 @@ async function pathExists2(path) {
2598
2618
  }
2599
2619
  }
2600
2620
  async function optionalContent(path) {
2601
- return await pathExists2(path) ? readFile8(path, "utf8") : null;
2621
+ return await pathExists2(path) ? readFile9(path, "utf8") : null;
2602
2622
  }
2603
2623
  function manifestPayloadHash(manifest) {
2604
2624
  const payload = { ...manifest };
@@ -2616,10 +2636,10 @@ async function loadBlob(root, client, artifactId, operation, requestId, dryRun)
2616
2636
  return null;
2617
2637
  }
2618
2638
  const hash = operation.content_sha256;
2619
- const cacheRoot = join9(root, ".harness", "cache", "server-artifacts", artifactId);
2620
- const cachePath = join9(cacheRoot, "blobs", hash.replace(":", "_"));
2639
+ const cacheRoot = join10(root, ".harness", "cache", "server-artifacts", artifactId);
2640
+ const cachePath = join10(cacheRoot, "blobs", hash.replace(":", "_"));
2621
2641
  if (await pathExists2(cachePath) && await sha256File(cachePath) === hash) {
2622
- return readFile8(cachePath, "utf8");
2642
+ return readFile9(cachePath, "utf8");
2623
2643
  }
2624
2644
  const bytes = await client.downloadArtifactBlob(artifactId, hash, requestId);
2625
2645
  if (bytes.byteLength !== operation.size_bytes || sha256Bytes(bytes) !== hash) {
@@ -2669,7 +2689,7 @@ async function updateProject(options) {
2669
2689
  const root = resolve5(options.projectRoot);
2670
2690
  let project;
2671
2691
  try {
2672
- project = projectConfigSchema.parse(parseYaml6(await readFile8(join9(root, ".harness", "project.yaml"), "utf8")));
2692
+ project = projectConfigSchema.parse(parseYaml6(await readFile9(join10(root, ".harness", "project.yaml"), "utf8")));
2673
2693
  } catch {
2674
2694
  throw new UpdateWorkflowError("project configuration is invalid", 3, "PROJECT_CONFIG_INVALID");
2675
2695
  }
@@ -2754,8 +2774,8 @@ async function updateProject(options) {
2754
2774
  skipped.push({ path: target, operation: operation.operation, reason: "baseline-diverged" });
2755
2775
  continue;
2756
2776
  }
2757
- const sourceContent = await optionalContent(join9(root, source));
2758
- const targetContent = target === source ? sourceContent : await optionalContent(join9(root, target));
2777
+ const sourceContent = await optionalContent(join10(root, source));
2778
+ const targetContent = target === source ? sourceContent : await optionalContent(join10(root, target));
2759
2779
  const incoming = blobs.get(operation) ?? null;
2760
2780
  const incomingHash = contentHash(operation);
2761
2781
  let equivalent = incomingHash !== null && targetContent !== null && sha256Bytes(targetContent) === incomingHash;
@@ -2814,7 +2834,7 @@ async function updateProject(options) {
2814
2834
  dryRun: true
2815
2835
  };
2816
2836
  }
2817
- await atomicWriteJson(join9(root, ".harness", "cache", "server-artifacts", manifest.artifact_id, "manifest.json"), manifest);
2837
+ await atomicWriteJson(join10(root, ".harness", "cache", "server-artifacts", manifest.artifact_id, "manifest.json"), manifest);
2818
2838
  const lock = await acquireProtocolLock(root, "update", { requestId });
2819
2839
  try {
2820
2840
  const nextBaseline = baselineManifestSchema.parse(structuredClone(baseline));
@@ -2895,8 +2915,8 @@ async function updateProject(options) {
2895
2915
  }
2896
2916
 
2897
2917
  // src/config/init-config.ts
2898
- import { isAbsolute as isAbsolute2, join as join10 } from "node:path";
2899
- import { readFile as readFile9 } from "node:fs/promises";
2918
+ import { isAbsolute as isAbsolute2, join as join11 } from "node:path";
2919
+ import { readFile as readFile10 } from "node:fs/promises";
2900
2920
  var InitConfigurationError = class extends Error {
2901
2921
  exitCode;
2902
2922
  constructor(message, exitCode = 3, options) {
@@ -2914,9 +2934,9 @@ function normalizeProfile(value) {
2914
2934
  async function resolveInitConfig(cwd, flags, promptMissing) {
2915
2935
  let fileConfig = {};
2916
2936
  if (flags.config !== void 0) {
2917
- const path = isAbsolute2(flags.config) ? flags.config : join10(cwd, flags.config);
2937
+ const path = isAbsolute2(flags.config) ? flags.config : join11(cwd, flags.config);
2918
2938
  try {
2919
- fileConfig = JSON.parse(await readFile9(path, "utf8"));
2939
+ fileConfig = JSON.parse(await readFile10(path, "utf8"));
2920
2940
  } catch (error) {
2921
2941
  throw new InitConfigurationError(
2922
2942
  "unable to read init config: " + (error instanceof Error ? error.message : String(error)),
@@ -3027,6 +3047,7 @@ async function runPush(options, dependencies) {
3027
3047
  try {
3028
3048
  const result = await pushProject({
3029
3049
  projectRoot: dependencies.cwd,
3050
+ resourcesRoot: dependencies.resourcesRoot,
3030
3051
  ...options.serverUrl === void 0 ? {} : { serverUrl: options.serverUrl },
3031
3052
  ...options.tokenEnv === void 0 ? {} : { tokenEnv: options.tokenEnv },
3032
3053
  env: dependencies.env,
@@ -3188,7 +3209,7 @@ async function runUpdate(options, dependencies) {
3188
3209
 
3189
3210
  // src/commands/recovery.ts
3190
3211
  import { stat as stat4 } from "node:fs/promises";
3191
- import { join as join11 } from "node:path";
3212
+ import { join as join12 } from "node:path";
3192
3213
  async function exists4(path) {
3193
3214
  try {
3194
3215
  await stat4(path);
@@ -3248,7 +3269,7 @@ async function runRecoveryMenuIfApplicable(options, dependencies) {
3248
3269
  return 5;
3249
3270
  }
3250
3271
  }
3251
- const initialized = await exists4(join11(dependencies.cwd, ".harness", "project.yaml"));
3272
+ const initialized = await exists4(join12(dependencies.cwd, ".harness", "project.yaml"));
3252
3273
  if (!initialized || options.nonInteractive === true || explicitConfigure(options)) {
3253
3274
  return null;
3254
3275
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunter-harness",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Local-first, server-governed agent harness",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  "node": ">=24.0.0"
9
9
  },
10
10
  "bin": {
11
- "hunter-harness": "./dist/bin.js"
11
+ "hunter-harness": "dist/bin.js"
12
12
  },
13
13
  "files": [
14
14
  "dist/bin.js",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
- "coreHash": "63af4c482d21b6be"
4
+ "coreHash": "76dd32302df53f0b"
5
5
  }