@aaronshaf/ger 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.
- package/.ast-grep/rules/no-as-casting.yml +13 -0
- package/.eslintrc.js +12 -0
- package/.github/workflows/ci-simple.yml +53 -0
- package/.github/workflows/ci.yml +171 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude.yml +64 -0
- package/.github/workflows/dependency-update.yml +84 -0
- package/.github/workflows/release.yml +166 -0
- package/.github/workflows/security-scan.yml +113 -0
- package/.github/workflows/security.yml +96 -0
- package/.husky/pre-commit +16 -0
- package/.husky/pre-push +25 -0
- package/.lintstagedrc.json +6 -0
- package/.tool-versions +1 -0
- package/CLAUDE.md +103 -0
- package/DEVELOPMENT.md +361 -0
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/bin/ger +3 -0
- package/biome.json +36 -0
- package/bun.lock +688 -0
- package/bunfig.toml +8 -0
- package/oxlint.json +24 -0
- package/package.json +55 -0
- package/scripts/check-coverage.ts +69 -0
- package/scripts/check-file-size.ts +38 -0
- package/scripts/fix-test-mocks.ts +55 -0
- package/src/api/gerrit.ts +466 -0
- package/src/cli/commands/abandon.ts +65 -0
- package/src/cli/commands/comment.ts +460 -0
- package/src/cli/commands/comments.ts +85 -0
- package/src/cli/commands/diff.ts +71 -0
- package/src/cli/commands/incoming.ts +226 -0
- package/src/cli/commands/init.ts +164 -0
- package/src/cli/commands/mine.ts +115 -0
- package/src/cli/commands/open.ts +57 -0
- package/src/cli/commands/review.ts +593 -0
- package/src/cli/commands/setup.ts +230 -0
- package/src/cli/commands/show.ts +303 -0
- package/src/cli/commands/status.ts +35 -0
- package/src/cli/commands/workspace.ts +200 -0
- package/src/cli/index.ts +420 -0
- package/src/prompts/default-review.md +80 -0
- package/src/prompts/system-inline-review.md +88 -0
- package/src/prompts/system-overall-review.md +152 -0
- package/src/schemas/config.test.ts +245 -0
- package/src/schemas/config.ts +75 -0
- package/src/schemas/gerrit.ts +455 -0
- package/src/services/ai-enhanced.ts +167 -0
- package/src/services/ai.ts +182 -0
- package/src/services/config.test.ts +414 -0
- package/src/services/config.ts +206 -0
- package/src/test-utils/mock-generator.ts +73 -0
- package/src/utils/comment-formatters.ts +153 -0
- package/src/utils/diff-context.ts +103 -0
- package/src/utils/diff-formatters.ts +141 -0
- package/src/utils/formatters.ts +85 -0
- package/src/utils/message-filters.ts +26 -0
- package/src/utils/shell-safety.ts +117 -0
- package/src/utils/status-indicators.ts +100 -0
- package/src/utils/url-parser.test.ts +123 -0
- package/src/utils/url-parser.ts +91 -0
- package/tests/abandon.test.ts +163 -0
- package/tests/ai-service.test.ts +489 -0
- package/tests/comment-batch-advanced.test.ts +431 -0
- package/tests/comment-gerrit-api-compliance.test.ts +414 -0
- package/tests/comment.test.ts +707 -0
- package/tests/comments.test.ts +323 -0
- package/tests/config-service-simple.test.ts +100 -0
- package/tests/diff.test.ts +419 -0
- package/tests/helpers/config-mock.ts +27 -0
- package/tests/incoming.test.ts +357 -0
- package/tests/interactive-incoming.test.ts +173 -0
- package/tests/mine.test.ts +318 -0
- package/tests/mocks/fetch-mock.ts +139 -0
- package/tests/mocks/msw-handlers.ts +80 -0
- package/tests/open.test.ts +233 -0
- package/tests/review.test.ts +669 -0
- package/tests/setup.ts +13 -0
- package/tests/show.test.ts +439 -0
- package/tests/unit/schemas/gerrit.test.ts +85 -0
- package/tests/unit/test-utils/mock-generator.test.ts +154 -0
- package/tests/unit/utils/comment-formatters.test.ts +415 -0
- package/tests/unit/utils/diff-context.test.ts +171 -0
- package/tests/unit/utils/diff-formatters.test.ts +165 -0
- package/tests/unit/utils/formatters.test.ts +411 -0
- package/tests/unit/utils/message-filters.test.ts +227 -0
- package/tests/unit/utils/prompt-helpers.test.ts +175 -0
- package/tests/unit/utils/shell-safety.test.ts +230 -0
- package/tests/unit/utils/status-indicators.test.ts +137 -0
- package/tsconfig.json +40 -0
package/bunfig.toml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[test]
|
|
2
|
+
# Test configuration
|
|
3
|
+
coverage = true
|
|
4
|
+
coverageThreshold = { line = 80.0, function = 80.0, statement = 80.0, branch = 80.0 }
|
|
5
|
+
coverageReporter = ["text", "lcov"]
|
|
6
|
+
coverageSkipTestFiles = true
|
|
7
|
+
# Exclude patterns from coverage
|
|
8
|
+
coverageExclude = ["node_modules", "dist", "tmp", "tests/mocks", "**/*.d.ts"]
|
package/oxlint.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rules": {
|
|
3
|
+
"no-implicit-any": "error",
|
|
4
|
+
"no-explicit-any": "error",
|
|
5
|
+
"no-unused-vars": "error",
|
|
6
|
+
"no-console": "warn",
|
|
7
|
+
"no-debugger": "error",
|
|
8
|
+
"prefer-const": "error",
|
|
9
|
+
"no-var": "error",
|
|
10
|
+
"eqeqeq": "error",
|
|
11
|
+
"no-eval": "error"
|
|
12
|
+
},
|
|
13
|
+
"ignorePatterns": [
|
|
14
|
+
"node_modules",
|
|
15
|
+
"dist",
|
|
16
|
+
"tmp",
|
|
17
|
+
"**/*.js",
|
|
18
|
+
"**/*.jsx",
|
|
19
|
+
"**/*.tsx",
|
|
20
|
+
"coverage",
|
|
21
|
+
"*.config.js",
|
|
22
|
+
"*.config.ts"
|
|
23
|
+
]
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aaronshaf/ger",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"module": "index.ts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ger": "./bin/ger"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/aaronshaf/ger.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/aaronshaf/ger/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/aaronshaf/ger#readme",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@biomejs/biome": "^2.2.2",
|
|
19
|
+
"@types/node": "^24.3.0",
|
|
20
|
+
"ast-grep": "^0.1.0",
|
|
21
|
+
"bun-types": "^1.2.21",
|
|
22
|
+
"husky": "^9.1.7",
|
|
23
|
+
"lint-staged": "^16.1.5",
|
|
24
|
+
"msw": "^2.10.5",
|
|
25
|
+
"oxlint": "^1.13.0",
|
|
26
|
+
"typescript": "^5.9.2"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"typescript": "^5.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@effect/platform": "^0.90.6",
|
|
33
|
+
"@effect/platform-node": "^0.94.2",
|
|
34
|
+
"@effect/schema": "^0.75.5",
|
|
35
|
+
"@inquirer/prompts": "^7.8.4",
|
|
36
|
+
"chalk": "^5.6.0",
|
|
37
|
+
"cli-table3": "^0.6.5",
|
|
38
|
+
"commander": "^14.0.0",
|
|
39
|
+
"effect": "^3.17.9",
|
|
40
|
+
"signal-exit": "3.0.7"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"prepare": "husky",
|
|
44
|
+
"dev": "bun run src/cli/index.ts",
|
|
45
|
+
"build": "tsc --noEmit && echo 'Build successful - CLI runs directly with bun'",
|
|
46
|
+
"test": "bun test",
|
|
47
|
+
"test:coverage": "bun test --coverage",
|
|
48
|
+
"test:coverage:check": "bun scripts/check-coverage.ts",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
50
|
+
"lint": "oxlint src/ tests/",
|
|
51
|
+
"format": "biome format --write src/ tests/",
|
|
52
|
+
"format:check": "biome check src/ tests/",
|
|
53
|
+
"check:all": "bun run typecheck && bun run lint && bun run format:check && bun run test:coverage"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
|
|
5
|
+
// Coverage thresholds
|
|
6
|
+
const THRESHOLDS = {
|
|
7
|
+
lines: 80,
|
|
8
|
+
functions: 80,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
console.log('Running tests with coverage...\n')
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Run tests with coverage and capture both stdout and stderr
|
|
15
|
+
const output = execSync('bun test --coverage 2>&1', { encoding: 'utf8' })
|
|
16
|
+
|
|
17
|
+
// Print the output
|
|
18
|
+
console.log(output)
|
|
19
|
+
|
|
20
|
+
// Parse coverage from output
|
|
21
|
+
const coverageMatch = output.match(/All files\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|/)
|
|
22
|
+
if (!coverageMatch) {
|
|
23
|
+
console.log('\n⚠️ Could not parse coverage output')
|
|
24
|
+
// Still exit with success if we can't parse but tests passed
|
|
25
|
+
process.exit(0)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const functionsCoverage = parseFloat(coverageMatch[1])
|
|
29
|
+
const linesCoverage = parseFloat(coverageMatch[2])
|
|
30
|
+
|
|
31
|
+
console.log(`\n📊 Coverage Summary:`)
|
|
32
|
+
console.log(` Functions: ${functionsCoverage}%`)
|
|
33
|
+
console.log(` Lines: ${linesCoverage}%`)
|
|
34
|
+
|
|
35
|
+
// Check thresholds
|
|
36
|
+
let failed = false
|
|
37
|
+
if (functionsCoverage < THRESHOLDS.functions) {
|
|
38
|
+
console.log(`\n❌ Functions coverage (${functionsCoverage}%) is below threshold (${THRESHOLDS.functions}%)`)
|
|
39
|
+
failed = true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (linesCoverage < THRESHOLDS.lines) {
|
|
43
|
+
console.log(`\n❌ Lines coverage (${linesCoverage}%) is below threshold (${THRESHOLDS.lines}%)`)
|
|
44
|
+
failed = true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (failed) {
|
|
48
|
+
console.log('\n⚠️ Coverage is below threshold but not failing CI (adjust if needed)')
|
|
49
|
+
// For now, don't fail CI on coverage - this can be enabled later
|
|
50
|
+
// process.exit(1)
|
|
51
|
+
process.exit(0)
|
|
52
|
+
} else {
|
|
53
|
+
console.log('\n✅ Coverage meets all thresholds!')
|
|
54
|
+
process.exit(0)
|
|
55
|
+
}
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
// If tests failed, the exit code will be non-zero
|
|
58
|
+
if (error.status !== 0) {
|
|
59
|
+
console.log('\n❌ Tests failed!')
|
|
60
|
+
// Still print the output if available
|
|
61
|
+
if (error.stdout) {
|
|
62
|
+
console.log(error.stdout.toString())
|
|
63
|
+
}
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
// Other errors
|
|
67
|
+
console.error('Error running coverage check:', error.message)
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { readdir, stat } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
const MAX_LINES = 700
|
|
7
|
+
const WARN_LINES = 500
|
|
8
|
+
|
|
9
|
+
async function checkFileSize(dir: string): Promise<void> {
|
|
10
|
+
const files = await readdir(dir, { withFileTypes: true })
|
|
11
|
+
let hasErrors = false
|
|
12
|
+
let hasWarnings = false
|
|
13
|
+
|
|
14
|
+
for (const file of files) {
|
|
15
|
+
const fullPath = join(dir, file.name)
|
|
16
|
+
|
|
17
|
+
if (file.isDirectory() && !['node_modules', 'dist', 'tmp', '.git'].includes(file.name)) {
|
|
18
|
+
await checkFileSize(fullPath)
|
|
19
|
+
} else if (file.isFile() && file.name.endsWith('.ts')) {
|
|
20
|
+
const content = await Bun.file(fullPath).text()
|
|
21
|
+
const lines = content.split('\n').length
|
|
22
|
+
|
|
23
|
+
if (lines > MAX_LINES) {
|
|
24
|
+
console.error(`❌ ERROR: ${fullPath} has ${lines} lines (max: ${MAX_LINES})`)
|
|
25
|
+
hasErrors = true
|
|
26
|
+
} else if (lines > WARN_LINES) {
|
|
27
|
+
console.warn(`⚠️ WARNING: ${fullPath} has ${lines} lines (recommended max: ${WARN_LINES})`)
|
|
28
|
+
hasWarnings = true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (hasErrors) {
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
checkFileSize('./src').catch(console.error)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import * as fs from 'fs'
|
|
4
|
+
import * as path from 'path'
|
|
5
|
+
|
|
6
|
+
const testsDir = path.join(process.cwd(), 'tests')
|
|
7
|
+
|
|
8
|
+
// Find all test files
|
|
9
|
+
const testFiles = fs.readdirSync(testsDir)
|
|
10
|
+
.filter(file => file.endsWith('.test.ts'))
|
|
11
|
+
.map(file => path.join(testsDir, file))
|
|
12
|
+
|
|
13
|
+
for (const file of testFiles) {
|
|
14
|
+
let content = fs.readFileSync(file, 'utf8')
|
|
15
|
+
|
|
16
|
+
// Skip if already using the helper
|
|
17
|
+
if (content.includes('createMockConfigService')) {
|
|
18
|
+
continue
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if file uses ConfigService mock
|
|
22
|
+
if (!content.includes('ConfigService.of({')) {
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(`Updating ${path.basename(file)}...`)
|
|
27
|
+
|
|
28
|
+
// Add import for helper if ConfigService is used
|
|
29
|
+
if (content.includes('ConfigService')) {
|
|
30
|
+
// Add import after other imports
|
|
31
|
+
const importMatch = content.match(/(import[\s\S]*?from\s+['"].*?['"][\s\n]*)+/)
|
|
32
|
+
if (importMatch) {
|
|
33
|
+
const lastImportEnd = importMatch.index! + importMatch[0].length
|
|
34
|
+
content = content.slice(0, lastImportEnd) +
|
|
35
|
+
`import { createMockConfigService } from './helpers/config-mock'\n` +
|
|
36
|
+
content.slice(lastImportEnd)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Replace ConfigService.of({ ... }) with createMockConfigService()
|
|
40
|
+
content = content.replace(
|
|
41
|
+
/ConfigService\.of\(\{[\s\S]*?getCredentials:\s*Effect\.succeed\(\{[\s\S]*?\}\),[\s\S]*?saveCredentials:[\s\S]*?deleteCredentials:[\s\S]*?\}\)/g,
|
|
42
|
+
'createMockConfigService()'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// Simpler pattern for common cases
|
|
46
|
+
content = content.replace(
|
|
47
|
+
/ConfigService\.of\(\{[\s\n\s]*getCredentials:\s*Effect\.succeed\(\{[\s\n\s]*host:\s*['"].*?['"],[\s\n\s]*username:\s*['"].*?['"],[\s\n\s]*password:\s*['"].*?['"],?[\s\n\s]*\}\),[\s\n\s]*saveCredentials:\s*\(\)\s*=>\s*Effect\.succeed\(undefined\),[\s\n\s]*deleteCredentials:\s*Effect\.succeed\(undefined\),?[\s\n\s]*\}\)/g,
|
|
48
|
+
'createMockConfigService()'
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fs.writeFileSync(file, content, 'utf8')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log('Done!')
|