chore: apply requested changes and improve coderabbit config

This commit is contained in:
Ralph Khreish
2025-10-08 14:47:23 +02:00
parent f71cdb4eaa
commit 4b6ad19bc4
5 changed files with 161 additions and 39 deletions

View File

@@ -1,10 +1,3 @@
reviews:
profile: assertive
poem: false
auto_review:
base_branches:
- rc
- beta
- alpha
- production
- next

View File

@@ -3,7 +3,7 @@
* Validates environment and prerequisites for autopilot execution
*/
import { readFileSync } from 'fs';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { execSync } from 'child_process';
import { getLogger } from '../logger/factory.js';
@@ -147,28 +147,97 @@ export class PreflightChecker {
}
}
/**
* Detect project types based on common configuration files
*/
private detectProjectTypes(): string[] {
const types: string[] = [];
if (existsSync(join(this.projectRoot, 'package.json'))) types.push('node');
if (
existsSync(join(this.projectRoot, 'requirements.txt')) ||
existsSync(join(this.projectRoot, 'setup.py')) ||
existsSync(join(this.projectRoot, 'pyproject.toml'))
)
types.push('python');
if (
existsSync(join(this.projectRoot, 'pom.xml')) ||
existsSync(join(this.projectRoot, 'build.gradle'))
)
types.push('java');
if (existsSync(join(this.projectRoot, 'go.mod'))) types.push('go');
if (existsSync(join(this.projectRoot, 'Cargo.toml'))) types.push('rust');
if (existsSync(join(this.projectRoot, 'composer.json'))) types.push('php');
if (existsSync(join(this.projectRoot, 'Gemfile'))) types.push('ruby');
if (
existsSync(join(this.projectRoot, '*.csproj')) ||
existsSync(join(this.projectRoot, '*.sln'))
)
types.push('dotnet');
return types;
}
/**
* Get required tools for a project type
*/
private getToolsForProjectType(
type: string
): Array<{ command: string; args: string[] }> {
const toolMap: Record<
string,
Array<{ command: string; args: string[] }>
> = {
node: [
{ command: 'node', args: ['--version'] },
{ command: 'npm', args: ['--version'] }
],
python: [
{ command: 'python3', args: ['--version'] },
{ command: 'pip3', args: ['--version'] }
],
java: [{ command: 'java', args: ['--version'] }],
go: [{ command: 'go', args: ['version'] }],
rust: [{ command: 'cargo', args: ['--version'] }],
php: [
{ command: 'php', args: ['--version'] },
{ command: 'composer', args: ['--version'] }
],
ruby: [
{ command: 'ruby', args: ['--version'] },
{ command: 'bundle', args: ['--version'] }
],
dotnet: [{ command: 'dotnet', args: ['--version'] }]
};
return toolMap[type] || [];
}
/**
* Validate required tools availability
*/
async validateRequiredTools(): Promise<CheckResult> {
const tools: ToolCheck[] = [];
// Check git
// Always check git and gh CLI
tools.push(this.checkTool('git', ['--version']));
tools.push(await this.checkGhCli());
// Check gh CLI
const ghAvailable = await isGhCliAvailable(this.projectRoot);
tools.push({
name: 'gh',
available: ghAvailable,
message: ghAvailable ? 'GitHub CLI available' : 'GitHub CLI not available'
});
// Detect project types and check their tools
const projectTypes = this.detectProjectTypes();
// Check node
tools.push(this.checkTool('node', ['--version']));
if (projectTypes.length === 0) {
logger.warn('No recognized project type detected');
} else {
logger.info(`Detected project types: ${projectTypes.join(', ')}`);
}
// Check npm
tools.push(this.checkTool('npm', ['--version']));
for (const type of projectTypes) {
const typeTools = this.getToolsForProjectType(type);
for (const tool of typeTools) {
tools.push(this.checkTool(tool.command, tool.args));
}
}
// Determine overall success
const allAvailable = tools.every((tool) => tool.available);
@@ -219,6 +288,32 @@ export class PreflightChecker {
}
}
/**
* Check GitHub CLI installation and authentication status
*/
private async checkGhCli(): Promise<ToolCheck> {
try {
const version = execSync('gh --version', {
cwd: this.projectRoot,
encoding: 'utf-8',
stdio: 'pipe'
})
.trim()
.split('\n')[0];
const authed = await isGhCliAvailable(this.projectRoot);
return {
name: 'gh',
available: true,
version,
message: authed
? 'GitHub CLI installed (authenticated)'
: 'GitHub CLI installed (not authenticated)'
};
} catch {
return { name: 'gh', available: false, message: 'GitHub CLI not found' };
}
}
/**
* Detect default branch
*/
@@ -280,9 +375,10 @@ export class PreflightChecker {
if (defaultBranch.success) passed.push('Default branch');
else failed.push('Default branch');
const total = passed.length + failed.length;
const summary = allSuccess
? `All preflight checks passed (${passed.length}/4)`
: `Preflight checks failed: ${failed.join(', ')} (${passed.length}/4 passed)`;
? `All preflight checks passed (${passed.length}/${total})`
: `Preflight checks failed: ${failed.join(', ')} (${passed.length}/${total} passed)`;
logger.info(summary);

View File

@@ -350,7 +350,7 @@ export class TaskLoaderService {
// Keep adding subtasks whose dependencies are all completed
while (ordered.length < task.subtasks.length) {
const added = false;
let added = false;
for (const subtask of task.subtasks) {
const subtaskId = String(subtask.id);
@@ -368,6 +368,7 @@ export class TaskLoaderService {
if (allDepsCompleted) {
ordered.push(subtask);
completed.add(subtaskId);
added = true;
break;
}
}

View File

@@ -102,7 +102,7 @@ export async function getLocalBranches(projectRoot: string): Promise<string[]> {
try {
const { stdout } = await execAsync(
'git branch --format="%(refname:short)"',
{ cwd: projectRoot }
{ cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 }
);
return stdout
.trim()
@@ -127,13 +127,13 @@ export async function getRemoteBranches(
try {
const { stdout } = await execAsync(
'git branch -r --format="%(refname:short)"',
{ cwd: projectRoot }
{ cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 }
);
return stdout
.trim()
.split('\n')
.filter((branch) => branch.length > 0 && !branch.includes('HEAD'))
.map((branch) => branch.replace(/^origin\//, '').trim());
.map((branch) => branch.replace(/^[^/]+\//, '').trim());
} catch (error) {
return [];
}
@@ -212,19 +212,41 @@ export async function getDefaultBranch(
}
}
// Fallback to git remote info
const { stdout } = await execAsync(
'git symbolic-ref refs/remotes/origin/HEAD',
{ cwd: projectRoot }
);
return stdout.replace('refs/remotes/origin/', '').trim();
// Fallback to git remote info (support non-origin remotes)
const remotesRaw = await execAsync('git remote', { cwd: projectRoot });
const remotes = remotesRaw.stdout.trim().split('\n').filter(Boolean);
if (remotes.length > 0) {
const primary = remotes.includes('origin') ? 'origin' : remotes[0];
// Parse `git remote show` (preferred)
try {
const { stdout } = await execAsync(`git remote show ${primary}`, {
cwd: projectRoot
});
const m = stdout.match(/HEAD branch:\s+([^\s]+)/);
if (m) return m[1].trim();
} catch {}
// Fallback to symbolic-ref of remote HEAD
try {
const { stdout } = await execAsync(
`git symbolic-ref refs/remotes/${primary}/HEAD`,
{ cwd: projectRoot }
);
return stdout.replace(`refs/remotes/${primary}/`, '').trim();
} catch {}
}
// If we couldn't determine, throw to trigger final fallbacks
throw new Error('default-branch-not-found');
} catch (error) {
// Final fallback - common default branch names
const commonDefaults = ['main', 'master'];
const branches = await getLocalBranches(projectRoot);
const remoteBranches = await getRemoteBranches(projectRoot);
for (const defaultName of commonDefaults) {
if (branches.includes(defaultName)) {
if (
branches.includes(defaultName) ||
remoteBranches.includes(defaultName)
) {
return defaultName;
}
}
@@ -279,7 +301,7 @@ export function sanitizeBranchNameForTag(branchName: string): string {
// Replace invalid characters with hyphens and clean up
return branchName
.replace(/[^a-zA-Z0-9_-]/g, '-') // Replace invalid chars with hyphens
.replace(/[^a-zA-Z0-9_.-]/g, '-') // Replace invalid chars with hyphens (allow dots)
.replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
.replace(/-+/g, '-') // Collapse multiple hyphens
.toLowerCase() // Convert to lowercase
@@ -295,7 +317,7 @@ export function isValidBranchForTag(branchName: string): boolean {
}
// Check if it's a reserved branch name that shouldn't become tags
const reservedBranches = ['main', 'master', 'develop', 'dev', 'HEAD'];
const reservedBranches = ['main', 'master', 'develop', 'dev', 'head'];
if (reservedBranches.includes(branchName.toLowerCase())) {
return false;
}

View File

@@ -3,15 +3,20 @@
# Create a git worktree for parallel Claude Code development
# Usage: ./scripts/create-worktree.sh [branch-name]
set -e
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
WORKTREES_DIR="$(cd "$PROJECT_ROOT/.." && pwd)/claude-task-master-worktrees"
cd "$PROJECT_ROOT"
# Get branch name (default to current branch with auto/ prefix)
if [ -z "$1" ]; then
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
echo "Detached HEAD detected. Please specify a branch: ./scripts/create-worktree.sh <branch-name>"
exit 1
fi
BRANCH_NAME="auto/$CURRENT_BRANCH"
echo "No branch specified, using: $BRANCH_NAME"
else
@@ -36,9 +41,14 @@ if [ -d "$WORKTREE_PATH" ]; then
exit 1
fi
# Create new branch and worktree
git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" 2>/dev/null || \
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
# Create worktree (new or existing branch)
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
elif git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then
git worktree add "$WORKTREE_PATH" "origin/$BRANCH_NAME"
else
git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH"
fi
echo ""
echo "✅ Worktree created successfully!"