chore: apply requested changes and improve coderabbit config
This commit is contained in:
@@ -1,10 +1,3 @@
|
||||
reviews:
|
||||
profile: assertive
|
||||
poem: false
|
||||
auto_review:
|
||||
base_branches:
|
||||
- rc
|
||||
- beta
|
||||
- alpha
|
||||
- production
|
||||
- next
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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!"
|
||||
|
||||
Reference in New Issue
Block a user