@esbenwiberg/corpus-default 1.1.0 → 1.2.0

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 (68) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +24 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/probes/_shared/guidance-content.d.ts +9 -0
  5. package/dist/probes/_shared/guidance-content.d.ts.map +1 -0
  6. package/dist/probes/_shared/guidance-content.js +140 -0
  7. package/dist/probes/_shared/guidance-content.js.map +1 -0
  8. package/dist/probes/agent-guidance-aligned.d.ts +3 -0
  9. package/dist/probes/agent-guidance-aligned.d.ts.map +1 -0
  10. package/dist/probes/agent-guidance-aligned.js +130 -0
  11. package/dist/probes/agent-guidance-aligned.js.map +1 -0
  12. package/dist/probes/agent-guidance-fresh.d.ts.map +1 -1
  13. package/dist/probes/agent-guidance-fresh.js +3 -13
  14. package/dist/probes/agent-guidance-fresh.js.map +1 -1
  15. package/dist/probes/agent-guidance-quality.d.ts.map +1 -1
  16. package/dist/probes/agent-guidance-quality.js +3 -13
  17. package/dist/probes/agent-guidance-quality.js.map +1 -1
  18. package/dist/probes/agent-guidance-substance.d.ts.map +1 -1
  19. package/dist/probes/agent-guidance-substance.js +20 -3
  20. package/dist/probes/agent-guidance-substance.js.map +1 -1
  21. package/dist/probes/agent-skills-present.d.ts +3 -0
  22. package/dist/probes/agent-skills-present.d.ts.map +1 -0
  23. package/dist/probes/agent-skills-present.js +82 -0
  24. package/dist/probes/agent-skills-present.js.map +1 -0
  25. package/dist/probes/agent-skills-quality.d.ts +3 -0
  26. package/dist/probes/agent-skills-quality.d.ts.map +1 -0
  27. package/dist/probes/agent-skills-quality.js +148 -0
  28. package/dist/probes/agent-skills-quality.js.map +1 -0
  29. package/dist/probes/arch-coupling-hotspots.d.ts +3 -0
  30. package/dist/probes/arch-coupling-hotspots.d.ts.map +1 -0
  31. package/dist/probes/arch-coupling-hotspots.js +222 -0
  32. package/dist/probes/arch-coupling-hotspots.js.map +1 -0
  33. package/dist/probes/contracts-machine-readable.d.ts +3 -0
  34. package/dist/probes/contracts-machine-readable.d.ts.map +1 -0
  35. package/dist/probes/contracts-machine-readable.js +139 -0
  36. package/dist/probes/contracts-machine-readable.js.map +1 -0
  37. package/dist/probes/generated-code-marked.d.ts +3 -0
  38. package/dist/probes/generated-code-marked.d.ts.map +1 -0
  39. package/dist/probes/generated-code-marked.js +110 -0
  40. package/dist/probes/generated-code-marked.js.map +1 -0
  41. package/dist/probes/hooks-precommit-present.d.ts.map +1 -1
  42. package/dist/probes/hooks-precommit-present.js +81 -5
  43. package/dist/probes/hooks-precommit-present.js.map +1 -1
  44. package/dist/probes/specs-test-traceability.d.ts +3 -0
  45. package/dist/probes/specs-test-traceability.d.ts.map +1 -0
  46. package/dist/probes/specs-test-traceability.js +198 -0
  47. package/dist/probes/specs-test-traceability.js.map +1 -0
  48. package/dist/probes/tests-agent-safe-command.d.ts +3 -0
  49. package/dist/probes/tests-agent-safe-command.d.ts.map +1 -0
  50. package/dist/probes/tests-agent-safe-command.js +85 -0
  51. package/dist/probes/tests-agent-safe-command.js.map +1 -0
  52. package/dist/probes/tests-deterministic.d.ts +3 -0
  53. package/dist/probes/tests-deterministic.d.ts.map +1 -0
  54. package/dist/probes/tests-deterministic.js +155 -0
  55. package/dist/probes/tests-deterministic.js.map +1 -0
  56. package/dist/probes/tests-failure-actionability.d.ts +3 -0
  57. package/dist/probes/tests-failure-actionability.d.ts.map +1 -0
  58. package/dist/probes/tests-failure-actionability.js +139 -0
  59. package/dist/probes/tests-failure-actionability.js.map +1 -0
  60. package/dist/probes/tests-oracle-quality.d.ts +3 -0
  61. package/dist/probes/tests-oracle-quality.d.ts.map +1 -0
  62. package/dist/probes/tests-oracle-quality.js +140 -0
  63. package/dist/probes/tests-oracle-quality.js.map +1 -0
  64. package/dist/probes/types-escape-hatches.d.ts +3 -0
  65. package/dist/probes/types-escape-hatches.d.ts.map +1 -0
  66. package/dist/probes/types-escape-hatches.js +229 -0
  67. package/dist/probes/types-escape-hatches.js.map +1 -0
  68. package/package.json +2 -2
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@esbenwiberg/repofit/sdk").Probe;
2
+ export default _default;
3
+ //# sourceMappingURL=generated-code-marked.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generated-code-marked.d.ts","sourceRoot":"","sources":["../../src/probes/generated-code-marked.ts"],"names":[],"mappings":";AAaA,wBAiHG"}
@@ -0,0 +1,110 @@
1
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
+ const SOURCE_OR_SCHEMA = /\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|cs|json|ya?ml|graphql|proto)$/i;
3
+ const GENERATED_PATH = /(?:^|\/)(?:generated|__generated__|gen|autogen|openapi-client|graphql\/generated)(?:\/|$)|(?:^|\/)[^/]+\.(?:gen|generated|pb)\.[^.]+$/i;
4
+ const GENERATED_HEADER = /^\s*(?:\/\/|\/\*|\*|#|<!--)\s*(?:@generated|code generated|.*do not edit|auto-generated)/im;
5
+ const SKIP_DIRS = /(?:^|\/)(?:node_modules|coverage|\.git)\//;
6
+ const TEST_OR_FIXTURE_PATH = /(?:\.test\.|\.spec\.|__tests__|^tests?\/|\/tests?\/|^fixtures\/|\/fixtures\/|^examples\/|\/examples\/)/i;
7
+ export default defineProbe({
8
+ id: "generated.code-marked",
9
+ version: "1.0.0",
10
+ dimensions: [
11
+ { id: "context", weight: 0.5 },
12
+ { id: "cost", weight: 1 },
13
+ { id: "consistency", weight: 0.5 },
14
+ ],
15
+ tier: "static",
16
+ evidence: ["files", "size_stats"],
17
+ rationale: `
18
+ Generated code is often necessary, but agents should know not to edit it
19
+ as source. The size subsystem already respects files marked
20
+ linguist-generated. This probe flags tracked files that look generated by
21
+ path or header but are not marked generated.
22
+ `,
23
+ remediation: "Mark generated files in `.gitattributes` with `linguist-generated=true`, keep generated code under a clear directory, and document the source command that regenerates it. Agents should patch the source schema/template, not the artifact.",
24
+ async detect(ev) {
25
+ const candidates = ev.size_stats.files
26
+ .filter((f) => !f.generated)
27
+ .filter((f) => SOURCE_OR_SCHEMA.test(f.path) &&
28
+ !SKIP_DIRS.test(`/${f.path}`) &&
29
+ !TEST_OR_FIXTURE_PATH.test(f.path))
30
+ .sort((a, b) => a.path.localeCompare(b.path));
31
+ const items = [];
32
+ for (const f of candidates) {
33
+ let looksGenerated = GENERATED_PATH.test(f.path);
34
+ if (!looksGenerated && f.bytes < 100_000) {
35
+ const text = await ev.files.readText(f.path);
36
+ const head = text?.slice(0, 1_000) ?? "";
37
+ looksGenerated = GENERATED_HEADER.test(head);
38
+ }
39
+ if (looksGenerated) {
40
+ items.push({
41
+ location: { path: f.path },
42
+ severity: "warn",
43
+ message: "looks generated but is not marked generated",
44
+ });
45
+ }
46
+ }
47
+ return { kind: "inventory", items };
48
+ },
49
+ score: {
50
+ kind: "inventory",
51
+ severityWeights: { info: 1, warn: 3, error: 10 },
52
+ bands: [{ upTo: 0, score: 100 }, { upTo: 3, score: 80 }, { upTo: 9, score: 50 }, { score: 0 }],
53
+ },
54
+ fixtures: [
55
+ {
56
+ name: "no-generated-looking-files",
57
+ evidence: {
58
+ files: { "src/index.ts": "export const ok = true;\n" },
59
+ size_stats: {
60
+ source: "git-ls-files",
61
+ totalBytes: 100,
62
+ totalFiles: 1,
63
+ files: [{ path: "src/index.ts", bytes: 100, lines: 1, depth: 1 }],
64
+ },
65
+ },
66
+ expect: { reading: { kind: "inventory", items: [] }, score: 100 },
67
+ },
68
+ {
69
+ name: "marked-generated-is-ok",
70
+ evidence: {
71
+ files: { "src/generated/client.ts": "// Code generated by OpenAPI. DO NOT EDIT.\n" },
72
+ size_stats: {
73
+ source: "git-ls-files",
74
+ totalBytes: 100,
75
+ totalFiles: 1,
76
+ files: [
77
+ { path: "src/generated/client.ts", bytes: 100, lines: 1, depth: 2, generated: true },
78
+ ],
79
+ },
80
+ },
81
+ expect: { reading: { kind: "inventory", items: [] }, score: 100 },
82
+ },
83
+ {
84
+ name: "unmarked-generated",
85
+ evidence: {
86
+ files: { "src/generated/client.ts": "// Code generated by OpenAPI. DO NOT EDIT.\n" },
87
+ size_stats: {
88
+ source: "git-ls-files",
89
+ totalBytes: 100,
90
+ totalFiles: 1,
91
+ files: [{ path: "src/generated/client.ts", bytes: 100, lines: 1, depth: 2 }],
92
+ },
93
+ },
94
+ expect: {
95
+ reading: {
96
+ kind: "inventory",
97
+ items: [
98
+ {
99
+ location: { path: "src/generated/client.ts" },
100
+ severity: "warn",
101
+ message: "looks generated but is not marked generated",
102
+ },
103
+ ],
104
+ },
105
+ score: 80,
106
+ },
107
+ },
108
+ ],
109
+ });
110
+ //# sourceMappingURL=generated-code-marked.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generated-code-marked.js","sourceRoot":"","sources":["../../src/probes/generated-code-marked.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-precommit-present.d.ts","sourceRoot":"","sources":["../../src/probes/hooks-precommit-present.ts"],"names":[],"mappings":";AAIA,wBA6CG"}
1
+ {"version":3,"file":"hooks-precommit-present.d.ts","sourceRoot":"","sources":["../../src/probes/hooks-precommit-present.ts"],"names":[],"mappings":";AAoBA,wBA4EG"}
@@ -1,20 +1,40 @@
1
1
  import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
- const HOOK_PATHS = [".husky/pre-commit", ".githooks/pre-commit", ".pre-commit-config.yaml"];
2
+ const DIRECT_HOOK_PATHS = [
3
+ ".husky/pre-commit",
4
+ ".githooks/pre-commit",
5
+ ".git/hooks/pre-commit",
6
+ ".pre-commit-config.yaml",
7
+ ".pre-commit-config.yml",
8
+ ];
9
+ const CONFIG_HOOK_PATHS = [
10
+ "lefthook.yml",
11
+ "lefthook.yaml",
12
+ ".lefthook.yml",
13
+ ".lefthook.yaml",
14
+ ".lefthook/pre-commit.yml",
15
+ ".lefthook/pre-commit.yaml",
16
+ ".overcommit.yml",
17
+ ];
3
18
  export default defineProbe({
4
19
  id: "hooks.precommit-present",
5
- version: "1.0.0",
20
+ version: "1.1.0",
6
21
  dimensions: [{ id: "feedback", weight: 1 }],
7
22
  tier: "static",
8
- evidence: ["files"],
23
+ evidence: ["files", "node_package"],
9
24
  rationale: `
10
25
  A pre-commit hook is local feedback an agent can actually feel: if the
11
26
  commit it just made fails the hook, the failure is immediate and
12
27
  actionable. Without one, mistakes survive until CI (slower loop) or
13
28
  code review (much slower).
14
29
  `,
15
- remediation: "Wire a pre-commit hook. Easiest options: `husky` (Node), `pre-commit` (Python — `.pre-commit-config.yaml`), or commit hooks under `.githooks/` + `git config core.hooksPath .githooks`. Run lint/format/test from it.",
30
+ remediation: "Wire a pre-commit hook. Easiest options: `husky` or `simple-git-hooks` (Node), `pre-commit` (Python — `.pre-commit-config.yaml`), `lefthook`, `overcommit`, or commit hooks under `.githooks/` + `git config core.hooksPath .githooks`. Run lint/format/test from it.",
16
31
  async detect(ev) {
17
- return { kind: "predicate", value: HOOK_PATHS.some((p) => ev.files.has(p)) };
32
+ return {
33
+ kind: "predicate",
34
+ value: DIRECT_HOOK_PATHS.some((p) => ev.files.has(p)) ||
35
+ (await hasPreCommitManagerConfig(ev.files)) ||
36
+ hasPackageJsonPreCommitConfig(ev.node_package.raw),
37
+ };
18
38
  },
19
39
  score: { kind: "predicate", direction: "positive" },
20
40
  fixtures: [
@@ -33,6 +53,31 @@ export default defineProbe({
33
53
  evidence: { files: [".pre-commit-config.yaml"] },
34
54
  expect: { reading: { kind: "predicate", value: true }, score: 100 },
35
55
  },
56
+ {
57
+ name: "pre-commit-framework-yml",
58
+ evidence: { files: [".pre-commit-config.yml"] },
59
+ expect: { reading: { kind: "predicate", value: true }, score: 100 },
60
+ },
61
+ {
62
+ name: "lefthook-config",
63
+ evidence: {
64
+ files: { "lefthook.yml": "pre-commit:\n commands:\n lint:\n run: npm run lint\n" },
65
+ },
66
+ expect: { reading: { kind: "predicate", value: true }, score: 100 },
67
+ },
68
+ {
69
+ name: "simple-git-hooks-package-config",
70
+ evidence: {
71
+ node_package: {
72
+ present: true,
73
+ dependencies: {},
74
+ devDependencies: { "simple-git-hooks": "^2.13.1" },
75
+ scripts: {},
76
+ raw: { "simple-git-hooks": { "pre-commit": "npm test" } },
77
+ },
78
+ },
79
+ expect: { reading: { kind: "predicate", value: true }, score: 100 },
80
+ },
36
81
  {
37
82
  name: "no-hooks",
38
83
  evidence: { files: [] },
@@ -40,4 +85,35 @@ export default defineProbe({
40
85
  },
41
86
  ],
42
87
  });
88
+ async function hasPreCommitManagerConfig(files) {
89
+ for (const path of CONFIG_HOOK_PATHS) {
90
+ if (!files.has(path))
91
+ continue;
92
+ if (path.includes("/pre-commit."))
93
+ return true;
94
+ const content = await files.readText(path);
95
+ if (content === undefined)
96
+ return true;
97
+ if (/^pre-commit\s*:/m.test(content))
98
+ return true;
99
+ }
100
+ return false;
101
+ }
102
+ function hasPackageJsonPreCommitConfig(raw) {
103
+ if (!raw)
104
+ return false;
105
+ const husky = asRecord(raw.husky);
106
+ const huskyHooks = asRecord(husky?.hooks);
107
+ if (typeof huskyHooks?.["pre-commit"] === "string")
108
+ return true;
109
+ const simpleGitHooks = asRecord(raw["simple-git-hooks"]);
110
+ if (typeof simpleGitHooks?.["pre-commit"] === "string")
111
+ return true;
112
+ return false;
113
+ }
114
+ function asRecord(value) {
115
+ return value && typeof value === "object" && !Array.isArray(value)
116
+ ? value
117
+ : undefined;
118
+ }
43
119
  //# sourceMappingURL=hooks-precommit-present.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-precommit-present.js","sourceRoot":"","sources":["../../src/probes/hooks-precommit-present.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,UAAU,GAAG,CAAC,mBAAmB,EAAE,sBAAsB,EAAE,yBAAyB,CAAC,CAAC;AAE5F,eAAe,WAAW,CAAC;IACzB,EAAE,EAAE,yBAAyB;IAC7B,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAC3C,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,OAAO,CAAC;IAEnB,SAAS,EAAE;;;;;GAKV;IAED,WAAW,EACT,uNAAuN;IAEzN,KAAK,CAAC,MAAM,CAAC,EAAE;QACb,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,CAAC;IAED,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE;IAEnD,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,mBAAmB,CAAC,EAAE;YAC1C,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;SACpE;QACD;YACE,IAAI,EAAE,eAAe;YACrB,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,sBAAsB,CAAC,EAAE;YAC7C,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;SACpE;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,yBAAyB,CAAC,EAAE;YAChD,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;SACpE;QACD;YACE,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACvB,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;SACnE;KACF;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"hooks-precommit-present.js","sourceRoot":"","sources":["../../src/probes/hooks-precommit-present.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@esbenwiberg/repofit/sdk").Probe;
2
+ export default _default;
3
+ //# sourceMappingURL=specs-test-traceability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs-test-traceability.d.ts","sourceRoot":"","sources":["../../src/probes/specs-test-traceability.ts"],"names":[],"mappings":";AAsEA,wBAsJG"}
@@ -0,0 +1,198 @@
1
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
+ const PROBE_VERSION = "1.0.0";
3
+ const MAX_SPECS = 5;
4
+ const MAX_TEST_FILES = 40;
5
+ const MAX_CHARS_PER_SPEC = 2_500;
6
+ const MAX_INPUT_CHARS = 18_000;
7
+ const SPEC_DIRS = [
8
+ "specs",
9
+ "spec",
10
+ ".specify",
11
+ "docs/specs",
12
+ "docs/features",
13
+ "features",
14
+ ".features",
15
+ "rfcs",
16
+ "docs/rfcs",
17
+ ];
18
+ const SPEC_FILE = /\.(?:md|markdown)$/i;
19
+ const TEST_FILE = /(?:\.test\.|\.spec\.|__tests__|^tests?\/|^e2e\/)/i;
20
+ const TEST_SOURCE_FILE = /\.(?:ts|tsx|js|jsx|mjs|cjs|py|cs|go|rs|java|kt)$/i;
21
+ const TEST_NAME_LINE = /\b(?:test|it|describe|scenario|context)\s*\(\s*["'`]([^"'`]{4,120})["'`]/i;
22
+ const PY_TEST_NAME = /^\s*def\s+(test_[a-zA-Z0-9_]+)/;
23
+ const GO_TEST_NAME = /^\s*func\s+(Test[A-Za-z0-9_]+)\s*\(/;
24
+ const RUBRIC = {
25
+ task: "Judge whether acceptance criteria and scenario facts in the repo's specs are traceable to executable tests.",
26
+ criteria: [
27
+ {
28
+ id: "criteria-extracted",
29
+ description: "Do the specs contain concrete acceptance criteria, test cases, examples, or Given/When/Then scenarios that can become tests? Vague prose without checkable facts scores low.",
30
+ },
31
+ {
32
+ id: "tests-map-to-criteria",
33
+ description: "Do test names or test files clearly correspond to those criteria or scenarios? Strong traceability means a reviewer can point from a spec bullet to a test.",
34
+ },
35
+ {
36
+ id: "behavior-not-implementation",
37
+ description: "Are the traced tests framed around user-visible behavior, API contracts, or business rules rather than implementation details?",
38
+ },
39
+ {
40
+ id: "coverage-of-negative-cases",
41
+ description: "Are important failure, edge, permission, or invalid-input criteria represented in tests, not only happy paths?",
42
+ },
43
+ ],
44
+ };
45
+ function extractInterestingTestLines(text) {
46
+ const out = [];
47
+ for (const line of text.split(/\n/)) {
48
+ const trimmed = line.trim();
49
+ if (TEST_NAME_LINE.test(trimmed) || PY_TEST_NAME.test(trimmed) || GO_TEST_NAME.test(trimmed)) {
50
+ out.push(trimmed);
51
+ }
52
+ }
53
+ return out;
54
+ }
55
+ function specExcerpt(text) {
56
+ if (text.length <= MAX_CHARS_PER_SPEC)
57
+ return text;
58
+ const half = Math.floor((MAX_CHARS_PER_SPEC - 80) / 2);
59
+ return `${text.slice(0, half)}\n\n[...middle omitted...]\n\n${text.slice(-half)}`;
60
+ }
61
+ export default defineProbe({
62
+ id: "specs.test-traceability",
63
+ version: PROBE_VERSION,
64
+ dimensions: [
65
+ { id: "context", weight: 0.7 },
66
+ { id: "feedback", weight: 1 },
67
+ ],
68
+ tier: "reasoned",
69
+ evidence: ["files", "size_stats", "judge"],
70
+ rationale: `
71
+ Acceptance criteria only become reliable agent feedback when they are
72
+ connected to tests. This probe samples feature specs plus test names and
73
+ asks whether G/W/T, acceptance criteria, and test-case facts are traceable
74
+ to executable checks.
75
+ `,
76
+ remediation: "For every acceptance criterion or Given/When/Then scenario, add or name a matching test. Keep the wording close enough that reviewers and agents can trace spec facts to executable checks, including negative and edge cases.",
77
+ async detect(ev) {
78
+ const specPaths = ev.size_stats.files
79
+ .map((f) => f.path)
80
+ .filter((p) => SPEC_FILE.test(p) && SPEC_DIRS.some((dir) => p.startsWith(`${dir}/`)))
81
+ .sort();
82
+ if (specPaths.length === 0) {
83
+ return { kind: "na", reason: "no feature specs found" };
84
+ }
85
+ const testPaths = ev.size_stats.files
86
+ .map((f) => f.path)
87
+ .filter((p) => TEST_SOURCE_FILE.test(p) && TEST_FILE.test(p))
88
+ .sort();
89
+ if (testPaths.length === 0) {
90
+ return { kind: "na", reason: "no test files detected" };
91
+ }
92
+ const specs = [];
93
+ let chars = 0;
94
+ for (const p of specPaths) {
95
+ if (specs.length >= MAX_SPECS)
96
+ break;
97
+ const text = await ev.files.readText(p);
98
+ if (!text)
99
+ continue;
100
+ const slice = specExcerpt(text);
101
+ specs.push({ path: p, text: slice });
102
+ chars += slice.length;
103
+ if (chars >= MAX_INPUT_CHARS)
104
+ break;
105
+ }
106
+ const tests = [];
107
+ for (const p of testPaths.slice(0, MAX_TEST_FILES)) {
108
+ const text = await ev.files.readText(p);
109
+ const names = text ? extractInterestingTestLines(text) : [];
110
+ tests.push(`${p}${names.length ? `\n${names.map((n) => ` ${n}`).join("\n")}` : ""}`);
111
+ }
112
+ if (specs.length === 0) {
113
+ return { kind: "na", reason: "spec files declared but unreadable" };
114
+ }
115
+ const input = [
116
+ "# Specs",
117
+ specs.map((s) => `## ${s.path}\n${s.text}`).join("\n\n---\n\n"),
118
+ "",
119
+ "# Test files and test names",
120
+ tests.join("\n\n"),
121
+ ]
122
+ .join("\n")
123
+ .slice(0, MAX_INPUT_CHARS);
124
+ const result = await ev.judge.score({
125
+ probeId: "specs.test-traceability",
126
+ probeVersion: PROBE_VERSION,
127
+ input,
128
+ rubric: RUBRIC,
129
+ });
130
+ return {
131
+ kind: "judge",
132
+ score: result.score,
133
+ perCriterion: result.perCriterion,
134
+ rationale: result.rationale,
135
+ model: result.model,
136
+ };
137
+ },
138
+ score: { kind: "judge" },
139
+ fixtures: [
140
+ {
141
+ name: "no-specs",
142
+ evidence: {
143
+ size_stats: {
144
+ source: "git-ls-files",
145
+ totalBytes: 100,
146
+ totalFiles: 1,
147
+ files: [{ path: "src/login.test.ts", bytes: 100, lines: 3, depth: 1 }],
148
+ },
149
+ },
150
+ expect: { reading: { kind: "na", reason: "no feature specs found" }, score: null },
151
+ },
152
+ {
153
+ name: "criteria-traced-to-tests",
154
+ evidence: {
155
+ files: {
156
+ "specs/login.md": "# Login\n\n## Acceptance\n- Given valid credentials, when the user signs in, then the app creates a session.\n- Given invalid credentials, then the app returns an error.\n",
157
+ "src/login.test.ts": 'test("valid credentials create a session", () => {});\ntest("invalid credentials return an error", () => {});\n',
158
+ },
159
+ size_stats: {
160
+ source: "git-ls-files",
161
+ totalBytes: 300,
162
+ totalFiles: 2,
163
+ files: [
164
+ { path: "specs/login.md", bytes: 180, lines: 5, depth: 1 },
165
+ { path: "src/login.test.ts", bytes: 120, lines: 2, depth: 1 },
166
+ ],
167
+ },
168
+ judge: {
169
+ score: 80,
170
+ perCriterion: {
171
+ "criteria-extracted": 80,
172
+ "tests-map-to-criteria": 80,
173
+ "behavior-not-implementation": 80,
174
+ "coverage-of-negative-cases": 80,
175
+ },
176
+ rationale: "Concrete criteria are named in matching behavior tests.",
177
+ model: "fixture",
178
+ },
179
+ },
180
+ expect: {
181
+ reading: {
182
+ kind: "judge",
183
+ score: 80,
184
+ perCriterion: {
185
+ "criteria-extracted": 80,
186
+ "tests-map-to-criteria": 80,
187
+ "behavior-not-implementation": 80,
188
+ "coverage-of-negative-cases": 80,
189
+ },
190
+ rationale: "Concrete criteria are named in matching behavior tests.",
191
+ model: "fixture",
192
+ },
193
+ score: 80,
194
+ },
195
+ },
196
+ ],
197
+ });
198
+ //# sourceMappingURL=specs-test-traceability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs-test-traceability.js","sourceRoot":"","sources":["../../src/probes/specs-test-traceability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,aAAa,GAAG,OAAO,CAAC;AAC9B,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,MAAM,SAAS,GAAG;IAChB,OAAO;IACP,MAAM;IACN,UAAU;IACV,YAAY;IACZ,eAAe;IACf,UAAU;IACV,WAAW;IACX,MAAM;IACN,WAAW;CACZ,CAAC;AAEF,MAAM,SAAS,GAAG,qBAAqB,CAAC;AACxC,MAAM,SAAS,GAAG,mDAAmD,CAAC;AACtE,MAAM,gBAAgB,GAAG,mDAAmD,CAAC;AAC7E,MAAM,cAAc,GAAG,2EAA2E,CAAC;AACnG,MAAM,YAAY,GAAG,gCAAgC,CAAC;AACtD,MAAM,YAAY,GAAG,qCAAqC,CAAC;AAE3D,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,6GAA6G;IACnH,QAAQ,EAAE;QACR;YACE,EAAE,EAAE,oBAAoB;YACxB,WAAW,EACT,8KAA8K;SACjL;QACD;YACE,EAAE,EAAE,uBAAuB;YAC3B,WAAW,EACT,6JAA6J;SAChK;QACD;YACE,EAAE,EAAE,6BAA6B;YACjC,WAAW,EACT,gIAAgI;SACnI;QACD;YACE,EAAE,EAAE,4BAA4B;YAChC,WAAW,EACT,gHAAgH;SACnH;KACF;CACO,CAAC;AAEX,SAAS,2BAA2B,CAAC,IAAY;IAC/C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7F,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,IAAI,CAAC,MAAM,IAAI,kBAAkB;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,kBAAkB,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,iCAAiC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AACpF,CAAC;AAED,eAAe,WAAW,CAAC;IACzB,EAAE,EAAE,yBAAyB;IAC7B,OAAO,EAAE,aAAa;IACtB,UAAU,EAAE;QACV,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE;QAC9B,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE;KAC9B;IACD,IAAI,EAAE,UAAU;IAChB,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC;IAE1C,SAAS,EAAE;;;;;GAKV;IAED,WAAW,EACT,gOAAgO;IAElO,KAAK,CAAC,MAAM,CAAC,EAAE;QACb,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;aACpF,IAAI,EAAE,CAAC;QAEV,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;QAC1D,CAAC;QAED,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAC5D,IAAI,EAAE,CAAC;QAEV,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;QAC1D,CAAC;QAED,MAAM,KAAK,GAAqC,EAAE,CAAC;QACnD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS;gBAAE,MAAM;YACrC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxC,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACrC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,KAAK,IAAI,eAAe;gBAAE,MAAM;QACtC,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxF,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,oCAAoC,EAAE,CAAC;QACtE,CAAC;QAED,MAAM,KAAK,GAAG;YACZ,SAAS;YACT,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;YAC/D,EAAE;YACF,6BAA6B;YAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;SACnB;aACE,IAAI,CAAC,IAAI,CAAC;aACV,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC;YAClC,OAAO,EAAE,yBAAyB;YAClC,YAAY,EAAE,aAAa;YAC3B,KAAK;YACL,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QAEH,OAAO;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC;IACJ,CAAC;IAED,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;IAExB,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE;gBACR,UAAU,EAAE;oBACV,MAAM,EAAE,cAAc;oBACtB,UAAU,EAAE,GAAG;oBACf,UAAU,EAAE,CAAC;oBACb,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;iBACvE;aACF;YACD,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;SACnF;QACD;YACE,IAAI,EAAE,0BAA0B;YAChC,QAAQ,EAAE;gBACR,KAAK,EAAE;oBACL,gBAAgB,EACd,6KAA6K;oBAC/K,mBAAmB,EACjB,iHAAiH;iBACpH;gBACD,UAAU,EAAE;oBACV,MAAM,EAAE,cAAc;oBACtB,UAAU,EAAE,GAAG;oBACf,UAAU,EAAE,CAAC;oBACb,KAAK,EAAE;wBACL,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;wBAC1D,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;qBAC9D;iBACF;gBACD,KAAK,EAAE;oBACL,KAAK,EAAE,EAAE;oBACT,YAAY,EAAE;wBACZ,oBAAoB,EAAE,EAAE;wBACxB,uBAAuB,EAAE,EAAE;wBAC3B,6BAA6B,EAAE,EAAE;wBACjC,4BAA4B,EAAE,EAAE;qBACjC;oBACD,SAAS,EAAE,yDAAyD;oBACpE,KAAK,EAAE,SAAS;iBACjB;aACF;YACD,MAAM,EAAE;gBACN,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE;oBACT,YAAY,EAAE;wBACZ,oBAAoB,EAAE,EAAE;wBACxB,uBAAuB,EAAE,EAAE;wBAC3B,6BAA6B,EAAE,EAAE;wBACjC,4BAA4B,EAAE,EAAE;qBACjC;oBACD,SAAS,EAAE,yDAAyD;oBACpE,KAAK,EAAE,SAAS;iBACjB;gBACD,KAAK,EAAE,EAAE;aACV;SACF;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@esbenwiberg/repofit/sdk").Probe;
2
+ export default _default;
3
+ //# sourceMappingURL=tests-agent-safe-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests-agent-safe-command.d.ts","sourceRoot":"","sources":["../../src/probes/tests-agent-safe-command.ts"],"names":[],"mappings":";AAMA,wBAuFG"}
@@ -0,0 +1,85 @@
1
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
+ const AGENT_SAFE_TEST_SCRIPTS = ["test:agent", "test:unit", "test:smoke", "test:fast"];
3
+ const E2E_TEST_HINT = /\b(playwright|cypress|testcafe|webdriver|selenium|detox|e2e)\b/i;
4
+ export default defineProbe({
5
+ id: "tests.agent-safe-command",
6
+ version: "1.0.0",
7
+ dimensions: [
8
+ { id: "feedback", weight: 1 },
9
+ { id: "latency", weight: 0.5 },
10
+ ],
11
+ tier: "static",
12
+ evidence: ["node_package"],
13
+ rationale: `
14
+ Executed probes should not surprise a repo by running hundreds of browser
15
+ tests. For Node projects, repofit prefers fast agent-safe scripts
16
+ (test:agent, test:unit, test:smoke, test:fast) before npm test. If npm
17
+ test looks like a full e2e suite, this probe asks the repo to expose a
18
+ smaller verification command or configure toolchain.commands.test.
19
+ `,
20
+ remediation: "Add a fast verification script such as `test:agent`, `test:unit`, `test:smoke`, or `test:fast`, and keep full browser/e2e suites under `test:e2e` or an explicit CI-only command. Alternatively set `toolchain.commands.test` in `repofit.config.json` to the exact command repofit should run in executed mode.",
21
+ async detect(ev) {
22
+ if (!ev.node_package.present)
23
+ return { kind: "na", reason: "no package.json" };
24
+ const scripts = ev.node_package.scripts ?? {};
25
+ const hasSafe = AGENT_SAFE_TEST_SCRIPTS.some((script) => hasScript(scripts, script));
26
+ const test = scripts.test ?? "";
27
+ const items = [];
28
+ if (!hasSafe && E2E_TEST_HINT.test(test)) {
29
+ items.push({
30
+ location: { path: "package.json#scripts.test" },
31
+ severity: "warn",
32
+ message: "npm test looks e2e-heavy; add test:agent/test:unit/test:smoke or configure toolchain.commands.test",
33
+ });
34
+ }
35
+ return { kind: "inventory", items };
36
+ },
37
+ score: {
38
+ kind: "inventory",
39
+ severityWeights: { info: 1, warn: 3, error: 10 },
40
+ bands: [{ upTo: 0, score: 100 }, { upTo: 3, score: 70 }, { score: 0 }],
41
+ },
42
+ fixtures: [
43
+ {
44
+ name: "no-package-json",
45
+ evidence: { node_package: { present: false } },
46
+ expect: { reading: { kind: "na", reason: "no package.json" }, score: null },
47
+ },
48
+ {
49
+ name: "safe-test-script-present",
50
+ evidence: {
51
+ node_package: {
52
+ present: true,
53
+ scripts: { test: "playwright test", "test:agent": "vitest run" },
54
+ },
55
+ },
56
+ expect: { reading: { kind: "inventory", items: [] }, score: 100 },
57
+ },
58
+ {
59
+ name: "normal-npm-test",
60
+ evidence: { node_package: { present: true, scripts: { test: "vitest run" } } },
61
+ expect: { reading: { kind: "inventory", items: [] }, score: 100 },
62
+ },
63
+ {
64
+ name: "e2e-only-npm-test",
65
+ evidence: { node_package: { present: true, scripts: { test: "playwright test" } } },
66
+ expect: {
67
+ reading: {
68
+ kind: "inventory",
69
+ items: [
70
+ {
71
+ location: { path: "package.json#scripts.test" },
72
+ severity: "warn",
73
+ message: "npm test looks e2e-heavy; add test:agent/test:unit/test:smoke or configure toolchain.commands.test",
74
+ },
75
+ ],
76
+ },
77
+ score: 70,
78
+ },
79
+ },
80
+ ],
81
+ });
82
+ function hasScript(scripts, name) {
83
+ return typeof scripts[name] === "string" && scripts[name].trim().length > 0;
84
+ }
85
+ //# sourceMappingURL=tests-agent-safe-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests-agent-safe-command.js","sourceRoot":"","sources":["../../src/probes/tests-agent-safe-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@esbenwiberg/repofit/sdk").Probe;
2
+ export default _default;
3
+ //# sourceMappingURL=tests-deterministic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests-deterministic.d.ts","sourceRoot":"","sources":["../../src/probes/tests-deterministic.ts"],"names":[],"mappings":";AAwCA,wBA8HG"}