Skip to content

Commit 70dcc0d

Browse files
authored
refactor: reorganize codebase into feature-based architecture
refactor: reorganize codebase into feature-based architecture 10a7b46 Restructured src/ from technical grouping (utils/) to domain-driven feature modules following SRP and Clean Code principles. Changes: - Created 5 feature directories: config-collection/, project-scaffolding/, documentation/, tool-setup/, assistant-kickoff/ - Split 209-line project-structure.ts into 8 modular file generators - Split 226-line agents-md.ts into 4 section builders - Split 97-line prompts.ts into validators and choice builders - Added README.md to all top-level feature directories explaining purpose and responsibilities - Added TSDoc with examples to all 30+ functions - Created 6 test spec files with it.todo for logic branches (Vitest) - Applied consistent formatting (double quotes, spacing) Each feature now has single responsibility, thin files (<100 lines), and colocated tests in __tests__ directories. @drewdrewthis test: add E2E test infrastructure with fluent CLI runner cff4ae4 - Install vitest and @vitest/ui for testing - Create CLITestRunner class with fluent API for readable test flows - Add utilities for CLI spawning, temp directory management - Configure separate vitest configs for unit and E2E tests - Add 44 comprehensive test todos covering full user flow: * Prompt sequence validation * Input validation and re-prompting * File generation for all framework/language combinations * Progress indication and spinner updates * Error handling scenarios - Support inline snapshots for file content verification - Add test scripts to package.json (test, test:watch, test:e2e) The CLITestRunner enables tests that verify actual user experience by asserting on prompts, inputs, outputs, and filesystem artifacts in a readable chained syntax.
2 parents 045151f + cff4ae4 commit 70dcc0d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2065
-672
lines changed

.cursor/rules/coding-guidelines.mdc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ DIRECTIVES:
1515
- Code should be modular
1616
- Functions should be small
1717
- Classes should be small
18-
- functions, classes, methods should have TSDoc with example usage
18+
- functions, classes, methods should have TSDoc with example usage
19+
- top-level directories represent core features, and should each have a README.md that describes the feature and its purpose.

.cursor/rules/commits.mdc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
description: After writing code
3+
alwaysApply: false
4+
---
5+
After you finish writing code, PROPOSE a command to have a professional convensional commit + description for your work.
6+
7+
DO NOT EXECUTE THE COMMAND, ONLY PROPOSE IT.
8+
9+
Example:
10+
```bash
11+
git commit -m "feat: add new feature" -m "This commit adds a new feature to the project."
12+
```
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
---
2-
description: When proposing/writing unit tests
2+
description: When proposing/writing unit test specs
33
alwaysApply: false
44
---
5-
65
Stack:
76
- Vitest
87

@@ -12,6 +11,7 @@ Testing rules:
1211
- A ‘when’ should indicate a describe block, and then we will out the todos.
1312
- Don’t test implementation details, only the public API.
1413

14+
- DO NOT WRITE THE ACTUAL TESTING, ONLY THE TODOS.
1515
- Propose only a spec test of todos:
1616

1717
Example:

src/__tests__/e2e/init.e2e.test.ts

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { describe, it, beforeEach, afterEach } from "vitest";
2+
import { CLITestRunner } from "../utils/cli-test-runner.js";
3+
4+
describe("superagents init - complete user flow", () => {
5+
let runner: CLITestRunner;
6+
7+
beforeEach(() => {
8+
runner = new CLITestRunner();
9+
});
10+
11+
afterEach(async () => {
12+
await runner.cleanup();
13+
});
14+
15+
describe("full flow scenarios", () => {
16+
it.todo("completes typescript + agno project setup");
17+
// When implemented:
18+
// await runner
19+
// .command('init', 'test-project')
20+
// .expectPrompt('Welcome to Superagents')
21+
// .expectPrompt('What programming language')
22+
// .input('typescript')
23+
// .expectPrompt('What agent framework')
24+
// .input('agno')
25+
// .expectPrompt('What coding assistant')
26+
// .input('claude-code')
27+
// .expectPrompt('What LLM provider')
28+
// .input('openai')
29+
// .expectPrompt('Enter your OpenAI API key')
30+
// .input('sk-test123')
31+
// .expectPrompt('Enter your LangWatch API key')
32+
// .input('lw_test123')
33+
// .expectPrompt('What do you want to build')
34+
// .input('Build a chatbot')
35+
// .expectOutput('Project structure created')
36+
// .expectOutput('MCP configuration set up')
37+
// .expectOutput('Agno configuration set up')
38+
// .expectOutput('AGENTS.md generated')
39+
// .expectOutput('Project setup complete')
40+
// .expectFile('src/prompts/sample.ts')
41+
// .expectFile('src/scenarios/sample.test.ts')
42+
// .expectFile('evaluations/sample.ts')
43+
// .expectFile('.env')
44+
// .expectFile('.gitignore')
45+
// .expectFile('AGENTS.md')
46+
// .expectFile('.cursorrules')
47+
// .expectFileContains('.env', 'OPENAI_API_KEY=sk-test123')
48+
// .expectFileContains('.env', 'LANGWATCH_API_KEY=lw_test123')
49+
// .expectFileToMatchInlineSnapshot('.env')
50+
// .expectFileToMatchInlineSnapshot('src/prompts/sample.ts')
51+
// .run();
52+
53+
it.todo("completes typescript + mastra project setup");
54+
55+
it.todo("completes python + agno project setup");
56+
});
57+
58+
describe("prompt sequence validation", () => {
59+
it.todo("displays welcome message");
60+
61+
it.todo("prompts for language selection");
62+
63+
it.todo("prompts for framework after language");
64+
65+
it.todo(
66+
"shows only typescript-compatible frameworks when typescript selected"
67+
);
68+
69+
it.todo("shows only python-compatible frameworks when python selected");
70+
71+
it.todo("prompts for coding assistant");
72+
73+
it.todo("prompts for LLM provider");
74+
75+
it.todo("displays LangWatch authorization URL before key prompt");
76+
77+
it.todo("prompts for OpenAI API key with masked input");
78+
79+
it.todo("prompts for LangWatch API key with masked input");
80+
81+
it.todo("prompts for project goal");
82+
});
83+
84+
describe("input validation", () => {
85+
it.todo("re-prompts when OpenAI key is invalid");
86+
// When implemented:
87+
// await runner
88+
// .command('init', 'test')
89+
// // ... navigate to API key prompt ...
90+
// .expectPrompt('Enter your OpenAI API key')
91+
// .input('invalid-key')
92+
// .expectOutput("must start with 'sk-'")
93+
// .expectPrompt('Enter your OpenAI API key')
94+
// .input('sk-valid123')
95+
// .run();
96+
97+
it.todo("re-prompts when LangWatch key is invalid");
98+
99+
it.todo("re-prompts when project goal is empty");
100+
101+
it.todo("accepts valid inputs and proceeds");
102+
});
103+
104+
describe("file generation", () => {
105+
describe("when typescript + agno", () => {
106+
it.todo("creates correct directory structure");
107+
108+
it.todo("generates typescript main entry point");
109+
110+
it.todo("generates typescript scenario test");
111+
112+
it.todo("includes agno-specific tools configuration");
113+
114+
it.todo("includes agno rules in .cursorrules");
115+
116+
it.todo("generates AGENTS.md with agno guidance");
117+
});
118+
119+
describe("when typescript + mastra", () => {
120+
it.todo("generates mastra-compatible entry point");
121+
122+
it.todo("skips agno-specific configuration");
123+
});
124+
125+
describe("when python + agno", () => {
126+
it.todo("generates python main entry point");
127+
128+
it.todo("generates python scenario test");
129+
130+
it.todo("uses python-compatible file structure");
131+
});
132+
});
133+
134+
describe("file content verification", () => {
135+
it.todo("generates .env with correct API keys");
136+
137+
it.todo("generates .gitignore with appropriate patterns");
138+
139+
it.todo("generates sample prompt file");
140+
141+
it.todo("generates sample scenario test");
142+
143+
it.todo("generates sample evaluation file");
144+
145+
it.todo("generates MCP settings configuration");
146+
147+
it.todo("generates AGENTS.md with project-specific content");
148+
});
149+
150+
describe("progress indication", () => {
151+
it.todo("displays spinner during setup");
152+
153+
it.todo("updates spinner text after structure creation");
154+
155+
it.todo("updates spinner text after MCP setup");
156+
157+
it.todo("updates spinner text after framework setup");
158+
159+
it.todo("updates spinner text after documentation generation");
160+
161+
it.todo("displays success message on completion");
162+
163+
it.todo("displays project location path");
164+
});
165+
166+
describe("assistant kickoff", () => {
167+
it.todo("displays assistant instructions after setup");
168+
169+
it.todo("includes project goal in assistant context");
170+
});
171+
172+
describe("error handling", () => {
173+
it.todo("handles directory creation failure");
174+
175+
it.todo("handles file generation failure");
176+
177+
it.todo("displays error message on failure");
178+
179+
it.todo("exits with non-zero code on error");
180+
181+
it.todo("fails spinner on error");
182+
});
183+
184+
describe("existing directory handling", () => {
185+
it.todo("uses existing directory without error");
186+
187+
it.todo("creates subdirectories in existing directory");
188+
});
189+
});

src/__tests__/utils/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# E2E Test Utilities
2+
3+
Utilities for end-to-end testing of the superagents CLI.
4+
5+
## CLITestRunner
6+
7+
Fluent API for testing CLI user interactions.
8+
9+
### Example
10+
11+
```typescript
12+
import { CLITestRunner } from "./utils/cli-test-runner.js";
13+
14+
it("creates a project", async () => {
15+
const runner = new CLITestRunner();
16+
17+
await runner
18+
.command('init', 'my-project')
19+
.expectPrompt('What programming language')
20+
.input('typescript')
21+
.expectPrompt('What agent framework')
22+
.input('agno')
23+
.expectOutput('Project setup complete')
24+
.expectFile('src/main.ts')
25+
.expectFileToMatchInlineSnapshot('package.json')
26+
.run();
27+
28+
await runner.cleanup();
29+
});
30+
```
31+
32+
## Helper Functions
33+
34+
- **spawnCLI** - Spawns CLI process with stdin injection
35+
- **createTempDir** - Creates isolated temp directory
36+
- **cleanupTempDir** - Removes temp directory
37+

0 commit comments

Comments
 (0)