Skip to content

Commit 80d2e3b

Browse files
committed
feat: add smithery, super agent man, improve instructions further
1 parent 5c84ed7 commit 80d2e3b

File tree

16 files changed

+2393
-87
lines changed

16 files changed

+2393
-87
lines changed

.specstory/history/2025-11-07_14-06Z-explore-project-and-add-coding-agent.md

Lines changed: 1013 additions & 0 deletions
Large diffs are not rendered by default.

.specstory/history/2025-11-08_08-48Z-make-superman-fly-in-ascii-art.md

Lines changed: 1169 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Superagents is a CLI tool and a set of standards for agent building.
44

5-
It supercharges your coding assistant (Claude Code, Cursor, etc), making it an expert in any agent framework you choose (Agno, Mastra, etc) and all their best practices.
5+
It supercharges your coding assistant (Claude Code, Cursor, etc), making it an expert in any agent framework you choose (Agno, Mastra, etc) and all their best practices, being able to autodiscover MCP tools to augment your agent.
66

77
The Superagent Structure and AGENTS.md ensures industry best practices, making your agent ready for production:
88
- [Scenario](https://github.com/langwatch/scenario) agent tests written for every feature to ensure agent behaviour
@@ -76,9 +76,9 @@ The CLI will guide you through:
7676

7777
1. **Programming Language**: Python or TypeScript
7878
2. **Agent Framework**: Agno (Python) or Mastra (TypeScript)
79-
3. **Coding Assistant**: Claude Code, Cursor CLI, Kilocode CLI, or None (manual prompt)
80-
4. **LLM Provider**: OpenAI
81-
5. **API Keys**: OpenAI and LangWatch
79+
3. **Coding Assistant**: Claude Code, Cursor, Kilocode CLI, or None (manual prompt)
80+
4. **LLM Provider**: OpenAI, Anthropic, Gemini, Bedrock, OpenRouter, or Grok
81+
5. **API Keys**: LLM Provider and LangWatch (required), Smithery (optional)
8282
6. **Project Goal**: What you want to build
8383

8484
## Philosophy
@@ -105,9 +105,15 @@ Learn more: https://scenario.langwatch.ai/best-practices/the-agent-testing-pyram
105105
- **Evaluations**: Measure component performance
106106
- **MCP Server**: Expert guidance built into your coding assistant
107107

108+
### 🔧 MCP Tool Integration
109+
110+
- **Smithery Toolbox** (optional): When you provide a Smithery API key during setup, your coding agent gets automatic access to MCP tools for enhanced capabilities
111+
- Auto-configured in `.mcp.json` for seamless integration
112+
- Your coding assistant can discover and use tools to help build your agent
113+
108114
### 🤖 Coding Assistant Setup
109115

110-
Your coding assistant (e.g., Claude Code, Cursor CLI, Kilocode CLI) is:
116+
Your coding assistant (e.g., Claude Code, Cursor, Kilocode CLI) is:
111117
- **Automatically launched** after project setup with initial prompt
112118
- Pre-configured with framework-specific knowledge (via MCP or docs)
113119
- Loaded with LangWatch best practices
@@ -119,13 +125,14 @@ Your coding assistant (e.g., Claude Code, Cursor CLI, Kilocode CLI) is:
119125

120126
- Node.js 18+
121127
- npm or pnpm
122-
- A coding assistant CLI (one of the following):
123-
- [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code-agent) (`claude`)
124-
- [Cursor CLI](https://www.cursor.com/) (`cursor-agent`)
128+
- A coding assistant (one of the following):
129+
- [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code-agent) (`claude` CLI)
130+
- [Cursor](https://www.cursor.com/)
125131
- [Kilocode CLI](https://www.kilocode.ai/) (`kilocode`)
126132
- API Keys:
127-
- OpenAI API key
133+
- Your chosen LLM Provider API key
128134
- LangWatch API key (get one at https://app.langwatch.ai/authorize)
135+
- Smithery API key (optional - for MCP tool auto-discovery, get one at https://smithery.ai/account/api-keys)
129136

130137
## Development
131138

src/assistant-kickoff/build-initial-prompt.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ First steps:
4343
Remember:
4444
- ALWAYS use LangWatch Prompt CLI for prompts (ask the MCP how)
4545
- ALWAYS write Scenario tests for new features (ask the MCP how)
46+
- The LLM and LangWatch API keys are already available in the .env file
4647
- Test everything before considering it done`;
4748

48-
return `${instructions}\n\nProject Goal: ${config.projectGoal}`;
49+
return `${instructions}\n\nAgent Goal: ${config.projectGoal}`;
4950
};

src/assistant-kickoff/kickoff-assistant.ts

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,12 @@ export const kickoffAssistant = async ({
2626
const prompt = buildInitialPrompt({ config });
2727
const provider = getCodingAssistantProvider({ assistant: config.codingAssistant });
2828

29-
// If user selected "none", just show them the initial prompt and exit
30-
if (config.codingAssistant === 'none') {
31-
console.log(chalk.bold.cyan('\n✨ Project setup complete!\n'));
32-
console.log(chalk.gray('When you\'re ready to start, use this initial prompt with your coding assistant:\n'));
33-
console.log(chalk.white(`"${prompt}"\n`));
34-
console.log(chalk.gray(`Project location: ${projectPath}\n`));
35-
return;
36-
}
29+
console.log(chalk.bold.cyan('\n✨ Project setup complete!\n'));
30+
console.log(chalk.gray('Initial prompt:\n'));
31+
console.log(chalk.white(`"${prompt}"\n`));
32+
console.log(chalk.gray(`Project location: ${projectPath}\n`));
3733

38-
console.log(chalk.bold.cyan(`\n🤖 Launching ${provider.displayName}...\n`));
39-
40-
console.log(chalk.gray('\nInitial prompt:'));
41-
console.log(chalk.white(`"${prompt}"`));
42-
43-
try {
44-
await provider.launch({ projectPath, prompt });
45-
// execSync is blocking, so if we get here, the assistant has finished
46-
console.log(chalk.bold.green('\n✨ Session complete!\n'));
47-
} catch (error) {
48-
if (error instanceof Error) {
49-
console.error(chalk.red(`\n❌ Failed to launch ${provider.displayName}: ${error.message}`));
50-
console.log(chalk.yellow('\nYou can manually start the assistant by running:'));
51-
console.log(chalk.cyan(` cd ${projectPath}`));
52-
console.log(chalk.cyan(` ${provider.command} "${prompt}"`));
53-
}
54-
process.exit(1);
55-
}
34+
// Let the provider handle its own launch behavior
35+
await provider.launch({ projectPath, prompt });
5636
};
5737

src/builders/mcp-config-builder.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ export const buildMCPConfig = async ({
3333
args: ["-y", "@langwatch/mcp-server", `--apiKey=${config.langwatchApiKey}`],
3434
};
3535

36+
// Add Smithery Toolbox MCP if API key provided
37+
if (config.smitheryApiKey) {
38+
mcpConfig.mcpServers.toolbox = {
39+
command: "npx",
40+
args: [
41+
"-y",
42+
"@smithery/cli@latest",
43+
"run",
44+
"@smithery/toolbox",
45+
"--key",
46+
config.smitheryApiKey,
47+
],
48+
};
49+
}
50+
3651
// Add framework-specific MCP if available
3752
const frameworkProvider = getFrameworkProvider({
3853
framework: config.framework,

src/commands/init.ts

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,60 @@ import { kickoffAssistant } from "../assistant-kickoff/kickoff-assistant.js";
1111
import type { ProjectConfig } from "../types.js";
1212

1313
/**
14-
* Creates an animated rainbow banner with moving colors
14+
* Creates an animated rainbow banner with moving colors and flying Superman
1515
*/
1616
const showAnimatedBanner = (): Promise<void> => {
1717
return new Promise((resolve) => {
18-
// Define precise RGB colors for a smooth rainbow gradient
18+
// Purple gradient for letters
1919
const colors = [
20-
(text: string) => chalk.rgb(255, 0, 0)(text), // Red
21-
(text: string) => chalk.rgb(255, 127, 0)(text), // Orange
22-
(text: string) => chalk.rgb(255, 255, 0)(text), // Yellow
23-
(text: string) => chalk.rgb(0, 255, 0)(text), // Green
24-
(text: string) => chalk.rgb(0, 127, 255)(text), // Light Blue
25-
(text: string) => chalk.rgb(0, 0, 255)(text), // Blue
26-
(text: string) => chalk.rgb(139, 0, 255)(text), // Purple
27-
(text: string) => chalk.rgb(255, 0, 255)(text), // Magenta
20+
(text: string) => chalk.ansi256(93)(text), // Light purple
21+
(text: string) => chalk.ansi256(92)(text), // Medium-light purple
22+
(text: string) => chalk.ansi256(91)(text), // Medium purple
23+
(text: string) => chalk.ansi256(127)(text), // Deep purple
24+
(text: string) => chalk.ansi256(129)(text), // Purple
25+
(text: string) => chalk.ansi256(93)(text), // Light purple (cycle back)
2826
];
2927

28+
// Original Superman art
29+
const supermanArtOriginal = [
30+
" ",
31+
" ",
32+
" ███░ ",
33+
" █████████ ",
34+
" ░████▒░░█████ ",
35+
" ▓██░░░░░░░█░░█ ",
36+
" ░░░░░░░░░▒░░▒░▒ ",
37+
" ░░░░░░░░░░░░ ",
38+
" ░▓▓▓▓▓▓░░░░░░░░░░▒░░░░ ",
39+
" ▓▓▓▓▓▓░░▓▓▓▓▓▒░░░░▒▓▓▓▓░░ ",
40+
" ▓▓▓▓▓▒▓▓▓░▓░▓▓▓░ ",
41+
" ░▓▓▓▓▓▓░▓▓░░ ",
42+
" ▓▓▓▓▓▓▓▓ ",
43+
" ▓▓░ ",
44+
" ",
45+
" ",
46+
" ",
47+
];
48+
49+
// Reverse shading for better display on dark terminals
50+
// Swap light and dark characters so face appears light and hair appears dark
51+
const reverseShading = (line: string): string => {
52+
return line
53+
.split('')
54+
.map((char) => {
55+
switch (char) {
56+
case '░': return '█'; // Light shade → Solid
57+
case '▒': return '▓'; // Medium shade → Dark shade
58+
case '▓': return '▒'; // Dark shade → Medium shade
59+
case '█': return '░'; // Solid → Light shade
60+
default: return char;
61+
}
62+
})
63+
.join('');
64+
};
65+
66+
const supermanArt = supermanArtOriginal.map(reverseShading);
67+
3068
const superArt = [
3169
" ███████╗██╗ ██╗██████╗ ███████╗██████╗ ",
3270
" ██╔════╝██║ ██║██╔══██╗██╔════╝██╔══██╗",
@@ -53,20 +91,56 @@ const showAnimatedBanner = (): Promise<void> => {
5391
console.clear();
5492
console.log(); // Empty line at top
5593

56-
// Print SUPER - each line gets one color, colors shift down over time
57-
superArt.forEach((line, lineIndex) => {
58-
const colorIndex = (colorOffset + lineIndex) % colors.length;
59-
console.log(colors[colorIndex](line));
60-
});
61-
62-
console.log(); // Empty line between
63-
64-
// Print AGENTS - continue the color progression
65-
agentsArt.forEach((line, lineIndex) => {
66-
const colorIndex =
67-
(colorOffset + superArt.length + lineIndex) % colors.length;
68-
console.log(colors[colorIndex](line));
69-
});
94+
// Calculate Superman's vertical offset for flying animation
95+
// Using sine wave for smooth up and down motion
96+
const flyingOffset = Math.round(
97+
2 * Math.sin((frameCount * Math.PI) / 6)
98+
);
99+
100+
// Calculate total height: Superman, SUPER, spacing, and AGENTS
101+
const totalTextLines = superArt.length + 1 + agentsArt.length; // +1 for spacing
102+
const maxLines = Math.max(supermanArt.length, totalTextLines);
103+
104+
for (let i = 0; i < maxLines; i++) {
105+
let line = "";
106+
107+
// Add Superman with flying offset
108+
const supermanLineIndex = i - flyingOffset;
109+
if (
110+
supermanLineIndex >= 0 &&
111+
supermanLineIndex < supermanArt.length
112+
) {
113+
line += supermanArt[supermanLineIndex]; // Use default terminal color
114+
} else {
115+
// Add empty space when Superman line is out of bounds
116+
line += " ".repeat(supermanArt[0].length);
117+
}
118+
119+
// Add spacing between Superman and text
120+
line += " ";
121+
122+
// Push text down by 2 lines
123+
const textLineIndex = i - 2;
124+
125+
// Add SUPER text (starting at line 2)
126+
if (textLineIndex >= 0 && textLineIndex < superArt.length) {
127+
const colorIndex = (colorOffset + textLineIndex) % colors.length;
128+
line += colors[colorIndex](superArt[textLineIndex]);
129+
}
130+
// Add empty line between SUPER and AGENTS
131+
else if (textLineIndex === superArt.length) {
132+
// Just spacing, no text
133+
}
134+
// Add AGENTS text
135+
else if (textLineIndex > superArt.length && textLineIndex - superArt.length - 1 < agentsArt.length) {
136+
const agentsLineIndex = textLineIndex - superArt.length - 1;
137+
const colorIndex =
138+
(colorOffset + superArt.length + agentsLineIndex) % colors.length;
139+
line += colors[colorIndex](agentsArt[agentsLineIndex]);
140+
}
141+
142+
console.log(line);
143+
}
70144

71145
console.log(); // Empty line at bottom
72146

src/config-collection/collect-config.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,26 @@ export const collectConfig = async (): Promise<ProjectConfig> => {
102102
validate: validateLangWatchKey,
103103
});
104104

105+
console.log(chalk.gray("\nTo get your Smithery API key (optional), visit:"));
106+
console.log(chalk.blue.underline("https://smithery.ai/account/api-keys\n"));
107+
console.log(chalk.gray("Smithery enables your coding agent to auto-discover MCP tools to integrate with your agent.\n"));
108+
109+
const smitheryApiKey = await password({
110+
message: "Enter your Smithery API key (Optional - press Enter to skip):",
111+
mask: "*",
112+
validate: (value) => {
113+
// Optional field - empty is valid
114+
if (!value || value.trim() === "") {
115+
return true;
116+
}
117+
// If provided, basic validation
118+
if (value.length < 10) {
119+
return "Smithery API key must be at least 10 characters";
120+
}
121+
return true;
122+
},
123+
});
124+
105125
const projectGoal = await input({
106126
message: "What is your agent going to do?",
107127
validate: validateProjectGoal,
@@ -115,6 +135,7 @@ export const collectConfig = async (): Promise<ProjectConfig> => {
115135
llmApiKey,
116136
llmAdditionalInputs,
117137
langwatchApiKey,
138+
smitheryApiKey: smitheryApiKey && smitheryApiKey.trim() !== "" ? smitheryApiKey : undefined,
118139
projectGoal,
119140
};
120141
} catch (error) {

src/config-collection/detect-installed-agents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const detectInstalledAgents = async (): Promise<
4646

4747
return {
4848
"claude-code": hasClaude,
49-
"cursor-cli": hasCursor,
49+
cursor: hasCursor,
5050
kilocode: hasKilocode,
5151
none: true, // Always available since it doesn't require installation
5252
};

src/documentation/sections/principles-section.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ const langwatch = new LangWatch({
7676
});
7777
7878
const prompt = await langwatch.prompts.get("my_prompt")
79-
Agent(prompt=prompt?.prompt || "")
79+
Agent(prompt=prompt!.prompt)
8080
\`\`\`
8181
82-
Prompt fetching is very reliable, there is no need to try/catch around it.
82+
Prompt fetching is very reliable when using the prompts cli because the files are local (double check they were created with the CLI and are listed on the prompts.json file).
83+
DO NOT add try/catch around it and DO NOT duplicate the prompt here as a fallback
8384
8485
Explore the prompt management get started and data model docs if you need more advanced usages such as compiled prompts with variables or messages list.
8586

0 commit comments

Comments
 (0)