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
18 changes: 18 additions & 0 deletions src/cli/command-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { copilotPreToolUse } from './commands/hook/copilot-pre-tool-use';
import { gitPreCommit } from './commands/hook/git-pre-commit';
import { gitPrePush, type GitPrePushOptions } from './commands/hook/git-pre-push';
import type { IntegrateAgentOptions } from './commands/integrate/_common/types';
import { integrateAntigravity } from './commands/integrate/antigravity';
import { integrateClaude } from './commands/integrate/claude';
import { integrateCodex } from './commands/integrate/codex';
import { integrateCopilot } from './commands/integrate/copilot';
Expand Down Expand Up @@ -247,6 +248,23 @@ integrateCommand
.addHelpText('after', projectKeyExtraHelp)
.authenticatedAction((auth, options: IntegrateAgentOptions) => integrateCodex(options, auth));

integrateCommand

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.

See comment above. Maybe it's best to keep the groundwork but remove wiring from customer-facing surfaces.

.command('antigravity')
.description(
'Setup SonarQube integration for Antigravity. This will install secrets scanning hooks, configure SonarQube Agentic Analysis and MCP Server.',
)
Comment thread
nquinquenel marked this conversation as resolved.
.option('-p, --project <project>', 'Project key. Mutually exclusive with --global.')
.option('--non-interactive', 'Non-interactive mode (no prompts)')
.option(
'-g, --global',
'Install hooks and config globally under ~/.gemini/config instead of the project .agents/ directory',
)
.option('--skip-context', 'Skip the sonar-context-augmentation install/init/skill step')
.addHelpText('after', projectKeyExtraHelp)
.authenticatedAction((auth, options: IntegrateAgentOptions) =>
integrateAntigravity(options, auth),
);

// List Sonar resources
const list = COMMAND_TREE.command('list').description(
'List issues and projects from SonarQube Cloud or Server',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { isGlobalIntegrateScope, resolveIntegrateScope } from './integrate-scope
import { printAgentPreflightSummary } from './preflight-summary';
import type { IntegrateAgentOptions } from './types';

export type AgentIntegrateSubcommand = 'claude' | 'codex' | 'copilot';
export type AgentIntegrateSubcommand = 'antigravity' | 'claude' | 'codex' | 'copilot';

export interface AgentIntegrateContext {
project: DiscoveredProject;
Expand Down
74 changes: 74 additions & 0 deletions src/cli/commands/integrate/antigravity/declaration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* SonarQube CLI
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { join } from 'node:path';

import {
ANTIGRAVITY_GLOBAL_SKILLS_DIR,
ANTIGRAVITY_PROJECT_AGENTS_DIR,
} from '../../../../lib/config-constants';
import { createContextAugmentationFeature } from '../_common/features/context-augmentation-feature';
import type { IntegrationContext, IntegrationDeclaration } from '../_common/registry';
import { askUser } from '../_common/registry';
import type { IntegrateAgentOptions } from '../_common/types';

export const ANTIGRAVITY_INTEGRATION_ID = 'antigravity-cli';

export interface AntigravityIntegrationOptions extends IntegrateAgentOptions {
projectRoot?: string;
globalSecretsHookExists?: boolean;
/** Install PostInvocation SQAA hook (project scope). Wired in CLI-551. */

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.

I think it's fine to drop this comment

@nquinquenel nquinquenel Jun 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I like to write comments on feature branch to make sure I don't forget about implementation of each tickets, but thanks for suggesting to target master directly! I think it makes sense, I'll remove all the temporary bits I added

installSqaaHook?: boolean;
sqaaEntitled?: boolean;
installContextAugmentation?: boolean;
}

/**
* Minimal declarative integration for CLI-549 orchestration. Secrets hooks,

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.

TMI ? :D

* SQAA, and MCP features are added in CLI-550/CLI-551.
*/
export const antigravityIntegration: IntegrationDeclaration<AntigravityIntegrationOptions> = {
id: ANTIGRAVITY_INTEGRATION_ID,
displayName: 'Antigravity',
features: [
{
id: 'integration-setup',
displayName: 'Antigravity integration setup',
shouldInstall: () => askUser(),
},
createContextAugmentationFeature<AntigravityIntegrationOptions>({
agentDisplayName: 'Antigravity',

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.

Will the same integration also work with Antigravity IDE? Or only CLI? Depending on the answer we might want to update this display name or the integration ID on line 32

targetPath: resolveAntigravitySkillPath,
}),
],
};

function resolveAntigravitySkillPath(context: IntegrationContext): string {
if (context.scope === 'global') {
return join(ANTIGRAVITY_GLOBAL_SKILLS_DIR, 'sonar-context-augmentation', 'SKILL.md');
}
return join(
context.targetRoot,
ANTIGRAVITY_PROJECT_AGENTS_DIR,
'skills',
'sonar-context-augmentation',
'SKILL.md',
);
}
28 changes: 28 additions & 0 deletions src/cli/commands/integrate/antigravity/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* SonarQube CLI
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

/**
* Probe for a global Antigravity secrets hook install. Returns the active hook
* script path when a healthy global install is found so project-level secrets
* hooks can be skipped to avoid double-scanning.
*/
export function detectGlobalSecretsHook(): string | undefined {
return undefined;
}
109 changes: 109 additions & 0 deletions src/cli/commands/integrate/antigravity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* SonarQube CLI
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import type { ResolvedAuth } from '../../../../lib/auth-resolver';
import type { IntegrationStateAttribute } from '../../../../lib/state';
import { info } from '../../../../ui';
import { displayAgentIntegratePrelude } from '../_common/agent-integrate-prelude';
import {
buildContextAugmentationAttrs,
resolveContextAugmentationSetup,
} from '../_common/context-augmentation';
import { installIntegration } from '../_common/registry';
import { resolveSqaaSetup } from '../_common/sqaa-entitlement';
import type { IntegrateAgentOptions } from '../_common/types';
import { supportedIntegrations } from '../index.js';
import { ANTIGRAVITY_INTEGRATION_ID, type AntigravityIntegrationOptions } from './declaration';
import { detectGlobalSecretsHook } from './hooks';
import { resolveAntigravityInstallTarget } from './install-target';

export async function integrateAntigravity(
options: IntegrateAgentOptions,
auth: ResolvedAuth,
): Promise<void> {
const ctx = await displayAgentIntegratePrelude('Antigravity', 'antigravity', options, auth);

if (options.skipContext) {
info('Skipping Context Augmentation (--skip-context).');
}
const contextAugmentation = options.skipContext
? null
: await resolveContextAugmentationSetup({
auth,
projectKey: ctx.projectKey,
isGlobal: ctx.isGlobal,
});

const sqaaEntitled = await resolveSqaaSetup({
serverURL: ctx.serverUrl,
token: ctx.token,
organization: ctx.organization,
isGlobal: ctx.isGlobal,
});
const installSqaaHook = sqaaEntitled && ctx.projectKey !== undefined;

const { installRoot: targetRoot, installScope: scope } = resolveAntigravityInstallTarget(
ctx.isGlobal,
ctx.project.rootDir,
);
const existingGlobalHookPath = ctx.isGlobal ? undefined : detectGlobalSecretsHook();
const globalSecretsHookExists = existingGlobalHookPath !== undefined;

const integrationOptions: AntigravityIntegrationOptions = {
...options,
projectRoot: ctx.project.rootDir,
globalSecretsHookExists,
installSqaaHook,
sqaaEntitled,
installContextAugmentation: contextAugmentation !== null,
};

await installIntegration({
registry: supportedIntegrations,
integrationId: ANTIGRAVITY_INTEGRATION_ID,
options: integrationOptions,
targetRoot,
scope,
auth,
nonInteractive: options.nonInteractive,
attrs: {
...buildIntegrationAttrs(ctx),
...(contextAugmentation
? buildContextAugmentationAttrs(
ctx.serverUrl,
ctx.organization,
contextAugmentation.scaEnabled,
)
: {}),
},
});
}

function buildIntegrationAttrs(ctx: {
serverUrl: string;
organization: string | undefined;
projectKey: string | undefined;
}): Record<string, IntegrationStateAttribute> {
return {
projectKey: ctx.projectKey ?? null,
serverUrl: ctx.serverUrl,
orgKey: ctx.organization ?? null,
};
}
32 changes: 32 additions & 0 deletions src/cli/commands/integrate/antigravity/install-target.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* SonarQube CLI
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { ANTIGRAVITY_GLOBAL_CONFIG_DIR } from '../../../../lib/config-constants';
import type { IntegrationScope } from '../../../../lib/state';

export function resolveAntigravityInstallTarget(
isGlobal: boolean,
projectRoot: string,
): { installRoot: string; installScope: IntegrationScope } {
return {
installRoot: isGlobal ? ANTIGRAVITY_GLOBAL_CONFIG_DIR : projectRoot,
installScope: isGlobal ? 'global' : 'project',
};
}
2 changes: 2 additions & 0 deletions src/cli/commands/integrate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

import { createIntegrationRegistry } from './_common/registry';
import { antigravityIntegration } from './antigravity/declaration';
import { claudeIntegration } from './claude/declaration';
import { codexIntegration } from './codex/declaration';
import { copilotIntegration } from './copilot/declaration';
Expand All @@ -28,6 +29,7 @@ export const ALL_INTEGRATIONS = [
claudeIntegration,
copilotIntegration,
codexIntegration,
antigravityIntegration,

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.

I think at some point we said we should not have feature branches. So instead I would suggest removing this change / making antigravity command hidden, and merging directly to master. We can also discuss with Damien.

...GIT_INTEGRATIONS,
] as const;

Expand Down
36 changes: 36 additions & 0 deletions src/lib/config-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,42 @@ export const BIN_DIR = join(CLI_DIR, 'bin');
/** Directory used for global git hooks when core.hooksPath is set via sonar integrate git --global. */
export const GLOBAL_HOOKS_DIR = join(CLI_DIR, 'hooks');

// ---------------------------------------------------------------------------
// Antigravity
// ---------------------------------------------------------------------------

/** Antigravity workspace agents directory (relative to the project root). */
export const ANTIGRAVITY_PROJECT_AGENTS_DIR = '.agents';

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.

I might be wrong but I vaguely remember Codex also having a similar path? Just something to double-check, not to have duplicate or overwritten integrations


/** Project Antigravity hooks config (relative to the project root). */
export const ANTIGRAVITY_PROJECT_HOOKS_JSON = join(ANTIGRAVITY_PROJECT_AGENTS_DIR, 'hooks.json');

/** Project Sonar hook scripts directory (relative to the project root). */
export const ANTIGRAVITY_PROJECT_SONAR_HOOKS_DIR = join(
ANTIGRAVITY_PROJECT_AGENTS_DIR,
'sonar',
'hooks',
);

/**
* Global Antigravity config root (`sonar integrate antigravity --global`).
* Hook scripts and config patches live under this tree (not a plugin bundle).
*/
export const ANTIGRAVITY_GLOBAL_CONFIG_DIR = join(homedir(), '.gemini', 'config');

/** Global Antigravity hooks config (`sonar integrate antigravity --global`). */
export const ANTIGRAVITY_GLOBAL_HOOKS_JSON = join(ANTIGRAVITY_GLOBAL_CONFIG_DIR, 'hooks.json');

/** Global Sonar hook scripts directory (`sonar integrate antigravity --global`). */
export const ANTIGRAVITY_GLOBAL_SONAR_HOOKS_DIR = join(
ANTIGRAVITY_GLOBAL_CONFIG_DIR,
'sonar',
'hooks',
);

/** Global Antigravity skills directory (`sonar integrate antigravity --global` CAG skill). */
export const ANTIGRAVITY_GLOBAL_SKILLS_DIR = join(ANTIGRAVITY_GLOBAL_CONFIG_DIR, 'skills');

// ---------------------------------------------------------------------------
// Sonarsource binaries
// ---------------------------------------------------------------------------
Expand Down
Loading
Loading