@esbenwiberg/corpus-default 1.1.0 → 1.2.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 (74) 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/errors-actionability.d.ts.map +1 -1
  38. package/dist/probes/errors-actionability.js +132 -22
  39. package/dist/probes/errors-actionability.js.map +1 -1
  40. package/dist/probes/generated-code-marked.d.ts +3 -0
  41. package/dist/probes/generated-code-marked.d.ts.map +1 -0
  42. package/dist/probes/generated-code-marked.js +110 -0
  43. package/dist/probes/generated-code-marked.js.map +1 -0
  44. package/dist/probes/hooks-precommit-present.d.ts.map +1 -1
  45. package/dist/probes/hooks-precommit-present.js +81 -5
  46. package/dist/probes/hooks-precommit-present.js.map +1 -1
  47. package/dist/probes/specs-test-traceability.d.ts +3 -0
  48. package/dist/probes/specs-test-traceability.d.ts.map +1 -0
  49. package/dist/probes/specs-test-traceability.js +198 -0
  50. package/dist/probes/specs-test-traceability.js.map +1 -0
  51. package/dist/probes/tests-agent-safe-command.d.ts +3 -0
  52. package/dist/probes/tests-agent-safe-command.d.ts.map +1 -0
  53. package/dist/probes/tests-agent-safe-command.js +158 -0
  54. package/dist/probes/tests-agent-safe-command.js.map +1 -0
  55. package/dist/probes/tests-cover-public-surface.d.ts.map +1 -1
  56. package/dist/probes/tests-cover-public-surface.js +93 -12
  57. package/dist/probes/tests-cover-public-surface.js.map +1 -1
  58. package/dist/probes/tests-deterministic.d.ts +3 -0
  59. package/dist/probes/tests-deterministic.d.ts.map +1 -0
  60. package/dist/probes/tests-deterministic.js +204 -0
  61. package/dist/probes/tests-deterministic.js.map +1 -0
  62. package/dist/probes/tests-failure-actionability.d.ts +3 -0
  63. package/dist/probes/tests-failure-actionability.d.ts.map +1 -0
  64. package/dist/probes/tests-failure-actionability.js +139 -0
  65. package/dist/probes/tests-failure-actionability.js.map +1 -0
  66. package/dist/probes/tests-oracle-quality.d.ts +3 -0
  67. package/dist/probes/tests-oracle-quality.d.ts.map +1 -0
  68. package/dist/probes/tests-oracle-quality.js +140 -0
  69. package/dist/probes/tests-oracle-quality.js.map +1 -0
  70. package/dist/probes/types-escape-hatches.d.ts +3 -0
  71. package/dist/probes/types-escape-hatches.d.ts.map +1 -0
  72. package/dist/probes/types-escape-hatches.js +229 -0
  73. package/dist/probes/types-escape-hatches.js.map +1 -0
  74. package/package.json +2 -2
@@ -0,0 +1,82 @@
1
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
+ const SKILL_FILE_PATTERNS = [
3
+ /^\.codex\/skills\/[^/]+\/SKILL\.md$/i,
4
+ /^\.agents\/skills\/[^/]+\/SKILL\.md$/i,
5
+ /^agents\/skills\/[^/]+\/SKILL\.md$/i,
6
+ /^\.claude\/commands\/.+\.md$/i,
7
+ /^\.cursor\/rules\/.+\.mdc$/i,
8
+ /^\.windsurf\/rules\/.+\.md$/i,
9
+ /^prompts\/agents\/.+\.md$/i,
10
+ ];
11
+ export default defineProbe({
12
+ id: "agent.skills-present",
13
+ version: "1.0.0",
14
+ dimensions: [
15
+ { id: "context", weight: 1 },
16
+ { id: "feedback", weight: 0.3 },
17
+ ],
18
+ tier: "static",
19
+ evidence: ["size_stats"],
20
+ rationale: `
21
+ Repository-local skills and slash commands turn recurring agent work into
22
+ reusable procedures: commit workflow, new-probe authoring, release, smoke
23
+ testing, debugging, review. Generic guidance tells an agent what matters;
24
+ skills give it a repeatable path through high-friction tasks.
25
+ `,
26
+ remediation: "Add repo-local agent skills or commands for repeated workflows. Good starting points: `/commit`, `/new-probe`, `/release`, `/smoke`, `/debug-failing-ci`, and `/review`. Store them in a tool-native location such as `.codex/skills/<name>/SKILL.md`, `.claude/commands/*.md`, or `.cursor/rules/*.mdc`.",
27
+ async detect(ev) {
28
+ const samples = ev.size_stats.files
29
+ .map((f) => f.path)
30
+ .filter((p) => SKILL_FILE_PATTERNS.some((pattern) => pattern.test(p)))
31
+ .sort()
32
+ .map((path) => ({ path }));
33
+ return { kind: "count", value: samples.length, samples };
34
+ },
35
+ score: {
36
+ kind: "count",
37
+ direction: "positive",
38
+ bands: [{ upTo: 0, score: 0 }, { upTo: 1, score: 60 }, { upTo: 3, score: 80 }, { score: 100 }],
39
+ },
40
+ fixtures: [
41
+ {
42
+ name: "no-local-skills",
43
+ evidence: {
44
+ size_stats: {
45
+ source: "git-ls-files",
46
+ totalBytes: 100,
47
+ totalFiles: 1,
48
+ files: [{ path: "CLAUDE.md", bytes: 100, lines: 5, depth: 0 }],
49
+ },
50
+ },
51
+ expect: { reading: { kind: "count", value: 0, samples: [] }, score: 0 },
52
+ },
53
+ {
54
+ name: "codex-and-claude-skills",
55
+ evidence: {
56
+ size_stats: {
57
+ source: "git-ls-files",
58
+ totalBytes: 300,
59
+ totalFiles: 3,
60
+ files: [
61
+ { path: ".codex/skills/new-probe/SKILL.md", bytes: 100, lines: 20, depth: 3 },
62
+ { path: ".claude/commands/commit.md", bytes: 100, lines: 15, depth: 2 },
63
+ { path: ".cursor/rules/testing.mdc", bytes: 100, lines: 15, depth: 2 },
64
+ ],
65
+ },
66
+ },
67
+ expect: {
68
+ reading: {
69
+ kind: "count",
70
+ value: 3,
71
+ samples: [
72
+ { path: ".claude/commands/commit.md" },
73
+ { path: ".codex/skills/new-probe/SKILL.md" },
74
+ { path: ".cursor/rules/testing.mdc" },
75
+ ],
76
+ },
77
+ score: 80,
78
+ },
79
+ },
80
+ ],
81
+ });
82
+ //# sourceMappingURL=agent-skills-present.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-skills-present.js","sourceRoot":"","sources":["../../src/probes/agent-skills-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=agent-skills-quality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-skills-quality.d.ts","sourceRoot":"","sources":["../../src/probes/agent-skills-quality.ts"],"names":[],"mappings":";AA2CA,wBAwHG"}
@@ -0,0 +1,148 @@
1
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
+ const PROBE_VERSION = "1.0.0";
3
+ const MAX_SKILLS = 5;
4
+ const MAX_CHARS_PER_SKILL = 3_000;
5
+ const MAX_INPUT_CHARS = 18_000;
6
+ const SKILL_FILE_PATTERNS = [
7
+ /^\.codex\/skills\/[^/]+\/SKILL\.md$/i,
8
+ /^\.agents\/skills\/[^/]+\/SKILL\.md$/i,
9
+ /^agents\/skills\/[^/]+\/SKILL\.md$/i,
10
+ /^\.claude\/commands\/.+\.md$/i,
11
+ /^\.cursor\/rules\/.+\.mdc$/i,
12
+ /^\.windsurf\/rules\/.+\.md$/i,
13
+ /^prompts\/agents\/.+\.md$/i,
14
+ ];
15
+ const RUBRIC = {
16
+ task: "Judge whether repository-local agent skills or slash commands would help a coding agent perform repeated workflows correctly.",
17
+ criteria: [
18
+ {
19
+ id: "workflow-specific",
20
+ description: "Does each skill target a concrete recurring workflow such as commit, new probe, release, smoke test, debugging CI, or review? Generic advice scores low.",
21
+ },
22
+ {
23
+ id: "actionable-steps",
24
+ description: "Does the skill give ordered, executable steps with commands, files, and expected validation signals?",
25
+ },
26
+ {
27
+ id: "repo-fit",
28
+ description: "Is the skill specific to this repository's structure, tools, conventions, and hazards rather than broadly reusable platitudes?",
29
+ },
30
+ {
31
+ id: "guardrails",
32
+ description: "Does the skill include safety checks, non-goals, verification, and when to stop or ask for help?",
33
+ },
34
+ ],
35
+ };
36
+ export default defineProbe({
37
+ id: "agent.skills-quality",
38
+ version: PROBE_VERSION,
39
+ dimensions: [
40
+ { id: "context", weight: 1 },
41
+ { id: "feedback", weight: 0.5 },
42
+ ],
43
+ tier: "reasoned",
44
+ evidence: ["files", "size_stats", "judge"],
45
+ rationale: `
46
+ Local skills can make agents dramatically better, but only when they
47
+ encode real workflows. A stub named /commit that says "make a good
48
+ commit" is not useful. This probe samples local skills and judges their
49
+ specificity, actionability, repo fit, and guardrails.
50
+ `,
51
+ remediation: "Turn local skills into concrete recipes: name the workflow, list exact commands and files, include validation signals, document hazards, and explain when the agent should stop. Good first skills are `/commit`, `/new-probe`, `/release`, `/smoke`, `/debug-failing-ci`, and `/review`.",
52
+ async detect(ev) {
53
+ const skillPaths = ev.size_stats.files
54
+ .map((f) => f.path)
55
+ .filter((p) => SKILL_FILE_PATTERNS.some((pattern) => pattern.test(p)))
56
+ .sort();
57
+ if (skillPaths.length === 0) {
58
+ return { kind: "na", reason: "no local agent skills found" };
59
+ }
60
+ const sampled = [];
61
+ let totalChars = 0;
62
+ for (const p of skillPaths) {
63
+ if (sampled.length >= MAX_SKILLS)
64
+ break;
65
+ const text = await ev.files.readText(p);
66
+ if (!text)
67
+ continue;
68
+ const slice = text.slice(0, MAX_CHARS_PER_SKILL);
69
+ sampled.push({ path: p, text: slice });
70
+ totalChars += slice.length;
71
+ if (totalChars >= MAX_INPUT_CHARS)
72
+ break;
73
+ }
74
+ if (sampled.length === 0) {
75
+ return { kind: "na", reason: "local agent skills declared but unreadable" };
76
+ }
77
+ const input = sampled.map((s) => `# ${s.path}\n\n${s.text}`).join("\n\n---\n\n");
78
+ const result = await ev.judge.score({
79
+ probeId: "agent.skills-quality",
80
+ probeVersion: PROBE_VERSION,
81
+ input,
82
+ rubric: RUBRIC,
83
+ });
84
+ return {
85
+ kind: "judge",
86
+ score: result.score,
87
+ perCriterion: result.perCriterion,
88
+ rationale: result.rationale,
89
+ model: result.model,
90
+ };
91
+ },
92
+ score: { kind: "judge" },
93
+ fixtures: [
94
+ {
95
+ name: "no-skills",
96
+ evidence: {
97
+ size_stats: {
98
+ source: "git-ls-files",
99
+ totalBytes: 100,
100
+ totalFiles: 1,
101
+ files: [{ path: "CLAUDE.md", bytes: 100, lines: 5, depth: 0 }],
102
+ },
103
+ },
104
+ expect: { reading: { kind: "na", reason: "no local agent skills found" }, score: null },
105
+ },
106
+ {
107
+ name: "strong-skill",
108
+ evidence: {
109
+ files: {
110
+ ".codex/skills/new-probe/SKILL.md": "# New Probe\n\nUse when adding a corpus probe.\n\n1. Add `packages/corpus-default/src/probes/<id>.ts`.\n2. Add fixtures covering N/A, pass, and fail.\n3. Wire it in `src/index.ts`.\n4. Run `npm --workspace @esbenwiberg/corpus-default run typecheck` and `npm --workspace @esbenwiberg/corpus-default test`.\n5. Commit with `feat(corpus): ...`.\n",
111
+ },
112
+ size_stats: {
113
+ source: "git-ls-files",
114
+ totalBytes: 400,
115
+ totalFiles: 1,
116
+ files: [{ path: ".codex/skills/new-probe/SKILL.md", bytes: 400, lines: 10, depth: 3 }],
117
+ },
118
+ judge: {
119
+ score: 80,
120
+ perCriterion: {
121
+ "workflow-specific": 80,
122
+ "actionable-steps": 80,
123
+ "repo-fit": 80,
124
+ guardrails: 80,
125
+ },
126
+ rationale: "Concrete repo-specific workflow with validation and commit convention.",
127
+ model: "fixture",
128
+ },
129
+ },
130
+ expect: {
131
+ reading: {
132
+ kind: "judge",
133
+ score: 80,
134
+ perCriterion: {
135
+ "workflow-specific": 80,
136
+ "actionable-steps": 80,
137
+ "repo-fit": 80,
138
+ guardrails: 80,
139
+ },
140
+ rationale: "Concrete repo-specific workflow with validation and commit convention.",
141
+ model: "fixture",
142
+ },
143
+ score: 80,
144
+ },
145
+ },
146
+ ],
147
+ });
148
+ //# sourceMappingURL=agent-skills-quality.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-skills-quality.js","sourceRoot":"","sources":["../../src/probes/agent-skills-quality.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=arch-coupling-hotspots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arch-coupling-hotspots.d.ts","sourceRoot":"","sources":["../../src/probes/arch-coupling-hotspots.ts"],"names":[],"mappings":";AAeA,wBA+JG"}
@@ -0,0 +1,222 @@
1
+ import { posix as path } from "node:path";
2
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
3
+ const SOURCE_FILE = /\.(?:ts|tsx|js|jsx|mjs|cjs)$/i;
4
+ const TEST_FILE = /(?:\.test\.|\.spec\.|__tests__|^tests?\/|^e2e\/)/i;
5
+ const SKIP_DIRS = /(?:^|\/)(?:node_modules|dist|build|coverage|\.next|\.nuxt|out|target|bin|obj)\//;
6
+ const EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
7
+ const FAN_OUT_WARN = 12;
8
+ const FAN_IN_WARN = 20;
9
+ const MAX_ITEMS = 30;
10
+ const IMPORT_RE = /\bimport\s+(?:type\s+)?(?:[^"'`]*?\s+from\s+)?["'`]([^"'`]+)["'`]|\brequire\s*\(\s*["'`]([^"'`]+)["'`]\s*\)|\bimport\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g;
11
+ export default defineProbe({
12
+ id: "arch.coupling-hotspots",
13
+ version: "1.0.0",
14
+ dimensions: [
15
+ { id: "context", weight: 0.5 },
16
+ { id: "cost", weight: 1 },
17
+ { id: "consistency", weight: 0.5 },
18
+ ],
19
+ tier: "static",
20
+ evidence: ["files", "size_stats"],
21
+ rationale: `
22
+ Agents pay for coupling twice: they need more context before changing a
23
+ hotspot, and each edit has a wider blast radius. This probe builds a
24
+ lightweight graph from relative JS/TS imports and flags import cycles,
25
+ high fan-out files, and high fan-in files.
26
+ `,
27
+ remediation: "Break import cycles, split files that import too many local modules, and stabilize high fan-in modules with clearer contracts and tests. Hotspots are not always wrong, but they deserve names, docs, and extra care.",
28
+ async detect(ev) {
29
+ const sourcePaths = ev.size_stats.files
30
+ .filter((f) => !f.generated)
31
+ .map((f) => f.path)
32
+ .filter((p) => SOURCE_FILE.test(p) && !TEST_FILE.test(p) && !SKIP_DIRS.test(`/${p}`))
33
+ .sort();
34
+ if (sourcePaths.length === 0) {
35
+ return { kind: "na", reason: "no JS/TS source files detected" };
36
+ }
37
+ const sourceSet = new Set(sourcePaths);
38
+ const graph = new Map();
39
+ for (const p of sourcePaths)
40
+ graph.set(p, new Set());
41
+ for (const p of sourcePaths) {
42
+ const text = await ev.files.readText(p);
43
+ if (!text)
44
+ continue;
45
+ for (const spec of extractImports(text)) {
46
+ const resolved = resolveRelativeImport(p, spec, sourceSet);
47
+ if (resolved)
48
+ graph.get(p)?.add(resolved);
49
+ }
50
+ }
51
+ const items = [];
52
+ const cycles = findCycles(graph);
53
+ for (const cycle of cycles.slice(0, 10)) {
54
+ items.push({
55
+ location: { path: cycle[0] ?? "" },
56
+ severity: "error",
57
+ message: `import cycle: ${cycle.join(" -> ")}`,
58
+ });
59
+ }
60
+ const fanIn = new Map();
61
+ for (const p of sourcePaths)
62
+ fanIn.set(p, 0);
63
+ for (const deps of graph.values()) {
64
+ for (const dep of deps)
65
+ fanIn.set(dep, (fanIn.get(dep) ?? 0) + 1);
66
+ }
67
+ for (const [p, deps] of graph) {
68
+ if (deps.size > FAN_OUT_WARN) {
69
+ items.push({
70
+ location: { path: p },
71
+ severity: "warn",
72
+ message: `high fan-out: imports ${deps.size} local modules`,
73
+ });
74
+ }
75
+ }
76
+ for (const [p, count] of fanIn) {
77
+ if (count > FAN_IN_WARN) {
78
+ items.push({
79
+ location: { path: p },
80
+ severity: "warn",
81
+ message: `high fan-in: imported by ${count} local modules`,
82
+ });
83
+ }
84
+ }
85
+ return { kind: "inventory", items: items.slice(0, MAX_ITEMS) };
86
+ },
87
+ score: {
88
+ kind: "inventory",
89
+ severityWeights: { info: 1, warn: 3, error: 10 },
90
+ bands: [
91
+ { upTo: 0, score: 100 },
92
+ { upTo: 3, score: 80 },
93
+ { upTo: 6, score: 50 },
94
+ { upTo: 9, score: 20 },
95
+ { score: 0 },
96
+ ],
97
+ },
98
+ fixtures: [
99
+ {
100
+ name: "no-js-ts-source",
101
+ evidence: {
102
+ size_stats: {
103
+ source: "git-ls-files",
104
+ totalBytes: 100,
105
+ totalFiles: 1,
106
+ files: [{ path: "README.md", bytes: 100, lines: 5, depth: 0 }],
107
+ },
108
+ },
109
+ expect: { reading: { kind: "na", reason: "no JS/TS source files detected" }, score: null },
110
+ },
111
+ {
112
+ name: "simple-acyclic",
113
+ evidence: {
114
+ files: {
115
+ "src/a.ts": 'import { b } from "./b";\nexport const a = b;\n',
116
+ "src/b.ts": "export const b = 1;\n",
117
+ },
118
+ size_stats: {
119
+ source: "git-ls-files",
120
+ totalBytes: 120,
121
+ totalFiles: 2,
122
+ files: [
123
+ { path: "src/a.ts", bytes: 80, lines: 2, depth: 1 },
124
+ { path: "src/b.ts", bytes: 40, lines: 1, depth: 1 },
125
+ ],
126
+ },
127
+ },
128
+ expect: { reading: { kind: "inventory", items: [] }, score: 100 },
129
+ },
130
+ {
131
+ name: "cycle",
132
+ evidence: {
133
+ files: {
134
+ "src/a.ts": 'import { b } from "./b";\nexport const a = b;\n',
135
+ "src/b.ts": 'import { a } from "./a";\nexport const b = a;\n',
136
+ },
137
+ size_stats: {
138
+ source: "git-ls-files",
139
+ totalBytes: 160,
140
+ totalFiles: 2,
141
+ files: [
142
+ { path: "src/a.ts", bytes: 80, lines: 2, depth: 1 },
143
+ { path: "src/b.ts", bytes: 80, lines: 2, depth: 1 },
144
+ ],
145
+ },
146
+ },
147
+ expect: {
148
+ reading: {
149
+ kind: "inventory",
150
+ items: [
151
+ {
152
+ location: { path: "src/a.ts" },
153
+ severity: "error",
154
+ message: "import cycle: src/a.ts -> src/b.ts -> src/a.ts",
155
+ },
156
+ ],
157
+ },
158
+ score: 0,
159
+ },
160
+ },
161
+ ],
162
+ });
163
+ function extractImports(text) {
164
+ const out = [];
165
+ IMPORT_RE.lastIndex = 0;
166
+ let match = IMPORT_RE.exec(text);
167
+ while (match !== null) {
168
+ const spec = match[1] ?? match[2] ?? match[3];
169
+ if (spec?.startsWith("."))
170
+ out.push(spec);
171
+ match = IMPORT_RE.exec(text);
172
+ }
173
+ return out;
174
+ }
175
+ function resolveRelativeImport(fromPath, spec, sourceSet) {
176
+ const base = path.normalize(path.join(path.dirname(fromPath), spec));
177
+ const candidates = [base, ...EXTENSIONS.map((ext) => `${base}${ext}`)];
178
+ for (const ext of EXTENSIONS)
179
+ candidates.push(path.join(base, `index${ext}`));
180
+ return candidates.find((candidate) => sourceSet.has(candidate)) ?? null;
181
+ }
182
+ function findCycles(graph) {
183
+ const cycles = [];
184
+ const seen = new Set();
185
+ const visiting = new Set();
186
+ const visited = new Set();
187
+ const stack = [];
188
+ function visit(node) {
189
+ if (cycles.length >= 10)
190
+ return;
191
+ visiting.add(node);
192
+ stack.push(node);
193
+ for (const next of graph.get(node) ?? []) {
194
+ if (visiting.has(next)) {
195
+ const idx = stack.indexOf(next);
196
+ const cycle = [...stack.slice(idx), next];
197
+ const key = canonicalCycleKey(cycle);
198
+ if (!seen.has(key)) {
199
+ seen.add(key);
200
+ cycles.push(cycle);
201
+ }
202
+ }
203
+ else if (!visited.has(next)) {
204
+ visit(next);
205
+ }
206
+ }
207
+ stack.pop();
208
+ visiting.delete(node);
209
+ visited.add(node);
210
+ }
211
+ for (const node of graph.keys()) {
212
+ if (!visited.has(node))
213
+ visit(node);
214
+ }
215
+ return cycles;
216
+ }
217
+ function canonicalCycleKey(cycle) {
218
+ const body = cycle.slice(0, -1);
219
+ const rotations = body.map((_, i) => [...body.slice(i), ...body.slice(0, i)].join(" -> "));
220
+ return rotations.sort()[0] ?? cycle.join(" -> ");
221
+ }
222
+ //# sourceMappingURL=arch-coupling-hotspots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arch-coupling-hotspots.js","sourceRoot":"","sources":["../../src/probes/arch-coupling-hotspots.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=contracts-machine-readable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts-machine-readable.d.ts","sourceRoot":"","sources":["../../src/probes/contracts-machine-readable.ts"],"names":[],"mappings":";AAiBA,wBAmIG"}
@@ -0,0 +1,139 @@
1
+ import { defineProbe } from "@esbenwiberg/repofit/sdk";
2
+ const CONTRACT_PATTERNS = [
3
+ /(?:^|\/)(?:openapi|swagger)\.(?:ya?ml|json)$/i,
4
+ /(?:^|\/)[^/]+\.proto$/i,
5
+ /(?:^|\/)(?:schema|[^/]+)\.graphql$/i,
6
+ /(?:^|\/)[^/]+\.schema\.json$/i,
7
+ /(?:^|\/)prisma\/schema\.prisma$/i,
8
+ /(?:^|\/)db\/schema\.(?:ts|js)$/i,
9
+ /(?:^|\/)src\/schema\.(?:ts|js)$/i,
10
+ ];
11
+ const TEST_FILE = /(?:\.test\.|\.spec\.|__tests__|^tests?\/|^e2e\/)/i;
12
+ const GENERATION_OR_VALIDATION_SCRIPT = /\b(openapi|swagger|proto|buf|graphql|prisma|drizzle|schema|contract)\b/i;
13
+ export default defineProbe({
14
+ id: "contracts.machine-readable",
15
+ version: "1.0.0",
16
+ dimensions: [
17
+ { id: "context", weight: 1 },
18
+ { id: "feedback", weight: 0.7 },
19
+ ],
20
+ tier: "static",
21
+ evidence: ["size_stats", "node_package"],
22
+ rationale: `
23
+ Agents do better when boundary facts live in machine-readable contracts:
24
+ OpenAPI, GraphQL, protobuf, JSON Schema, Prisma/DB schema, or equivalent.
25
+ This probe does not require every repo to expose an API; it activates when
26
+ contract files exist and then checks for tests or validation/generation
27
+ scripts that keep those contracts honest.
28
+ `,
29
+ remediation: "Keep API, message, and data-shape contracts in machine-readable files, then wire them to tests or generation/validation scripts. A contract that is not checked can drift into decorative documentation.",
30
+ async detect(ev) {
31
+ const paths = ev.size_stats.files.filter((f) => !f.generated).map((f) => f.path);
32
+ const contracts = paths
33
+ .filter((p) => CONTRACT_PATTERNS.some((pattern) => pattern.test(p)))
34
+ .sort();
35
+ if (contracts.length === 0) {
36
+ return { kind: "na", reason: "no machine-readable contract files detected" };
37
+ }
38
+ const testPaths = paths.filter((p) => TEST_FILE.test(p));
39
+ const scripts = ev.node_package.present ? Object.values(ev.node_package.scripts ?? {}) : [];
40
+ const hasContractScript = scripts.some((script) => GENERATION_OR_VALIDATION_SCRIPT.test(script));
41
+ const items = [];
42
+ for (const contract of contracts) {
43
+ const stem = contractStem(contract);
44
+ const hasNearbyTest = testPaths.some((p) => p.toLowerCase().includes(stem));
45
+ if (!hasNearbyTest) {
46
+ items.push({
47
+ location: { path: contract },
48
+ severity: "warn",
49
+ message: "contract has no obvious matching test file",
50
+ });
51
+ }
52
+ }
53
+ if (!hasContractScript) {
54
+ items.push({
55
+ location: { path: "package.json#scripts" },
56
+ severity: "info",
57
+ message: "no obvious contract generation or validation script",
58
+ });
59
+ }
60
+ return { kind: "inventory", items };
61
+ },
62
+ score: {
63
+ kind: "inventory",
64
+ severityWeights: { info: 1, warn: 3, error: 10 },
65
+ bands: [{ upTo: 0, score: 100 }, { upTo: 2, score: 80 }, { upTo: 6, score: 50 }, { score: 0 }],
66
+ },
67
+ fixtures: [
68
+ {
69
+ name: "no-contracts",
70
+ evidence: {
71
+ size_stats: {
72
+ source: "git-ls-files",
73
+ totalBytes: 100,
74
+ totalFiles: 1,
75
+ files: [{ path: "src/index.ts", bytes: 100, lines: 5, depth: 1 }],
76
+ },
77
+ node_package: { present: true, scripts: {} },
78
+ },
79
+ expect: {
80
+ reading: { kind: "na", reason: "no machine-readable contract files detected" },
81
+ score: null,
82
+ },
83
+ },
84
+ {
85
+ name: "contract-tested-and-generated",
86
+ evidence: {
87
+ size_stats: {
88
+ source: "git-ls-files",
89
+ totalBytes: 200,
90
+ totalFiles: 2,
91
+ files: [
92
+ { path: "openapi.yaml", bytes: 100, lines: 10, depth: 0 },
93
+ { path: "test/openapi.test.ts", bytes: 100, lines: 5, depth: 1 },
94
+ ],
95
+ },
96
+ node_package: { present: true, scripts: { "openapi:check": "openapi lint openapi.yaml" } },
97
+ },
98
+ expect: { reading: { kind: "inventory", items: [] }, score: 100 },
99
+ },
100
+ {
101
+ name: "contract-without-checks",
102
+ evidence: {
103
+ size_stats: {
104
+ source: "git-ls-files",
105
+ totalBytes: 100,
106
+ totalFiles: 1,
107
+ files: [{ path: "schema.graphql", bytes: 100, lines: 10, depth: 0 }],
108
+ },
109
+ node_package: { present: true, scripts: { test: "vitest run" } },
110
+ },
111
+ expect: {
112
+ reading: {
113
+ kind: "inventory",
114
+ items: [
115
+ {
116
+ location: { path: "schema.graphql" },
117
+ severity: "warn",
118
+ message: "contract has no obvious matching test file",
119
+ },
120
+ {
121
+ location: { path: "package.json#scripts" },
122
+ severity: "info",
123
+ message: "no obvious contract generation or validation script",
124
+ },
125
+ ],
126
+ },
127
+ score: 50,
128
+ },
129
+ },
130
+ ],
131
+ });
132
+ function contractStem(contract) {
133
+ const name = contract.split("/").pop() ?? contract;
134
+ return name
135
+ .replace(/\.(?:ya?ml|json|proto|graphql|prisma|ts|js)$/i, "")
136
+ .replace(/\.schema$/i, "")
137
+ .toLowerCase();
138
+ }
139
+ //# sourceMappingURL=contracts-machine-readable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts-machine-readable.js","sourceRoot":"","sources":["../../src/probes/contracts-machine-readable.ts"],"names":[],"mappings":"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"}