Skip to content

Commit ee739fb

Browse files
committed
feat: add Cursor CLI and Kilocode CLI support with auto-launch
- Add Cursor CLI and Kilocode CLI as coding assistant options - Implement auto-detection of installed coding assistants using which command - Auto-launch selected coding assistant with initial prompt after project setup - Show installed assistants first in white, not installed in gray with (not installed) - Replace manual instruction display with automatic process spawning - Update README with new assistants and auto-launch feature
1 parent f3e785b commit ee739fb

File tree

11 files changed

+285
-75
lines changed

11 files changed

+285
-75
lines changed

README.md

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Superagents helps you kickstart agent projects with the right structure, tooling
99
- **Agent Testing Pyramid** methodology
1010
- **LangWatch** integration for prompt management, testing, and evaluation
1111
- **Framework-specific** configurations (Agno, Mastra)
12-
- **Coding assistant** setup (Claude Code) pre-configured to be an expert in your chosen framework
12+
- **Auto-launched coding assistant** (Claude Code, Cursor CLI, or Kilocode CLI) pre-configured as an expert in your chosen framework
1313
- **Production-ready** project structure
1414

1515
## Installation
@@ -47,7 +47,7 @@ The CLI will guide you through:
4747

4848
1. **Programming Language**: Python or TypeScript
4949
2. **Agent Framework**: Agno (Python) or Mastra (TypeScript)
50-
3. **Coding Assistant**: Claude Code
50+
3. **Coding Assistant**: Claude Code, Cursor CLI, or Kilocode CLI
5151
4. **LLM Provider**: OpenAI
5252
5. **API Keys**: OpenAI and LangWatch
5353
6. **Project Goal**: What you want to build
@@ -97,17 +97,22 @@ Learn more: https://scenario.langwatch.ai/best-practices/the-agent-testing-pyram
9797

9898
### 🤖 Coding Assistant Setup
9999

100-
Your coding assistant (e.g., Claude Code) is pre-configured with:
101-
- Framework-specific knowledge (via MCP or docs)
102-
- LangWatch best practices
103-
- Prompt management expertise
104-
- Testing methodologies
100+
Your coding assistant (e.g., Claude Code, Cursor CLI, Kilocode CLI) is:
101+
- **Automatically launched** after project setup with initial prompt
102+
- Pre-configured with framework-specific knowledge (via MCP or docs)
103+
- Loaded with LangWatch best practices
104+
- Equipped with prompt management expertise
105+
- Set up with testing methodologies
106+
- Auto-detected - the CLI shows which assistants are installed on your system
105107

106108
## Requirements
107109

108110
- Node.js 18+
109111
- npm or pnpm
110-
- Claude Code (for coding assistant)
112+
- A coding assistant CLI (one of the following):
113+
- [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code-agent) (`claude`)
114+
- [Cursor CLI](https://www.cursor.com/) (`cursor-agent`)
115+
- [Kilocode CLI](https://www.kilocode.ai/) (`kilocode`)
111116
- API Keys:
112117
- OpenAI API key
113118
- LangWatch API key (get one at https://app.langwatch.ai/authorize)
@@ -135,18 +140,28 @@ pnpm build
135140

136141
```bash
137142
superagents init trading-agent
138-
# Select: Python, Agno, Claude Code, OpenAI
143+
# Select: Python, Agno, your preferred coding assistant, OpenAI
139144
# Goal: "Build an agent that can analyze stock prices and provide trading recommendations"
140145
```
141146

142147
### TypeScript + Mastra
143148

144149
```bash
145150
superagents init customer-support
146-
# Select: TypeScript, Mastra, Claude Code, OpenAI
151+
# Select: TypeScript, Mastra, your preferred coding assistant, OpenAI
147152
# Goal: "Build a customer support agent that can handle common queries and escalate complex issues"
148153
```
149154

155+
### Coding Assistant Auto-Launch
156+
157+
After project setup completes, Superagents **automatically launches** your chosen coding assistant with a customized initial prompt that includes:
158+
- Your project goal
159+
- Framework-specific context
160+
- Best practices guidance
161+
- Next steps to get started
162+
163+
The CLI detects which coding assistants are installed on your system and shows installed options first in the selection menu. Not installed assistants appear in gray with "(not installed)" but can still be selected.
164+
150165
## Resources
151166

152167
- [LangWatch](https://langwatch.ai)

src/assistant-kickoff/display-instructions.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
1+
import chalk from 'chalk';
12
import type { ProjectConfig } from '../types.js';
23
import { buildInitialPrompt } from './build-initial-prompt.js';
3-
import { displayInstructions } from './display-instructions.js';
4+
import { getCodingAssistantProvider } from '../providers/coding-assistants/index.js';
45

56
/**
67
* Kicks off the coding assistant with initial instructions.
7-
*
8+
*
89
* @param params - Parameters object
910
* @param params.projectPath - Absolute path to project root
1011
* @param params.config - Project configuration
11-
* @returns Promise that resolves when instructions are displayed
12-
*
12+
* @returns Promise that resolves when assistant is launched
13+
*
1314
* @example
1415
* ```ts
1516
* await kickoffAssistant({ projectPath: '/path/to/project', config });
1617
* ```
1718
*/
18-
export const kickoffAssistant = async ({
19-
projectPath,
20-
config
21-
}: {
22-
projectPath: string;
23-
config: ProjectConfig;
19+
export const kickoffAssistant = async ({
20+
projectPath,
21+
config
22+
}: {
23+
projectPath: string;
24+
config: ProjectConfig;
2425
}): Promise<void> => {
2526
const prompt = buildInitialPrompt({ config });
26-
displayInstructions({ projectPath, config, prompt });
27+
const provider = getCodingAssistantProvider({ assistant: config.codingAssistant });
28+
29+
console.log(chalk.bold.cyan('\n🤖 Launching your coding assistant...\n'));
30+
console.log(chalk.gray('Initial prompt:'));
31+
console.log(chalk.white(`"${prompt}"\n`));
32+
console.log(chalk.yellow(`Starting ${provider.displayName}...\n`));
33+
34+
try {
35+
await provider.launch({ projectPath, prompt });
36+
} catch (error) {
37+
if (error instanceof Error) {
38+
console.error(chalk.red(`\n❌ Failed to launch ${provider.displayName}: ${error.message}`));
39+
console.log(chalk.yellow('\nYou can manually start the assistant by running:'));
40+
console.log(chalk.cyan(` cd ${projectPath}`));
41+
console.log(chalk.cyan(` ${provider.command} "${prompt}"`));
42+
}
43+
throw error;
44+
}
2745
};
2846

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import chalk from "chalk";
2+
import { getAllCodingAssistants } from "../../providers/coding-assistants/index.js";
3+
import type { CodingAssistant } from "../../types.js";
4+
import { detectInstalledAgents } from "../detect-installed-agents.js";
5+
6+
type Choice = {
7+
name: string;
8+
value: CodingAssistant;
9+
};
10+
11+
/**
12+
* Builds coding assistant choices with installed agents shown first in white,
13+
* and not installed agents shown later in gray with "(not installed)" suffix.
14+
*
15+
* @returns Promise resolving to array of choice objects for inquirer
16+
*
17+
* @example
18+
* ```ts
19+
* const choices = await buildCodingAssistantChoices();
20+
* // Returns: [
21+
* // { name: "Claude Code", value: "claude-code" },
22+
* // { name: chalk.gray("Cursor CLI (not installed)"), value: "cursor-cli" }
23+
* // ]
24+
* ```
25+
*/
26+
export const buildCodingAssistantChoices = async (): Promise<Choice[]> => {
27+
const assistants = getAllCodingAssistants();
28+
const installedMap = await detectInstalledAgents();
29+
30+
const installed: Choice[] = [];
31+
const notInstalled: Choice[] = [];
32+
33+
for (const assistant of assistants) {
34+
const isInstalled = installedMap[assistant.id];
35+
const choice: Choice = {
36+
name: isInstalled
37+
? assistant.displayName
38+
: chalk.gray(`${assistant.displayName} (not installed)`),
39+
value: assistant.id as CodingAssistant,
40+
};
41+
42+
if (isInstalled) {
43+
installed.push(choice);
44+
} else {
45+
notInstalled.push(choice);
46+
}
47+
}
48+
49+
return [...installed, ...notInstalled];
50+
};
51+

src/config-collection/collect-config.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
} from "../types.js";
99
import { buildLanguageChoices } from "./choice-builders/language-choices.js";
1010
import { buildFrameworkChoices } from "./choice-builders/framework-choices.js";
11+
import { buildCodingAssistantChoices } from "./choice-builders/coding-assistant-choices.js";
1112
import { validateOpenAIKey } from "./validators/openai-key.js";
1213
import { validateLangWatchKey } from "./validators/langwatch-key.js";
1314
import { validateProjectGoal } from "./validators/project-goal.js";
@@ -41,8 +42,8 @@ export const collectConfig = async (): Promise<ProjectConfig> => {
4142
});
4243

4344
const codingAssistant = await select<CodingAssistant>({
44-
message: "What coding assistant do you want to use?",
45-
choices: [{ name: "Claude Code", value: "claude-code" }],
45+
message: "What is your preferred coding assistant?",
46+
choices: await buildCodingAssistantChoices(),
4647
});
4748

4849
const llmProvider = await select<LLMProvider>({
@@ -66,7 +67,7 @@ export const collectConfig = async (): Promise<ProjectConfig> => {
6667
});
6768

6869
const projectGoal = await input({
69-
message: "What do you want to build?",
70+
message: "What is your agent going to do?",
7071
validate: validateProjectGoal,
7172
});
7273

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { exec } from "child_process";
2+
import { promisify } from "util";
3+
4+
const execAsync = promisify(exec);
5+
6+
/**
7+
* Checks if a command is available in the system PATH.
8+
*
9+
* @param command - The command to check (e.g., 'claude', 'cursor-agent')
10+
* @returns Promise resolving to true if command exists, false otherwise
11+
*
12+
* @example
13+
* ```ts
14+
* const hasCommand = await isCommandAvailable('claude');
15+
* // Returns: true if claude is installed
16+
* ```
17+
*/
18+
export const isCommandAvailable = async (command: string): Promise<boolean> => {
19+
try {
20+
await execAsync(`which ${command}`);
21+
return true;
22+
} catch {
23+
return false;
24+
}
25+
};
26+
27+
/**
28+
* Detects which coding assistants are installed on the system.
29+
*
30+
* @returns Promise resolving to a map of assistant IDs to installation status
31+
*
32+
* @example
33+
* ```ts
34+
* const installed = await detectInstalledAgents();
35+
* // Returns: { 'claude-code': true, 'cursor': false, 'kilocode': true }
36+
* ```
37+
*/
38+
export const detectInstalledAgents = async (): Promise<
39+
Record<string, boolean>
40+
> => {
41+
const [hasClaude, hasCursor, hasKilocode] = await Promise.all([
42+
isCommandAvailable("claude"),
43+
isCommandAvailable("cursor-agent"),
44+
isCommandAvailable("kilocode"),
45+
]);
46+
47+
return {
48+
"claude-code": hasClaude,
49+
"cursor-cli": hasCursor,
50+
kilocode: hasKilocode,
51+
};
52+
};
53+
Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as fs from "fs/promises";
22
import * as path from "path";
3+
import { spawn } from "child_process";
34
import type { CodingAssistantProvider, MCPConfigFile } from "../index.js";
45

56
/**
@@ -9,13 +10,33 @@ import type { CodingAssistantProvider, MCPConfigFile } from "../index.js";
910
export const ClaudeCodingAssistantProvider: CodingAssistantProvider = {
1011
id: "claude-code",
1112
displayName: "Claude Code",
13+
command: "claude",
1214

1315
async writeMCPConfig({ projectPath, config }) {
1416
const mcpConfigPath = path.join(projectPath, ".mcp.json");
1517
await fs.writeFile(mcpConfigPath, JSON.stringify(config, null, 2));
1618
},
1719

18-
getKickoffInstructions: () =>
19-
"Open this project in Claude Code and start the conversation.",
20+
async launch({ projectPath, prompt }) {
21+
return new Promise((resolve, reject) => {
22+
const child = spawn("claude", [prompt], {
23+
cwd: projectPath,
24+
stdio: "inherit",
25+
shell: true,
26+
});
27+
28+
child.on("error", (error) => {
29+
reject(new Error(`Failed to launch Claude Code: ${error.message}`));
30+
});
31+
32+
child.on("close", (code) => {
33+
if (code === 0) {
34+
resolve();
35+
} else {
36+
reject(new Error(`Claude Code exited with code ${code}`));
37+
}
38+
});
39+
});
40+
},
2041
};
2142

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as fs from "fs/promises";
2+
import * as path from "path";
3+
import { spawn } from "child_process";
4+
import type { CodingAssistantProvider, MCPConfigFile } from "../index.js";
5+
6+
/**
7+
* Cursor CLI assistant provider implementation.
8+
* Writes MCP configuration as .mcp.json in project root.
9+
*/
10+
export const CursorCodingAssistantProvider: CodingAssistantProvider = {
11+
id: "cursor-cli",
12+
displayName: "Cursor CLI",
13+
command: "cursor-agent",
14+
15+
async writeMCPConfig({ projectPath, config }) {
16+
const mcpConfigPath = path.join(projectPath, ".mcp.json");
17+
await fs.writeFile(mcpConfigPath, JSON.stringify(config, null, 2));
18+
},
19+
20+
async launch({ projectPath, prompt }) {
21+
return new Promise((resolve, reject) => {
22+
const child = spawn("cursor-agent", [prompt], {
23+
cwd: projectPath,
24+
stdio: "inherit",
25+
shell: true,
26+
});
27+
28+
child.on("error", (error) => {
29+
reject(new Error(`Failed to launch Cursor CLI: ${error.message}`));
30+
});
31+
32+
child.on("close", (code) => {
33+
if (code === 0) {
34+
resolve();
35+
} else {
36+
reject(new Error(`Cursor CLI exited with code ${code}`));
37+
}
38+
});
39+
});
40+
},
41+
};
42+

0 commit comments

Comments
 (0)