@patricksardinha/agentkit-cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -130
- package/dist/cli.cjs +202 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +198 -1
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +7 -3
- package/src/commands/init.ts +14 -0
- package/src/generators/readmeGenerator.ts +199 -0
- package/tests/generators/readmeGenerator.test.ts +200 -0
package/src/commands/init.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { generateClaudeMd } from '../generators/claudeMdGenerator.js'
|
|
|
9
9
|
import { generateWorkflow } from '../generators/workflowGenerator.js'
|
|
10
10
|
import { generatePlaybook } from '../generators/playbookGenerator.js'
|
|
11
11
|
import { generateSkills } from '../generators/skillsGenerator.js'
|
|
12
|
+
import { generateReadme } from '../generators/readmeGenerator.js'
|
|
12
13
|
import { extractAgentsFromWorkflow } from '../utils/agentParser.js'
|
|
13
14
|
import { logger } from '../utils/logger.js'
|
|
14
15
|
import type { StackInfo } from '../detectors/stackDetector.js'
|
|
@@ -112,6 +113,7 @@ export function registerInit(program: Command): void {
|
|
|
112
113
|
const claudeMdPath = join(cwd, 'CLAUDE.md')
|
|
113
114
|
const workflowPath = join(cwd, 'AGENT_WORKFLOW.md')
|
|
114
115
|
const playbookPath = join(cwd, 'PLAYBOOK.md')
|
|
116
|
+
const readmePath = join(cwd, 'README.md')
|
|
115
117
|
|
|
116
118
|
// Resolve project name from package.json or directory name
|
|
117
119
|
let projectName = basename(cwd)
|
|
@@ -162,11 +164,23 @@ export function registerInit(program: Command): void {
|
|
|
162
164
|
await writeFile(workflowPath, workflowContent, 'utf-8')
|
|
163
165
|
await writeFile(playbookPath, playbookContent, 'utf-8')
|
|
164
166
|
await generateSkills(agents, cwd)
|
|
167
|
+
|
|
168
|
+
const readmeExists = await fileExists(readmePath)
|
|
169
|
+
if (!readmeExists) {
|
|
170
|
+
const readmeContent = generateReadme({ projectName, blueprintContent, stack: resolvedStack })
|
|
171
|
+
await writeFile(readmePath, readmeContent, 'utf-8')
|
|
172
|
+
}
|
|
173
|
+
|
|
165
174
|
genSpinner.succeed('Fichiers générés')
|
|
166
175
|
|
|
167
176
|
logger.success('CLAUDE.md → créé')
|
|
168
177
|
logger.success('AGENT_WORKFLOW.md → créé')
|
|
169
178
|
logger.success('PLAYBOOK.md → créé')
|
|
170
179
|
logger.success(`agents/ → ${agents.length} dossier(s) créé(s)`)
|
|
180
|
+
if (readmeExists) {
|
|
181
|
+
logger.warn('README.md already exists — skipped (not overwritten)')
|
|
182
|
+
} else {
|
|
183
|
+
logger.success('README.md → créé')
|
|
184
|
+
}
|
|
171
185
|
})
|
|
172
186
|
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { StackInfo } from '../detectors/stackDetector.js'
|
|
2
|
+
|
|
3
|
+
export interface ReadmeInput {
|
|
4
|
+
projectName: string
|
|
5
|
+
blueprintContent?: string
|
|
6
|
+
stack: StackInfo
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function extractSection(content: string, heading: string): string {
|
|
10
|
+
const regex = new RegExp(`##\\s+${heading}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`)
|
|
11
|
+
const match = regex.exec(content)
|
|
12
|
+
return match ? match[1].trim() : ''
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function stackLabel(stack: StackInfo): string {
|
|
16
|
+
switch (stack.framework) {
|
|
17
|
+
case 'react': return 'React + Vite'
|
|
18
|
+
case 'nextjs': return 'Next.js'
|
|
19
|
+
case 'tauri': return 'Tauri v2'
|
|
20
|
+
case 'fastapi': return 'FastAPI'
|
|
21
|
+
case 'express': return 'Express'
|
|
22
|
+
case 'node': return 'Node.js'
|
|
23
|
+
default: return 'Unknown'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function gettingStarted(stack: StackInfo): string {
|
|
28
|
+
switch (stack.framework) {
|
|
29
|
+
case 'tauri':
|
|
30
|
+
return `\`\`\`bash
|
|
31
|
+
npm install
|
|
32
|
+
npm run tauri:dev # development (hot reload)
|
|
33
|
+
npm run tauri:build # production bundle
|
|
34
|
+
\`\`\``
|
|
35
|
+
case 'react':
|
|
36
|
+
return `\`\`\`bash
|
|
37
|
+
npm install
|
|
38
|
+
npm run dev # development server
|
|
39
|
+
npm run build # production build
|
|
40
|
+
\`\`\``
|
|
41
|
+
case 'nextjs':
|
|
42
|
+
return `\`\`\`bash
|
|
43
|
+
npm install
|
|
44
|
+
npm run dev # development server (http://localhost:3000)
|
|
45
|
+
npm run build # production build
|
|
46
|
+
\`\`\``
|
|
47
|
+
case 'fastapi':
|
|
48
|
+
return `\`\`\`bash
|
|
49
|
+
pip install -r requirements.txt
|
|
50
|
+
uvicorn main:app --reload
|
|
51
|
+
\`\`\``
|
|
52
|
+
case 'express':
|
|
53
|
+
case 'node':
|
|
54
|
+
return `\`\`\`bash
|
|
55
|
+
npm install
|
|
56
|
+
npm start
|
|
57
|
+
\`\`\``
|
|
58
|
+
default:
|
|
59
|
+
return `\`\`\`bash
|
|
60
|
+
npm install
|
|
61
|
+
npm run dev
|
|
62
|
+
\`\`\``
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function projectStructure(stack: StackInfo): string {
|
|
67
|
+
switch (stack.framework) {
|
|
68
|
+
case 'react':
|
|
69
|
+
return `\`\`\`
|
|
70
|
+
src/
|
|
71
|
+
components/ ← UI components (PascalCase)
|
|
72
|
+
hooks/ ← custom hooks (prefix: use*)
|
|
73
|
+
pages/ ← page-level components
|
|
74
|
+
utils/ ← shared helpers
|
|
75
|
+
\`\`\``
|
|
76
|
+
case 'nextjs':
|
|
77
|
+
return `\`\`\`
|
|
78
|
+
src/
|
|
79
|
+
app/ ← App Router pages and layouts
|
|
80
|
+
components/ ← shared UI components
|
|
81
|
+
lib/ ← server utilities, db clients
|
|
82
|
+
utils/ ← shared helpers
|
|
83
|
+
\`\`\``
|
|
84
|
+
case 'tauri':
|
|
85
|
+
return `\`\`\`
|
|
86
|
+
src/ ← frontend (${stack.hasTypeScript ? 'TypeScript' : 'JavaScript'})
|
|
87
|
+
components/
|
|
88
|
+
utils/
|
|
89
|
+
src-tauri/ ← Rust backend
|
|
90
|
+
src/
|
|
91
|
+
main.rs ← Tauri entry point
|
|
92
|
+
commands.rs ← Tauri commands (IPC)
|
|
93
|
+
tauri.conf.json
|
|
94
|
+
\`\`\``
|
|
95
|
+
case 'fastapi':
|
|
96
|
+
return `\`\`\`
|
|
97
|
+
app/
|
|
98
|
+
main.py ← FastAPI app entry point
|
|
99
|
+
routers/ ← API route groups
|
|
100
|
+
models/ ← Pydantic models
|
|
101
|
+
services/ ← business logic
|
|
102
|
+
dependencies/ ← FastAPI dependencies (DI)
|
|
103
|
+
tests/ ← pytest tests
|
|
104
|
+
\`\`\``
|
|
105
|
+
case 'express':
|
|
106
|
+
return `\`\`\`
|
|
107
|
+
src/
|
|
108
|
+
routes/ ← Express routers (one per domain)
|
|
109
|
+
controllers/ ← request handlers
|
|
110
|
+
services/ ← business logic
|
|
111
|
+
middleware/ ← Express middleware
|
|
112
|
+
utils/ ← shared helpers
|
|
113
|
+
\`\`\``
|
|
114
|
+
case 'node':
|
|
115
|
+
return `\`\`\`
|
|
116
|
+
src/
|
|
117
|
+
index.ts ← entry point
|
|
118
|
+
lib/ ← core library code
|
|
119
|
+
utils/ ← shared helpers
|
|
120
|
+
\`\`\``
|
|
121
|
+
default:
|
|
122
|
+
return `\`\`\`
|
|
123
|
+
src/ ← source files
|
|
124
|
+
\`\`\``
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function stackTable(stack: StackInfo, blueprintConstraints: string): string {
|
|
129
|
+
const lang = stack.language === 'python' ? 'Python' : stack.hasTypeScript ? 'TypeScript' : 'JavaScript'
|
|
130
|
+
const rows: [string, string][] = [
|
|
131
|
+
['Framework', stackLabel(stack)],
|
|
132
|
+
['Language', lang],
|
|
133
|
+
]
|
|
134
|
+
if (stack.extras.includes('prisma')) rows.push(['Database', 'Prisma ORM'])
|
|
135
|
+
if (stack.extras.includes('tailwind')) rows.push(['Styling', 'Tailwind CSS'])
|
|
136
|
+
if (stack.extras.includes('testing')) rows.push(['Testing', 'Vitest / Jest'])
|
|
137
|
+
|
|
138
|
+
const header = '| Technology | Details |\n|---|---|'
|
|
139
|
+
const rowLines = rows.map(([k, v]) => `| **${k}** | ${v} |`).join('\n')
|
|
140
|
+
|
|
141
|
+
let table = `${header}\n${rowLines}`
|
|
142
|
+
|
|
143
|
+
if (blueprintConstraints) {
|
|
144
|
+
table += `\n\n**Tech constraints (from blueprint)**:\n${blueprintConstraints}`
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return table
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function generateReadme({ projectName, blueprintContent, stack }: ReadmeInput): string {
|
|
151
|
+
const goal = blueprintContent ? extractSection(blueprintContent, 'Goal') : ''
|
|
152
|
+
const features = blueprintContent ? extractSection(blueprintContent, 'Features') : ''
|
|
153
|
+
const techConstraints = blueprintContent
|
|
154
|
+
? extractSection(blueprintContent, 'Tech constraints')
|
|
155
|
+
: ''
|
|
156
|
+
|
|
157
|
+
const tagline = goal
|
|
158
|
+
? goal.split('\n')[0].replace(/^[-*>]\s*/, '')
|
|
159
|
+
: `A ${stackLabel(stack)} project.`
|
|
160
|
+
|
|
161
|
+
const overview = goal
|
|
162
|
+
? goal
|
|
163
|
+
: `This project was scaffolded with [AgentKit](https://www.npmjs.com/package/@patricksardinha/agentkit-cli).`
|
|
164
|
+
|
|
165
|
+
const featuresSection = features
|
|
166
|
+
? features
|
|
167
|
+
: '_Features will be described here._'
|
|
168
|
+
|
|
169
|
+
return `# ${projectName}
|
|
170
|
+
|
|
171
|
+
> ${tagline}
|
|
172
|
+
|
|
173
|
+
## Overview
|
|
174
|
+
|
|
175
|
+
${overview}
|
|
176
|
+
|
|
177
|
+
## Tech Stack
|
|
178
|
+
|
|
179
|
+
${stackTable(stack, techConstraints)}
|
|
180
|
+
|
|
181
|
+
## Getting Started
|
|
182
|
+
|
|
183
|
+
${gettingStarted(stack)}
|
|
184
|
+
|
|
185
|
+
## Features
|
|
186
|
+
|
|
187
|
+
${featuresSection}
|
|
188
|
+
|
|
189
|
+
## Project Structure
|
|
190
|
+
|
|
191
|
+
${projectStructure(stack)}
|
|
192
|
+
|
|
193
|
+
## Built with AgentKit
|
|
194
|
+
|
|
195
|
+
This project was scaffolded with [AgentKit](https://www.npmjs.com/package/@patricksardinha/agentkit-cli) — a CLI that generates AI-native agent workflows for Claude Code.
|
|
196
|
+
|
|
197
|
+
Read \`PLAYBOOK.md\` to understand the agent workflow that built this project.
|
|
198
|
+
`
|
|
199
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { generateReadme } from '../../src/generators/readmeGenerator.js'
|
|
3
|
+
import type { StackInfo } from '../../src/detectors/stackDetector.js'
|
|
4
|
+
|
|
5
|
+
function makeStack(
|
|
6
|
+
framework: StackInfo['framework'],
|
|
7
|
+
opts: Partial<Omit<StackInfo, 'framework'>> = {},
|
|
8
|
+
): StackInfo {
|
|
9
|
+
return {
|
|
10
|
+
framework,
|
|
11
|
+
language: framework === 'fastapi' ? 'python' : opts.hasTypeScript ? 'typescript' : 'javascript',
|
|
12
|
+
hasTypeScript: opts.hasTypeScript ?? false,
|
|
13
|
+
extras: opts.extras ?? [],
|
|
14
|
+
...opts,
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const sampleBlueprint = `# My Awesome App
|
|
19
|
+
|
|
20
|
+
## Goal
|
|
21
|
+
A desktop app for logging development sessions with local AI search.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
- Session logging with timestamps
|
|
25
|
+
- Weekly summary reports
|
|
26
|
+
- Natural language search via Ollama
|
|
27
|
+
|
|
28
|
+
## Tech constraints
|
|
29
|
+
- Tauri v2, all local, no cloud
|
|
30
|
+
- Ollama for AI, no external API keys
|
|
31
|
+
`
|
|
32
|
+
|
|
33
|
+
// ─── Project name ─────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
describe('generateReadme — project name', () => {
|
|
36
|
+
it('contains the project name in the title', () => {
|
|
37
|
+
const result = generateReadme({ projectName: 'my-project', stack: makeStack('react') })
|
|
38
|
+
expect(result).toContain('# my-project')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('contains the project name when blueprint is present', () => {
|
|
42
|
+
const result = generateReadme({
|
|
43
|
+
projectName: 'devlog-desktop',
|
|
44
|
+
blueprintContent: sampleBlueprint,
|
|
45
|
+
stack: makeStack('tauri', { hasTypeScript: true }),
|
|
46
|
+
})
|
|
47
|
+
expect(result).toContain('# devlog-desktop')
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// ─── Built with AgentKit ──────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
describe('generateReadme — Built with AgentKit section', () => {
|
|
54
|
+
it('always contains the Built with AgentKit section', () => {
|
|
55
|
+
for (const framework of ['react', 'nextjs', 'tauri', 'fastapi', 'express', 'node', 'unknown'] as StackInfo['framework'][]) {
|
|
56
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack(framework) })
|
|
57
|
+
expect(result).toContain('Built with AgentKit')
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('contains the npmjs link', () => {
|
|
62
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
63
|
+
expect(result).toContain('https://www.npmjs.com/package/@patricksardinha/agentkit-cli')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('contains the PLAYBOOK.md one-liner', () => {
|
|
67
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
68
|
+
expect(result).toContain('PLAYBOOK.md')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// ─── Getting Started — commands per stack ─────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
describe('generateReadme — Getting Started commands', () => {
|
|
75
|
+
it('Tauri: contains tauri:dev and tauri:build', () => {
|
|
76
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('tauri', { hasTypeScript: true }) })
|
|
77
|
+
expect(result).toContain('npm run tauri:dev')
|
|
78
|
+
expect(result).toContain('npm run tauri:build')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('React: contains npm run dev and npm run build', () => {
|
|
82
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
83
|
+
expect(result).toContain('npm run dev')
|
|
84
|
+
expect(result).toContain('npm run build')
|
|
85
|
+
expect(result).not.toContain('tauri')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('Next.js: contains npm run dev and npm run build', () => {
|
|
89
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('nextjs') })
|
|
90
|
+
expect(result).toContain('npm run dev')
|
|
91
|
+
expect(result).toContain('npm run build')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('FastAPI: contains pip install and uvicorn', () => {
|
|
95
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('fastapi') })
|
|
96
|
+
expect(result).toContain('pip install -r requirements.txt')
|
|
97
|
+
expect(result).toContain('uvicorn main:app --reload')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('Express: contains npm install and npm start', () => {
|
|
101
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('express') })
|
|
102
|
+
expect(result).toContain('npm install')
|
|
103
|
+
expect(result).toContain('npm start')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('Node.js: contains npm install and npm start', () => {
|
|
107
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('node') })
|
|
108
|
+
expect(result).toContain('npm install')
|
|
109
|
+
expect(result).toContain('npm start')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('Unknown: contains npm install and npm run dev', () => {
|
|
113
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('unknown') })
|
|
114
|
+
expect(result).toContain('npm install')
|
|
115
|
+
expect(result).toContain('npm run dev')
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// ─── Blueprint: Goal extraction ───────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
describe('generateReadme — Goal extraction from blueprint', () => {
|
|
122
|
+
it('uses the Goal section as the tagline when blueprint is present', () => {
|
|
123
|
+
const result = generateReadme({
|
|
124
|
+
projectName: 'devlog',
|
|
125
|
+
blueprintContent: sampleBlueprint,
|
|
126
|
+
stack: makeStack('tauri', { hasTypeScript: true }),
|
|
127
|
+
})
|
|
128
|
+
expect(result).toContain('A desktop app for logging development sessions')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('uses a generic tagline when no blueprint is provided', () => {
|
|
132
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
133
|
+
expect(result).toContain('React + Vite')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('includes Goal content in the Overview section', () => {
|
|
137
|
+
const result = generateReadme({
|
|
138
|
+
projectName: 'devlog',
|
|
139
|
+
blueprintContent: sampleBlueprint,
|
|
140
|
+
stack: makeStack('tauri', { hasTypeScript: true }),
|
|
141
|
+
})
|
|
142
|
+
expect(result).toContain('## Overview')
|
|
143
|
+
expect(result).toContain('A desktop app for logging development sessions')
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// ─── Blueprint: Features extraction ──────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
describe('generateReadme — Features extraction from blueprint', () => {
|
|
150
|
+
it('extracts Features section from blueprint', () => {
|
|
151
|
+
const result = generateReadme({
|
|
152
|
+
projectName: 'devlog',
|
|
153
|
+
blueprintContent: sampleBlueprint,
|
|
154
|
+
stack: makeStack('tauri', { hasTypeScript: true }),
|
|
155
|
+
})
|
|
156
|
+
expect(result).toContain('## Features')
|
|
157
|
+
expect(result).toContain('Session logging with timestamps')
|
|
158
|
+
expect(result).toContain('Weekly summary reports')
|
|
159
|
+
expect(result).toContain('Natural language search via Ollama')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('uses placeholder when no blueprint is provided', () => {
|
|
163
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
164
|
+
expect(result).toContain('## Features')
|
|
165
|
+
expect(result).toContain('Features will be described here')
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('includes tech constraints from blueprint in the Tech Stack section', () => {
|
|
169
|
+
const result = generateReadme({
|
|
170
|
+
projectName: 'devlog',
|
|
171
|
+
blueprintContent: sampleBlueprint,
|
|
172
|
+
stack: makeStack('tauri', { hasTypeScript: true }),
|
|
173
|
+
})
|
|
174
|
+
expect(result).toContain('Tauri v2, all local, no cloud')
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// ─── Project structure ────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
describe('generateReadme — Project Structure section', () => {
|
|
181
|
+
it('always contains the Project Structure section', () => {
|
|
182
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
183
|
+
expect(result).toContain('## Project Structure')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('React structure contains src/components/', () => {
|
|
187
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('react') })
|
|
188
|
+
expect(result).toContain('components/')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('Tauri structure mentions src-tauri/', () => {
|
|
192
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('tauri', { hasTypeScript: true }) })
|
|
193
|
+
expect(result).toContain('src-tauri/')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('FastAPI structure mentions app/routers/', () => {
|
|
197
|
+
const result = generateReadme({ projectName: 'test', stack: makeStack('fastapi') })
|
|
198
|
+
expect(result).toContain('routers/')
|
|
199
|
+
})
|
|
200
|
+
})
|