@graypark/loophaus 3.5.0 → 3.6.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.
@@ -0,0 +1,208 @@
1
+ // core/benchmark.ts
2
+ // Project-level quality measurement (autoresearch pattern: val_bpb → project score)
3
+ import { execFile } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { readFile, appendFile, mkdir, stat } from "node:fs/promises";
6
+ import { join, dirname } from "node:path";
7
+ const execFileAsync = promisify(execFile);
8
+ export function scoreBenchmark(metrics) {
9
+ const breakdown = {};
10
+ // Tests: 0-10 based on pass rate
11
+ const testRate = metrics.testsTotal > 0 ? metrics.testsPassed / metrics.testsTotal : 0;
12
+ breakdown.tests = { value: metrics.testsPassed, max: metrics.testsTotal, score: Math.round(testRate * 10) };
13
+ // Typecheck: 10 if 0 errors, degrade
14
+ const tcScore = metrics.typecheckErrors === 0 ? 10 : metrics.typecheckErrors <= 5 ? 6 : metrics.typecheckErrors <= 20 ? 3 : 0;
15
+ breakdown.typecheck = { value: metrics.typecheckErrors, max: 0, score: tcScore };
16
+ // Build: binary
17
+ breakdown.build = { value: metrics.buildSuccess ? 1 : 0, max: 1, score: metrics.buildSuccess ? 10 : 0 };
18
+ // Test time: < 2s = 10, < 5s = 8, < 10s = 6, < 30s = 4, else 2
19
+ const ttScore = metrics.testTimeMs < 2000 ? 10 : metrics.testTimeMs < 5000 ? 8 : metrics.testTimeMs < 10000 ? 6 : metrics.testTimeMs < 30000 ? 4 : 2;
20
+ breakdown.testTime = { value: metrics.testTimeMs, max: 2000, score: ttScore };
21
+ // Coverage: direct percentage mapping to 0-10
22
+ const covScore = Math.min(10, Math.round(metrics.coveragePct / 10));
23
+ breakdown.coverage = { value: metrics.coveragePct, max: 100, score: covScore };
24
+ // Package size: < 50KB = 10, < 100KB = 8, < 200KB = 6, < 500KB = 4, else 2
25
+ const sizeScore = metrics.pkgSizeKb < 50 ? 10 : metrics.pkgSizeKb < 100 ? 8 : metrics.pkgSizeKb < 200 ? 6 : metrics.pkgSizeKb < 500 ? 4 : 2;
26
+ breakdown.pkgSize = { value: metrics.pkgSizeKb, max: 50, score: sizeScore };
27
+ // Weighted average (tests 3x, typecheck 2.5x, build 1.5x, coverage 2x, testTime 0.5x, pkgSize 0.5x)
28
+ const weights = { tests: 3, typecheck: 2.5, build: 1.5, coverage: 2, testTime: 0.5, pkgSize: 0.5 };
29
+ let weightedSum = 0;
30
+ let totalWeight = 0;
31
+ for (const [key, w] of Object.entries(weights)) {
32
+ weightedSum += breakdown[key].score * w;
33
+ totalWeight += 10 * w;
34
+ }
35
+ const score = Math.round((weightedSum / totalWeight) * 100);
36
+ const grade = score >= 90 ? "A+" : score >= 85 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
37
+ return { score, grade, breakdown, metrics };
38
+ }
39
+ export async function runBenchmark(cwd) {
40
+ const dir = cwd || process.cwd();
41
+ const metrics = {
42
+ testsPassed: 0,
43
+ testsFailed: 0,
44
+ testsTotal: 0,
45
+ testTimeMs: 0,
46
+ typecheckErrors: 0,
47
+ buildSuccess: false,
48
+ coveragePct: 0,
49
+ pkgSizeKb: 0,
50
+ };
51
+ // 1. Tests
52
+ const testStart = Date.now();
53
+ try {
54
+ const { stdout } = await execFileAsync("npx", ["vitest", "run", "--reporter=json"], { cwd: dir, timeout: 120_000 });
55
+ metrics.testTimeMs = Date.now() - testStart;
56
+ try {
57
+ const json = JSON.parse(stdout);
58
+ metrics.testsPassed = json.numPassedTests ?? 0;
59
+ metrics.testsFailed = json.numFailedTests ?? 0;
60
+ metrics.testsTotal = json.numTotalTests ?? 0;
61
+ }
62
+ catch {
63
+ // JSON parse failed, try regex fallback
64
+ const passMatch = stdout.match(/(\d+) passed/);
65
+ const failMatch = stdout.match(/(\d+) failed/);
66
+ if (passMatch)
67
+ metrics.testsPassed = parseInt(passMatch[1]);
68
+ if (failMatch)
69
+ metrics.testsFailed = parseInt(failMatch[1]);
70
+ metrics.testsTotal = metrics.testsPassed + metrics.testsFailed;
71
+ }
72
+ }
73
+ catch (err) {
74
+ metrics.testTimeMs = Date.now() - testStart;
75
+ const output = err.stdout || "";
76
+ const passMatch = output.match(/(\d+) passed/);
77
+ if (passMatch)
78
+ metrics.testsPassed = parseInt(passMatch[1]);
79
+ const failMatch = output.match(/(\d+) failed/);
80
+ if (failMatch)
81
+ metrics.testsFailed = parseInt(failMatch[1]);
82
+ metrics.testsTotal = metrics.testsPassed + metrics.testsFailed;
83
+ }
84
+ // 2. Typecheck
85
+ try {
86
+ await execFileAsync("npx", ["tsc", "--noEmit"], { cwd: dir, timeout: 60_000 });
87
+ metrics.typecheckErrors = 0;
88
+ }
89
+ catch (err) {
90
+ const output = err.stdout || err.stderr || "";
91
+ const errorCount = (output.match(/error TS/g) || []).length;
92
+ metrics.typecheckErrors = errorCount || 1;
93
+ }
94
+ // 3. Build
95
+ try {
96
+ await execFileAsync("npm", ["run", "build"], { cwd: dir, timeout: 60_000 });
97
+ metrics.buildSuccess = true;
98
+ }
99
+ catch {
100
+ metrics.buildSuccess = false;
101
+ }
102
+ // 4. Coverage
103
+ try {
104
+ const summaryPath = join(dir, "coverage", "coverage-summary.json");
105
+ const raw = await readFile(summaryPath, "utf-8");
106
+ const summary = JSON.parse(raw);
107
+ metrics.coveragePct = summary.total?.lines?.pct ?? 0;
108
+ }
109
+ catch {
110
+ // Run coverage if summary doesn't exist
111
+ try {
112
+ await execFileAsync("npx", ["vitest", "run", "--coverage"], { cwd: dir, timeout: 120_000 });
113
+ const summaryPath = join(dir, "coverage", "coverage-summary.json");
114
+ const raw = await readFile(summaryPath, "utf-8");
115
+ const summary = JSON.parse(raw);
116
+ metrics.coveragePct = summary.total?.lines?.pct ?? 0;
117
+ }
118
+ catch {
119
+ metrics.coveragePct = 0;
120
+ }
121
+ }
122
+ // 5. Package size
123
+ try {
124
+ const distDir = join(dir, "dist");
125
+ const s = await stat(distDir);
126
+ if (s.isDirectory()) {
127
+ const { stdout } = await execFileAsync("du", ["-sk", distDir], { timeout: 10_000 });
128
+ const match = stdout.match(/^(\d+)/);
129
+ metrics.pkgSizeKb = match ? parseInt(match[1]) : 0;
130
+ }
131
+ }
132
+ catch {
133
+ metrics.pkgSizeKb = 0;
134
+ }
135
+ return scoreBenchmark(metrics);
136
+ }
137
+ function getBenchmarkPath(cwd) {
138
+ return join(cwd || process.cwd(), ".loophaus", "benchmark.tsv");
139
+ }
140
+ const HEADER = "ts\tcommit\tversion\tscore\tgrade\ttests_passed\ttests_total\ttest_time_ms\ttypecheck_errors\tbuild_ok\tcoverage_pct\tpkg_size_kb\n";
141
+ export async function logBenchmark(result, cwd) {
142
+ const benchPath = getBenchmarkPath(cwd);
143
+ await mkdir(dirname(benchPath), { recursive: true });
144
+ let commitHash = "unknown";
145
+ try {
146
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--short", "HEAD"], { timeout: 5_000 });
147
+ commitHash = stdout.trim();
148
+ }
149
+ catch { /* not in git */ }
150
+ let version = "unknown";
151
+ try {
152
+ const pkgPath = join(cwd || process.cwd(), "package.json");
153
+ const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
154
+ version = pkg.version || "unknown";
155
+ }
156
+ catch { /* no package.json */ }
157
+ // Write header if file is new
158
+ try {
159
+ await stat(benchPath);
160
+ }
161
+ catch {
162
+ await appendFile(benchPath, HEADER, "utf-8");
163
+ }
164
+ const m = result.metrics;
165
+ const line = [
166
+ new Date().toISOString(),
167
+ commitHash,
168
+ version,
169
+ result.score,
170
+ result.grade,
171
+ m.testsPassed,
172
+ m.testsTotal,
173
+ m.testTimeMs,
174
+ m.typecheckErrors,
175
+ m.buildSuccess ? 1 : 0,
176
+ m.coveragePct.toFixed(1),
177
+ m.pkgSizeKb,
178
+ ].join("\t") + "\n";
179
+ await appendFile(benchPath, line, "utf-8");
180
+ }
181
+ export async function readBenchmarkHistory(cwd) {
182
+ const benchPath = getBenchmarkPath(cwd);
183
+ try {
184
+ const raw = await readFile(benchPath, "utf-8");
185
+ const lines = raw.trim().split("\n").slice(1); // skip header
186
+ return lines.map(line => {
187
+ const cols = line.split("\t");
188
+ return {
189
+ ts: cols[0] || "",
190
+ commit: cols[1] || "",
191
+ version: cols[2] || "",
192
+ score: parseInt(cols[3]) || 0,
193
+ grade: cols[4] || "",
194
+ testsPassed: parseInt(cols[5]) || 0,
195
+ testsTotal: parseInt(cols[6]) || 0,
196
+ testTimeMs: parseInt(cols[7]) || 0,
197
+ typecheckErrors: parseInt(cols[8]) || 0,
198
+ buildSuccess: cols[9] === "1",
199
+ coveragePct: parseFloat(cols[10]) || 0,
200
+ pkgSizeKb: parseInt(cols[11]) || 0,
201
+ };
202
+ });
203
+ }
204
+ catch {
205
+ return [];
206
+ }
207
+ }
208
+ //# sourceMappingURL=benchmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"benchmark.js","sourceRoot":"","sources":["../../core/benchmark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface CleanupConfig {
2
+ cleanup: {
3
+ onNewPlan: "archive" | "delete" | "keep";
4
+ traceRetentionDays: number;
5
+ sessionRetentionDays: number;
6
+ };
7
+ }
8
+ export declare function readConfig(cwd?: string): Promise<CleanupConfig>;
9
+ export declare function writeConfig(config: CleanupConfig, cwd?: string): Promise<void>;
10
+ export interface CleanResult {
11
+ removed: string[];
12
+ archived: string[];
13
+ skipped: string[];
14
+ }
15
+ export declare function cleanTraces(cwd?: string): Promise<CleanResult>;
16
+ export declare function cleanResults(cwd?: string): Promise<CleanResult>;
17
+ export declare function cleanSessions(options?: {
18
+ cwd?: string;
19
+ before?: Date;
20
+ }): Promise<CleanResult>;
21
+ export declare function cleanAll(cwd?: string): Promise<CleanResult>;
22
+ export declare function archiveCurrentData(cwd?: string): Promise<CleanResult>;
23
+ export declare function applyOnNewPlanPolicy(cwd?: string): Promise<CleanResult>;
24
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../core/cleanup.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QACP,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;QACzC,kBAAkB,EAAE,MAAM,CAAC;QAC3B,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH;AAiBD,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAWrE;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpF;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAWpE;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAWrE;AAED,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBnG;AAED,wBAAsB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAUjE;AAED,wBAAsB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoC3E;AAED,wBAAsB,oBAAoB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAW7E"}
@@ -0,0 +1,145 @@
1
+ // core/cleanup.ts
2
+ // Data lifecycle management for .loophaus/ directory
3
+ import { readFile, writeFile, readdir, rm, rename, mkdir, stat } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ const DEFAULT_CONFIG = {
6
+ cleanup: {
7
+ onNewPlan: "keep",
8
+ traceRetentionDays: 30,
9
+ sessionRetentionDays: 7,
10
+ },
11
+ };
12
+ // Files that are NEVER deleted by any clean operation
13
+ const PROTECTED_FILES = new Set(["benchmark.tsv", "config.json"]);
14
+ function getLoophausDir(cwd) {
15
+ return join(cwd || process.cwd(), ".loophaus");
16
+ }
17
+ export async function readConfig(cwd) {
18
+ const configPath = join(getLoophausDir(cwd), "config.json");
19
+ try {
20
+ const raw = await readFile(configPath, "utf-8");
21
+ const parsed = JSON.parse(raw);
22
+ return {
23
+ cleanup: { ...DEFAULT_CONFIG.cleanup, ...parsed.cleanup },
24
+ };
25
+ }
26
+ catch {
27
+ return { ...DEFAULT_CONFIG };
28
+ }
29
+ }
30
+ export async function writeConfig(config, cwd) {
31
+ const dir = getLoophausDir(cwd);
32
+ await mkdir(dir, { recursive: true });
33
+ await writeFile(join(dir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
34
+ }
35
+ export async function cleanTraces(cwd) {
36
+ const dir = getLoophausDir(cwd);
37
+ const result = { removed: [], archived: [], skipped: [] };
38
+ const tracePath = join(dir, "trace.jsonl");
39
+ try {
40
+ await rm(tracePath);
41
+ result.removed.push("trace.jsonl");
42
+ }
43
+ catch {
44
+ result.skipped.push("trace.jsonl");
45
+ }
46
+ return result;
47
+ }
48
+ export async function cleanResults(cwd) {
49
+ const dir = getLoophausDir(cwd);
50
+ const result = { removed: [], archived: [], skipped: [] };
51
+ const resultsPath = join(dir, "results.tsv");
52
+ try {
53
+ await rm(resultsPath);
54
+ result.removed.push("results.tsv");
55
+ }
56
+ catch {
57
+ result.skipped.push("results.tsv");
58
+ }
59
+ return result;
60
+ }
61
+ export async function cleanSessions(options) {
62
+ const dir = join(getLoophausDir(options?.cwd), "sessions");
63
+ const result = { removed: [], archived: [], skipped: [] };
64
+ try {
65
+ const files = await readdir(dir);
66
+ for (const file of files) {
67
+ if (!file.endsWith(".json"))
68
+ continue;
69
+ const filePath = join(dir, file);
70
+ if (options?.before) {
71
+ const s = await stat(filePath);
72
+ if (s.mtime >= options.before) {
73
+ result.skipped.push(file);
74
+ continue;
75
+ }
76
+ }
77
+ await rm(filePath);
78
+ result.removed.push(file);
79
+ }
80
+ }
81
+ catch {
82
+ // sessions/ doesn't exist
83
+ }
84
+ return result;
85
+ }
86
+ export async function cleanAll(cwd) {
87
+ const result = { removed: [], archived: [], skipped: [] };
88
+ const r1 = await cleanTraces(cwd);
89
+ const r2 = await cleanResults(cwd);
90
+ const r3 = await cleanSessions({ cwd });
91
+ result.removed.push(...r1.removed, ...r2.removed, ...r3.removed);
92
+ result.skipped.push(...r1.skipped, ...r2.skipped, ...r3.skipped);
93
+ // Explicitly note protected files
94
+ result.skipped.push("benchmark.tsv (protected)", "config.json (protected)");
95
+ return result;
96
+ }
97
+ export async function archiveCurrentData(cwd) {
98
+ const dir = getLoophausDir(cwd);
99
+ const result = { removed: [], archived: [], skipped: [] };
100
+ const dateStr = new Date().toISOString().split("T")[0];
101
+ const archiveDir = join(dir, "archive", dateStr);
102
+ await mkdir(archiveDir, { recursive: true });
103
+ const filesToArchive = ["trace.jsonl", "results.tsv"];
104
+ for (const file of filesToArchive) {
105
+ const src = join(dir, file);
106
+ const dest = join(archiveDir, file);
107
+ try {
108
+ await rename(src, dest);
109
+ result.archived.push(`${file} → archive/${dateStr}/${file}`);
110
+ }
111
+ catch {
112
+ result.skipped.push(file);
113
+ }
114
+ }
115
+ // Archive sessions
116
+ const sessionsDir = join(dir, "sessions");
117
+ try {
118
+ const files = await readdir(sessionsDir);
119
+ if (files.length > 0) {
120
+ const sessArchive = join(archiveDir, "sessions");
121
+ await mkdir(sessArchive, { recursive: true });
122
+ for (const file of files) {
123
+ await rename(join(sessionsDir, file), join(sessArchive, file));
124
+ result.archived.push(`sessions/${file}`);
125
+ }
126
+ }
127
+ }
128
+ catch {
129
+ // no sessions
130
+ }
131
+ return result;
132
+ }
133
+ export async function applyOnNewPlanPolicy(cwd) {
134
+ const config = await readConfig(cwd);
135
+ switch (config.cleanup.onNewPlan) {
136
+ case "archive":
137
+ return archiveCurrentData(cwd);
138
+ case "delete":
139
+ return cleanAll(cwd);
140
+ case "keep":
141
+ default:
142
+ return { removed: [], archived: [], skipped: ["policy: keep (no cleanup)"] };
143
+ }
144
+ }
145
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../core/cleanup.ts"],"names":[],"mappings":"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"}
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graypark/loophaus",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "type": "module",
5
5
  "description": "loophaus — Control plane for coding agents. Iterative dev loops with multi-agent orchestration.",
6
6
  "license": "MIT",
@@ -37,6 +37,7 @@
37
37
  "postbuild": "node -e \"const fs=require('fs');const p=require('path');['platforms','hooks','codex','commands','scripts','skills','.claude-plugin'].forEach(d=>{const s=p.join(__dirname,d);const t=p.join(__dirname,'dist',d);if(fs.existsSync(s))fs.cpSync(s,t,{recursive:true})});['package.json','LICENSE','README.md','README.ko.md'].forEach(f=>{const s=p.join(__dirname,f);const t=p.join(__dirname,'dist',f);if(fs.existsSync(s))fs.cpSync(s,t)})\"",
38
38
  "typecheck": "tsc --noEmit",
39
39
  "test": "vitest run",
40
+ "test:coverage": "vitest run --coverage",
40
41
  "postinstall": "echo 'Run: npx loophaus --help'",
41
42
  "prepublishOnly": "npm run build",
42
43
  "prepare": "npm run build"
@@ -55,6 +56,7 @@
55
56
  ],
56
57
  "devDependencies": {
57
58
  "@types/node": "^25.5.0",
59
+ "@vitest/coverage-v8": "^4.1.2",
58
60
  "typescript": "^6.0.2",
59
61
  "vitest": "^4.1.0"
60
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graypark/loophaus",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "type": "module",
5
5
  "description": "loophaus — Control plane for coding agents. Iterative dev loops with multi-agent orchestration.",
6
6
  "license": "MIT",
@@ -37,6 +37,7 @@
37
37
  "postbuild": "node -e \"const fs=require('fs');const p=require('path');['platforms','hooks','codex','commands','scripts','skills','.claude-plugin'].forEach(d=>{const s=p.join(__dirname,d);const t=p.join(__dirname,'dist',d);if(fs.existsSync(s))fs.cpSync(s,t,{recursive:true})});['package.json','LICENSE','README.md','README.ko.md'].forEach(f=>{const s=p.join(__dirname,f);const t=p.join(__dirname,'dist',f);if(fs.existsSync(s))fs.cpSync(s,t)})\"",
38
38
  "typecheck": "tsc --noEmit",
39
39
  "test": "vitest run",
40
+ "test:coverage": "vitest run --coverage",
40
41
  "postinstall": "echo 'Run: npx loophaus --help'",
41
42
  "prepublishOnly": "npm run build",
42
43
  "prepare": "npm run build"
@@ -55,6 +56,7 @@
55
56
  ],
56
57
  "devDependencies": {
57
58
  "@types/node": "^25.5.0",
59
+ "@vitest/coverage-v8": "^4.1.2",
58
60
  "typescript": "^6.0.2",
59
61
  "vitest": "^4.1.0"
60
62
  }