fix: expand brownfield project detection to cover Android, Kotlin, Gradle, and 15+ additional ecosystems
The code/package detection in cmdInitNewProject only recognized 7 file extensions and 5 package files, missing Android (Kotlin + Gradle), Flutter (Dart + pubspec.yaml), C/C++, C#, Ruby, PHP, Scala, and others. This caused new-project to treat brownfield projects in those ecosystems as greenfield, skipping the codebase mapping step. Added 18 code extensions and 11 package/build files to the detection lists. Fixes #1325 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -252,7 +252,23 @@ function cmdInitNewProject(cwd, raw) {
|
||||
let hasCode = false;
|
||||
let hasPackageFile = false;
|
||||
try {
|
||||
const codeExtensions = new Set(['.ts', '.js', '.py', '.go', '.rs', '.swift', '.java']);
|
||||
const codeExtensions = new Set([
|
||||
'.ts', '.js', '.py', '.go', '.rs', '.swift', '.java',
|
||||
'.kt', '.kts', // Kotlin (Android, server-side)
|
||||
'.c', '.cpp', '.h', // C/C++
|
||||
'.cs', // C#
|
||||
'.rb', // Ruby
|
||||
'.php', // PHP
|
||||
'.dart', // Dart (Flutter)
|
||||
'.m', '.mm', // Objective-C / Objective-C++
|
||||
'.scala', // Scala
|
||||
'.groovy', // Groovy (Gradle build scripts)
|
||||
'.lua', // Lua
|
||||
'.r', '.R', // R
|
||||
'.zig', // Zig
|
||||
'.ex', '.exs', // Elixir
|
||||
'.clj', // Clojure
|
||||
]);
|
||||
const skipDirs = new Set(['node_modules', '.git', '.planning', '.claude', '__pycache__', 'target', 'dist', 'build']);
|
||||
function findCodeFiles(dir, depth) {
|
||||
if (depth > 3) return false;
|
||||
@@ -273,7 +289,18 @@ function cmdInitNewProject(cwd, raw) {
|
||||
pathExistsInternal(cwd, 'requirements.txt') ||
|
||||
pathExistsInternal(cwd, 'Cargo.toml') ||
|
||||
pathExistsInternal(cwd, 'go.mod') ||
|
||||
pathExistsInternal(cwd, 'Package.swift');
|
||||
pathExistsInternal(cwd, 'Package.swift') ||
|
||||
pathExistsInternal(cwd, 'build.gradle') ||
|
||||
pathExistsInternal(cwd, 'build.gradle.kts') ||
|
||||
pathExistsInternal(cwd, 'pom.xml') ||
|
||||
pathExistsInternal(cwd, 'Gemfile') ||
|
||||
pathExistsInternal(cwd, 'composer.json') ||
|
||||
pathExistsInternal(cwd, 'pubspec.yaml') ||
|
||||
pathExistsInternal(cwd, 'CMakeLists.txt') ||
|
||||
pathExistsInternal(cwd, 'Makefile') ||
|
||||
pathExistsInternal(cwd, 'build.zig') ||
|
||||
pathExistsInternal(cwd, 'mix.exs') ||
|
||||
pathExistsInternal(cwd, 'project.clj');
|
||||
|
||||
const result = {
|
||||
// Models
|
||||
|
||||
@@ -1022,6 +1022,88 @@ describe('cmdInitNewProject', () => {
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.planning_exists, true);
|
||||
});
|
||||
|
||||
test('brownfield with Kotlin files detected (Android project)', () => {
|
||||
const srcDir = path.join(tmpDir, 'app', 'src', 'main');
|
||||
fs.mkdirSync(srcDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(srcDir, 'MainActivity.kt'), 'class MainActivity');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_existing_code, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
});
|
||||
|
||||
test('brownfield with build.gradle detected (Android/Gradle project)', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'build.gradle'), 'apply plugin: "com.android.application"');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_package_file, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
assert.strictEqual(output.needs_codebase_map, true);
|
||||
});
|
||||
|
||||
test('brownfield with build.gradle.kts detected (Kotlin DSL)', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'build.gradle.kts'), 'plugins { id("com.android.application") }');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_package_file, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
});
|
||||
|
||||
test('brownfield with pom.xml detected (Maven project)', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'pom.xml'), '<project></project>');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_package_file, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
});
|
||||
|
||||
test('brownfield with pubspec.yaml detected (Flutter/Dart project)', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'pubspec.yaml'), 'name: my_app');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_package_file, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
});
|
||||
|
||||
test('brownfield with Dart files detected', () => {
|
||||
const libDir = path.join(tmpDir, 'lib');
|
||||
fs.mkdirSync(libDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(libDir, 'main.dart'), 'void main() {}');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_existing_code, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
});
|
||||
|
||||
test('brownfield with C++ files detected', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'main.cpp'), 'int main() { return 0; }');
|
||||
|
||||
const result = runGsdTools('init new-project', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.has_existing_code, true);
|
||||
assert.strictEqual(output.is_brownfield, true);
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user