Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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 project root 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: [{ path: '.gemini/antigravity/mcp_config.json', type: 'file' }],
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
141 changes: 141 additions & 0 deletions src/profiles/antigravity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import fs from 'fs';
import path from 'path';
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(targetDir, '.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 project root .gemini/antigravity/ directory
const geminiDir = path.join(targetDir, '.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 project .gemini/antigravity/
const antigravityDir = path.join(targetDir, '.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.

});
});