@runchr/gstack-antigravity 0.1.1 → 0.1.3
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.
Potentially problematic release.
This version of @runchr/gstack-antigravity might be problematic. Click here for more details.
- package/.agents/skills/gstack/.agents/skills/gstack/SKILL.md +651 -0
- package/.agents/skills/gstack/.agents/skills/gstack-autoplan/SKILL.md +678 -0
- package/.agents/skills/gstack/.agents/skills/gstack-benchmark/SKILL.md +482 -0
- package/.agents/skills/gstack/.agents/skills/gstack-browse/SKILL.md +511 -0
- package/.agents/skills/gstack/.agents/skills/gstack-canary/SKILL.md +486 -0
- package/.agents/skills/gstack/.agents/skills/gstack-careful/SKILL.md +50 -0
- package/.agents/skills/gstack/.agents/skills/gstack-cso/SKILL.md +607 -0
- package/.agents/skills/gstack/.agents/skills/gstack-design-consultation/SKILL.md +615 -0
- package/.agents/skills/gstack/.agents/skills/gstack-design-review/SKILL.md +988 -0
- package/.agents/skills/gstack/.agents/skills/gstack-document-release/SKILL.md +604 -0
- package/.agents/skills/gstack/.agents/skills/gstack-freeze/SKILL.md +67 -0
- package/.agents/skills/gstack/.agents/skills/gstack-guard/SKILL.md +62 -0
- package/.agents/skills/gstack/.agents/skills/gstack-investigate/SKILL.md +415 -0
- package/.agents/skills/gstack/.agents/skills/gstack-land-and-deploy/SKILL.md +873 -0
- package/.agents/skills/gstack/.agents/skills/gstack-office-hours/SKILL.md +986 -0
- package/.agents/skills/gstack/.agents/skills/gstack-plan-ceo-review/SKILL.md +1268 -0
- package/.agents/skills/gstack/.agents/skills/gstack-plan-design-review/SKILL.md +668 -0
- package/.agents/skills/gstack/.agents/skills/gstack-plan-eng-review/SKILL.md +826 -0
- package/.agents/skills/gstack/.agents/skills/gstack-qa/SKILL.md +1006 -0
- package/.agents/skills/gstack/.agents/skills/gstack-qa-only/SKILL.md +626 -0
- package/.agents/skills/gstack/.agents/skills/gstack-retro/SKILL.md +1065 -0
- package/.agents/skills/gstack/.agents/skills/gstack-review/SKILL.md +704 -0
- package/.agents/skills/gstack/.agents/skills/gstack-setup-browser-cookies/SKILL.md +325 -0
- package/.agents/skills/gstack/.agents/skills/gstack-setup-deploy/SKILL.md +450 -0
- package/.agents/skills/gstack/.agents/skills/gstack-ship/SKILL.md +1312 -0
- package/.agents/skills/gstack/.agents/skills/gstack-unfreeze/SKILL.md +36 -0
- package/.agents/skills/gstack/.agents/skills/gstack-upgrade/SKILL.md +220 -0
- package/.agents/skills/gstack/.env.example +5 -0
- package/.agents/skills/gstack/.github/workflows/skill-docs.yml +17 -0
- package/.agents/skills/gstack/AGENTS.md +49 -0
- package/.agents/skills/gstack/ARCHITECTURE.md +359 -0
- package/.agents/skills/gstack/BROWSER.md +271 -0
- package/.agents/skills/gstack/CHANGELOG.md +800 -0
- package/.agents/skills/gstack/CLAUDE.md +284 -0
- package/.agents/skills/gstack/CONTRIBUTING.md +370 -0
- package/.agents/skills/gstack/ETHOS.md +129 -0
- package/.agents/skills/gstack/LICENSE +21 -0
- package/.agents/skills/gstack/README.md +228 -0
- package/.agents/skills/gstack/SKILL.md +657 -0
- package/.agents/skills/gstack/SKILL.md.tmpl +281 -0
- package/.agents/skills/gstack/TODOS.md +564 -0
- package/.agents/skills/gstack/VERSION +1 -0
- package/.agents/skills/gstack/autoplan/SKILL.md +689 -0
- package/.agents/skills/gstack/autoplan/SKILL.md.tmpl +416 -0
- package/.agents/skills/gstack/benchmark/SKILL.md +489 -0
- package/.agents/skills/gstack/benchmark/SKILL.md.tmpl +233 -0
- package/.agents/skills/gstack/bin/dev-setup +68 -0
- package/.agents/skills/gstack/bin/dev-teardown +56 -0
- package/.agents/skills/gstack/bin/gstack-analytics +191 -0
- package/.agents/skills/gstack/bin/gstack-community-dashboard +113 -0
- package/.agents/skills/gstack/bin/gstack-config +38 -0
- package/.agents/skills/gstack/bin/gstack-diff-scope +71 -0
- package/.agents/skills/gstack/bin/gstack-global-discover.ts +591 -0
- package/.agents/skills/gstack/bin/gstack-repo-mode +93 -0
- package/.agents/skills/gstack/bin/gstack-review-log +9 -0
- package/.agents/skills/gstack/bin/gstack-review-read +12 -0
- package/.agents/skills/gstack/bin/gstack-slug +15 -0
- package/.agents/skills/gstack/bin/gstack-telemetry-log +158 -0
- package/.agents/skills/gstack/bin/gstack-telemetry-sync +127 -0
- package/.agents/skills/gstack/bin/gstack-update-check +196 -0
- package/.agents/skills/gstack/browse/SKILL.md +517 -0
- package/.agents/skills/gstack/browse/SKILL.md.tmpl +141 -0
- package/.agents/skills/gstack/browse/bin/find-browse +21 -0
- package/.agents/skills/gstack/browse/bin/remote-slug +14 -0
- package/.agents/skills/gstack/browse/scripts/build-node-server.sh +48 -0
- package/.agents/skills/gstack/browse/src/browser-manager.ts +634 -0
- package/.agents/skills/gstack/browse/src/buffers.ts +137 -0
- package/.agents/skills/gstack/browse/src/bun-polyfill.cjs +109 -0
- package/.agents/skills/gstack/browse/src/cli.ts +420 -0
- package/.agents/skills/gstack/browse/src/commands.ts +111 -0
- package/.agents/skills/gstack/browse/src/config.ts +150 -0
- package/.agents/skills/gstack/browse/src/cookie-import-browser.ts +417 -0
- package/.agents/skills/gstack/browse/src/cookie-picker-routes.ts +207 -0
- package/.agents/skills/gstack/browse/src/cookie-picker-ui.ts +541 -0
- package/.agents/skills/gstack/browse/src/find-browse.ts +61 -0
- package/.agents/skills/gstack/browse/src/meta-commands.ts +269 -0
- package/.agents/skills/gstack/browse/src/platform.ts +17 -0
- package/.agents/skills/gstack/browse/src/read-commands.ts +335 -0
- package/.agents/skills/gstack/browse/src/server.ts +369 -0
- package/.agents/skills/gstack/browse/src/snapshot.ts +398 -0
- package/.agents/skills/gstack/browse/src/url-validation.ts +91 -0
- package/.agents/skills/gstack/browse/src/write-commands.ts +352 -0
- package/.agents/skills/gstack/browse/test/bun-polyfill.test.ts +72 -0
- package/.agents/skills/gstack/browse/test/commands.test.ts +1836 -0
- package/.agents/skills/gstack/browse/test/config.test.ts +250 -0
- package/.agents/skills/gstack/browse/test/cookie-import-browser.test.ts +397 -0
- package/.agents/skills/gstack/browse/test/cookie-picker-routes.test.ts +205 -0
- package/.agents/skills/gstack/browse/test/find-browse.test.ts +50 -0
- package/.agents/skills/gstack/browse/test/fixtures/basic.html +33 -0
- package/.agents/skills/gstack/browse/test/fixtures/cursor-interactive.html +22 -0
- package/.agents/skills/gstack/browse/test/fixtures/dialog.html +15 -0
- package/.agents/skills/gstack/browse/test/fixtures/empty.html +2 -0
- package/.agents/skills/gstack/browse/test/fixtures/forms.html +55 -0
- package/.agents/skills/gstack/browse/test/fixtures/qa-eval-checkout.html +108 -0
- package/.agents/skills/gstack/browse/test/fixtures/qa-eval-spa.html +98 -0
- package/.agents/skills/gstack/browse/test/fixtures/qa-eval.html +51 -0
- package/.agents/skills/gstack/browse/test/fixtures/responsive.html +49 -0
- package/.agents/skills/gstack/browse/test/fixtures/snapshot.html +55 -0
- package/.agents/skills/gstack/browse/test/fixtures/spa.html +24 -0
- package/.agents/skills/gstack/browse/test/fixtures/states.html +17 -0
- package/.agents/skills/gstack/browse/test/fixtures/upload.html +25 -0
- package/.agents/skills/gstack/browse/test/gstack-config.test.ts +125 -0
- package/.agents/skills/gstack/browse/test/gstack-update-check.test.ts +467 -0
- package/.agents/skills/gstack/browse/test/handoff.test.ts +235 -0
- package/.agents/skills/gstack/browse/test/path-validation.test.ts +63 -0
- package/.agents/skills/gstack/browse/test/platform.test.ts +37 -0
- package/.agents/skills/gstack/browse/test/snapshot.test.ts +467 -0
- package/.agents/skills/gstack/browse/test/test-server.ts +57 -0
- package/.agents/skills/gstack/browse/test/url-validation.test.ts +72 -0
- package/.agents/skills/gstack/canary/SKILL.md +493 -0
- package/.agents/skills/gstack/canary/SKILL.md.tmpl +220 -0
- package/.agents/skills/gstack/careful/SKILL.md +59 -0
- package/.agents/skills/gstack/careful/SKILL.md.tmpl +57 -0
- package/.agents/skills/gstack/careful/bin/check-careful.sh +112 -0
- package/.agents/skills/gstack/codex/SKILL.md +677 -0
- package/.agents/skills/gstack/codex/SKILL.md.tmpl +356 -0
- package/.agents/skills/gstack/conductor.json +6 -0
- package/.agents/skills/gstack/cso/SKILL.md +615 -0
- package/.agents/skills/gstack/cso/SKILL.md.tmpl +376 -0
- package/.agents/skills/gstack/design-consultation/SKILL.md +625 -0
- package/.agents/skills/gstack/design-consultation/SKILL.md.tmpl +369 -0
- package/.agents/skills/gstack/design-review/SKILL.md +998 -0
- package/.agents/skills/gstack/design-review/SKILL.md.tmpl +262 -0
- package/.agents/skills/gstack/docs/images/github-2013.png +0 -0
- package/.agents/skills/gstack/docs/images/github-2026.png +0 -0
- package/.agents/skills/gstack/docs/skills.md +877 -0
- package/.agents/skills/gstack/document-release/SKILL.md +613 -0
- package/.agents/skills/gstack/document-release/SKILL.md.tmpl +357 -0
- package/.agents/skills/gstack/freeze/SKILL.md +82 -0
- package/.agents/skills/gstack/freeze/SKILL.md.tmpl +80 -0
- package/.agents/skills/gstack/freeze/bin/check-freeze.sh +68 -0
- package/.agents/skills/gstack/gstack-upgrade/SKILL.md +226 -0
- package/.agents/skills/gstack/gstack-upgrade/SKILL.md.tmpl +224 -0
- package/.agents/skills/gstack/guard/SKILL.md +82 -0
- package/.agents/skills/gstack/guard/SKILL.md.tmpl +80 -0
- package/.agents/skills/gstack/investigate/SKILL.md +435 -0
- package/.agents/skills/gstack/investigate/SKILL.md.tmpl +196 -0
- package/.agents/skills/gstack/land-and-deploy/SKILL.md +880 -0
- package/.agents/skills/gstack/land-and-deploy/SKILL.md.tmpl +575 -0
- package/.agents/skills/gstack/office-hours/SKILL.md +996 -0
- package/.agents/skills/gstack/office-hours/SKILL.md.tmpl +624 -0
- package/.agents/skills/gstack/package.json +55 -0
- package/.agents/skills/gstack/plan-ceo-review/SKILL.md +1277 -0
- package/.agents/skills/gstack/plan-ceo-review/SKILL.md.tmpl +838 -0
- package/.agents/skills/gstack/plan-design-review/SKILL.md +676 -0
- package/.agents/skills/gstack/plan-design-review/SKILL.md.tmpl +314 -0
- package/.agents/skills/gstack/plan-eng-review/SKILL.md +836 -0
- package/.agents/skills/gstack/plan-eng-review/SKILL.md.tmpl +279 -0
- package/.agents/skills/gstack/qa/SKILL.md +1016 -0
- package/.agents/skills/gstack/qa/SKILL.md.tmpl +316 -0
- package/.agents/skills/gstack/qa/references/issue-taxonomy.md +85 -0
- package/.agents/skills/gstack/qa/templates/qa-report-template.md +126 -0
- package/.agents/skills/gstack/qa-only/SKILL.md +633 -0
- package/.agents/skills/gstack/qa-only/SKILL.md.tmpl +101 -0
- package/.agents/skills/gstack/retro/SKILL.md +1072 -0
- package/.agents/skills/gstack/retro/SKILL.md.tmpl +833 -0
- package/.agents/skills/gstack/review/SKILL.md +849 -0
- package/.agents/skills/gstack/review/SKILL.md.tmpl +259 -0
- package/.agents/skills/gstack/review/TODOS-format.md +62 -0
- package/.agents/skills/gstack/review/checklist.md +190 -0
- package/.agents/skills/gstack/review/design-checklist.md +132 -0
- package/.agents/skills/gstack/review/greptile-triage.md +220 -0
- package/.agents/skills/gstack/scripts/analytics.ts +190 -0
- package/.agents/skills/gstack/scripts/dev-skill.ts +82 -0
- package/.agents/skills/gstack/scripts/eval-compare.ts +96 -0
- package/.agents/skills/gstack/scripts/eval-list.ts +116 -0
- package/.agents/skills/gstack/scripts/eval-select.ts +86 -0
- package/.agents/skills/gstack/scripts/eval-summary.ts +187 -0
- package/.agents/skills/gstack/scripts/eval-watch.ts +172 -0
- package/.agents/skills/gstack/scripts/gen-skill-docs.ts +2414 -0
- package/.agents/skills/gstack/scripts/skill-check.ts +167 -0
- package/.agents/skills/gstack/setup +269 -0
- package/.agents/skills/gstack/setup-browser-cookies/SKILL.md +330 -0
- package/.agents/skills/gstack/setup-browser-cookies/SKILL.md.tmpl +74 -0
- package/.agents/skills/gstack/setup-deploy/SKILL.md +459 -0
- package/.agents/skills/gstack/setup-deploy/SKILL.md.tmpl +220 -0
- package/.agents/skills/gstack/ship/SKILL.md +1457 -0
- package/.agents/skills/gstack/ship/SKILL.md.tmpl +528 -0
- package/.agents/skills/gstack/supabase/config.sh +10 -0
- package/.agents/skills/gstack/supabase/functions/community-pulse/index.ts +59 -0
- package/.agents/skills/gstack/supabase/functions/telemetry-ingest/index.ts +135 -0
- package/.agents/skills/gstack/supabase/functions/update-check/index.ts +37 -0
- package/.agents/skills/gstack/supabase/migrations/001_telemetry.sql +89 -0
- package/.agents/skills/gstack/test/analytics.test.ts +277 -0
- package/.agents/skills/gstack/test/codex-e2e.test.ts +197 -0
- package/.agents/skills/gstack/test/fixtures/coverage-audit-fixture.ts +76 -0
- package/.agents/skills/gstack/test/fixtures/eval-baselines.json +7 -0
- package/.agents/skills/gstack/test/fixtures/qa-eval-checkout-ground-truth.json +43 -0
- package/.agents/skills/gstack/test/fixtures/qa-eval-ground-truth.json +43 -0
- package/.agents/skills/gstack/test/fixtures/qa-eval-spa-ground-truth.json +43 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-design-slop.css +86 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-design-slop.html +41 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-enum-diff.rb +30 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-enum.rb +27 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-vuln.rb +14 -0
- package/.agents/skills/gstack/test/gemini-e2e.test.ts +173 -0
- package/.agents/skills/gstack/test/gen-skill-docs.test.ts +1049 -0
- package/.agents/skills/gstack/test/global-discover.test.ts +187 -0
- package/.agents/skills/gstack/test/helpers/codex-session-runner.ts +282 -0
- package/.agents/skills/gstack/test/helpers/e2e-helpers.ts +239 -0
- package/.agents/skills/gstack/test/helpers/eval-store.test.ts +548 -0
- package/.agents/skills/gstack/test/helpers/eval-store.ts +689 -0
- package/.agents/skills/gstack/test/helpers/gemini-session-runner.test.ts +104 -0
- package/.agents/skills/gstack/test/helpers/gemini-session-runner.ts +201 -0
- package/.agents/skills/gstack/test/helpers/llm-judge.ts +130 -0
- package/.agents/skills/gstack/test/helpers/observability.test.ts +283 -0
- package/.agents/skills/gstack/test/helpers/session-runner.test.ts +96 -0
- package/.agents/skills/gstack/test/helpers/session-runner.ts +357 -0
- package/.agents/skills/gstack/test/helpers/skill-parser.ts +206 -0
- package/.agents/skills/gstack/test/helpers/touchfiles.ts +260 -0
- package/.agents/skills/gstack/test/hook-scripts.test.ts +373 -0
- package/.agents/skills/gstack/test/skill-e2e-browse.test.ts +293 -0
- package/.agents/skills/gstack/test/skill-e2e-deploy.test.ts +279 -0
- package/.agents/skills/gstack/test/skill-e2e-design.test.ts +614 -0
- package/.agents/skills/gstack/test/skill-e2e-plan.test.ts +538 -0
- package/.agents/skills/gstack/test/skill-e2e-qa-bugs.test.ts +194 -0
- package/.agents/skills/gstack/test/skill-e2e-qa-workflow.test.ts +412 -0
- package/.agents/skills/gstack/test/skill-e2e-review.test.ts +535 -0
- package/.agents/skills/gstack/test/skill-e2e-workflow.test.ts +586 -0
- package/.agents/skills/gstack/test/skill-e2e.test.ts +3325 -0
- package/.agents/skills/gstack/test/skill-llm-eval.test.ts +787 -0
- package/.agents/skills/gstack/test/skill-parser.test.ts +179 -0
- package/.agents/skills/gstack/test/skill-routing-e2e.test.ts +605 -0
- package/.agents/skills/gstack/test/skill-validation.test.ts +1520 -0
- package/.agents/skills/gstack/test/telemetry.test.ts +278 -0
- package/.agents/skills/gstack/test/touchfiles.test.ts +262 -0
- package/.agents/skills/gstack/unfreeze/SKILL.md +40 -0
- package/.agents/skills/gstack/unfreeze/SKILL.md.tmpl +38 -0
- package/package.json +2 -1
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
2
|
+
import { runSkillTest } from './helpers/session-runner';
|
|
3
|
+
import {
|
|
4
|
+
ROOT, browseBin, runId, evalsEnabled, selectedTests,
|
|
5
|
+
describeIfSelected, testConcurrentIfSelected,
|
|
6
|
+
copyDirSync, setupBrowseShims, logCost, recordE2E,
|
|
7
|
+
createEvalCollector, finalizeEvalCollector,
|
|
8
|
+
} from './helpers/e2e-helpers';
|
|
9
|
+
import { spawnSync } from 'child_process';
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
|
|
14
|
+
const evalCollector = createEvalCollector('e2e-review');
|
|
15
|
+
|
|
16
|
+
// --- B5: Review skill E2E ---
|
|
17
|
+
|
|
18
|
+
describeIfSelected('Review skill E2E', ['review-sql-injection'], () => {
|
|
19
|
+
let reviewDir: string;
|
|
20
|
+
|
|
21
|
+
beforeAll(() => {
|
|
22
|
+
reviewDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-review-'));
|
|
23
|
+
|
|
24
|
+
// Pre-build a git repo with a vulnerable file on a feature branch (decision 5A)
|
|
25
|
+
const run = (cmd: string, args: string[]) =>
|
|
26
|
+
spawnSync(cmd, args, { cwd: reviewDir, stdio: 'pipe', timeout: 5000 });
|
|
27
|
+
|
|
28
|
+
run('git', ['init', '-b', 'main']);
|
|
29
|
+
run('git', ['config', 'user.email', 'test@test.com']);
|
|
30
|
+
run('git', ['config', 'user.name', 'Test']);
|
|
31
|
+
|
|
32
|
+
// Commit a clean base on main
|
|
33
|
+
fs.writeFileSync(path.join(reviewDir, 'app.rb'), '# clean base\nclass App\nend\n');
|
|
34
|
+
run('git', ['add', 'app.rb']);
|
|
35
|
+
run('git', ['commit', '-m', 'initial commit']);
|
|
36
|
+
|
|
37
|
+
// Create feature branch with vulnerable code
|
|
38
|
+
run('git', ['checkout', '-b', 'feature/add-user-controller']);
|
|
39
|
+
const vulnContent = fs.readFileSync(path.join(ROOT, 'test', 'fixtures', 'review-eval-vuln.rb'), 'utf-8');
|
|
40
|
+
fs.writeFileSync(path.join(reviewDir, 'user_controller.rb'), vulnContent);
|
|
41
|
+
run('git', ['add', 'user_controller.rb']);
|
|
42
|
+
run('git', ['commit', '-m', 'add user controller']);
|
|
43
|
+
|
|
44
|
+
// Copy review skill files
|
|
45
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'SKILL.md'), path.join(reviewDir, 'review-SKILL.md'));
|
|
46
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'checklist.md'), path.join(reviewDir, 'review-checklist.md'));
|
|
47
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'greptile-triage.md'), path.join(reviewDir, 'review-greptile-triage.md'));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterAll(() => {
|
|
51
|
+
try { fs.rmSync(reviewDir, { recursive: true, force: true }); } catch {}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('/review produces findings on SQL injection branch', async () => {
|
|
55
|
+
const result = await runSkillTest({
|
|
56
|
+
prompt: `You are in a git repo on a feature branch with changes against main.
|
|
57
|
+
Read review-SKILL.md for the review workflow instructions.
|
|
58
|
+
Also read review-checklist.md and apply it.
|
|
59
|
+
Skip the preamble bash block, lake intro, telemetry, and contributor mode sections — go straight to the review.
|
|
60
|
+
Run /review on the current diff (git diff main...HEAD).
|
|
61
|
+
Write your review findings to ${reviewDir}/review-output.md`,
|
|
62
|
+
workingDirectory: reviewDir,
|
|
63
|
+
maxTurns: 20,
|
|
64
|
+
timeout: 180_000,
|
|
65
|
+
testName: 'review-sql-injection',
|
|
66
|
+
runId,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
logCost('/review', result);
|
|
70
|
+
recordE2E(evalCollector, '/review SQL injection', 'Review skill E2E', result);
|
|
71
|
+
expect(result.exitReason).toBe('success');
|
|
72
|
+
|
|
73
|
+
// Verify the review output mentions SQL injection-related findings
|
|
74
|
+
const reviewOutputPath = path.join(reviewDir, 'review-output.md');
|
|
75
|
+
if (fs.existsSync(reviewOutputPath)) {
|
|
76
|
+
const reviewContent = fs.readFileSync(reviewOutputPath, 'utf-8').toLowerCase();
|
|
77
|
+
const hasSqlContent =
|
|
78
|
+
reviewContent.includes('sql') ||
|
|
79
|
+
reviewContent.includes('injection') ||
|
|
80
|
+
reviewContent.includes('sanitiz') ||
|
|
81
|
+
reviewContent.includes('parameteriz') ||
|
|
82
|
+
reviewContent.includes('interpolat') ||
|
|
83
|
+
reviewContent.includes('user_input') ||
|
|
84
|
+
reviewContent.includes('unsanitized');
|
|
85
|
+
expect(hasSqlContent).toBe(true);
|
|
86
|
+
}
|
|
87
|
+
}, 210_000);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// --- Review: Enum completeness E2E ---
|
|
91
|
+
|
|
92
|
+
describeIfSelected('Review enum completeness E2E', ['review-enum-completeness'], () => {
|
|
93
|
+
let enumDir: string;
|
|
94
|
+
|
|
95
|
+
beforeAll(() => {
|
|
96
|
+
enumDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-enum-'));
|
|
97
|
+
|
|
98
|
+
const run = (cmd: string, args: string[]) =>
|
|
99
|
+
spawnSync(cmd, args, { cwd: enumDir, stdio: 'pipe', timeout: 5000 });
|
|
100
|
+
|
|
101
|
+
run('git', ['init', '-b', 'main']);
|
|
102
|
+
run('git', ['config', 'user.email', 'test@test.com']);
|
|
103
|
+
run('git', ['config', 'user.name', 'Test']);
|
|
104
|
+
|
|
105
|
+
// Commit baseline on main — order model with 4 statuses
|
|
106
|
+
const baseContent = fs.readFileSync(path.join(ROOT, 'test', 'fixtures', 'review-eval-enum.rb'), 'utf-8');
|
|
107
|
+
fs.writeFileSync(path.join(enumDir, 'order.rb'), baseContent);
|
|
108
|
+
run('git', ['add', 'order.rb']);
|
|
109
|
+
run('git', ['commit', '-m', 'initial order model']);
|
|
110
|
+
|
|
111
|
+
// Feature branch adds "returned" status but misses handlers
|
|
112
|
+
run('git', ['checkout', '-b', 'feature/add-returned-status']);
|
|
113
|
+
const diffContent = fs.readFileSync(path.join(ROOT, 'test', 'fixtures', 'review-eval-enum-diff.rb'), 'utf-8');
|
|
114
|
+
fs.writeFileSync(path.join(enumDir, 'order.rb'), diffContent);
|
|
115
|
+
run('git', ['add', 'order.rb']);
|
|
116
|
+
run('git', ['commit', '-m', 'add returned status']);
|
|
117
|
+
|
|
118
|
+
// Copy review skill files
|
|
119
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'SKILL.md'), path.join(enumDir, 'review-SKILL.md'));
|
|
120
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'checklist.md'), path.join(enumDir, 'review-checklist.md'));
|
|
121
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'greptile-triage.md'), path.join(enumDir, 'review-greptile-triage.md'));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
afterAll(() => {
|
|
125
|
+
try { fs.rmSync(enumDir, { recursive: true, force: true }); } catch {}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('/review catches missing enum handlers for new status value', async () => {
|
|
129
|
+
const result = await runSkillTest({
|
|
130
|
+
prompt: `You are in a git repo on branch feature/add-returned-status with changes against main.
|
|
131
|
+
Read review-SKILL.md for the review workflow instructions.
|
|
132
|
+
Also read review-checklist.md and apply it — pay special attention to the Enum & Value Completeness section.
|
|
133
|
+
Run /review on the current diff (git diff main...HEAD).
|
|
134
|
+
Write your review findings to ${enumDir}/review-output.md
|
|
135
|
+
|
|
136
|
+
The diff adds a new "returned" status to the Order model. Your job is to check if all consumers handle it.`,
|
|
137
|
+
workingDirectory: enumDir,
|
|
138
|
+
maxTurns: 15,
|
|
139
|
+
timeout: 90_000,
|
|
140
|
+
testName: 'review-enum-completeness',
|
|
141
|
+
runId,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
logCost('/review enum', result);
|
|
145
|
+
recordE2E(evalCollector, '/review enum completeness', 'Review enum completeness E2E', result);
|
|
146
|
+
expect(result.exitReason).toBe('success');
|
|
147
|
+
|
|
148
|
+
// Verify the review caught the missing enum handlers
|
|
149
|
+
const reviewPath = path.join(enumDir, 'review-output.md');
|
|
150
|
+
if (fs.existsSync(reviewPath)) {
|
|
151
|
+
const review = fs.readFileSync(reviewPath, 'utf-8');
|
|
152
|
+
// Should mention the missing "returned" handling in at least one of the methods
|
|
153
|
+
const mentionsReturned = review.toLowerCase().includes('returned');
|
|
154
|
+
const mentionsEnum = review.toLowerCase().includes('enum') || review.toLowerCase().includes('status');
|
|
155
|
+
const mentionsCritical = review.toLowerCase().includes('critical');
|
|
156
|
+
expect(mentionsReturned).toBe(true);
|
|
157
|
+
expect(mentionsEnum || mentionsCritical).toBe(true);
|
|
158
|
+
}
|
|
159
|
+
}, 120_000);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// --- Review: Design review lite E2E ---
|
|
163
|
+
|
|
164
|
+
describeIfSelected('Review design lite E2E', ['review-design-lite'], () => {
|
|
165
|
+
let designDir: string;
|
|
166
|
+
|
|
167
|
+
beforeAll(() => {
|
|
168
|
+
designDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-design-lite-'));
|
|
169
|
+
|
|
170
|
+
const run = (cmd: string, args: string[]) =>
|
|
171
|
+
spawnSync(cmd, args, { cwd: designDir, stdio: 'pipe', timeout: 5000 });
|
|
172
|
+
|
|
173
|
+
run('git', ['init', '-b', 'main']);
|
|
174
|
+
run('git', ['config', 'user.email', 'test@test.com']);
|
|
175
|
+
run('git', ['config', 'user.name', 'Test']);
|
|
176
|
+
|
|
177
|
+
// Commit clean base on main
|
|
178
|
+
fs.writeFileSync(path.join(designDir, 'index.html'), '<h1>Clean</h1>\n');
|
|
179
|
+
fs.writeFileSync(path.join(designDir, 'styles.css'), 'body { font-size: 16px; }\n');
|
|
180
|
+
run('git', ['add', '.']);
|
|
181
|
+
run('git', ['commit', '-m', 'initial']);
|
|
182
|
+
|
|
183
|
+
// Feature branch adds AI slop CSS + HTML
|
|
184
|
+
run('git', ['checkout', '-b', 'feature/add-landing-page']);
|
|
185
|
+
const slopCss = fs.readFileSync(path.join(ROOT, 'test', 'fixtures', 'review-eval-design-slop.css'), 'utf-8');
|
|
186
|
+
const slopHtml = fs.readFileSync(path.join(ROOT, 'test', 'fixtures', 'review-eval-design-slop.html'), 'utf-8');
|
|
187
|
+
fs.writeFileSync(path.join(designDir, 'styles.css'), slopCss);
|
|
188
|
+
fs.writeFileSync(path.join(designDir, 'landing.html'), slopHtml);
|
|
189
|
+
run('git', ['add', '.']);
|
|
190
|
+
run('git', ['commit', '-m', 'add landing page']);
|
|
191
|
+
|
|
192
|
+
// Copy review skill files
|
|
193
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'SKILL.md'), path.join(designDir, 'review-SKILL.md'));
|
|
194
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'checklist.md'), path.join(designDir, 'review-checklist.md'));
|
|
195
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'design-checklist.md'), path.join(designDir, 'review-design-checklist.md'));
|
|
196
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'greptile-triage.md'), path.join(designDir, 'review-greptile-triage.md'));
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
afterAll(() => {
|
|
200
|
+
try { fs.rmSync(designDir, { recursive: true, force: true }); } catch {}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('/review catches design anti-patterns in CSS/HTML diff', async () => {
|
|
204
|
+
const result = await runSkillTest({
|
|
205
|
+
prompt: `You are in a git repo on branch feature/add-landing-page with changes against main.
|
|
206
|
+
Read review-SKILL.md for the review workflow instructions.
|
|
207
|
+
Read review-checklist.md for the code review checklist.
|
|
208
|
+
Read review-design-checklist.md for the design review checklist.
|
|
209
|
+
Run /review on the current diff (git diff main...HEAD).
|
|
210
|
+
|
|
211
|
+
Skip the preamble bash block, lake intro, telemetry, and contributor mode sections — go straight to the review.
|
|
212
|
+
|
|
213
|
+
The diff adds a landing page with CSS and HTML. Check for both code issues AND design anti-patterns.
|
|
214
|
+
Write your review findings to ${designDir}/review-output.md
|
|
215
|
+
|
|
216
|
+
Important: The design checklist should catch issues like blacklisted fonts, small font sizes, outline:none, !important, AI slop patterns (purple gradients, generic hero copy, 3-column feature grid), etc.`,
|
|
217
|
+
workingDirectory: designDir,
|
|
218
|
+
maxTurns: 35,
|
|
219
|
+
timeout: 240_000,
|
|
220
|
+
testName: 'review-design-lite',
|
|
221
|
+
runId,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
logCost('/review design lite', result);
|
|
225
|
+
recordE2E(evalCollector, '/review design lite', 'Review design lite E2E', result);
|
|
226
|
+
expect(result.exitReason).toBe('success');
|
|
227
|
+
|
|
228
|
+
// Verify the review caught at least 4 of 7 planted design issues
|
|
229
|
+
const reviewPath = path.join(designDir, 'review-output.md');
|
|
230
|
+
if (fs.existsSync(reviewPath)) {
|
|
231
|
+
const review = fs.readFileSync(reviewPath, 'utf-8').toLowerCase();
|
|
232
|
+
let detected = 0;
|
|
233
|
+
|
|
234
|
+
// Issue 1: Blacklisted font (Papyrus) — HIGH
|
|
235
|
+
if (review.includes('papyrus') || review.includes('blacklisted font') || review.includes('font family')) detected++;
|
|
236
|
+
// Issue 2: Body text < 16px — HIGH
|
|
237
|
+
if (review.includes('14px') || review.includes('font-size') || review.includes('font size') || review.includes('body text')) detected++;
|
|
238
|
+
// Issue 3: outline: none — HIGH
|
|
239
|
+
if (review.includes('outline') || review.includes('focus')) detected++;
|
|
240
|
+
// Issue 4: !important — HIGH
|
|
241
|
+
if (review.includes('!important') || review.includes('important')) detected++;
|
|
242
|
+
// Issue 5: Purple gradient — MEDIUM
|
|
243
|
+
if (review.includes('gradient') || review.includes('purple') || review.includes('violet') || review.includes('#6366f1') || review.includes('#8b5cf6')) detected++;
|
|
244
|
+
// Issue 6: Generic hero copy — MEDIUM
|
|
245
|
+
if (review.includes('welcome to') || review.includes('all-in-one') || review.includes('generic') || review.includes('hero copy') || review.includes('ai slop')) detected++;
|
|
246
|
+
// Issue 7: 3-column feature grid — LOW
|
|
247
|
+
if (review.includes('3-column') || review.includes('three-column') || review.includes('feature grid') || review.includes('icon') || review.includes('circle')) detected++;
|
|
248
|
+
|
|
249
|
+
console.log(`Design review detected ${detected}/7 planted issues`);
|
|
250
|
+
expect(detected).toBeGreaterThanOrEqual(4);
|
|
251
|
+
}
|
|
252
|
+
}, 300_000);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// --- Base branch detection smoke tests ---
|
|
256
|
+
|
|
257
|
+
describeIfSelected('Base branch detection', ['review-base-branch', 'ship-base-branch', 'retro-base-branch'], () => {
|
|
258
|
+
let baseBranchDir: string;
|
|
259
|
+
const run = (cmd: string, args: string[], cwd: string) =>
|
|
260
|
+
spawnSync(cmd, args, { cwd, stdio: 'pipe', timeout: 5000 });
|
|
261
|
+
|
|
262
|
+
beforeAll(() => {
|
|
263
|
+
baseBranchDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-basebranch-'));
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
afterAll(() => {
|
|
267
|
+
try { fs.rmSync(baseBranchDir, { recursive: true, force: true }); } catch {}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
testConcurrentIfSelected('review-base-branch', async () => {
|
|
271
|
+
const dir = path.join(baseBranchDir, 'review-base');
|
|
272
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
273
|
+
|
|
274
|
+
// Create git repo with a feature branch off main
|
|
275
|
+
run('git', ['init'], dir);
|
|
276
|
+
run('git', ['config', 'user.email', 'test@test.com'], dir);
|
|
277
|
+
run('git', ['config', 'user.name', 'Test'], dir);
|
|
278
|
+
|
|
279
|
+
fs.writeFileSync(path.join(dir, 'app.rb'), '# clean base\nclass App\nend\n');
|
|
280
|
+
run('git', ['add', 'app.rb'], dir);
|
|
281
|
+
run('git', ['commit', '-m', 'initial commit'], dir);
|
|
282
|
+
|
|
283
|
+
// Create feature branch with a change
|
|
284
|
+
run('git', ['checkout', '-b', 'feature/test-review'], dir);
|
|
285
|
+
fs.writeFileSync(path.join(dir, 'app.rb'), '# clean base\nclass App\n def hello; "world"; end\nend\n');
|
|
286
|
+
run('git', ['add', 'app.rb'], dir);
|
|
287
|
+
run('git', ['commit', '-m', 'feat: add hello method'], dir);
|
|
288
|
+
|
|
289
|
+
// Copy review skill files
|
|
290
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'SKILL.md'), path.join(dir, 'review-SKILL.md'));
|
|
291
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'checklist.md'), path.join(dir, 'review-checklist.md'));
|
|
292
|
+
fs.copyFileSync(path.join(ROOT, 'review', 'greptile-triage.md'), path.join(dir, 'review-greptile-triage.md'));
|
|
293
|
+
|
|
294
|
+
const result = await runSkillTest({
|
|
295
|
+
prompt: `You are in a git repo on a feature branch with changes.
|
|
296
|
+
Read review-SKILL.md for the review workflow instructions.
|
|
297
|
+
Also read review-checklist.md and apply it.
|
|
298
|
+
|
|
299
|
+
IMPORTANT: Follow Step 0 to detect the base branch. Since there is no remote, gh commands will fail — fall back to main.
|
|
300
|
+
Then run the review against the detected base branch.
|
|
301
|
+
Write your findings to ${dir}/review-output.md`,
|
|
302
|
+
workingDirectory: dir,
|
|
303
|
+
maxTurns: 15,
|
|
304
|
+
timeout: 90_000,
|
|
305
|
+
testName: 'review-base-branch',
|
|
306
|
+
runId,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
logCost('/review base-branch', result);
|
|
310
|
+
recordE2E(evalCollector, '/review base branch detection', 'Base branch detection', result);
|
|
311
|
+
expect(result.exitReason).toBe('success');
|
|
312
|
+
|
|
313
|
+
// Verify the review used "base branch" language (from Step 0)
|
|
314
|
+
const toolOutputs = result.toolCalls.map(tc => tc.output || '').join('\n');
|
|
315
|
+
const allOutput = (result.output || '') + toolOutputs;
|
|
316
|
+
// The agent should have run git diff against main (the fallback)
|
|
317
|
+
const usedGitDiff = result.toolCalls.some(tc => {
|
|
318
|
+
if (tc.tool !== 'Bash') return false;
|
|
319
|
+
const cmd = typeof tc.input === 'string' ? tc.input : tc.input?.command || JSON.stringify(tc.input);
|
|
320
|
+
return cmd.includes('git diff');
|
|
321
|
+
});
|
|
322
|
+
expect(usedGitDiff).toBe(true);
|
|
323
|
+
}, 120_000);
|
|
324
|
+
|
|
325
|
+
testConcurrentIfSelected('ship-base-branch', async () => {
|
|
326
|
+
const dir = path.join(baseBranchDir, 'ship-base');
|
|
327
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
328
|
+
|
|
329
|
+
// Create git repo with feature branch
|
|
330
|
+
run('git', ['init'], dir);
|
|
331
|
+
run('git', ['config', 'user.email', 'test@test.com'], dir);
|
|
332
|
+
run('git', ['config', 'user.name', 'Test'], dir);
|
|
333
|
+
|
|
334
|
+
fs.writeFileSync(path.join(dir, 'app.ts'), 'console.log("v1");\n');
|
|
335
|
+
run('git', ['add', 'app.ts'], dir);
|
|
336
|
+
run('git', ['commit', '-m', 'initial'], dir);
|
|
337
|
+
|
|
338
|
+
run('git', ['checkout', '-b', 'feature/ship-test'], dir);
|
|
339
|
+
fs.writeFileSync(path.join(dir, 'app.ts'), 'console.log("v2");\n');
|
|
340
|
+
run('git', ['add', 'app.ts'], dir);
|
|
341
|
+
run('git', ['commit', '-m', 'feat: update to v2'], dir);
|
|
342
|
+
|
|
343
|
+
// Copy ship skill
|
|
344
|
+
fs.copyFileSync(path.join(ROOT, 'ship', 'SKILL.md'), path.join(dir, 'ship-SKILL.md'));
|
|
345
|
+
|
|
346
|
+
const result = await runSkillTest({
|
|
347
|
+
prompt: `Read ship-SKILL.md for the ship workflow.
|
|
348
|
+
|
|
349
|
+
Skip the preamble bash block, lake intro, telemetry, and contributor mode sections — go straight to Step 0.
|
|
350
|
+
|
|
351
|
+
Run ONLY Step 0 (Detect base branch) and Step 1 (Pre-flight) from the ship workflow.
|
|
352
|
+
Since there is no remote, gh commands will fail — fall back to main.
|
|
353
|
+
|
|
354
|
+
After completing Step 0 and Step 1, STOP. Do NOT proceed to Step 2 or beyond.
|
|
355
|
+
Do NOT push, create PRs, or modify VERSION/CHANGELOG.
|
|
356
|
+
|
|
357
|
+
Write a summary of what you detected to ${dir}/ship-preflight.md including:
|
|
358
|
+
- The detected base branch name
|
|
359
|
+
- The current branch name
|
|
360
|
+
- The diff stat against the base branch`,
|
|
361
|
+
workingDirectory: dir,
|
|
362
|
+
maxTurns: 18,
|
|
363
|
+
timeout: 150_000,
|
|
364
|
+
testName: 'ship-base-branch',
|
|
365
|
+
runId,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
logCost('/ship base-branch', result);
|
|
369
|
+
recordE2E(evalCollector, '/ship base branch detection', 'Base branch detection', result);
|
|
370
|
+
expect(result.exitReason).toBe('success');
|
|
371
|
+
|
|
372
|
+
// Verify preflight output was written
|
|
373
|
+
const preflightPath = path.join(dir, 'ship-preflight.md');
|
|
374
|
+
if (fs.existsSync(preflightPath)) {
|
|
375
|
+
const content = fs.readFileSync(preflightPath, 'utf-8');
|
|
376
|
+
expect(content.length).toBeGreaterThan(20);
|
|
377
|
+
// Should mention the branch name
|
|
378
|
+
expect(content.toLowerCase()).toMatch(/main|base/);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Verify no destructive actions — no push, no PR creation
|
|
382
|
+
const destructiveTools = result.toolCalls.filter(tc =>
|
|
383
|
+
tc.tool === 'Bash' && typeof tc.input === 'string' &&
|
|
384
|
+
(tc.input.includes('git push') || tc.input.includes('gh pr create'))
|
|
385
|
+
);
|
|
386
|
+
expect(destructiveTools).toHaveLength(0);
|
|
387
|
+
}, 180_000);
|
|
388
|
+
|
|
389
|
+
testConcurrentIfSelected('retro-base-branch', async () => {
|
|
390
|
+
const dir = path.join(baseBranchDir, 'retro-base');
|
|
391
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
392
|
+
|
|
393
|
+
// Create git repo with commit history
|
|
394
|
+
run('git', ['init'], dir);
|
|
395
|
+
run('git', ['config', 'user.email', 'dev@example.com'], dir);
|
|
396
|
+
run('git', ['config', 'user.name', 'Dev'], dir);
|
|
397
|
+
|
|
398
|
+
fs.writeFileSync(path.join(dir, 'app.ts'), 'console.log("hello");\n');
|
|
399
|
+
run('git', ['add', 'app.ts'], dir);
|
|
400
|
+
run('git', ['commit', '-m', 'feat: initial app', '--date', '2026-03-14T09:00:00'], dir);
|
|
401
|
+
|
|
402
|
+
fs.writeFileSync(path.join(dir, 'auth.ts'), 'export function login() {}\n');
|
|
403
|
+
run('git', ['add', 'auth.ts'], dir);
|
|
404
|
+
run('git', ['commit', '-m', 'feat: add auth', '--date', '2026-03-15T10:00:00'], dir);
|
|
405
|
+
|
|
406
|
+
fs.writeFileSync(path.join(dir, 'test.ts'), 'test("it works", () => {});\n');
|
|
407
|
+
run('git', ['add', 'test.ts'], dir);
|
|
408
|
+
run('git', ['commit', '-m', 'test: add tests', '--date', '2026-03-16T11:00:00'], dir);
|
|
409
|
+
|
|
410
|
+
// Copy retro skill
|
|
411
|
+
fs.mkdirSync(path.join(dir, 'retro'), { recursive: true });
|
|
412
|
+
fs.copyFileSync(path.join(ROOT, 'retro', 'SKILL.md'), path.join(dir, 'retro', 'SKILL.md'));
|
|
413
|
+
|
|
414
|
+
const result = await runSkillTest({
|
|
415
|
+
prompt: `Read retro/SKILL.md for instructions on how to run a retrospective.
|
|
416
|
+
|
|
417
|
+
IMPORTANT: Follow the "Detect default branch" step first. Since there is no remote, gh will fail — fall back to main.
|
|
418
|
+
Then use the detected branch name for all git queries.
|
|
419
|
+
|
|
420
|
+
Run /retro for the last 7 days of this git repo. Skip any AskUserQuestion calls — this is non-interactive.
|
|
421
|
+
This is a local-only repo so use the local branch (main) instead of origin/main for all git log commands.
|
|
422
|
+
|
|
423
|
+
Write your retrospective to ${dir}/retro-output.md`,
|
|
424
|
+
workingDirectory: dir,
|
|
425
|
+
maxTurns: 25,
|
|
426
|
+
timeout: 240_000,
|
|
427
|
+
testName: 'retro-base-branch',
|
|
428
|
+
runId,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
logCost('/retro base-branch', result);
|
|
432
|
+
recordE2E(evalCollector, '/retro default branch detection', 'Base branch detection', result, {
|
|
433
|
+
passed: ['success', 'error_max_turns'].includes(result.exitReason),
|
|
434
|
+
});
|
|
435
|
+
expect(['success', 'error_max_turns']).toContain(result.exitReason);
|
|
436
|
+
|
|
437
|
+
// Verify retro output was produced
|
|
438
|
+
const retroPath = path.join(dir, 'retro-output.md');
|
|
439
|
+
if (fs.existsSync(retroPath)) {
|
|
440
|
+
const content = fs.readFileSync(retroPath, 'utf-8');
|
|
441
|
+
expect(content.length).toBeGreaterThan(100);
|
|
442
|
+
}
|
|
443
|
+
}, 300_000);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// --- Retro E2E ---
|
|
447
|
+
|
|
448
|
+
describeIfSelected('Retro E2E', ['retro'], () => {
|
|
449
|
+
let retroDir: string;
|
|
450
|
+
|
|
451
|
+
beforeAll(() => {
|
|
452
|
+
retroDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-retro-'));
|
|
453
|
+
const run = (cmd: string, args: string[]) =>
|
|
454
|
+
spawnSync(cmd, args, { cwd: retroDir, stdio: 'pipe', timeout: 5000 });
|
|
455
|
+
|
|
456
|
+
// Create a git repo with varied commit history
|
|
457
|
+
run('git', ['init', '-b', 'main']);
|
|
458
|
+
run('git', ['config', 'user.email', 'dev@example.com']);
|
|
459
|
+
run('git', ['config', 'user.name', 'Dev']);
|
|
460
|
+
|
|
461
|
+
// Day 1 commits
|
|
462
|
+
fs.writeFileSync(path.join(retroDir, 'app.ts'), 'console.log("hello");\n');
|
|
463
|
+
run('git', ['add', 'app.ts']);
|
|
464
|
+
run('git', ['commit', '-m', 'feat: initial app setup', '--date', '2026-03-10T09:00:00']);
|
|
465
|
+
|
|
466
|
+
fs.writeFileSync(path.join(retroDir, 'auth.ts'), 'export function login() {}\n');
|
|
467
|
+
run('git', ['add', 'auth.ts']);
|
|
468
|
+
run('git', ['commit', '-m', 'feat: add auth module', '--date', '2026-03-10T11:00:00']);
|
|
469
|
+
|
|
470
|
+
// Day 2 commits
|
|
471
|
+
fs.writeFileSync(path.join(retroDir, 'app.ts'), 'import { login } from "./auth";\nconsole.log("hello");\nlogin();\n');
|
|
472
|
+
run('git', ['add', 'app.ts']);
|
|
473
|
+
run('git', ['commit', '-m', 'fix: wire up auth to app', '--date', '2026-03-11T10:00:00']);
|
|
474
|
+
|
|
475
|
+
fs.writeFileSync(path.join(retroDir, 'test.ts'), 'import { test } from "bun:test";\ntest("login", () => {});\n');
|
|
476
|
+
run('git', ['add', 'test.ts']);
|
|
477
|
+
run('git', ['commit', '-m', 'test: add login test', '--date', '2026-03-11T14:00:00']);
|
|
478
|
+
|
|
479
|
+
// Day 3 commits
|
|
480
|
+
fs.writeFileSync(path.join(retroDir, 'api.ts'), 'export function getUsers() { return []; }\n');
|
|
481
|
+
run('git', ['add', 'api.ts']);
|
|
482
|
+
run('git', ['commit', '-m', 'feat: add users API endpoint', '--date', '2026-03-12T09:30:00']);
|
|
483
|
+
|
|
484
|
+
fs.writeFileSync(path.join(retroDir, 'README.md'), '# My App\nA test application.\n');
|
|
485
|
+
run('git', ['add', 'README.md']);
|
|
486
|
+
run('git', ['commit', '-m', 'docs: add README', '--date', '2026-03-12T16:00:00']);
|
|
487
|
+
|
|
488
|
+
// Copy retro skill
|
|
489
|
+
fs.mkdirSync(path.join(retroDir, 'retro'), { recursive: true });
|
|
490
|
+
fs.copyFileSync(
|
|
491
|
+
path.join(ROOT, 'retro', 'SKILL.md'),
|
|
492
|
+
path.join(retroDir, 'retro', 'SKILL.md'),
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
afterAll(() => {
|
|
497
|
+
try { fs.rmSync(retroDir, { recursive: true, force: true }); } catch {}
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test('/retro produces analysis from git history', async () => {
|
|
501
|
+
const result = await runSkillTest({
|
|
502
|
+
prompt: `Read retro/SKILL.md for instructions on how to run a retrospective.
|
|
503
|
+
|
|
504
|
+
Run /retro for the last 7 days of this git repo. Skip any AskUserQuestion calls — this is non-interactive.
|
|
505
|
+
Write your retrospective report to ${retroDir}/retro-output.md
|
|
506
|
+
|
|
507
|
+
Analyze the git history and produce the narrative report as described in the SKILL.md.`,
|
|
508
|
+
workingDirectory: retroDir,
|
|
509
|
+
maxTurns: 30,
|
|
510
|
+
timeout: 300_000,
|
|
511
|
+
testName: 'retro',
|
|
512
|
+
runId,
|
|
513
|
+
model: 'claude-opus-4-6',
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
logCost('/retro', result);
|
|
517
|
+
recordE2E(evalCollector, '/retro', 'Retro E2E', result, {
|
|
518
|
+
passed: ['success', 'error_max_turns'].includes(result.exitReason),
|
|
519
|
+
});
|
|
520
|
+
// Accept error_max_turns — retro does many git commands to analyze history
|
|
521
|
+
expect(['success', 'error_max_turns']).toContain(result.exitReason);
|
|
522
|
+
|
|
523
|
+
// Verify the retro was written
|
|
524
|
+
const retroPath = path.join(retroDir, 'retro-output.md');
|
|
525
|
+
if (fs.existsSync(retroPath)) {
|
|
526
|
+
const retro = fs.readFileSync(retroPath, 'utf-8');
|
|
527
|
+
expect(retro.length).toBeGreaterThan(100);
|
|
528
|
+
}
|
|
529
|
+
}, 420_000);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Module-level afterAll — finalize eval collector after all tests complete
|
|
533
|
+
afterAll(async () => {
|
|
534
|
+
await finalizeEvalCollector(evalCollector);
|
|
535
|
+
});
|