gitgauge 0.1.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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/dist/adapters/claudeCode.d.ts +6 -0
  4. package/dist/adapters/claudeCode.d.ts.map +1 -0
  5. package/dist/adapters/claudeCode.js +53 -0
  6. package/dist/adapters/claudeCode.js.map +1 -0
  7. package/dist/adapters/script.d.ts +3 -0
  8. package/dist/adapters/script.d.ts.map +1 -0
  9. package/dist/adapters/script.js +17 -0
  10. package/dist/adapters/script.js.map +1 -0
  11. package/dist/adapters/types.d.ts +25 -0
  12. package/dist/adapters/types.d.ts.map +1 -0
  13. package/dist/adapters/types.js +35 -0
  14. package/dist/adapters/types.js.map +1 -0
  15. package/dist/cli.d.ts +3 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +77 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/commands/init.d.ts +2 -0
  20. package/dist/commands/init.d.ts.map +1 -0
  21. package/dist/commands/init.js +22 -0
  22. package/dist/commands/init.js.map +1 -0
  23. package/dist/commands/mine.d.ts +12 -0
  24. package/dist/commands/mine.d.ts.map +1 -0
  25. package/dist/commands/mine.js +94 -0
  26. package/dist/commands/mine.js.map +1 -0
  27. package/dist/commands/report.d.ts +4 -0
  28. package/dist/commands/report.d.ts.map +1 -0
  29. package/dist/commands/report.js +16 -0
  30. package/dist/commands/report.js.map +1 -0
  31. package/dist/commands/run.d.ts +6 -0
  32. package/dist/commands/run.d.ts.map +1 -0
  33. package/dist/commands/run.js +27 -0
  34. package/dist/commands/run.js.map +1 -0
  35. package/dist/config.d.ts +78 -0
  36. package/dist/config.d.ts.map +1 -0
  37. package/dist/config.js +74 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/exec.d.ts +19 -0
  40. package/dist/exec.d.ts.map +1 -0
  41. package/dist/exec.js +21 -0
  42. package/dist/exec.js.map +1 -0
  43. package/dist/git.d.ts +20 -0
  44. package/dist/git.d.ts.map +1 -0
  45. package/dist/git.js +114 -0
  46. package/dist/git.js.map +1 -0
  47. package/dist/linkdeps.d.ts +2 -0
  48. package/dist/linkdeps.d.ts.map +1 -0
  49. package/dist/linkdeps.js +22 -0
  50. package/dist/linkdeps.js.map +1 -0
  51. package/dist/mine/heuristics.d.ts +13 -0
  52. package/dist/mine/heuristics.d.ts.map +1 -0
  53. package/dist/mine/heuristics.js +115 -0
  54. package/dist/mine/heuristics.js.map +1 -0
  55. package/dist/mine/rewrite.d.ts +20 -0
  56. package/dist/mine/rewrite.d.ts.map +1 -0
  57. package/dist/mine/rewrite.js +61 -0
  58. package/dist/mine/rewrite.js.map +1 -0
  59. package/dist/mine/verify.d.ts +9 -0
  60. package/dist/mine/verify.d.ts.map +1 -0
  61. package/dist/mine/verify.js +50 -0
  62. package/dist/mine/verify.js.map +1 -0
  63. package/dist/overlay.d.ts +2 -0
  64. package/dist/overlay.d.ts.map +1 -0
  65. package/dist/overlay.js +19 -0
  66. package/dist/overlay.js.map +1 -0
  67. package/dist/report.d.ts +15 -0
  68. package/dist/report.d.ts.map +1 -0
  69. package/dist/report.js +61 -0
  70. package/dist/report.js.map +1 -0
  71. package/dist/results.d.ts +18 -0
  72. package/dist/results.d.ts.map +1 -0
  73. package/dist/results.js +21 -0
  74. package/dist/results.js.map +1 -0
  75. package/dist/runner.d.ts +7 -0
  76. package/dist/runner.d.ts.map +1 -0
  77. package/dist/runner.js +135 -0
  78. package/dist/runner.js.map +1 -0
  79. package/dist/score.d.ts +15 -0
  80. package/dist/score.d.ts.map +1 -0
  81. package/dist/score.js +50 -0
  82. package/dist/score.js.map +1 -0
  83. package/dist/tasks.d.ts +14 -0
  84. package/dist/tasks.d.ts.map +1 -0
  85. package/dist/tasks.js +36 -0
  86. package/dist/tasks.js.map +1 -0
  87. package/dist/version.d.ts +2 -0
  88. package/dist/version.d.ts.map +1 -0
  89. package/dist/version.js +2 -0
  90. package/dist/version.js.map +1 -0
  91. package/package.json +50 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 whelp99
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # gitgauge
2
+
3
+ gitgauge mines fail-to-pass evaluation tasks straight from your own repository's git history. It walks recent commits, finds bugfixes whose tests fail before the fix and pass after, and verifies each candidate by actually running the test suite in a throwaway git worktree at both commits. The result is a set of tasks that are native to your codebase, not a hand-curated benchmark repo.
4
+
5
+ Those tasks exist to answer one question: does a change to your agent's harness actually help? gitgauge runs one or more named agent configurations against every mined task and reports resolve rate, cost, wall time, and diff size per configuration. This is a different axis than SWE-bench (a fixed, research-oriented benchmark over a handful of famous open-source repos) or agent-vs-agent tools like repogauge. gitgauge instead compares *harness configurations* on *your* codebase: the same underlying model with a different `CLAUDE.md`, a different skill set, different CLI flags, or a different prompt, run against the same fail-to-pass tasks so you can see whether the change moved the needle.
6
+
7
+ Because the tasks come from your own history, they track the kind of bugs your codebase actually produces, and they cost nothing to source beyond `git log`. The tradeoff is that gitgauge is not a standardized cross-repo benchmark: numbers from one repo's mined tasks are not comparable to another repo's, and old commits can fail to build if their dependency versions have drifted. See Limitations below.
8
+
9
+ ## Requirements
10
+
11
+ - Node.js >= 20
12
+ - git
13
+ - A repository with a runnable test command (e.g. `npm test`, `pytest`)
14
+
15
+ ## Quickstart
16
+
17
+ ```bash
18
+ npm install -g gitgauge
19
+ cd your-repo
20
+ gitgauge init # writes gitgauge.yaml and .gitgauge/
21
+ # edit gitgauge.yaml: set testCommand (and setupCommand if needed)
22
+ gitgauge mine # mines and verifies fail-to-pass tasks from git history
23
+ # write one agent config YAML per harness configuration you want to compare
24
+ gitgauge run -c a.yaml -c b.yaml
25
+ gitgauge report
26
+ ```
27
+
28
+ `gitgauge init` creates `gitgauge.yaml` in the repo root and a `.gitgauge/.gitignore` (marking `.gitgauge/*` — where mined tasks and run results are stored — as untracked). It will not overwrite files that already exist.
29
+
30
+ ## gitgauge.yaml reference
31
+
32
+ ```yaml
33
+ testCommand: "npm test" # required: how to run the test suite
34
+ setupCommand: "npm install" # optional: run before testCommand in each worktree
35
+ mine:
36
+ last: 200 # optional, default 200: how many recent commits to scan
37
+ timeouts:
38
+ test: 300000 # optional, default 300000ms: timeout for testCommand
39
+ setup: 600000 # optional, default 600000ms: timeout for setupCommand
40
+ adapter: 900000 # optional, default 900000ms: timeout for the agent adapter run
41
+ worktree:
42
+ link: # optional, default []: dirs to symlink into throwaway worktrees
43
+ - node_modules
44
+ - .venv
45
+ ```
46
+
47
+ `worktree.link` symlinks the listed directories from the main repo into every worktree gitgauge creates (for mining verification and for agent runs), instead of reinstalling dependencies from scratch each time. This is recommended whenever `setupCommand` is slow or old commits may not resolve the same dependency versions — linking `node_modules` or `.venv` from the current checkout sidesteps both problems. Linked directories are also excluded from the diff-size metric so they don't inflate an agent's diff stats.
48
+
49
+ ## Agent config YAML reference
50
+
51
+ Each harness configuration you want to benchmark is a separate YAML file:
52
+
53
+ ```yaml
54
+ name: baseline # required: label shown in the report
55
+ adapter: claude-code # required: "claude-code" or "script"
56
+ model: sonnet # optional: passed to the adapter
57
+ args: # optional: extra CLI args appended to the adapter invocation
58
+ - "--permission-mode"
59
+ - "acceptEdits"
60
+ ```
61
+
62
+ - `adapter: claude-code` invokes the `claude` CLI with `-p --output-format json --permission-mode acceptEdits` (the prompt itself is piped to stdin rather than passed as an argv argument, so it does not appear in `ps` output), plus `--model <model>` if set, plus any `args` appended after. Cost and token usage are parsed from the JSON output.
63
+ - `adapter: script` requires a `command` field (a shell command) and runs it with the task prompt available in the `GITGAUGE_PROMPT` environment variable. Use this to plug in any other agent or harness. No cost/token metrics are collected for this adapter (both are reported as `null`).
64
+
65
+ Pass one or more configs to `gitgauge run` with repeated `-c`/`--config` flags: `gitgauge run -c a.yaml -c b.yaml`. Use `--task <id>` to run a single mined task instead of the full set.
66
+
67
+ ## How mining works
68
+
69
+ `gitgauge mine` walks the last `mine.last` commits (or `--last <n>` on the command line) and classifies each one by its subject line only: if the subject follows the Conventional Commits format (`type: ...` or `type(scope): ...`), the type must be `fix`, `bugfix`, or `hotfix`; otherwise the subject is checked against a fallback pattern of bug-related words (`fix`, `bug`, `error`, `crash`, `defect`, `regression`, and Korean equivalents `수정`, `오류`, `버그`). Commits are further required to touch at least one test file and at least one non-test, non-Markdown source file.
70
+
71
+ Each candidate is then verified in throwaway git worktrees (created with `git worktree add --detach` and removed afterward):
72
+
73
+ 1. Check out the fix commit, run `setupCommand` (if set) and `testCommand` — the suite must pass.
74
+ 2. Check out the fix commit's parent, run `setupCommand` (if set), overlay just the candidate's test files from the fix commit on top, and run `testCommand` — the suite must fail.
75
+
76
+ Only candidates that fail before the fix and pass after it are saved as tasks (in `.gitgauge/tasks/`), each with an id derived from the commit sha and subject, the base and fix commit shas, a prompt built from the commit subject and body, and the list of test files to overlay when scoring an agent's attempt.
77
+
78
+ ### Prompt quality classification
79
+
80
+ Each candidate commit body is classified as either **rich** (40 or more non-whitespace characters) or **title-only** (fewer than 40). The classification is stored on the saved task as `promptQuality` and reported in the `mine` command output so you can see how many tasks have substantive commit bodies versus minimal ones.
81
+
82
+ The `--require-body` flag tells `gitgauge mine` to skip any candidate whose body is title-only before verification, saving the time and worktree overhead of verifying commits whose body text would make for a thin task prompt. Candidates skipped this way are still counted in the total candidates found; they are not counted as verified, rich, or title-only.
83
+
84
+ Mined tasks are worth a quick human review — delete task YAML files from `.gitgauge/tasks/` whose fix required information that was unavailable from the source alone (e.g. live API responses), since no agent can solve those from the prompt.
85
+
86
+ ### LLM prompt rewriting (experimental)
87
+
88
+ By default a task prompt is simply `Fix the following bug:\n\n<subject>\n\n<body>`, which can contain spoilers — the commit message may describe exactly what changed and why. `--rewrite-prompts` passes the commit's subject, body, source diff, and test file contents to an LLM that produces a rewritten prompt describing only the symptom and expected behavior without revealing how the fix was implemented. This gives a more realistic evaluation of an agent's ability to diagnose a bug from scratch.
89
+
90
+ When rewriting succeeds, the original (unrewritten) prompt is preserved on the task as `originalPrompt`. If the rewrite fails (the LLM call errors or returns unexpected output), the original prompt is kept as-is, a warning is logged, and mining continues — a rewrite failure is never fatal.
91
+
92
+ Usage:
93
+
94
+ ```bash
95
+ gitgauge mine --rewrite-prompts --rewrite-model haiku
96
+ ```
97
+
98
+ `--rewrite-model` defaults to `haiku` (the fastest/cheapest Claude model) and is passed directly to the `claude` CLI's `--model` flag.
99
+
100
+ **Trust boundary:** mining — and prompt rewriting in particular — is designed for repositories whose history you trust. Commit messages and diffs are attacker-controlled input in an adversarial repo, and rewritten prompts are fed to the benchmarked agent, which runs with real permissions. The rewrite prompt wraps repository content in untrusted-data delimiters and instructs the model to ignore embedded instructions, but this is a mitigation, not a guarantee — do not point `--rewrite-prompts` at untrusted repositories.
101
+
102
+ **Requirements and costs:** This feature requires the `claude` CLI (`npm install -g @anthropic-ai/claude-code`) to be installed and available on PATH. Each rewrite invocation consumes tokens on the model you specify, so rewriting a large batch of tasks will incur token costs. Overriding the binary path is possible via the `GITGAUGE_CLAUDE_BIN` environment variable — this is primarily intended for internal testing and should not be needed in normal use.
103
+
104
+ ## Metrics table
105
+
106
+ `gitgauge report` prints one row per agent configuration (the latest run by default; pass `--all` to include every run recorded in `.gitgauge/`):
107
+
108
+ | Column | Meaning |
109
+ | --- | --- |
110
+ | CONFIG | Agent config name |
111
+ | RESOLVED | Tasks where the agent's patch made the suite pass, out of total tasks run |
112
+ | ERRORS | Tasks where setup or the adapter itself failed (crash/timeout), not counted as resolved or unresolved |
113
+ | REGRESS | Count of tasks where the agent's final state fails the base commit's own original test suite (a regression relative to pre-existing behavior), shown as `-` when the task's base-suite-passing status is unknown/unrecorded (e.g. tasks mined before this feature existed, or where the base suite already failed at mine time) |
114
+ | AVG COST | Mean `total_cost_usd` across tasks with a reported cost (`-` if none) |
115
+ | AVG TIME | Mean wall-clock time of the adapter run |
116
+ | AVG DIFF | Mean of (insertions + deletions) in the agent's diff, excluding `worktree.link` paths |
117
+
118
+ ## Limitations
119
+
120
+ - **No container sandboxing.** Agent runs execute directly in local throwaway git worktrees, with your OS user's permissions — there is no isolation beyond the worktree's own file boundary. The `claude-code` adapter defaults to `--permission-mode acceptEdits`; if you want tighter or looser permissions, set them explicitly via `args` in the agent config and be deliberate about it.
121
+ - **Task prompts come verbatim from commit messages.** The prompt is the fix commit's subject and body, which may describe or hint at the actual fix (a "spoiler"). This is acceptable for comparing configurations against each other, since every configuration sees the exact same prompt — but it means resolve rates should not be read as a measure of an agent's ability to diagnose bugs from scratch.
122
+ - **Old commits may not build.** Mining and scoring both check out historical commits into worktrees and run `setupCommand`/`testCommand` there; if dependencies have moved on since that commit, setup or the test run can fail for reasons unrelated to the bug itself. Mitigate this by keeping `mine.last` reasonably small (recent history is more likely to still build) and by using `worktree.link` to reuse your current `node_modules`/`.venv` instead of reinstalling per-commit.
123
+ - **Regression detection is suite-level.** The REGRESS column tells you that *something* regressed, not *which* test, and the tail of the failing suite's output is available in the run output for manual inspection but is not parsed per-test. To prevent agents from gaming this signal, gitgauge reverts agent edits to **pre-existing** files that match either the test-file heuristic or the test-infrastructure heuristic (e.g. `package.json`, `vitest.config.ts`, `jest.config.js`, `pytest.ini`, `Makefile`) to their committed state before running the regression check. Brand-new test-infrastructure files added by the agent are deleted before scoring (unlike brand-new plain test files, which are left alone). Files that match neither heuristic (e.g. shared test helpers outside recognized test directories or naming patterns) remain unprotected; this is a known container-free tradeoff, not a bug. The flip side: a fix that legitimately needs to change a protected file (e.g. adding a dependency to package.json or requirements.txt) is also reverted and will likely score as unresolved — protected-file changes are outside what gitgauge can currently evaluate.
@@ -0,0 +1,6 @@
1
+ import type { Adapter, AdapterOutcome, AgentConfig } from './types.js';
2
+ export declare function buildClaudeArgs(config: AgentConfig, _prompt: string): string[];
3
+ export declare function parseClaudeJson(stdout: string): AdapterOutcome;
4
+ export declare const claudeCodeAdapter: Adapter;
5
+ export declare const adapters: Record<string, Adapter>;
6
+ //# sourceMappingURL=claudeCode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudeCode.d.ts","sourceRoot":"","sources":["../../src/adapters/claudeCode.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAgB,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGrF,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAa9E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAc9D;AAED,eAAO,MAAM,iBAAiB,EAAE,OAgB/B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAG5C,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { run } from '../exec.js';
2
+ import { scriptAdapter } from './script.js';
3
+ import { AdapterError } from './types.js';
4
+ export function buildClaudeArgs(config, _prompt) {
5
+ const args = [
6
+ '-p',
7
+ '--output-format', 'json',
8
+ '--permission-mode', 'acceptEdits',
9
+ ];
10
+ if (config.model) {
11
+ args.push('--model', config.model);
12
+ }
13
+ if (config.args) {
14
+ args.push(...config.args);
15
+ }
16
+ return args;
17
+ }
18
+ export function parseClaudeJson(stdout) {
19
+ try {
20
+ const parsed = JSON.parse(stdout);
21
+ const costUsd = parsed.total_cost_usd ?? null;
22
+ let tokens = null;
23
+ const usage = parsed.usage;
24
+ if (usage != null) {
25
+ tokens = (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0)
26
+ + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0);
27
+ }
28
+ return { costUsd, tokens };
29
+ }
30
+ catch {
31
+ return { costUsd: null, tokens: null };
32
+ }
33
+ }
34
+ export const claudeCodeAdapter = {
35
+ run(input) {
36
+ const args = buildClaudeArgs(input.config, input.prompt);
37
+ const result = run('claude', args, {
38
+ cwd: input.worktreeDir,
39
+ timeoutMs: input.timeoutMs,
40
+ input: input.prompt,
41
+ });
42
+ if (result.timedOut || result.code !== 0) {
43
+ const stderrTail = result.stderr.slice(-200);
44
+ throw new AdapterError(`claude-code adapter exited with code ${result.code}${result.timedOut ? ' (timed out)' : ''}: ${stderrTail}`);
45
+ }
46
+ return parseClaudeJson(result.stdout);
47
+ },
48
+ };
49
+ export const adapters = {
50
+ 'claude-code': claudeCodeAdapter,
51
+ script: scriptAdapter,
52
+ };
53
+ //# sourceMappingURL=claudeCode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudeCode.js","sourceRoot":"","sources":["../../src/adapters/claudeCode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,OAAe;IAClE,MAAM,IAAI,GAAa;QACrB,IAAI;QACJ,iBAAiB,EAAE,MAAM;QACzB,mBAAmB,EAAE,aAAa;KACnC,CAAC;IACF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,OAAO,GAAkB,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC;QAC7D,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3B,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,MAAM,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;kBAC3D,CAAC,KAAK,CAAC,2BAA2B,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAY;IACxC,GAAG,CAAC,KAAmB;QACrB,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE;YACjC,GAAG,EAAE,KAAK,CAAC,WAAW;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,KAAK,EAAE,KAAK,CAAC,MAAM;SACpB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;YAC7C,MAAM,IAAI,YAAY,CACpB,wCAAwC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,EAAE,CAC7G,CAAC;QACJ,CAAC;QACD,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAA4B;IAC/C,aAAa,EAAE,iBAAiB;IAChC,MAAM,EAAE,aAAa;CACtB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Adapter } from './types.js';
2
+ export declare const scriptAdapter: Adapter;
3
+ //# sourceMappingURL=script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../src/adapters/script.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,YAAY,CAAC;AAGxE,eAAO,MAAM,aAAa,EAAE,OAe3B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { runShell } from '../exec.js';
2
+ import { AdapterError } from './types.js';
3
+ export const scriptAdapter = {
4
+ run(input) {
5
+ const result = runShell(input.config.command, {
6
+ cwd: input.worktreeDir,
7
+ timeoutMs: input.timeoutMs,
8
+ env: { ...process.env, GITGAUGE_PROMPT: input.prompt },
9
+ });
10
+ if (result.timedOut || result.code !== 0) {
11
+ const stderrTail = result.stderr.slice(-200);
12
+ throw new AdapterError(`script adapter exited with code ${result.code}${result.timedOut ? ' (timed out)' : ''}: ${stderrTail}`);
13
+ }
14
+ return { costUsd: null, tokens: null };
15
+ },
16
+ };
17
+ //# sourceMappingURL=script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script.js","sourceRoot":"","sources":["../../src/adapters/script.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,GAAG,CAAC,KAAmB;QACrB,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAQ,EAAE;YAC7C,GAAG,EAAE,KAAK,CAAC,WAAW;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,CAAC,MAAM,EAAE;SACvD,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;YAC7C,MAAM,IAAI,YAAY,CACpB,mCAAmC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,EAAE,CACxG,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzC,CAAC;CACF,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface AgentConfig {
2
+ name: string;
3
+ adapter: 'claude-code' | 'script';
4
+ model?: string;
5
+ args?: string[];
6
+ command?: string;
7
+ }
8
+ export interface AdapterOutcome {
9
+ costUsd: number | null;
10
+ tokens: number | null;
11
+ }
12
+ export interface AdapterInput {
13
+ worktreeDir: string;
14
+ prompt: string;
15
+ config: AgentConfig;
16
+ timeoutMs: number;
17
+ }
18
+ export interface Adapter {
19
+ run(input: AdapterInput): AdapterOutcome;
20
+ }
21
+ export declare class AdapterError extends Error {
22
+ constructor(message: string);
23
+ }
24
+ export declare function loadAgentConfig(file: string): AgentConfig;
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,GAAG,QAAQ,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,KAAK,EAAE,YAAY,GAAG,cAAc,CAAC;CAC1C;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAqBzD"}
@@ -0,0 +1,35 @@
1
+ import { readFileSync } from 'fs';
2
+ import { parse as parseYaml } from 'yaml';
3
+ import { z } from 'zod';
4
+ const AgentConfigSchema = z.object({
5
+ name: z.string(),
6
+ adapter: z.enum(['claude-code', 'script']),
7
+ model: z.string().optional(),
8
+ args: z.array(z.string()).optional(),
9
+ command: z.string().optional(),
10
+ }).refine((data) => data.adapter !== 'script' || (data.command != null && data.command.length > 0), { message: 'command is required for script adapter' });
11
+ export class AdapterError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'AdapterError';
15
+ }
16
+ }
17
+ export function loadAgentConfig(file) {
18
+ let raw;
19
+ try {
20
+ raw = readFileSync(file, 'utf8');
21
+ }
22
+ catch {
23
+ throw new Error(`Failed to read agent config at ${file}`);
24
+ }
25
+ const parsed = parseYaml(raw);
26
+ if (typeof parsed !== 'object' || parsed === null) {
27
+ throw new Error(`agent config at ${file} must contain a YAML object`);
28
+ }
29
+ const result = AgentConfigSchema.safeParse(parsed);
30
+ if (!result.success) {
31
+ throw new Error(`agent config at ${file} validation error: ${result.error.message}`);
32
+ }
33
+ return result.data;
34
+ }
35
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC1C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACpC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC,MAAM,CACP,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,EACxF,EAAE,OAAO,EAAE,wCAAwC,EAAE,CACtD,CAAC;AA0BF,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,6BAA6B,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,sBAAsB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CACpE,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAmB,CAAC;AACpC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { VERSION } from './version.js';
4
+ import { initCommand } from './commands/init.js';
5
+ import { mineCommand } from './commands/mine.js';
6
+ import { runCommand } from './commands/run.js';
7
+ import { reportCommand } from './commands/report.js';
8
+ const program = new Command('gitgauge');
9
+ program.version(VERSION);
10
+ program
11
+ .command('init')
12
+ .description('Initialize gitgauge configuration file')
13
+ .action(() => {
14
+ try {
15
+ const paths = initCommand(process.cwd());
16
+ for (const p of paths) {
17
+ console.log(`Created ${p}`);
18
+ }
19
+ }
20
+ catch (err) {
21
+ console.error(err.message);
22
+ process.exit(1);
23
+ }
24
+ });
25
+ program
26
+ .command('mine')
27
+ .description('Mine fail-to-pass tasks from git history')
28
+ .option('--last <n>', 'Number of recent commits to scan', parseInt)
29
+ .option('--require-body', 'Skip candidates whose commit body is empty or too short (title-only)')
30
+ .option('--rewrite-prompts', 'Rewrite task prompts using an LLM to reduce spoilers')
31
+ .option('--rewrite-model <model>', 'Model to use for prompt rewriting (default: haiku)')
32
+ .action((opts) => {
33
+ try {
34
+ const result = mineCommand(process.cwd(), {
35
+ last: opts.last,
36
+ requireBody: opts.requireBody,
37
+ rewritePrompts: opts.rewritePrompts,
38
+ rewriteModel: opts.rewriteModel,
39
+ }, console.log);
40
+ console.log(`\nMining complete: ${result.candidates} candidates, ${result.verified} verified, ${result.rich} rich, ${result.titleOnly} title-only.`);
41
+ }
42
+ catch (err) {
43
+ console.error(err.message);
44
+ process.exit(1);
45
+ }
46
+ });
47
+ program
48
+ .command('run')
49
+ .description('Run agents against mined tasks')
50
+ .requiredOption('-c, --config <file...>', 'Agent configuration file(s)')
51
+ .option('--task <id>', 'Run only a specific task by ID')
52
+ .action((opts) => {
53
+ try {
54
+ const results = runCommand(process.cwd(), { configs: opts.config, task: opts.task }, console.log);
55
+ console.log(`\nRun complete: ${results.length} result(s).`);
56
+ }
57
+ catch (err) {
58
+ console.error(err.message);
59
+ process.exit(1);
60
+ }
61
+ });
62
+ program
63
+ .command('report')
64
+ .description('Show results summary')
65
+ .option('--all', 'Show results from all runs instead of latest only')
66
+ .action((opts) => {
67
+ try {
68
+ const table = reportCommand(process.cwd(), { all: opts.all });
69
+ console.log(table);
70
+ }
71
+ catch (err) {
72
+ console.error(err.message);
73
+ process.exit(1);
74
+ }
75
+ });
76
+ program.parse();
77
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function initCommand(repoDir: string): string[];
2
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAoBrD"}
@@ -0,0 +1,22 @@
1
+ import { existsSync, writeFileSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { defaultConfigYaml } from '../config.js';
4
+ export function initCommand(repoDir) {
5
+ const paths = [];
6
+ // gitgauge.yaml
7
+ const yamlPath = join(repoDir, 'gitgauge.yaml');
8
+ if (!existsSync(yamlPath)) {
9
+ writeFileSync(yamlPath, defaultConfigYaml(), 'utf8');
10
+ paths.push(yamlPath);
11
+ }
12
+ // .gitgauge/.gitignore
13
+ const gitignoreDir = join(repoDir, '.gitgauge');
14
+ const gitignorePath = join(gitignoreDir, '.gitignore');
15
+ if (!existsSync(gitignorePath)) {
16
+ mkdirSync(gitignoreDir, { recursive: true });
17
+ writeFileSync(gitignorePath, '*\n', 'utf8');
18
+ paths.push(gitignorePath);
19
+ }
20
+ return paths;
21
+ }
22
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,gBAAgB;IAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAChD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,aAAa,CAAC,QAAQ,EAAE,iBAAiB,EAAE,EAAE,MAAM,CAAC,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,CAAC;IAED,uBAAuB;IACvB,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,aAAa,CAAC,aAAa,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare function mineCommand(repoDir: string, opts: {
2
+ last?: number;
3
+ requireBody?: boolean;
4
+ rewritePrompts?: boolean;
5
+ rewriteModel?: string;
6
+ }, log: (s: string) => void): {
7
+ candidates: number;
8
+ verified: number;
9
+ rich: number;
10
+ titleOnly: number;
11
+ };
12
+ //# sourceMappingURL=mine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mine.d.ts","sourceRoot":"","sources":["../../src/commands/mine.ts"],"names":[],"mappings":"AAOA,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACvB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CA+F3E"}
@@ -0,0 +1,94 @@
1
+ import { loadConfig } from '../config.js';
2
+ import { listCommits, changedFiles, parentSha, showCommitDiff, showFileAt } from '../git.js';
3
+ import { classifyCommit, classifyPromptQuality } from '../mine/heuristics.js';
4
+ import { verifyCandidate } from '../mine/verify.js';
5
+ import { makeTaskId, saveTask } from '../tasks.js';
6
+ import { rewriteTaskPrompt, RewriteError } from '../mine/rewrite.js';
7
+ export function mineCommand(repoDir, opts, log) {
8
+ const cfg = loadConfig(repoDir);
9
+ const last = opts.last ?? cfg.mine.last;
10
+ const commits = listCommits(repoDir, last);
11
+ let candidates = 0;
12
+ let verified = 0;
13
+ let rich = 0;
14
+ let titleOnly = 0;
15
+ for (const commit of commits) {
16
+ const files = changedFiles(repoDir, commit.sha);
17
+ const candidate = classifyCommit(commit, files);
18
+ if (candidate === null) {
19
+ continue;
20
+ }
21
+ candidates++;
22
+ log(`Candidate: ${candidate.subject} (${candidate.sha.slice(0, 7)})`);
23
+ const quality = classifyPromptQuality(candidate.subject, candidate.body);
24
+ if (opts.requireBody && quality === 'title-only') {
25
+ log(` Skipped (title-only): ${candidate.subject}`);
26
+ continue;
27
+ }
28
+ log(`Verifying ${candidate.sha.slice(0, 7)} ${candidate.subject}...`);
29
+ const verifyResult = verifyCandidate(repoDir, cfg, candidate);
30
+ if (!verifyResult.ok) {
31
+ log(` Rejected: ${verifyResult.reason}`);
32
+ continue;
33
+ }
34
+ const base = parentSha(repoDir, candidate.sha);
35
+ const originalPrompt = `Fix the following bug:\n\n${candidate.subject}\n\n${candidate.body}`.trim();
36
+ let taskPrompt = originalPrompt;
37
+ if (opts.rewritePrompts) {
38
+ const rewriteModel = opts.rewriteModel ?? 'haiku';
39
+ const sourceDiff = showCommitDiff(repoDir, candidate.sha, candidate.sourceFiles);
40
+ const testParts = [];
41
+ for (const tf of candidate.testFiles) {
42
+ const content = showFileAt(repoDir, candidate.sha, tf);
43
+ if (content !== null) {
44
+ testParts.push(content);
45
+ }
46
+ }
47
+ const testContent = testParts.join('\n\n');
48
+ try {
49
+ const rewritten = rewriteTaskPrompt({
50
+ subject: candidate.subject,
51
+ body: candidate.body,
52
+ sourceDiff,
53
+ testContent,
54
+ }, {
55
+ model: rewriteModel,
56
+ bin: process.env.GITGAUGE_CLAUDE_BIN ?? 'claude',
57
+ timeoutMs: cfg.timeouts.adapter,
58
+ });
59
+ taskPrompt = rewritten;
60
+ }
61
+ catch (err) {
62
+ if (err instanceof RewriteError) {
63
+ log(` Rewrite failed, keeping original prompt: ${err.message}`);
64
+ }
65
+ else {
66
+ throw err;
67
+ }
68
+ }
69
+ }
70
+ const task = {
71
+ id: makeTaskId(candidate.sha, candidate.subject),
72
+ baseCommit: base,
73
+ fixCommit: candidate.sha,
74
+ prompt: taskPrompt,
75
+ testFiles: candidate.testFiles,
76
+ baseSuitePasses: verifyResult.baseSuitePasses,
77
+ promptQuality: quality,
78
+ };
79
+ if (opts.rewritePrompts && taskPrompt !== originalPrompt) {
80
+ task.originalPrompt = originalPrompt;
81
+ }
82
+ saveTask(repoDir, task);
83
+ log(` Verified and saved as task ${task.id}`);
84
+ verified++;
85
+ if (quality === 'rich') {
86
+ rich++;
87
+ }
88
+ else {
89
+ titleOnly++;
90
+ }
91
+ }
92
+ return { candidates, verified, rich, titleOnly };
93
+ }
94
+ //# sourceMappingURL=mine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mine.js","sourceRoot":"","sources":["../../src/commands/mine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function reportCommand(repoDir: string, opts: {
2
+ all?: boolean;
3
+ }): string;
4
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GACtB,MAAM,CAcR"}
@@ -0,0 +1,16 @@
1
+ import { loadResults } from '../results.js';
2
+ import { summarize, renderTable } from '../report.js';
3
+ export function reportCommand(repoDir, opts) {
4
+ const all = loadResults(repoDir);
5
+ if (all.length === 0) {
6
+ return 'No results found.';
7
+ }
8
+ let subset = all;
9
+ if (!opts.all) {
10
+ const latestRunId = all[all.length - 1].runId;
11
+ subset = all.filter((r) => r.runId === latestRunId);
12
+ }
13
+ const rows = summarize(subset);
14
+ return renderTable(rows);
15
+ }
16
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,IAAuB;IAEvB,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAED,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9C,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { RunResult } from '../results.js';
2
+ export declare function runCommand(repoDir: string, opts: {
3
+ configs: string[];
4
+ task?: string;
5
+ }, log: (s: string) => void): RunResult[];
6
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1C,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACvB,SAAS,EAAE,CAyBb"}