Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-antigravity-profile.md
Original file line number Diff line number Diff line change
@@ -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.
90 changes: 45 additions & 45 deletions .taskmaster/config.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
Comment on lines 1 to +46
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Avoid manual edits to .taskmaster/config.json.

This looks like formatting-only churn in a managed file; please update this via the supported command flow instead of direct edits.

As per coding guidelines: "Do not manually edit .taskmaster/config.json; use task-master models command or models MCP tool to manage configuration."

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.taskmaster/config.json around lines 1 - 46, The commit contains manual
edits to the managed "models" configuration (entries "main", "research",
"fallback") in the generated config file; revert these manual changes and update
model configuration using the supported workflow instead: undo the edits to the
managed config, then run the official CLI command (task-master models) or use
the models MCP tool to apply the desired provider/modelId/maxTokens/temperature
changes so the managed file is updated correctly and remains consistent with the
toolchain.

5 changes: 5 additions & 0 deletions packages/tm-profiles/src/detection/profiles-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }],
Expand Down
1 change: 1 addition & 0 deletions src/constants/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
4 changes: 3 additions & 1 deletion src/constants/profiles.js
Original file line number Diff line number Diff line change
@@ -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
*/

/**
Expand All @@ -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
Expand All @@ -32,6 +33,7 @@
*/
export const RULE_PROFILES = [
'amp',
'antigravity',
'claude',
'cline',
'codex',
Expand Down
142 changes: 142 additions & 0 deletions src/profiles/antigravity.js
Original file line number Diff line number Diff line change
@@ -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'];
Comment on lines +16 to +17
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion | 🟠 Major

Use shared readJSON/writeJSON utilities for MCP config file operations.

The current direct fs.readFileSync/fs.writeFileSync usage bypasses the project’s JSON I/O standard and centralized error/validation handling.

As per coding guidelines: "Use readJSON and writeJSON utilities for all JSON file operations instead of raw fs.readFileSync or fs.writeFileSync."

Also applies to: 40-40, 60-60, 76-76, 92-92, 102-102

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/profiles/antigravity.js` around lines 15 - 16, Replace direct
fs.readFileSync/fs.writeFileSync usage with the project's readJSON and writeJSON
utilities when accessing the MCP config: call readJSON(mcpConfigPath) to load
the config, use the returned object (e.g., check config.mcpServers &&
config.mcpServers['task-master-ai']), and use writeJSON(mcpConfigPath, config)
when persisting changes; ensure you propagate or handle errors the utilities
provide. Update all similar occurrences that operate on mcpConfigPath (the
instances at the noted locations) so they use readJSON/writeJSON and maintain
the same logic around config.mcpServers and the 'task-master-ai' key.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

isInstalled should return a strict boolean.

On Line 16, the function currently returns the server object or undefined, not true/false. Returning Boolean(...) avoids type ambiguity.

Suggested fix
-		return config.mcpServers && config.mcpServers['task-master-ai'];
+		return Boolean(config?.mcpServers?.['task-master-ai']);
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return config.mcpServers && config.mcpServers['task-master-ai'];
return Boolean(config?.mcpServers?.['task-master-ai']);
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/profiles/antigravity.js` at line 16, The isInstalled check currently
returns the server object or undefined (config.mcpServers &&
config.mcpServers['task-master-ai']), so change it to return a strict boolean by
wrapping the expression with Boolean(...); update the return in the isInstalled
function to return Boolean(config.mcpServers &&
config.mcpServers['task-master-ai']) so callers always get true/false rather
than an object or undefined.

} 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}`);
}
Comment on lines +80 to +82
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not swallow setup failures in onAddRulesProfile.

Catching and only logging here can report a successful profile add even when MCP config creation failed.

Suggested fix
 	} catch (error) {
 		log('error', `[Antigravity] Failed to create MCP config: ${error.message}`);
+		throw error;
 	}
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
log('error', `[Antigravity] Failed to create MCP config: ${error.message}`);
}
} catch (error) {
log('error', `[Antigravity] Failed to create MCP config: ${error.message}`);
throw error;
}
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/profiles/antigravity.js` around lines 80 - 82, The catch in
onAddRulesProfile currently only logs MCP config creation failures (log('error',
`[Antigravity] Failed to create MCP config: ${error.message}`)) which swallows
the error and can incorrectly signal success; modify onAddRulesProfile to
propagate the failure after logging by rethrowing the caught error (or returning
a rejected Promise) so callers see the setup failure, and ensure any upstream
callers handle the thrown error appropriately; keep the existing log but add
throw error (or return Promise.reject(error)) immediately after to prevent
false-success behavior.

}

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`);
Comment on lines +98 to +100
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid deleting the whole config file when non-server keys may still exist.

If mcpServers becomes empty but other top-level settings are present, removing the file drops unrelated user config.

Suggested fix
-				// 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`);
+				// If no servers left, only remove file when config is otherwise empty
+				if (Object.keys(config.mcpServers).length === 0) {
+					delete config.mcpServers;
+					if (Object.keys(config).length === 0) {
+						fs.rmSync(mcpConfigPath, { force: true });
+						log('info', `[Antigravity] Removed empty MCP config file`);
+					} else {
+						fs.writeFileSync(mcpConfigPath, JSON.stringify(config, null, 2) + '\n');
+						log('info', `[Antigravity] Removed Task Master from MCP config`);
+					}
 				} else {
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/profiles/antigravity.js` around lines 97 - 99, The current code
unconditionally removes the entire MCP config file when config.mcpServers is
empty; instead, check whether other top-level keys exist before deleting: if
Object.keys(config.mcpServers).length === 0 then remove only the mcpServers key
from config and write the updated config back to disk, and only call
fs.rmSync(mcpConfigPath, { force: true }) if Object.keys(config).length === 0
(no other keys remain); update the log calls accordingly (use log('info', ...)
when deleting the file and when updating the config) so unrelated user settings
are not lost.

} 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
});
1 change: 1 addition & 0 deletions src/profiles/index.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/profiles.test.js
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

mcpConfig assertion is inverted.

On Line 10, toBe(false) conflicts with the profile definition (src/profiles/antigravity.js, Line 134 sets mcpConfig: true), so this test should assert true.

Suggested fix
-		expect(profile.mcpConfig).toBe(false);
+		expect(profile.mcpConfig).toBe(true);
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(profile.mcpConfig).toBe(false);
expect(profile.mcpConfig).toBe(true);
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/profiles.test.js` at line 10, The test assertion for
profile.mcpConfig in tests/unit/profiles.test.js is inverted; update the
expectation to assert true instead of false. Locate the failing assertion
(expect(profile.mcpConfig).toBe(false)) and change it to
expect(profile.mcpConfig).toBe(true) so it matches the antigravity profile's
mcpConfig value set to true.

});
});