@erzhe/harness-kit 0.1.3 → 0.1.4

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/harness-kit.cjs +49 -13
  2. package/package.json +9 -10
@@ -16619,9 +16619,24 @@ function loadManifest(repo) {
16619
16619
  }
16620
16620
  function validateManifest(m) {
16621
16621
  const issues = [];
16622
+ function checkStrArr(arr, prefix) {
16623
+ if (!arr) return;
16624
+ for (let i = 0; i < arr.length; i++) {
16625
+ if (typeof arr[i] !== "string") {
16626
+ issues.push({
16627
+ level: "error",
16628
+ msg: `${prefix}[${i}] \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\uFF08\u5F53\u524D\u662F ${typeof arr[i]}\uFF09`
16629
+ });
16630
+ }
16631
+ }
16632
+ }
16622
16633
  if (!m.spec) issues.push({ level: "error", msg: "\u7F3A\u5C11 spec \u5B57\u6BB5" });
16623
16634
  if (!m.identity?.name) issues.push({ level: "error", msg: "identity.name \u5FC5\u586B" });
16624
16635
  if (!m.identity?.summary) issues.push({ level: "warn", msg: "identity.summary \u5EFA\u8BAE\u586B\u5199" });
16636
+ checkStrArr(m.identity?.scope_in, "identity.scope_in");
16637
+ checkStrArr(m.identity?.scope_out, "identity.scope_out");
16638
+ checkStrArr(m.identity?.upstream, "identity.upstream");
16639
+ checkStrArr(m.identity?.downstream, "identity.downstream");
16625
16640
  for (const [verb, cap] of Object.entries(m.capabilities ?? {})) {
16626
16641
  if (!cap?.run) issues.push({ level: "error", msg: `capabilities.${verb}.run \u5FC5\u586B` });
16627
16642
  }
@@ -16636,11 +16651,23 @@ function validateManifest(m) {
16636
16651
  if (!inv.enforcement && !inv.check && !inv.manual) {
16637
16652
  issues.push({ level: "warn", msg: `invariant ${inv.id} \u65E2\u65E0 enforcement/check \u4E5F\u672A\u6807 manual` });
16638
16653
  }
16654
+ const pfx = `invariant "${inv.id}" \u7684 enforcement`;
16655
+ checkStrArr(inv.enforcement?.forbid_pattern, `${pfx}.forbid_pattern`);
16656
+ checkStrArr(inv.enforcement?.forbid_import, `${pfx}.forbid_import`);
16657
+ checkStrArr(inv.enforcement?.require_pattern, `${pfx}.require_pattern`);
16658
+ checkStrArr(inv.enforcement?.path_glob, `${pfx}.path_glob`);
16659
+ }
16660
+ for (const k of m.knowledge ?? []) {
16661
+ checkStrArr(k.binds, `knowledge "${k.path}" \u7684 binds`);
16639
16662
  }
16640
16663
  const capVerbs = new Set(Object.keys(m.capabilities ?? {}));
16641
16664
  for (const r of m.routing ?? []) {
16642
16665
  if (!r.when) issues.push({ level: "error", msg: "\u5B58\u5728\u7F3A\u5C11 when \u7684 routing \u6761\u76EE" });
16666
+ for (const field of ["read", "entry", "dont_assume", "verify"]) {
16667
+ checkStrArr(r[field], `routing "${r.when}" \u7684 ${field}`);
16668
+ }
16643
16669
  for (const v of r.verify ?? []) {
16670
+ if (typeof v !== "string") continue;
16644
16671
  if (!v.includes(" ") && !capVerbs.has(v))
16645
16672
  issues.push({ level: "warn", msg: `routing "${r.when}" \u7684 verify \u5F15\u7528\u4E86\u672A\u58F0\u660E\u7684 capability: ${v}` });
16646
16673
  }
@@ -16655,11 +16682,20 @@ function validateManifest(m) {
16655
16682
  modSeen.add(mod.name);
16656
16683
  if (!mod.entry?.length)
16657
16684
  issues.push({ level: "warn", msg: `module ${mod.name} \u672A\u58F0\u660E entry\uFF08\u65E0\u6CD5\u505A\u65B0\u9C9C\u5EA6\u7ED1\u5B9A\uFF09` });
16685
+ for (const field of ["entry", "upstream", "downstream", "must_know", "pitfalls"]) {
16686
+ checkStrArr(mod[field], `module "${mod.name}" \u7684 ${field}`);
16687
+ }
16658
16688
  }
16659
16689
  return issues;
16660
16690
  }
16661
16691
 
16662
16692
  // src/render.ts
16693
+ function safeStr(v) {
16694
+ if (typeof v === "string") return v;
16695
+ if (v == null) return "";
16696
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
16697
+ return JSON.stringify(v);
16698
+ }
16663
16699
  function renderTargets(m) {
16664
16700
  const targets = [
16665
16701
  ["AGENTS.md", renderAgentsMd(m)],
@@ -16699,15 +16735,15 @@ function renderAgentsMd(m) {
16699
16735
  L.push("");
16700
16736
  if (id.scope_in?.length || id.scope_out?.length) {
16701
16737
  L.push("## Scope");
16702
- if (id.scope_in?.length) L.push("", "In scope:", ...id.scope_in.map((s) => `- ${s}`));
16738
+ if (id.scope_in?.length) L.push("", "In scope:", ...id.scope_in.map((s) => `- ${safeStr(s)}`));
16703
16739
  if (id.scope_out?.length)
16704
- L.push("", "Out of scope (do NOT modify here):", ...id.scope_out.map((s) => `- ${s}`));
16740
+ L.push("", "Out of scope (do NOT modify here):", ...id.scope_out.map((s) => `- ${safeStr(s)}`));
16705
16741
  L.push("");
16706
16742
  }
16707
16743
  if (id.upstream?.length || id.downstream?.length) {
16708
16744
  L.push("## Dependencies");
16709
- if (id.upstream?.length) L.push("", `Upstream: ${id.upstream.join(", ")}`);
16710
- if (id.downstream?.length) L.push("", `Downstream: ${id.downstream.join(", ")}`);
16745
+ if (id.upstream?.length) L.push("", `Upstream: ${id.upstream.map(safeStr).join(", ")}`);
16746
+ if (id.downstream?.length) L.push("", `Downstream: ${id.downstream.map(safeStr).join(", ")}`);
16711
16747
  L.push("");
16712
16748
  }
16713
16749
  const caps = Object.entries(m.capabilities ?? {});
@@ -16759,10 +16795,10 @@ function renderRoutingMd(m) {
16759
16795
  L.push("Find the change-type that matches your task and follow that row before editing.", "");
16760
16796
  for (const r of m.routing ?? []) {
16761
16797
  L.push(`## ${r.when}`);
16762
- if (r.read?.length) L.push(`- Read first: ${r.read.map((s) => `\`${s}\``).join(", ")}`);
16763
- if (r.entry?.length) L.push(`- Entry points: ${r.entry.map((s) => `\`${s}\``).join(", ")}`);
16764
- if (r.dont_assume?.length) for (const d of r.dont_assume) L.push(`- Do NOT assume: ${d}`);
16765
- if (r.verify?.length) L.push(`- Minimum verification: ${r.verify.map((s) => `\`${s}\``).join(", ")}`);
16798
+ if (r.read?.length) L.push(`- Read first: ${r.read.map((s) => `\`${safeStr(s)}\``).join(", ")}`);
16799
+ if (r.entry?.length) L.push(`- Entry points: ${r.entry.map((s) => `\`${safeStr(s)}\``).join(", ")}`);
16800
+ if (r.dont_assume?.length) for (const d of r.dont_assume) L.push(`- Do NOT assume: ${safeStr(d)}`);
16801
+ if (r.verify?.length) L.push(`- Minimum verification: ${r.verify.map((s) => `\`${safeStr(s)}\``).join(", ")}`);
16766
16802
  L.push("");
16767
16803
  }
16768
16804
  return L.join("\n");
@@ -16773,11 +16809,11 @@ function renderModulesMd(m) {
16773
16809
  L.push("# Module map", "");
16774
16810
  for (const mod of m.modules ?? []) {
16775
16811
  L.push(`## ${mod.name} \u2014 ${mod.role}`);
16776
- if (mod.entry?.length) L.push(`- Entry: ${mod.entry.map((s) => `\`${s}\``).join(", ")}`);
16777
- if (mod.upstream?.length) L.push(`- Upstream: ${mod.upstream.join(", ")}`);
16778
- if (mod.downstream?.length) L.push(`- Downstream: ${mod.downstream.join(", ")}`);
16779
- if (mod.must_know?.length) for (const k of mod.must_know) L.push(`- Must know: ${k}`);
16780
- if (mod.pitfalls?.length) for (const p of mod.pitfalls) L.push(`- Pitfall: ${p}`);
16812
+ if (mod.entry?.length) L.push(`- Entry: ${mod.entry.map((s) => `\`${safeStr(s)}\``).join(", ")}`);
16813
+ if (mod.upstream?.length) L.push(`- Upstream: ${mod.upstream.map(safeStr).join(", ")}`);
16814
+ if (mod.downstream?.length) L.push(`- Downstream: ${mod.downstream.map(safeStr).join(", ")}`);
16815
+ if (mod.must_know?.length) for (const k of mod.must_know) L.push(`- Must know: ${safeStr(k)}`);
16816
+ if (mod.pitfalls?.length) for (const p of mod.pitfalls) L.push(`- Pitfall: ${safeStr(p)}`);
16781
16817
  L.push("");
16782
16818
  }
16783
16819
  return L.join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erzhe/harness-kit",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "AI-friendly repo harness: a manifest.yaml single source of truth that generates AGENTS.md / CLAUDE.md / Cursor rules and enforces invariants, contracts and drift via `harness-kit verify`.",
6
6
  "keywords": [
@@ -39,14 +39,6 @@
39
39
  "access": "public",
40
40
  "registry": "https://registry.npmjs.org/"
41
41
  },
42
- "scripts": {
43
- "harness": "tsx src/cli.ts",
44
- "build": "esbuild src/cli.ts --bundle --platform=node --format=cjs --target=node18 --log-override:empty-import-meta=silent --outfile=dist/harness-kit.cjs && chmod +x dist/harness-kit.cjs",
45
- "typecheck": "tsc --noEmit",
46
- "test": "node --import tsx --test test/*.test.ts",
47
- "test:repos": "bash scripts/regression.sh",
48
- "prepare": "pnpm build"
49
- },
50
42
  "dependencies": {
51
43
  "commander": "^15.0.0",
52
44
  "fast-glob": "^3.3.3",
@@ -57,5 +49,12 @@
57
49
  "esbuild": "^0.28.1",
58
50
  "tsx": "^4.22.5",
59
51
  "typescript": "^6.0.3"
52
+ },
53
+ "scripts": {
54
+ "harness": "tsx src/cli.ts",
55
+ "build": "esbuild src/cli.ts --bundle --platform=node --format=cjs --target=node18 --log-override:empty-import-meta=silent --outfile=dist/harness-kit.cjs && chmod +x dist/harness-kit.cjs",
56
+ "typecheck": "tsc --noEmit",
57
+ "test": "node --import tsx --test test/*.test.ts",
58
+ "test:repos": "bash scripts/regression.sh"
60
59
  }
61
- }
60
+ }