chore: apply requested changes and improve coderabbit config
This commit is contained in:
@@ -1,10 +1,3 @@
|
|||||||
reviews:
|
reviews:
|
||||||
profile: assertive
|
profile: assertive
|
||||||
poem: false
|
poem: false
|
||||||
auto_review:
|
|
||||||
base_branches:
|
|
||||||
- rc
|
|
||||||
- beta
|
|
||||||
- alpha
|
|
||||||
- production
|
|
||||||
- next
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
* Validates environment and prerequisites for autopilot execution
|
* Validates environment and prerequisites for autopilot execution
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync, existsSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { getLogger } from '../logger/factory.js';
|
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
|
* Validate required tools availability
|
||||||
*/
|
*/
|
||||||
async validateRequiredTools(): Promise<CheckResult> {
|
async validateRequiredTools(): Promise<CheckResult> {
|
||||||
const tools: ToolCheck[] = [];
|
const tools: ToolCheck[] = [];
|
||||||
|
|
||||||
// Check git
|
// Always check git and gh CLI
|
||||||
tools.push(this.checkTool('git', ['--version']));
|
tools.push(this.checkTool('git', ['--version']));
|
||||||
|
tools.push(await this.checkGhCli());
|
||||||
|
|
||||||
// Check gh CLI
|
// Detect project types and check their tools
|
||||||
const ghAvailable = await isGhCliAvailable(this.projectRoot);
|
const projectTypes = this.detectProjectTypes();
|
||||||
tools.push({
|
|
||||||
name: 'gh',
|
|
||||||
available: ghAvailable,
|
|
||||||
message: ghAvailable ? 'GitHub CLI available' : 'GitHub CLI not available'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check node
|
if (projectTypes.length === 0) {
|
||||||
tools.push(this.checkTool('node', ['--version']));
|
logger.warn('No recognized project type detected');
|
||||||
|
} else {
|
||||||
|
logger.info(`Detected project types: ${projectTypes.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Check npm
|
for (const type of projectTypes) {
|
||||||
tools.push(this.checkTool('npm', ['--version']));
|
const typeTools = this.getToolsForProjectType(type);
|
||||||
|
for (const tool of typeTools) {
|
||||||
|
tools.push(this.checkTool(tool.command, tool.args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Determine overall success
|
// Determine overall success
|
||||||
const allAvailable = tools.every((tool) => tool.available);
|
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
|
* Detect default branch
|
||||||
*/
|
*/
|
||||||
@@ -280,9 +375,10 @@ export class PreflightChecker {
|
|||||||
if (defaultBranch.success) passed.push('Default branch');
|
if (defaultBranch.success) passed.push('Default branch');
|
||||||
else failed.push('Default branch');
|
else failed.push('Default branch');
|
||||||
|
|
||||||
|
const total = passed.length + failed.length;
|
||||||
const summary = allSuccess
|
const summary = allSuccess
|
||||||
? `All preflight checks passed (${passed.length}/4)`
|
? `All preflight checks passed (${passed.length}/${total})`
|
||||||
: `Preflight checks failed: ${failed.join(', ')} (${passed.length}/4 passed)`;
|
: `Preflight checks failed: ${failed.join(', ')} (${passed.length}/${total} passed)`;
|
||||||
|
|
||||||
logger.info(summary);
|
logger.info(summary);
|
||||||
|
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ export class TaskLoaderService {
|
|||||||
|
|
||||||
// Keep adding subtasks whose dependencies are all completed
|
// Keep adding subtasks whose dependencies are all completed
|
||||||
while (ordered.length < task.subtasks.length) {
|
while (ordered.length < task.subtasks.length) {
|
||||||
const added = false;
|
let added = false;
|
||||||
|
|
||||||
for (const subtask of task.subtasks) {
|
for (const subtask of task.subtasks) {
|
||||||
const subtaskId = String(subtask.id);
|
const subtaskId = String(subtask.id);
|
||||||
@@ -368,6 +368,7 @@ export class TaskLoaderService {
|
|||||||
if (allDepsCompleted) {
|
if (allDepsCompleted) {
|
||||||
ordered.push(subtask);
|
ordered.push(subtask);
|
||||||
completed.add(subtaskId);
|
completed.add(subtaskId);
|
||||||
|
added = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export async function getLocalBranches(projectRoot: string): Promise<string[]> {
|
|||||||
try {
|
try {
|
||||||
const { stdout } = await execAsync(
|
const { stdout } = await execAsync(
|
||||||
'git branch --format="%(refname:short)"',
|
'git branch --format="%(refname:short)"',
|
||||||
{ cwd: projectRoot }
|
{ cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 }
|
||||||
);
|
);
|
||||||
return stdout
|
return stdout
|
||||||
.trim()
|
.trim()
|
||||||
@@ -127,13 +127,13 @@ export async function getRemoteBranches(
|
|||||||
try {
|
try {
|
||||||
const { stdout } = await execAsync(
|
const { stdout } = await execAsync(
|
||||||
'git branch -r --format="%(refname:short)"',
|
'git branch -r --format="%(refname:short)"',
|
||||||
{ cwd: projectRoot }
|
{ cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 }
|
||||||
);
|
);
|
||||||
return stdout
|
return stdout
|
||||||
.trim()
|
.trim()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((branch) => branch.length > 0 && !branch.includes('HEAD'))
|
.filter((branch) => branch.length > 0 && !branch.includes('HEAD'))
|
||||||
.map((branch) => branch.replace(/^origin\//, '').trim());
|
.map((branch) => branch.replace(/^[^/]+\//, '').trim());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -212,19 +212,41 @@ export async function getDefaultBranch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to git remote info
|
// Fallback to git remote info (support non-origin remotes)
|
||||||
const { stdout } = await execAsync(
|
const remotesRaw = await execAsync('git remote', { cwd: projectRoot });
|
||||||
'git symbolic-ref refs/remotes/origin/HEAD',
|
const remotes = remotesRaw.stdout.trim().split('\n').filter(Boolean);
|
||||||
{ cwd: projectRoot }
|
if (remotes.length > 0) {
|
||||||
);
|
const primary = remotes.includes('origin') ? 'origin' : remotes[0];
|
||||||
return stdout.replace('refs/remotes/origin/', '').trim();
|
// 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) {
|
} catch (error) {
|
||||||
// Final fallback - common default branch names
|
// Final fallback - common default branch names
|
||||||
const commonDefaults = ['main', 'master'];
|
const commonDefaults = ['main', 'master'];
|
||||||
const branches = await getLocalBranches(projectRoot);
|
const branches = await getLocalBranches(projectRoot);
|
||||||
|
const remoteBranches = await getRemoteBranches(projectRoot);
|
||||||
|
|
||||||
for (const defaultName of commonDefaults) {
|
for (const defaultName of commonDefaults) {
|
||||||
if (branches.includes(defaultName)) {
|
if (
|
||||||
|
branches.includes(defaultName) ||
|
||||||
|
remoteBranches.includes(defaultName)
|
||||||
|
) {
|
||||||
return defaultName;
|
return defaultName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,7 +301,7 @@ export function sanitizeBranchNameForTag(branchName: string): string {
|
|||||||
|
|
||||||
// Replace invalid characters with hyphens and clean up
|
// Replace invalid characters with hyphens and clean up
|
||||||
return branchName
|
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, '') // Remove leading/trailing hyphens
|
||||||
.replace(/-+/g, '-') // Collapse multiple hyphens
|
.replace(/-+/g, '-') // Collapse multiple hyphens
|
||||||
.toLowerCase() // Convert to lowercase
|
.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
|
// 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())) {
|
if (reservedBranches.includes(branchName.toLowerCase())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,20 @@
|
|||||||
# Create a git worktree for parallel Claude Code development
|
# Create a git worktree for parallel Claude Code development
|
||||||
# Usage: ./scripts/create-worktree.sh [branch-name]
|
# Usage: ./scripts/create-worktree.sh [branch-name]
|
||||||
|
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
WORKTREES_DIR="$(cd "$PROJECT_ROOT/.." && pwd)/claude-task-master-worktrees"
|
WORKTREES_DIR="$(cd "$PROJECT_ROOT/.." && pwd)/claude-task-master-worktrees"
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
# Get branch name (default to current branch with auto/ prefix)
|
# Get branch name (default to current branch with auto/ prefix)
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
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"
|
BRANCH_NAME="auto/$CURRENT_BRANCH"
|
||||||
echo "No branch specified, using: $BRANCH_NAME"
|
echo "No branch specified, using: $BRANCH_NAME"
|
||||||
else
|
else
|
||||||
@@ -36,9 +41,14 @@ if [ -d "$WORKTREE_PATH" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create new branch and worktree
|
# Create worktree (new or existing branch)
|
||||||
git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" 2>/dev/null || \
|
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
|
||||||
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
|
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 ""
|
||||||
echo "✅ Worktree created successfully!"
|
echo "✅ Worktree created successfully!"
|
||||||
|
|||||||
Reference in New Issue
Block a user