diff --git a/.changeset/add-antigravity-profile.md b/.changeset/add-antigravity-profile.md new file mode 100644 index 0000000000..592d16cf35 --- /dev/null +++ b/.changeset/add-antigravity-profile.md @@ -0,0 +1,5 @@ +--- +"task-master-ai": minor +--- + +Add Antigravity as a selectable initialization profile in `task-master init` that creates MCP configuration in the user's home directory as `~/.gemini/antigravity/mcp_config.json` with task-master-ai server settings. diff --git a/.taskmaster/config.json b/.taskmaster/config.json index 698de60d93..6f9c5c8cc3 100644 --- a/.taskmaster/config.json +++ b/.taskmaster/config.json @@ -1,46 +1,46 @@ { - "models": { - "main": { - "provider": "claude-code", - "modelId": "opus", - "maxTokens": 32000, - "temperature": 0.2 - }, - "research": { - "provider": "perplexity", - "modelId": "sonar-reasoning-pro", - "maxTokens": 8700, - "temperature": 0.1 - }, - "fallback": { - "provider": "claude-code", - "modelId": "sonnet", - "maxTokens": 64000, - "temperature": 0.2 - } - }, - "global": { - "logLevel": "silent", - "debug": false, - "defaultNumTasks": 10, - "defaultSubtasks": 5, - "defaultPriority": "medium", - "projectName": "Taskmaster", - "ollamaBaseURL": "http://localhost:11434/api", - "bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com", - "responseLanguage": "English", - "enableCodebaseAnalysis": true, - "enableProxy": false, - "anonymousTelemetry": true, - "userId": "1234567890", - "azureBaseURL": "https://your-endpoint.azure.com/", - "defaultTag": "master" - }, - "claudeCode": {}, - "codexCli": {}, - "grokCli": { - "timeout": 120000, - "workingDirectory": null, - "defaultModel": "grok-4-latest" - } -} + "models": { + "main": { + "provider": "claude-code", + "modelId": "opus", + "maxTokens": 32000, + "temperature": 0.2 + }, + "research": { + "provider": "perplexity", + "modelId": "sonar-reasoning-pro", + "maxTokens": 8700, + "temperature": 0.1 + }, + "fallback": { + "provider": "claude-code", + "modelId": "sonnet", + "maxTokens": 64000, + "temperature": 0.2 + } + }, + "global": { + "logLevel": "silent", + "debug": false, + "defaultNumTasks": 10, + "defaultSubtasks": 5, + "defaultPriority": "medium", + "projectName": "Taskmaster", + "ollamaBaseURL": "http://localhost:11434/api", + "bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com", + "responseLanguage": "English", + "enableCodebaseAnalysis": true, + "enableProxy": false, + "anonymousTelemetry": true, + "userId": "1234567890", + "azureBaseURL": "https://your-endpoint.azure.com/", + "defaultTag": "master" + }, + "claudeCode": {}, + "codexCli": {}, + "grokCli": { + "timeout": 120000, + "workingDirectory": null, + "defaultModel": "grok-4-latest" + } +} \ No newline at end of file diff --git a/packages/tm-profiles/src/detection/profiles-map.ts b/packages/tm-profiles/src/detection/profiles-map.ts index cb3444ba54..51d4904d1b 100644 --- a/packages/tm-profiles/src/detection/profiles-map.ts +++ b/packages/tm-profiles/src/detection/profiles-map.ts @@ -80,6 +80,11 @@ export const IDE_MARKERS: IDEMarker[] = [ ], displayName: 'Gemini' }, + { + profileName: 'antigravity', + markers: [], // Global profile, no project markers + displayName: 'Antigravity' + }, { profileName: 'opencode', markers: [{ path: '.opencode', type: 'directory' }], diff --git a/src/constants/paths.js b/src/constants/paths.js index 26f8956075..e2d6898b34 100644 --- a/src/constants/paths.js +++ b/src/constants/paths.js @@ -14,6 +14,7 @@ export const TASKMASTER_CONFIG_FILE = '.taskmaster/config.json'; export const TASKMASTER_STATE_FILE = '.taskmaster/state.json'; export const LEGACY_CONFIG_FILE = '.taskmasterconfig'; + // Task Master report files export const COMPLEXITY_REPORT_FILE = '.taskmaster/reports/task-complexity-report.json'; diff --git a/src/constants/profiles.js b/src/constants/profiles.js index 56d1cd55a3..21a876dbe1 100644 --- a/src/constants/profiles.js +++ b/src/constants/profiles.js @@ -1,5 +1,5 @@ /** - * @typedef {'amp' | 'claude' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'kiro' | 'opencode' | 'kilo' | 'roo' | 'trae' | 'windsurf' | 'vscode' | 'zed'} RulesProfile + * @typedef {'amp' | 'antigravity' | 'claude' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'kiro' | 'opencode' | 'kilo' | 'roo' | 'trae' | 'windsurf' | 'vscode' | 'zed'} RulesProfile */ /** @@ -11,6 +11,7 @@ * @type {RulesProfile[]} * @description Defines possible rule profile sets: * - amp: Amp Code integration + * - antigravity: Antigravity setup and configuration * - claude: Claude Code integration * - cline: Cline IDE rules * - codex: Codex integration @@ -32,6 +33,7 @@ */ export const RULE_PROFILES = [ 'amp', + 'antigravity', 'claude', 'cline', 'codex', diff --git a/src/profiles/antigravity.js b/src/profiles/antigravity.js new file mode 100644 index 0000000000..b140183b33 --- /dev/null +++ b/src/profiles/antigravity.js @@ -0,0 +1,142 @@ +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import { log } from '../../scripts/modules/utils.js'; +import { createProfile } from './base-profile.js'; + +function isInstalled(targetDir) { + // Check if Antigravity MCP config exists and contains task-master-ai server + const mcpConfigPath = path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json'); + + try { + if (!fs.existsSync(mcpConfigPath)) { + return false; + } + + const config = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8')); + return config.mcpServers && config.mcpServers['task-master-ai']; + } catch (error) { + log('debug', `[Antigravity] Error checking installation: ${error.message}`); + return false; + } +} + +function onAddRulesProfile(targetDir, assetsDir) { + // Antigravity creates MCP config in home .gemini/antigravity/ directory + const geminiDir = path.join(os.homedir(), '.gemini'); + const antigravityDir = path.join(geminiDir, 'antigravity'); + const mcpConfigPath = path.join(antigravityDir, 'mcp_config.json'); + + try { + // Ensure directories exist + if (!fs.existsSync(geminiDir)) { + fs.mkdirSync(geminiDir, { recursive: true }); + } + if (!fs.existsSync(antigravityDir)) { + fs.mkdirSync(antigravityDir, { recursive: true }); + } + + // Check if config already exists + if (fs.existsSync(mcpConfigPath)) { + const existingConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8')); + + // Check if task-master-ai server is already configured + if (existingConfig.mcpServers && existingConfig.mcpServers['task-master-ai']) { + log('info', `[Antigravity] Task Master AI server already configured in MCP config`); + return; + } + + // Add task-master-ai server to existing config + if (!existingConfig.mcpServers) { + existingConfig.mcpServers = {}; + } + existingConfig.mcpServers['task-master-ai'] = { + command: 'npx', + args: ['-y', 'task-master-ai', 'mcp-server'], + env: { + TASK_MASTER_PROJECT_ROOT: targetDir + } + }; + + fs.writeFileSync(mcpConfigPath, JSON.stringify(existingConfig, null, 2) + '\n'); + log('info', `[Antigravity] Added Task Master AI server to existing MCP config`); + } else { + // Create new MCP config + const mcpConfig = { + mcpServers: { + 'task-master-ai': { + command: 'npx', + args: ['-y', 'task-master-ai', 'mcp-server'], + env: { + TASK_MASTER_PROJECT_ROOT: targetDir + } + } + } + }; + + fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n'); + log('info', `[Antigravity] Created MCP config at ${mcpConfigPath}`); + } + } catch (error) { + log('error', `[Antigravity] Failed to create MCP config: ${error.message}`); + } +} + +function onRemoveRulesProfile(targetDir) { + // Clean up Antigravity MCP config from home .gemini/antigravity/ + const antigravityDir = path.join(os.homedir(), '.gemini', 'antigravity'); + const mcpConfigPath = path.join(antigravityDir, 'mcp_config.json'); + + try { + if (fs.existsSync(mcpConfigPath)) { + // Read current config and remove task-master-ai server + const config = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8')); + if (config.mcpServers && config.mcpServers['task-master-ai']) { + delete config.mcpServers['task-master-ai']; + + // If no servers left, remove the file + if (Object.keys(config.mcpServers).length === 0) { + fs.rmSync(mcpConfigPath, { force: true }); + log('info', `[Antigravity] Removed empty MCP config file`); + } else { + // Update config with task-master-ai removed + fs.writeFileSync(mcpConfigPath, JSON.stringify(config, null, 2) + '\n'); + log('info', `[Antigravity] Removed Task Master from MCP config`); + } + } + } + + // Clean up empty directories + try { + if (fs.existsSync(antigravityDir) && fs.readdirSync(antigravityDir).length === 0) { + fs.rmSync(antigravityDir, { recursive: true, force: true }); + log('debug', `[Antigravity] Removed empty antigravity directory`); + } + const geminiDir = path.join(targetDir, '.gemini'); + if (fs.existsSync(geminiDir) && fs.readdirSync(geminiDir).length === 0) { + fs.rmSync(geminiDir, { recursive: true, force: true }); + log('debug', `[Antigravity] Removed empty .gemini directory`); + } + } catch (dirError) { + // Directory cleanup is not critical + } + } catch (error) { + log('error', `[Antigravity] Failed to clean up MCP config: ${error.message}`); + } +} + +export const antigravityProfile = createProfile({ + name: 'antigravity', + displayName: 'Antigravity', + url: 'https://antigravity.dev', + docsUrl: 'https://antigravity.dev/docs', + profileDir: '.gemini/antigravity', + rulesDir: '.', + mcpConfig: true, + mcpConfigName: 'mcp_config.json', + includeDefaultRules: false, + fileMap: {}, + isInstalled, + onAdd: onAddRulesProfile, + onRemove: onRemoveRulesProfile +}); diff --git a/src/profiles/index.js b/src/profiles/index.js index 9bbbbcd0c5..5c9c2dfbcf 100644 --- a/src/profiles/index.js +++ b/src/profiles/index.js @@ -1,5 +1,6 @@ // Profile exports for centralized importing export { ampProfile } from './amp.js'; +export { antigravityProfile } from './antigravity.js'; export { claudeProfile } from './claude.js'; export { clineProfile } from './cline.js'; export { codexProfile } from './codex.js'; diff --git a/tests/unit/profiles.test.js b/tests/unit/profiles.test.js new file mode 100644 index 0000000000..d5d0a6c039 --- /dev/null +++ b/tests/unit/profiles.test.js @@ -0,0 +1,12 @@ +import { RULE_PROFILES } from '../../src/constants/profiles.js'; +import { getRulesProfile } from '../../src/utils/rule-transformer.js'; + +describe('Antigravity profile', () => { + it('is registered and resolves correctly', () => { + expect(RULE_PROFILES).toContain('antigravity'); + const profile = getRulesProfile('antigravity'); + expect(profile).toBeDefined(); + expect(profile.displayName).toBe('Antigravity'); + expect(profile.mcpConfig).toBe(false); + }); +});