rapidkit 0.11.1 → 0.11.3

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 CHANGED
@@ -30,8 +30,8 @@ cd my-workspace
30
30
  # Generate your first project
31
31
  node generate-demo.js api-project
32
32
  cd api-project
33
- poetry install
34
- poetry run dev
33
+ rapidkit init # Install dependencies
34
+ rapidkit dev # Start dev server
35
35
 
36
36
  # Go back and create more projects
37
37
  cd ..
@@ -74,14 +74,17 @@ my-workspace/ # Workspace (container)
74
74
  ├── package.json # npm configuration
75
75
  ├── README.md # Workspace instructions
76
76
  ├── api-project/ # Project 1
77
+ │ ├── .rapidkit/ # Local CLI (rapidkit init/dev/test)
77
78
  │ ├── src/
78
79
  │ ├── tests/
79
80
  │ └── pyproject.toml
80
81
  ├── auth-service/ # Project 2
82
+ │ ├── .rapidkit/
81
83
  │ ├── src/
82
84
  │ ├── tests/
83
85
  │ └── pyproject.toml
84
86
  └── data-service/ # Project 3
87
+ ├── .rapidkit/
85
88
  ├── src/
86
89
  ├── tests/
87
90
  └── pyproject.toml
@@ -299,8 +302,8 @@ npx rapidkit my-workspace --test-mode
299
302
 
300
303
  3. **Install dependencies and run**:
301
304
  ```bash
302
- poetry install
303
- poetry run dev
305
+ rapidkit init # Install dependencies
306
+ rapidkit dev # Start dev server
304
307
  ```
305
308
 
306
309
  4. **Create more projects** (go back to workspace):
@@ -335,7 +338,7 @@ npx rapidkit my-workspace --test-mode
335
338
  4. **Navigate and run**:
336
339
  ```bash
337
340
  cd api-service
338
- rapidkit run dev
341
+ rapidkit dev
339
342
  ```
340
343
 
341
344
  5. **Create more projects** (from workspace root):
@@ -421,7 +424,7 @@ After setting up your demo project:
421
424
  ## Related Projects
422
425
 
423
426
  - **RapidKit Python** - The core framework (coming soon to PyPI)
424
- - **RapidKit Marketplace** - Browse and share modules (https://rapidkit.dev)
427
+ - **RapidKit Marketplace** - Browse and share modules (https://rapidkit.top)
425
428
  - **GitHub**: https://github.com/getrapidkit
426
429
 
427
430
  ## Contributing
@@ -481,7 +484,7 @@ MIT
481
484
  ## Support
482
485
 
483
486
  - 🐛 Report issues: [GitHub Issues](https://github.com/getrapidkit/rapidkit-npm/issues)
484
- - 📚 Docs: https://rapidkit.dev
487
+ - 📚 Docs: https://rapidkit.top
485
488
  - 💬 Community: Coming soon
486
489
 
487
490
  ---
package/dist/index.js CHANGED
@@ -1,28 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import {Command}from'commander';import i from'chalk';import {readFileSync,promises}from'fs';import*as h from'fs-extra';import y,{dirname,join}from'path';import oe from'inquirer';import W from'ora';import {execa}from'execa';import ee from'os';import ce from'nunjucks';import {fileURLToPath}from'url';import je from'validate-npm-package-name';var G=class{debugEnabled=false;setDebug(e){this.debugEnabled=e;}debug(e,...o){this.debugEnabled&&console.log(i.gray(`[DEBUG] ${e}`),...o);}info(e,...o){console.log(i.blue(e),...o);}success(e,...o){console.log(i.green(e),...o);}warn(e,...o){console.log(i.yellow(e),...o);}error(e,...o){console.error(i.red(e),...o);}step(e,o,r){console.log(i.cyan(`
3
- [${e}/${o}]`),i.white(r));}},s=new G;var te=".rapidkitrc.json";async function z(){let t=y.join(ee.homedir(),te);try{let e=await promises.readFile(t,"utf-8"),o=JSON.parse(e);return s.debug(`Loaded config from ${t}`),o}catch{return s.debug("No user config found, using defaults"),{}}}function F(t){return process.env.RAPIDKIT_DEV_PATH||t.testRapidKitPath||void 0}var g=class extends Error{constructor(o,r,n){super(o);this.code=r;this.details=n;this.name="RapidKitError",Error.captureStackTrace(this,this.constructor);}},b=class extends g{constructor(e,o){let r=o?`Python ${e}+ required, found ${o}`:`Python ${e}+ not found`;super(r,"PYTHON_NOT_FOUND","Please install Python from https://www.python.org/downloads/");}},$=class extends g{constructor(){super("Poetry is not installed","POETRY_NOT_FOUND","Install Poetry from https://python-poetry.org/docs/#installation");}},N=class extends g{constructor(){super("pipx is not installed","PIPX_NOT_FOUND","Install pipx from https://pypa.github.io/pipx/installation/");}},T=class extends g{constructor(e){super(`Directory "${e}" already exists`,"DIRECTORY_EXISTS","Please choose a different name or remove the existing directory");}},k=class extends g{constructor(e,o){super(`Invalid project name: "${e}"`,"INVALID_PROJECT_NAME",o);}},j=class extends g{constructor(e,o){let r=`Installation failed at: ${e}`,n=`${o.message}
2
+ import {Command}from'commander';import i from'chalk';import {readFileSync,promises}from'fs';import*as y from'fs-extra';import h,{dirname,join}from'path';import oe from'inquirer';import z from'ora';import {execa}from'execa';import ee from'os';import le from'nunjucks';import {fileURLToPath}from'url';import Pe from'validate-npm-package-name';var G=class{debugEnabled=false;setDebug(e){this.debugEnabled=e;}debug(e,...o){this.debugEnabled&&console.log(i.gray(`[DEBUG] ${e}`),...o);}info(e,...o){console.log(i.blue(e),...o);}success(e,...o){console.log(i.green(e),...o);}warn(e,...o){console.log(i.yellow(e),...o);}error(e,...o){console.error(i.red(e),...o);}step(e,o,r){console.log(i.cyan(`
3
+ [${e}/${o}]`),i.white(r));}},s=new G;var te=".rapidkitrc.json";async function W(){let t=h.join(ee.homedir(),te);try{let e=await promises.readFile(t,"utf-8"),o=JSON.parse(e);return s.debug(`Loaded config from ${t}`),o}catch{return s.debug("No user config found, using defaults"),{}}}function F(t){return process.env.RAPIDKIT_DEV_PATH||t.testRapidKitPath||void 0}var u=class extends Error{constructor(o,r,a){super(o);this.code=r;this.details=a;this.name="RapidKitError",Error.captureStackTrace(this,this.constructor);}},P=class extends u{constructor(e,o){let r=o?`Python ${e}+ required, found ${o}`:`Python ${e}+ not found`;super(r,"PYTHON_NOT_FOUND","Please install Python from https://www.python.org/downloads/");}},T=class extends u{constructor(){super("Poetry is not installed","POETRY_NOT_FOUND","Install Poetry from https://python-poetry.org/docs/#installation");}},N=class extends u{constructor(){super("pipx is not installed","PIPX_NOT_FOUND","Install pipx from https://pypa.github.io/pipx/installation/");}},M=class extends u{constructor(e){super(`Directory "${e}" already exists`,"DIRECTORY_EXISTS","Please choose a different name or remove the existing directory");}},k=class extends u{constructor(e,o){super(`Invalid project name: "${e}"`,"INVALID_PROJECT_NAME",o);}},j=class extends u{constructor(e,o){let r=`Installation failed at: ${e}`,a=`${o.message}
4
4
 
5
5
  Troubleshooting:
6
6
  - Check your internet connection
7
7
  - Verify Python/Poetry installation
8
- - Try running with --debug flag for more details`;super(r,"INSTALLATION_ERROR",n);}},x=class extends g{constructor(){super("RapidKit Python package is not yet available on PyPI","RAPIDKIT_NOT_AVAILABLE",`Available options:
8
+ - Try running with --debug flag for more details`;super(r,"INSTALLATION_ERROR",a);}},I=class extends u{constructor(){super("RapidKit Python package is not yet available on PyPI","RAPIDKIT_NOT_AVAILABLE",`Available options:
9
9
  1. Demo mode: npx rapidkit my-workspace --demo
10
10
  2. Test mode: npx rapidkit my-workspace --test-mode (requires local RapidKit)
11
- 3. Wait for official PyPI release (coming soon)`);}};async function U(t,e){let{skipGit:o=false,testMode:r=false,demoMode:n=false,dryRun:m=false,userConfig:p={}}=e,a=t,l=y.resolve(process.cwd(),a);if(await h.pathExists(l))throw new T(a);if(m){await le(l,a,n,p);return}if(n){await se(l,a,o);return}let f=await oe.prompt([{type:"list",name:"pythonVersion",message:"Select Python version for RapidKit:",choices:["3.10","3.11","3.12"],default:p.pythonVersion||"3.11"},{type:"list",name:"installMethod",message:"How would you like to install RapidKit?",choices:[{name:"\u{1F3AF} Poetry (Recommended - includes virtual env)",value:"poetry"},{name:"\u{1F4E6} pip with venv (Standard)",value:"venv"},{name:"\u{1F527} pipx (Global isolated install)",value:"pipx"}],default:p.defaultInstallMethod||"poetry"}]);s.step(1,3,"Setting up RapidKit environment");let u=W("Creating directory").start();try{if(await h.ensureDir(l),u.succeed("Directory created"),f.installMethod==="poetry"?await ie(l,f.pythonVersion,u,r,p):f.installMethod==="venv"?await re(l,f.pythonVersion,u,r,p):await ne(l,u,r,p),await ae(l,f.installMethod),u.succeed("RapidKit environment ready!"),!e.skipGit){u.start("Initializing git repository");try{await execa("git",["init"],{cwd:l}),await h.outputFile(y.join(l,".gitignore"),`.venv/
11
+ 3. Wait for official PyPI release (coming soon)`);}};async function U(t,e){let{skipGit:o=false,testMode:r=false,demoMode:a=false,dryRun:m=false,userConfig:d={}}=e,n=t,c=h.resolve(process.cwd(),n);if(await y.pathExists(c))throw new M(n);if(m){await ce(c,n,a,d);return}if(a){await se(c,n,o);return}let f=await oe.prompt([{type:"list",name:"pythonVersion",message:"Select Python version for RapidKit:",choices:["3.10","3.11","3.12"],default:d.pythonVersion||"3.11"},{type:"list",name:"installMethod",message:"How would you like to install RapidKit?",choices:[{name:"\u{1F3AF} Poetry (Recommended - includes virtual env)",value:"poetry"},{name:"\u{1F4E6} pip with venv (Standard)",value:"venv"},{name:"\u{1F527} pipx (Global isolated install)",value:"pipx"}],default:d.defaultInstallMethod||"poetry"}]);s.step(1,3,"Setting up RapidKit environment");let g=z("Creating directory").start();try{if(await y.ensureDir(c),g.succeed("Directory created"),f.installMethod==="poetry"?await ie(c,f.pythonVersion,g,r,d):f.installMethod==="venv"?await re(c,f.pythonVersion,g,r,d):await ae(c,g,r,d),await ne(c,f.installMethod),g.succeed("RapidKit environment ready!"),!e.skipGit){g.start("Initializing git repository");try{await execa("git",["init"],{cwd:c}),await y.outputFile(h.join(c,".gitignore"),`.venv/
12
12
  __pycache__/
13
13
  *.pyc
14
14
  .env
15
15
  .rapidkit-workspace/
16
- `,"utf-8"),await execa("git",["add","."],{cwd:l}),await execa("git",["commit","-m","Initial commit: RapidKit environment"],{cwd:l}),u.succeed("Git repository initialized");}catch{u.warn("Could not initialize git repository");}}if(console.log(i.green(`
16
+ `,"utf-8"),await execa("git",["add","."],{cwd:c}),await execa("git",["commit","-m","Initial commit: RapidKit environment"],{cwd:c}),g.succeed("Git repository initialized");}catch{g.warn("Could not initialize git repository");}}if(console.log(i.green(`
17
17
  \u2728 RapidKit environment created successfully!
18
- `)),console.log(i.cyan("\u{1F4C2} Location:"),i.white(l)),console.log(i.cyan(`\u{1F680} Get started:
19
- `)),console.log(i.white(` cd ${a}`)),f.installMethod==="poetry"){let v="source $(poetry env info --path)/bin/activate";try{let{stdout:C}=await execa("poetry",["--version"]),D=C.match(/Poetry.*?(\d+)\.(\d+)/);D&&(parseInt(D[1])>=2?v="source $(poetry env info --path)/bin/activate":v="poetry shell");}catch{}console.log(i.white(` ${v} # Or: poetry run rapidkit`)),console.log(i.white(" rapidkit create # Interactive mode")),console.log(i.white(" cd <project-name> && poetry install && rapidkit run dev"));}else f.installMethod==="venv"?(console.log(i.white(" source .venv/bin/activate # On Windows: .venv\\Scripts\\activate")),console.log(i.white(" rapidkit create # Interactive mode")),console.log(i.white(" cd <project-name> && pip install -r requirements.txt && rapidkit run dev"))):(console.log(i.white(" rapidkit create # Interactive mode")),console.log(i.white(" cd <project-name> && rapidkit run dev")));console.log(i.white(`
18
+ `)),console.log(i.cyan("\u{1F4C2} Location:"),i.white(c)),console.log(i.cyan(`\u{1F680} Get started:
19
+ `)),console.log(i.white(` cd ${n}`)),f.installMethod==="poetry"){let v="source $(poetry env info --path)/bin/activate";try{let{stdout:K}=await execa("poetry",["--version"]),b=K.match(/Poetry.*?(\d+)\.(\d+)/);b&&(parseInt(b[1])>=2?v="source $(poetry env info --path)/bin/activate":v="poetry shell");}catch{}console.log(i.white(` ${v} # Or: poetry run rapidkit`)),console.log(i.white(" rapidkit create # Interactive mode")),console.log(i.white(" cd <project-name> && rapidkit init && rapidkit dev"));}else f.installMethod==="venv"?(console.log(i.white(" source .venv/bin/activate # On Windows: .venv\\Scripts\\activate")),console.log(i.white(" rapidkit create # Interactive mode")),console.log(i.white(" cd <project-name> && rapidkit init && rapidkit dev"))):(console.log(i.white(" rapidkit create # Interactive mode")),console.log(i.white(" cd <project-name> && rapidkit init && rapidkit dev")));console.log(i.white(`
20
20
  \u{1F4A1} For more information, check the README.md file.`)),console.log(i.cyan(`
21
- \u{1F4DA} RapidKit commands:`)),console.log(i.white(" rapidkit create - Create a new project (interactive)")),console.log(i.white(" rapidkit run dev - Run development server")),console.log(i.white(" rapidkit add module <name> - Add a module (e.g., settings)")),console.log(i.white(" rapidkit list - List available kits")),console.log(i.white(" rapidkit modules - List available modules")),console.log(i.white(` rapidkit --help - Show all commands
22
- `));}catch(v){u.fail("Failed to create RapidKit environment"),console.error(i.red(`
23
- \u274C Error:`),v);try{await h.remove(l);}catch{}process.exit(1);}}async function ie(t,e,o,r,n){o.start("Checking Poetry installation");try{await execa("poetry",["--version"]),o.succeed("Poetry found");}catch{throw new $}o.start("Initializing Poetry project"),await execa("poetry",["init","--no-interaction","--python",`^${e}`],{cwd:t});let m=y.join(t,"pyproject.toml"),a=(await promises.readFile(m,"utf-8")).replace("[tool.poetry]",`[tool.poetry]
24
- package-mode = false`);if(await promises.writeFile(m,a,"utf-8"),o.succeed("Poetry project initialized"),o.start("Installing RapidKit"),r){let l=F(n||{});if(!l)throw new j("Test mode installation",new Error("No local RapidKit path configured. Set RAPIDKIT_DEV_PATH environment variable."));s.debug(`Installing from local path: ${l}`),o.text="Installing RapidKit from local path (test mode)",await execa("poetry",["add",l],{cwd:t});}else {o.text="Installing RapidKit from PyPI";try{await execa("poetry",["add","rapidkit"],{cwd:t});}catch{throw new x}}o.succeed("RapidKit installed");}async function re(t,e,o,r,n){o.start(`Checking Python ${e}`);let m="python3";try{let{stdout:a}=await execa(m,["--version"]),l=a.match(/Python (\d+\.\d+)/)?.[1];if(l&&parseFloat(l)<parseFloat(e))throw new b(e,l);o.succeed(`Python ${l} found`);}catch(a){throw a instanceof b?a:new b(e)}o.start("Creating virtual environment"),await execa(m,["-m","venv",".venv"],{cwd:t}),o.succeed("Virtual environment created"),o.start("Installing RapidKit");let p=y.join(t,".venv","bin","pip");if(await execa(p,["install","--upgrade","pip"],{cwd:t}),r){let a=F(n||{});if(!a)throw new j("Test mode installation",new Error("No local RapidKit path configured. Set RAPIDKIT_DEV_PATH environment variable."));s.debug(`Installing from local path: ${a}`),o.text="Installing RapidKit from local path (test mode)",await execa(p,["install","-e",a],{cwd:t});}else {o.text="Installing RapidKit from PyPI";try{await execa(p,["install","rapidkit"],{cwd:t});}catch{throw new x}}o.succeed("RapidKit installed");}async function ne(t,e,o,r){e.start("Checking pipx installation");try{await execa("pipx",["--version"]),e.succeed("pipx found");}catch{throw new N}if(e.start("Installing RapidKit globally with pipx"),o){let n=F(r||{});if(!n)throw new j("Test mode installation",new Error("No local RapidKit path configured. Set RAPIDKIT_DEV_PATH environment variable."));s.debug(`Installing from local path: ${n}`),e.text="Installing RapidKit from local path (test mode)",await execa("pipx",["install","-e",n]);}else {e.text="Installing RapidKit from PyPI";try{await execa("pipx",["install","rapidkit"]);}catch{throw new x}}e.succeed("RapidKit installed globally"),await h.outputFile(y.join(t,".rapidkit-global"),`RapidKit installed globally with pipx
25
- `,"utf-8");}async function ae(t,e){let r=`# RapidKit Workspace
21
+ \u{1F4DA} RapidKit commands:`)),console.log(i.white(" rapidkit create - Create a new project (interactive)")),console.log(i.white(" rapidkit dev - Run development server")),console.log(i.white(" rapidkit add module <name> - Add a module (e.g., settings)")),console.log(i.white(" rapidkit list - List available kits")),console.log(i.white(" rapidkit modules - List available modules")),console.log(i.white(` rapidkit --help - Show all commands
22
+ `));}catch(v){g.fail("Failed to create RapidKit environment"),console.error(i.red(`
23
+ \u274C Error:`),v);try{await y.remove(c);}catch{}process.exit(1);}}async function ie(t,e,o,r,a){o.start("Checking Poetry installation");try{await execa("poetry",["--version"]),o.succeed("Poetry found");}catch{throw new T}o.start("Initializing Poetry project"),await execa("poetry",["init","--no-interaction","--python",`^${e}`],{cwd:t});let m=h.join(t,"pyproject.toml"),n=(await promises.readFile(m,"utf-8")).replace("[tool.poetry]",`[tool.poetry]
24
+ package-mode = false`);if(await promises.writeFile(m,n,"utf-8"),o.succeed("Poetry project initialized"),o.start("Installing RapidKit"),r){let c=F(a||{});if(!c)throw new j("Test mode installation",new Error("No local RapidKit path configured. Set RAPIDKIT_DEV_PATH environment variable."));s.debug(`Installing from local path: ${c}`),o.text="Installing RapidKit from local path (test mode)",await execa("poetry",["add",c],{cwd:t});}else {o.text="Installing RapidKit from PyPI";try{await execa("poetry",["add","rapidkit"],{cwd:t});}catch{throw new I}}o.succeed("RapidKit installed");}async function re(t,e,o,r,a){o.start(`Checking Python ${e}`);let m="python3";try{let{stdout:n}=await execa(m,["--version"]),c=n.match(/Python (\d+\.\d+)/)?.[1];if(c&&parseFloat(c)<parseFloat(e))throw new P(e,c);o.succeed(`Python ${c} found`);}catch(n){throw n instanceof P?n:new P(e)}o.start("Creating virtual environment"),await execa(m,["-m","venv",".venv"],{cwd:t}),o.succeed("Virtual environment created"),o.start("Installing RapidKit");let d=h.join(t,".venv","bin","pip");if(await execa(d,["install","--upgrade","pip"],{cwd:t}),r){let n=F(a||{});if(!n)throw new j("Test mode installation",new Error("No local RapidKit path configured. Set RAPIDKIT_DEV_PATH environment variable."));s.debug(`Installing from local path: ${n}`),o.text="Installing RapidKit from local path (test mode)",await execa(d,["install","-e",n],{cwd:t});}else {o.text="Installing RapidKit from PyPI";try{await execa(d,["install","rapidkit"],{cwd:t});}catch{throw new I}}o.succeed("RapidKit installed");}async function ae(t,e,o,r){e.start("Checking pipx installation");try{await execa("pipx",["--version"]),e.succeed("pipx found");}catch{throw new N}if(e.start("Installing RapidKit globally with pipx"),o){let a=F(r||{});if(!a)throw new j("Test mode installation",new Error("No local RapidKit path configured. Set RAPIDKIT_DEV_PATH environment variable."));s.debug(`Installing from local path: ${a}`),e.text="Installing RapidKit from local path (test mode)",await execa("pipx",["install","-e",a]);}else {e.text="Installing RapidKit from PyPI";try{await execa("pipx",["install","rapidkit"]);}catch{throw new I}}e.succeed("RapidKit installed globally"),await y.outputFile(h.join(t,".rapidkit-global"),`RapidKit installed globally with pipx
25
+ `,"utf-8");}async function ne(t,e){let r=`# RapidKit Workspace
26
26
 
27
27
  This directory contains a RapidKit development environment.
28
28
 
@@ -59,14 +59,14 @@ Interactive mode will guide you through selecting a kit and configuring your pro
59
59
 
60
60
  \`\`\`bash
61
61
  cd my-project
62
- # Install dependencies:
63
- poetry install
62
+ # Install dependencies (preferred):
63
+ rapidkit init
64
64
 
65
- # Run the server (simplest way):
66
- rapidkit run dev
65
+ # Run the server (project-aware):
66
+ rapidkit dev
67
67
 
68
- # Or with poetry run:
69
- poetry run rapidkit run dev
68
+ # Or with poetry run (manual / advanced):
69
+ poetry run rapidkit dev
70
70
 
71
71
  # Or manually:
72
72
  uvicorn src.main:app --reload
@@ -88,7 +88,7 @@ rapidkit modules list
88
88
 
89
89
  - \`rapidkit create\` - Create a new project (interactive)
90
90
  - \`rapidkit create project <kit> <name>\` - Create project with specific kit
91
- - \`rapidkit run dev\` - Run development server
91
+ - \`rapidkit dev\` - Run development server
92
92
  - \`rapidkit add module <name>\` - Add a module (e.g., \`rapidkit add module settings\`)
93
93
  - \`rapidkit list\` - List available kits
94
94
  - \`rapidkit modules\` - List available modules
@@ -98,7 +98,7 @@ rapidkit modules list
98
98
 
99
99
  ## RapidKit Documentation
100
100
 
101
- For full documentation, visit: [RapidKit Docs](https://rapidkit.dev) *(or appropriate URL)*
101
+ For full documentation, visit: [RapidKit Docs](https://rapidkit.top) *(or appropriate URL)*
102
102
 
103
103
  ## Workspace Structure
104
104
 
@@ -117,7 +117,7 @@ If you encounter issues:
117
117
  2. Check RapidKit installation: \`rapidkit --version\`
118
118
  3. Run diagnostics: \`rapidkit doctor\`
119
119
  4. Visit RapidKit documentation or GitHub issues
120
- `;await promises.writeFile(y.join(t,"README.md"),r,"utf-8");}async function se(t,e,o){let r=W("Creating demo workspace").start();try{await h.ensureDir(t),r.succeed("Directory created"),r.start("Setting up demo kit generator");let n=JSON.stringify({name:`${e}-workspace`,version:"1.0.0",private:!0,description:"RapidKit demo workspace",scripts:{generate:"node generate-demo.js"}},null,2);await promises.writeFile(y.join(t,"package.json"),n,"utf-8"),await promises.writeFile(y.join(t,"generate-demo.js"),`#!/usr/bin/env node
120
+ `;await promises.writeFile(h.join(t,"README.md"),r,"utf-8");}async function se(t,e,o){let r=z("Creating demo workspace").start();try{await y.ensureDir(t),r.succeed("Directory created"),r.start("Setting up demo kit generator");let a=JSON.stringify({name:`${e}-workspace`,version:"1.0.0",private:!0,description:"RapidKit demo workspace",scripts:{generate:"node generate-demo.js"}},null,2);await promises.writeFile(h.join(t,"package.json"),a,"utf-8"),await promises.writeFile(h.join(t,"generate-demo.js"),`#!/usr/bin/env node
121
121
  /**
122
122
  * Demo Kit Generator - Create FastAPI demo projects
123
123
  *
@@ -132,8 +132,9 @@ If you encounter issues:
132
132
  * npm run generate my-api
133
133
  */
134
134
 
135
- const { execSync } = require('child_process');
135
+ const fs = require('fs');
136
136
  const path = require('path');
137
+ const readline = require('readline');
137
138
 
138
139
  const projectName = process.argv[2];
139
140
 
@@ -144,21 +145,375 @@ if (!projectName) {
144
145
  process.exit(1);
145
146
  }
146
147
 
147
- // Use npx to run rapidkit in demo mode, targeting the current directory
148
- const targetPath = path.join(process.cwd(), projectName);
148
+ const rl = readline.createInterface({
149
+ input: process.stdin,
150
+ output: process.stdout
151
+ });
149
152
 
150
- try {
151
- console.log(\`\\nGenerating demo project: \${projectName}...\\n\`);
152
- execSync(\`npx rapidkit "\${projectName}" --demo-only\`, {
153
- stdio: 'inherit',
154
- cwd: process.cwd(),
153
+ function ask(question, defaultValue) {
154
+ return new Promise((resolve) => {
155
+ rl.question(\`\${question} (\${defaultValue}): \`, (answer) => {
156
+ resolve(answer || defaultValue);
157
+ });
155
158
  });
156
- console.log(\`\\n\u2705 Demo project created at: \${targetPath}\\n\`);
157
- } catch (_error) {
158
- console.error('\\n\u274C Failed to generate demo project\\n');
159
- process.exit(1);
160
159
  }
161
- `,"utf-8");try{await execa("chmod",["+x",y.join(t,"generate-demo.js")]);}catch{}let p=`# RapidKit Demo Workspace
160
+
161
+ async function main() {
162
+ const targetPath = path.join(process.cwd(), projectName);
163
+
164
+ if (fs.existsSync(targetPath)) {
165
+ console.error(\`\\n\u274C Directory "\${projectName}" already exists\\n\`);
166
+ process.exit(1);
167
+ }
168
+
169
+ console.log(\`\\n\u{1F680} Creating FastAPI project: \${projectName}\\n\`);
170
+
171
+ const snakeName = projectName.replace(/-/g, '_').toLowerCase();
172
+ const project_name = await ask('Project name (snake_case)', snakeName);
173
+ const author = await ask('Author name', process.env.USER || 'RapidKit User');
174
+ const description = await ask('Description', 'FastAPI service generated with RapidKit');
175
+
176
+ rl.close();
177
+
178
+ // Create project structure
179
+ const dirs = [
180
+ '',
181
+ 'src',
182
+ 'src/routing',
183
+ 'src/modules',
184
+ 'tests',
185
+ '.rapidkit'
186
+ ];
187
+
188
+ for (const dir of dirs) {
189
+ fs.mkdirSync(path.join(targetPath, dir), { recursive: true });
190
+ }
191
+
192
+ // Template files with content
193
+ const files = {
194
+ 'src/__init__.py': '"""' + project_name + ' package."""\\n',
195
+ 'src/modules/__init__.py': '"""Modules package."""\\n',
196
+ 'tests/__init__.py': '"""Tests package."""\\n',
197
+ 'src/main.py': \`"""\${project_name} application entrypoint."""
198
+
199
+ from __future__ import annotations
200
+
201
+ from contextlib import asynccontextmanager
202
+ from typing import AsyncIterator
203
+
204
+ from fastapi import FastAPI
205
+ from fastapi.middleware.cors import CORSMiddleware
206
+
207
+ from .routing import api_router
208
+
209
+
210
+ @asynccontextmanager
211
+ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
212
+ """Application lifespan context manager for startup/shutdown events."""
213
+ yield
214
+
215
+
216
+ app = FastAPI(
217
+ title="\${project_name}",
218
+ description="\${description}",
219
+ version="0.1.0",
220
+ docs_url="/docs",
221
+ redoc_url="/redoc",
222
+ lifespan=lifespan,
223
+ )
224
+
225
+ app.add_middleware(
226
+ CORSMiddleware,
227
+ allow_origins=["*"],
228
+ allow_credentials=True,
229
+ allow_methods=["*"],
230
+ allow_headers=["*"],
231
+ )
232
+
233
+ app.include_router(api_router, prefix="/api")
234
+
235
+
236
+ if __name__ == "__main__":
237
+ import uvicorn
238
+ uvicorn.run("src.main:app", host="0.0.0.0", port=8001, reload=True)
239
+ \`,
240
+ 'src/routing/__init__.py': \`"""API routing configuration."""
241
+
242
+ from fastapi import APIRouter
243
+
244
+ from .health import router as health_router
245
+
246
+ api_router = APIRouter()
247
+
248
+ api_router.include_router(health_router)
249
+ \`,
250
+ 'src/routing/health.py': \`"""Health check endpoints."""
251
+
252
+ from __future__ import annotations
253
+
254
+ from fastapi import APIRouter
255
+
256
+ router = APIRouter(prefix="/health", tags=["health"])
257
+
258
+
259
+ @router.get("/", summary="Health check")
260
+ async def heartbeat() -> dict[str, str]:
261
+ """Return basic service heartbeat."""
262
+ return {"status": "ok"}
263
+ \`,
264
+ 'src/cli.py': \`"""CLI commands for \${project_name}."""
265
+
266
+ import subprocess
267
+ import sys
268
+ from pathlib import Path
269
+
270
+
271
+ def dev():
272
+ """Start development server with hot reload."""
273
+ print("\u{1F680} Starting development server...")
274
+ subprocess.run([
275
+ sys.executable, "-m", "uvicorn",
276
+ "src.main:app", "--reload",
277
+ "--host", "0.0.0.0", "--port", "8000"
278
+ ])
279
+
280
+
281
+ def start():
282
+ """Start production server."""
283
+ print("\u26A1 Starting production server...")
284
+ subprocess.run([
285
+ sys.executable, "-m", "uvicorn",
286
+ "src.main:app",
287
+ "--host", "0.0.0.0", "--port", "8000"
288
+ ])
289
+
290
+
291
+ def test():
292
+ """Run tests."""
293
+ print("\u{1F9EA} Running tests...")
294
+ subprocess.run([sys.executable, "-m", "pytest", "-q"])
295
+
296
+
297
+ if __name__ == "__main__":
298
+ if len(sys.argv) < 2:
299
+ print("Usage: python -m src.cli <command>")
300
+ print("Commands: dev, start, test")
301
+ sys.exit(1)
302
+
303
+ cmd = sys.argv[1]
304
+ if cmd == "dev":
305
+ dev()
306
+ elif cmd == "start":
307
+ start()
308
+ elif cmd == "test":
309
+ test()
310
+ else:
311
+ print(f"Unknown command: {cmd}")
312
+ sys.exit(1)
313
+ \`,
314
+ 'pyproject.toml': \`[tool.poetry]
315
+ name = "\${project_name}"
316
+ version = "0.1.0"
317
+ description = "\${description}"
318
+ authors = ["\${author}"]
319
+ license = "MIT"
320
+ readme = "README.md"
321
+ package-mode = false
322
+
323
+ [tool.poetry.dependencies]
324
+ python = "^3.11"
325
+ fastapi = "^0.115.0"
326
+ uvicorn = {extras = ["standard"], version = "^0.32.0"}
327
+ pydantic = "^2.0"
328
+ pydantic-settings = "^2.0"
329
+
330
+ [tool.poetry.group.dev.dependencies]
331
+ pytest = "^8.0"
332
+ pytest-asyncio = "^0.24.0"
333
+ pytest-cov = "^6.0"
334
+ httpx = "^0.27"
335
+ black = "^24.0"
336
+ ruff = "^0.8"
337
+ mypy = "^1.0"
338
+
339
+ [tool.poetry.scripts]
340
+ dev = "src.cli:dev"
341
+ start = "src.cli:start"
342
+ test = "src.cli:test"
343
+
344
+ [build-system]
345
+ requires = ["poetry-core"]
346
+ build-backend = "poetry.core.masonry.api"
347
+
348
+ [tool.pytest.ini_options]
349
+ asyncio_mode = "auto"
350
+ testpaths = ["tests"]
351
+
352
+ [tool.ruff]
353
+ line-length = 100
354
+ target-version = "py311"
355
+
356
+ [tool.black]
357
+ line-length = 100
358
+ target-version = ["py311"]
359
+ \`,
360
+ 'README.md': \`# \${project_name}
361
+
362
+ \${description}
363
+
364
+ ## Quick start
365
+
366
+ \\\`\\\`\\\`bash
367
+ rapidkit init # Install dependencies
368
+ rapidkit dev # Start dev server
369
+ \\\`\\\`\\\`
370
+
371
+ ## Available commands
372
+
373
+ \\\`\\\`\\\`bash
374
+ rapidkit init # \u{1F527} Install dependencies
375
+ rapidkit dev # \u{1F680} Start development server with hot reload
376
+ rapidkit start # \u26A1 Start production server
377
+ rapidkit test # \u{1F9EA} Run tests
378
+ rapidkit help # \u{1F4DA} Show available commands
379
+ \\\`\\\`\\\`
380
+
381
+ ## Project layout
382
+
383
+ \\\`\\\`\\\`
384
+ \${project_name}/
385
+ \u251C\u2500\u2500 src/
386
+ \u2502 \u251C\u2500\u2500 main.py # FastAPI application
387
+ \u2502 \u251C\u2500\u2500 cli.py # CLI commands
388
+ \u2502 \u251C\u2500\u2500 routing/ # API routes
389
+ \u2502 \u2514\u2500\u2500 modules/ # Module system
390
+ \u251C\u2500\u2500 tests/ # Test suite
391
+ \u251C\u2500\u2500 pyproject.toml # Poetry configuration
392
+ \u2514\u2500\u2500 README.md
393
+ \\\`\\\`\\\`
394
+ \`,
395
+ '.rapidkit/project.json': JSON.stringify({
396
+ kit_name: "fastapi.standard",
397
+ profile: "fastapi/standard",
398
+ created_at: new Date().toISOString(),
399
+ rapidkit_version: "npm-demo"
400
+ }, null, 2),
401
+ '.rapidkit/cli.py': \`#!/usr/bin/env python3
402
+ """RapidKit CLI wrapper for demo projects."""
403
+
404
+ import subprocess
405
+ import sys
406
+ from pathlib import Path
407
+
408
+
409
+ def dev(port=8000, host="0.0.0.0"):
410
+ """Start development server."""
411
+ print("\u{1F680} Starting development server with hot reload...")
412
+ subprocess.run([
413
+ sys.executable, "-m", "uvicorn",
414
+ "src.main:app", "--reload",
415
+ "--host", host, "--port", str(port)
416
+ ])
417
+
418
+
419
+ def start(port=8000, host="0.0.0.0"):
420
+ """Start production server."""
421
+ print("\u26A1 Starting production server...")
422
+ subprocess.run([
423
+ sys.executable, "-m", "uvicorn",
424
+ "src.main:app",
425
+ "--host", host, "--port", str(port)
426
+ ])
427
+
428
+
429
+ def init():
430
+ """Install dependencies."""
431
+ print("\u{1F4E6} Installing dependencies...")
432
+ subprocess.run(["poetry", "install"])
433
+
434
+
435
+ def test():
436
+ """Run tests."""
437
+ print("\u{1F9EA} Running tests...")
438
+ subprocess.run([sys.executable, "-m", "pytest", "-q"])
439
+
440
+
441
+ def help_cmd():
442
+ """Show help."""
443
+ print("\u{1F4DA} Available commands:")
444
+ print(" init - Install dependencies")
445
+ print(" dev - Start dev server")
446
+ print(" start - Start production server")
447
+ print(" test - Run tests")
448
+
449
+
450
+ if __name__ == "__main__":
451
+ cmd = sys.argv[1] if len(sys.argv) > 1 else "help"
452
+ cmds = {"dev": dev, "start": start, "init": init, "test": test, "help": help_cmd}
453
+ cmds.get(cmd, help_cmd)()
454
+ \`,
455
+ '.rapidkit/rapidkit': '#!/usr/bin/env bash\\n# Local RapidKit launcher for demo projects\\nset -euo pipefail\\nSCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"\\nROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"\\ncd "$ROOT_DIR"\\n\\nif [ -f "pyproject.toml" ]; then\\n if command -v poetry >/dev/null 2>&1; then\\n exec poetry run python "$SCRIPT_DIR/cli.py" "$@"\\n fi\\nfi\\n\\necho "Poetry not found. Install with: pip install poetry"\\nexit 1\\n',
456
+ '.gitignore': \`# Python
457
+ __pycache__/
458
+ *.py[cod]
459
+ *.so
460
+ .Python
461
+ build/
462
+ dist/
463
+ *.egg-info/
464
+
465
+ # Virtual environments
466
+ .venv/
467
+ venv/
468
+
469
+ # IDEs
470
+ .vscode/
471
+ .idea/
472
+
473
+ # OS
474
+ .DS_Store
475
+
476
+ # Project
477
+ .env
478
+ .env.local
479
+ \`
480
+ };
481
+
482
+ for (const [filePath, content] of Object.entries(files)) {
483
+ fs.writeFileSync(path.join(targetPath, filePath), content);
484
+ }
485
+
486
+ // Make scripts executable
487
+ try {
488
+ fs.chmodSync(path.join(targetPath, '.rapidkit/cli.py'), 0o755);
489
+ fs.chmodSync(path.join(targetPath, '.rapidkit/rapidkit'), 0o755);
490
+ } catch (e) {
491
+ // Ignore on Windows
492
+ }
493
+
494
+ console.log(\`
495
+ \u2728 Demo project created successfully!
496
+
497
+ \u{1F4C2} Project: \${targetPath}
498
+
499
+ \u{1F680} Get started:
500
+ cd \${projectName}
501
+ rapidkit init # Install dependencies
502
+ rapidkit dev # Start dev server
503
+
504
+ \u{1F4DA} Available commands:
505
+ rapidkit init # \u{1F527} Install dependencies
506
+ rapidkit dev # \u{1F680} Start dev server with hot reload
507
+ rapidkit start # \u26A1 Start production server
508
+ rapidkit test # \u{1F9EA} Run tests
509
+ rapidkit help # \u{1F4DA} Show help
510
+
511
+ \u{1F4A1} For full RapidKit features: pipx install rapidkit
512
+ \`);
513
+ }
514
+
515
+ main().catch(console.error);
516
+ `,"utf-8");try{await execa("chmod",["+x",h.join(t,"generate-demo.js")]);}catch{}let d=`# RapidKit Demo Workspace
162
517
 
163
518
  Welcome to your RapidKit demo workspace! This environment lets you generate FastAPI demo projects using bundled RapidKit templates, without needing to install Python RapidKit.
164
519
 
@@ -174,10 +529,10 @@ node generate-demo.js my-api
174
529
  cd my-api
175
530
 
176
531
  # Install dependencies:
177
- poetry install
532
+ rapidkit init
178
533
 
179
534
  # Run the development server:
180
- poetry run python -m src.main
535
+ rapidkit dev
181
536
  \`\`\`
182
537
 
183
538
  Your API will be available at \`http://localhost:8000\`
@@ -210,7 +565,7 @@ Each generated demo project contains:
210
565
  1. **Explore the Generated Code** - Check out \`src/main.py\` and \`src/routing/\`
211
566
  2. **Add Routes** - Create new endpoints in \`src/routing/\`
212
567
  3. **Install Full RapidKit** - For advanced features: \`pipx install rapidkit\`
213
- 4. **Read the Documentation** - Visit [RapidKit Docs](https://rapidkit.dev)
568
+ 4. **Read the Documentation** - Visit [RapidKit Docs](https://rapidkit.top)
214
569
 
215
570
  ## \u26A0\uFE0F Demo Mode Limitations
216
571
 
@@ -245,7 +600,7 @@ ${e}/
245
600
  ---
246
601
 
247
602
  **Generated with RapidKit** | [GitHub](https://github.com/getrapidkit/rapidkit-npm)
248
- `;if(await promises.writeFile(y.join(t,"README.md"),p,"utf-8"),r.succeed("Demo workspace setup complete"),!o){r.start("Initializing git repository");try{await execa("git",["init"],{cwd:t}),await h.outputFile(y.join(t,".gitignore"),`# Dependencies
603
+ `;if(await promises.writeFile(h.join(t,"README.md"),d,"utf-8"),r.succeed("Demo workspace setup complete"),!o){r.start("Initializing git repository");try{await execa("git",["init"],{cwd:t}),await y.outputFile(h.join(t,".gitignore"),`# Dependencies
249
604
  node_modules/
250
605
 
251
606
  # Generated projects
@@ -261,7 +616,7 @@ __pycache__/
261
616
  `,"utf-8"),await execa("git",["add","."],{cwd:t}),await execa("git",["commit","-m","Initial commit: Demo workspace"],{cwd:t}),r.succeed("Git repository initialized");}catch{r.warn("Could not initialize git repository");}}console.log(i.green(`
262
617
  \u2728 Demo workspace created successfully!
263
618
  `)),console.log(i.cyan("\u{1F4C2} Location:"),i.white(t)),console.log(i.cyan(`\u{1F680} Get started:
264
- `)),console.log(i.white(` cd ${e}`)),console.log(i.white(" node generate-demo.js my-api")),console.log(i.white(" cd my-api")),console.log(i.white(" poetry install")),console.log(i.white(" poetry run python -m src.main")),console.log(),console.log(i.yellow("\u{1F4A1} Note:"),"This is a demo workspace. For full RapidKit features:"),console.log(i.cyan(" pipx install rapidkit")),console.log();}catch(n){throw r.fail("Failed to create demo workspace"),n}}async function le(t,e,o,r){console.log(i.cyan(`
619
+ `)),console.log(i.white(` cd ${e}`)),console.log(i.white(" node generate-demo.js my-api")),console.log(i.white(" cd my-api")),console.log(i.white(" rapidkit init")),console.log(i.white(" rapidkit dev")),console.log(),console.log(i.yellow("\u{1F4A1} Note:"),"This is a demo workspace. For full RapidKit features:"),console.log(i.cyan(" pipx install rapidkit")),console.log();}catch(a){throw r.fail("Failed to create demo workspace"),a}}async function ce(t,e,o,r){console.log(i.cyan(`
265
620
  \u{1F50D} Dry-run mode - showing what would be created:
266
621
  `)),console.log(i.white("\u{1F4C2} Project path:"),t),console.log(i.white("\u{1F4E6} Project type:"),o?"Demo workspace":"Full RapidKit environment"),o?(console.log(i.white(`
267
622
  \u{1F4DD} Files to create:`)),console.log(i.gray(" - package.json")),console.log(i.gray(" - generate-demo.js (project generator)")),console.log(i.gray(" - README.md")),console.log(i.gray(" - .gitignore")),console.log(i.white(`
@@ -270,7 +625,7 @@ __pycache__/
270
625
  \u{1F4DD} Files to create:`)),console.log(i.gray(" - pyproject.toml (Poetry) or .venv/ (venv)")),console.log(i.gray(" - README.md")),console.log(i.gray(" - .gitignore")),console.log(i.white(`
271
626
  \u{1F3AF} Next steps after creation:`)),console.log(i.gray(" 1. Install RapidKit Python package")),console.log(i.gray(" 2. Create projects with rapidkit CLI")),console.log(i.gray(" 3. Add modules and customize"))),console.log(i.white(`
272
627
  \u{1F4A1} To proceed with actual creation, run without --dry-run flag
273
- `));}var me=fileURLToPath(import.meta.url),ge=y.dirname(me);async function Y(t,e){let o=W("Generating FastAPI demo project...").start();try{let r=y.resolve(ge,".."),n=y.join(r,"templates","kits","fastapi-standard"),m=ce.configure(n,{autoescape:!1,trimBlocks:!0,lstripBlocks:!0}),p={project_name:e.project_name,author:e.author||"RapidKit User",description:e.description||"FastAPI service generated with RapidKit",app_version:e.app_version||"0.1.0",license:e.license||"MIT"},a=["src/main.py.j2","src/__init__.py.j2","src/cli.py.j2","src/routing/__init__.py.j2","src/routing/health.py.j2","src/modules/__init__.py.j2","tests/__init__.py.j2","README.md.j2","pyproject.toml.j2"];for(let f of a){let u=y.join(n,f),v=await promises.readFile(u,"utf-8"),C=m.renderString(v,p),D=f.replace(/\.j2$/,""),O=y.join(t,D);await promises.mkdir(y.dirname(O),{recursive:!0}),await promises.writeFile(O,C);}await promises.writeFile(y.join(t,".gitignore"),`# Python
628
+ `));}var me=fileURLToPath(import.meta.url),ue=h.dirname(me);async function q(t,e){let o=z("Generating FastAPI demo project...").start();try{let r=h.resolve(ue,".."),a=h.join(r,"templates","kits","fastapi-standard"),m=le.configure(a,{autoescape:!1,trimBlocks:!0,lstripBlocks:!0}),d={project_name:e.project_name,author:e.author||"RapidKit User",description:e.description||"FastAPI service generated with RapidKit",app_version:e.app_version||"0.1.0",license:e.license||"MIT"},n=["src/main.py.j2","src/__init__.py.j2","src/cli.py.j2","src/routing/__init__.py.j2","src/routing/health.py.j2","src/modules/__init__.py.j2","tests/__init__.py.j2","README.md.j2","pyproject.toml.j2",".rapidkit/project.json.j2",".rapidkit/cli.py.j2",".rapidkit/rapidkit.j2"];for(let f of n){let g=h.join(a,f),v=await promises.readFile(g,"utf-8"),K=m.renderString(v,d),b=f.replace(/\.j2$/,""),D=h.join(t,b);await promises.mkdir(h.dirname(D),{recursive:!0}),await promises.writeFile(D,K),(b===".rapidkit/rapidkit"||b===".rapidkit/cli.py")&&await promises.chmod(D,493);}await promises.writeFile(h.join(t,".gitignore"),`# Python
274
629
  __pycache__/
275
630
  *.py[cod]
276
631
  *$py.class
@@ -317,6 +672,10 @@ ${i.green("\u2728 Demo project created successfully!")}
317
672
 
318
673
  ${i.bold("\u{1F4C2} Project structure:")}
319
674
  ${t}/
675
+ \u251C\u2500\u2500 .rapidkit/ # RapidKit project config
676
+ \u2502 \u251C\u2500\u2500 project.json # Project metadata
677
+ \u2502 \u251C\u2500\u2500 cli.py # Local CLI handler
678
+ \u2502 \u2514\u2500\u2500 rapidkit # Local launcher
320
679
  \u251C\u2500\u2500 src/
321
680
  \u2502 \u251C\u2500\u2500 main.py # FastAPI application
322
681
  \u2502 \u251C\u2500\u2500 cli.py # CLI commands
@@ -327,29 +686,32 @@ ${t}/
327
686
  \u2514\u2500\u2500 README.md
328
687
 
329
688
  ${i.bold("\u{1F680} Get started:")}
330
- cd ${y.basename(t)}
689
+ cd ${h.basename(t)}
331
690
  poetry install
332
- poetry run python -m src.main
691
+ rapidkit dev # or: poetry run python -m src.main
333
692
 
334
- ${i.bold("\u{1F4DA} Next steps:")}
335
- \u2022 Add RapidKit modules: poetry add rapidkit
336
- \u2022 Read the README.md for more information
337
- \u2022 Start building your API!
693
+ ${i.bold("\u{1F4DA} Available commands:")}
694
+ rapidkit init # Install dependencies
695
+ rapidkit dev # Start dev server with hot reload
696
+ rapidkit start # Start production server
697
+ rapidkit test # Run tests
698
+ rapidkit lint # Lint code
699
+ rapidkit format # Format code
338
700
 
339
701
  ${i.yellow("Note:")} This is a standalone demo. For full RapidKit features and modules,
340
702
  install RapidKit Python package: ${i.cyan("pipx install rapidkit")}
341
- `);}catch(r){throw o.fail("Failed to generate demo project"),r}}var ve="rapidkit",ke=fileURLToPath(import.meta.url),Re=dirname(ke),Pe=join(Re,"..","package.json"),be=JSON.parse(readFileSync(Pe,"utf-8")),L=be.version;async function H(){try{s.debug("Checking for updates...");let{stdout:t}=await execa("npm",["view",ve,"version"],{timeout:3e3}),e=t.trim();e&&e!==L?(console.log(i.yellow(`
703
+ `);}catch(r){throw o.fail("Failed to generate demo project"),r}}var ve="rapidkit",ke=fileURLToPath(import.meta.url),_e=dirname(ke),Re=join(_e,"..","package.json"),be=JSON.parse(readFileSync(Re,"utf-8")),L=be.version;async function H(){try{s.debug("Checking for updates...");let{stdout:t}=await execa("npm",["view",ve,"version"],{timeout:3e3}),e=t.trim();e&&e!==L?(console.log(i.yellow(`
342
704
  \u26A0\uFE0F Update available: ${L} \u2192 ${e}`)),console.log(i.cyan(`Run: npm install -g rapidkit@latest
343
- `))):s.debug("You are using the latest version");}catch{s.debug("Could not check for updates");}}function J(){return L}function V(t){let e=je(t);if(!e.validForNewPackages){let r=e.errors||[],n=e.warnings||[],m=[...r,...n];throw new k(t,`NPM validation failed: ${m.join(", ")}`)}if(!/^[a-z][a-z0-9_-]*$/.test(t))throw new k(t,"Must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores");if(["test","tests","src","dist","build","lib","python","pip","poetry","node","npm","rapidkit","rapidkit"].includes(t.toLowerCase()))throw new k(t,`"${t}" is a reserved name. Please choose a different name.`);if(t.length<2)throw new k(t,"Name must be at least 2 characters long");if(t.length>214)throw new k(t,"Name must be less than 214 characters");return true}var w=null,M=false,X=new Command;X.name("rapidkit").description("Create a RapidKit development environment or workspace").version(J()).argument("[directory-name]","Name of the workspace or project directory").option("--skip-git","Skip git initialization").option("--test-mode","Install RapidKit from local path (for development/testing only)").option("--demo","Create workspace with demo kit templates (no Python installation required)").option("--demo-only","Generate a demo project in current directory (used by demo workspace)").option("--debug","Enable debug logging").option("--dry-run","Show what would be created without creating it").option("--no-update-check","Skip checking for updates").action(async(t,e)=>{try{e.debug&&(s.setDebug(!0),s.debug("Debug mode enabled"));let o=await z();if(s.debug("User config loaded",o),e.updateCheck!==!1&&await H(),console.log(i.blue.bold(`
705
+ `))):s.debug("You are using the latest version");}catch{s.debug("Could not check for updates");}}function J(){return L}function V(t){let e=Pe(t);if(!e.validForNewPackages){let r=e.errors||[],a=e.warnings||[],m=[...r,...a];throw new k(t,`NPM validation failed: ${m.join(", ")}`)}if(!/^[a-z][a-z0-9_-]*$/.test(t))throw new k(t,"Must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores");if(["test","tests","src","dist","build","lib","python","pip","poetry","node","npm","rapidkit","rapidkit"].includes(t.toLowerCase()))throw new k(t,`"${t}" is a reserved name. Please choose a different name.`);if(t.length<2)throw new k(t,"Name must be at least 2 characters long");if(t.length>214)throw new k(t,"Name must be less than 214 characters");return true}var w=null,O=false,Q=new Command;Q.name("rapidkit").description("Create a RapidKit development environment or workspace").version(J()).argument("[directory-name]","Name of the workspace or project directory").option("--skip-git","Skip git initialization").option("--test-mode","Install RapidKit from local path (for development/testing only)").option("--demo","Create workspace with demo kit templates (no Python installation required)").option("--demo-only","Generate a demo project in current directory (used by demo workspace)").option("--debug","Enable debug logging").option("--dry-run","Show what would be created without creating it").option("--no-update-check","Skip checking for updates").action(async(t,e)=>{try{e.debug&&(s.setDebug(!0),s.debug("Debug mode enabled"));let o=await W();if(s.debug("User config loaded",o),e.updateCheck!==!1&&await H(),console.log(i.blue.bold(`
344
706
  \u{1F680} Welcome to RapidKit!
345
- `)),e.demoOnly){let n=t||"my-fastapi-project";try{V(n);}catch(a){throw a instanceof g&&(s.error(`
346
- \u274C ${a.message}`),a.details&&s.warn(`\u{1F4A1} ${a.details}
347
- `),process.exit(1)),a}let m=y.resolve(process.cwd(),n);if(w=m,e.dryRun){console.log(i.cyan(`
707
+ `)),e.demoOnly){let a=t||"my-fastapi-project";try{V(a);}catch(n){throw n instanceof u&&(s.error(`
708
+ \u274C ${n.message}`),n.details&&s.warn(`\u{1F4A1} ${n.details}
709
+ `),process.exit(1)),n}let m=h.resolve(process.cwd(),a);if(w=m,e.dryRun){console.log(i.cyan(`
348
710
  \u{1F50D} Dry-run mode - showing what would be created:
349
711
  `)),console.log(i.white("\u{1F4C2} Project path:"),m),console.log(i.white("\u{1F4E6} Project type:"),"FastAPI demo project"),console.log(i.white("\u{1F4DD} Files to create:")),console.log(i.gray(" - src/main.py")),console.log(i.gray(" - src/cli.py")),console.log(i.gray(" - src/routing/")),console.log(i.gray(" - tests/")),console.log(i.gray(" - pyproject.toml")),console.log(i.gray(` - README.md
350
- `));return}let p=await oe.prompt([{type:"input",name:"project_name",message:"Project name (snake_case):",default:n.replace(/-/g,"_"),validate:a=>/^[a-z][a-z0-9_]*$/.test(a)?!0:"Please use snake_case (lowercase with underscores)"},{type:"input",name:"author",message:"Author name:",default:process.env.USER||"RapidKit User"},{type:"input",name:"description",message:"Project description:",default:"FastAPI service generated with RapidKit"}]);await Y(m,p);return}let r=t||"rapidkit-workspace";try{V(r);}catch(n){throw n instanceof g&&(s.error(`
351
- \u274C ${n.message}`),n.details&&s.warn(`\u{1F4A1} ${n.details}
352
- `),process.exit(1)),n}if(w=y.resolve(process.cwd(),r),e.demo){console.log(i.gray(`This will create a workspace with demo kit templates.
712
+ `));return}let d=await oe.prompt([{type:"input",name:"project_name",message:"Project name (snake_case):",default:a.replace(/-/g,"_"),validate:n=>/^[a-z][a-z0-9_]*$/.test(n)?!0:"Please use snake_case (lowercase with underscores)"},{type:"input",name:"author",message:"Author name:",default:process.env.USER||"RapidKit User"},{type:"input",name:"description",message:"Project description:",default:"FastAPI service generated with RapidKit"}]);await q(m,d);return}let r=t||"rapidkit-workspace";try{V(r);}catch(a){throw a instanceof u&&(s.error(`
713
+ \u274C ${a.message}`),a.details&&s.warn(`\u{1F4A1} ${a.details}
714
+ `),process.exit(1)),a}if(w=h.resolve(process.cwd(),r),e.demo){console.log(i.gray(`This will create a workspace with demo kit templates.
353
715
  You can generate demo projects inside without installing Python RapidKit.
354
716
  `)),await U(r,{skipGit:e.skipGit||o.skipGit,testMode:!1,demoMode:!0,dryRun:e.dryRun,userConfig:o});return}console.log(i.yellow.bold(`\u26A0\uFE0F BETA NOTICE
355
717
  `)),console.log(i.yellow(`RapidKit Python package is not yet available on PyPI.
@@ -359,8 +721,8 @@ Full installation mode will be available soon.
359
721
  `)),console.log(i.white(" 2. Test mode (if you have local RapidKit):")),console.log(i.gray(` npx rapidkit my-workspace --test-mode
360
722
  `)),e.testMode||(console.log(i.red(`\u274C Cannot proceed without --demo or --test-mode flag.
361
723
  `)),process.exit(1)),console.log(i.yellow(`\u26A0\uFE0F Running in TEST MODE - Installing from local path
362
- `)),await U(r,{skipGit:e.skipGit||o.skipGit,testMode:e.testMode,demoMode:!1,dryRun:e.dryRun,userConfig:o});}catch(o){o instanceof g?(s.error(`
724
+ `)),await U(r,{skipGit:e.skipGit||o.skipGit,testMode:e.testMode,demoMode:!1,dryRun:e.dryRun,userConfig:o});}catch(o){o instanceof u?(s.error(`
363
725
  \u274C ${o.message}`),o.details&&s.warn(`\u{1F4A1} ${o.details}`),s.debug("Error code:",o.code)):(s.error(`
364
- \u274C An unexpected error occurred:`),console.error(o)),process.exit(1);}finally{w=null;}});process.on("SIGINT",async()=>{if(!M){if(M=true,console.log(i.yellow(`
726
+ \u274C An unexpected error occurred:`),console.error(o)),process.exit(1);}finally{w=null;}});process.on("SIGINT",async()=>{if(!O){if(O=true,console.log(i.yellow(`
365
727
 
366
- \u26A0\uFE0F Interrupted by user`)),w&&await h.pathExists(w)){console.log(i.gray("Cleaning up partial installation..."));try{await h.remove(w),console.log(i.green("\u2713 Cleanup complete"));}catch(t){s.debug("Cleanup failed:",t);}}process.exit(130);}});process.on("SIGTERM",async()=>{if(!M){if(M=true,s.debug("Received SIGTERM"),w&&await h.pathExists(w))try{await h.remove(w);}catch(t){s.debug("Cleanup failed:",t);}process.exit(143);}});X.parse();
728
+ \u26A0\uFE0F Interrupted by user`)),w&&await y.pathExists(w)){console.log(i.gray("Cleaning up partial installation..."));try{await y.remove(w),console.log(i.green("\u2713 Cleanup complete"));}catch(t){s.debug("Cleanup failed:",t);}}process.exit(130);}});process.on("SIGTERM",async()=>{if(!O){if(O=true,s.debug("Received SIGTERM"),w&&await y.pathExists(w))try{await y.remove(w);}catch(t){s.debug("Cleanup failed:",t);}process.exit(143);}});Q.parse();
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapidkit",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "type": "module",
5
5
  "description": "Create RapidKit projects with a single command - The official CLI for RapidKit framework",
6
6
  "keywords": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapidkit",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "type": "module",
5
5
  "description": "Create RapidKit projects with a single command - The official CLI for RapidKit framework",
6
6
  "keywords": [
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env python3
2
+ """RapidKit CLI wrapper (npm demo template)
3
+
4
+ This file provides local project commands for demo projects generated
5
+ via npx rapidkit --demo. It mimics the behavior of the full RapidKit
6
+ engine for dev, start, test, lint, and format commands.
7
+ """
8
+
9
+ import subprocess
10
+ import shutil
11
+ import sys
12
+ from pathlib import Path
13
+ import socket
14
+ from typing import Any
15
+
16
+
17
+ def _print_banner(emoji: str, message: str) -> None:
18
+ print(f"{emoji} {message}")
19
+
20
+
21
+ def _run(*cmd: Any, cwd: Path | str | None = None, env: dict | None = None) -> int:
22
+ try:
23
+ run_env = None
24
+ if env is not None:
25
+ import os as _os
26
+ run_env = _os.environ.copy()
27
+ run_env.update(env)
28
+
29
+ proc = subprocess.run([str(c) for c in cmd], cwd=str(cwd) if cwd else None, env=run_env)
30
+ return proc.returncode
31
+ except KeyboardInterrupt:
32
+ print("\n🛑 Command interrupted by user")
33
+ return 130
34
+ except Exception as exc:
35
+ print(f"❌ Error running command: {exc}")
36
+ return 1
37
+
38
+
39
+ def _project_type(root: Path) -> str:
40
+ if (root / "pyproject.toml").exists():
41
+ return "python"
42
+ if (root / "package.json").exists():
43
+ return "node"
44
+ return "unknown"
45
+
46
+
47
+ def _python_module(module: str, *module_args: str) -> int:
48
+ return _run(sys.executable, "-m", module, *module_args)
49
+
50
+
51
+ def _python_code_targets(root: Path) -> list[str]:
52
+ targets: list[str] = []
53
+ for name in ("src", "tests"):
54
+ if (root / name).exists():
55
+ targets.append(name)
56
+ return targets or ["src"]
57
+
58
+
59
+ def _venv_has_uvicorn(venv_path: Path) -> bool:
60
+ if (venv_path / "bin" / "uvicorn").exists():
61
+ return True
62
+ for p in venv_path.rglob("site-packages/*"):
63
+ name = p.name.lower()
64
+ if "uvicorn" in name or "fastapi" in name:
65
+ return True
66
+ return False
67
+
68
+
69
+ def dev(port: int = 8000, host: str = "0.0.0.0", allow_global_runtime: bool = False) -> None:
70
+ """Start development server with reload"""
71
+ _print_banner("🚀", "Starting development server with hot reload...")
72
+ root = Path.cwd()
73
+ ptype = _project_type(root)
74
+
75
+ if ptype == "python":
76
+ venv_dir = root / ".venv"
77
+
78
+ if venv_dir.exists():
79
+ if not _venv_has_uvicorn(venv_dir):
80
+ print("❌ Project .venv was found but uvicorn/fastapi doesn't appear installed.")
81
+ print("💡 Run 'poetry install' to install dependencies.")
82
+ sys.exit(1)
83
+ else:
84
+ if not allow_global_runtime:
85
+ print("❌ Project environment not bootstrapped (no .venv found).")
86
+ print("")
87
+ print("💡 Initialize and install dependencies with:")
88
+ print("")
89
+ print(" poetry install")
90
+ print("")
91
+ print("If you intentionally want to use the system Python, run:")
92
+ print("")
93
+ print(" rapidkit dev --allow-global-runtime")
94
+ print("")
95
+ sys.exit(1)
96
+
97
+ if shutil.which("uvicorn") is None:
98
+ print("❌ No uvicorn executable found on PATH and no .venv present.")
99
+ print("")
100
+ print("💡 Install dependencies with: poetry install")
101
+ print("")
102
+ sys.exit(1)
103
+ print("⚠️ Running with system/global Python runtime.")
104
+
105
+ rc = _run(sys.executable, "-m", "uvicorn", "src.main:app", "--reload", "--host", host, "--port", str(port))
106
+ if rc != 0:
107
+ sys.exit(rc)
108
+ else:
109
+ print("❌ Unknown project type. Ensure pyproject.toml exists.")
110
+ sys.exit(1)
111
+
112
+
113
+ def init() -> None:
114
+ """Bootstrap project: install dependencies"""
115
+ _print_banner("🚀", "Bootstrapping project (installing dependencies)")
116
+ root = Path.cwd()
117
+ ptype = _project_type(root)
118
+
119
+ if ptype == "python":
120
+ if shutil.which("poetry"):
121
+ rc = _run("poetry", "install")
122
+ if rc != 0:
123
+ print("❌ Failed to install dependencies via poetry.")
124
+ sys.exit(rc)
125
+ print("✅ Dependencies installed successfully!")
126
+ else:
127
+ print("❌ Poetry not found. Install it with: pip install poetry")
128
+ sys.exit(1)
129
+ else:
130
+ print("❌ Unknown project type. Ensure pyproject.toml exists.")
131
+ sys.exit(1)
132
+
133
+
134
+ def start(port: int = 8000, host: str = "0.0.0.0", allow_global_runtime: bool = False) -> None:
135
+ """Start production server"""
136
+ _print_banner("⚡", "Starting production server...")
137
+ root = Path.cwd()
138
+ ptype = _project_type(root)
139
+
140
+ if ptype == "python":
141
+ venv_dir = root / ".venv"
142
+ if venv_dir.exists():
143
+ if not _venv_has_uvicorn(venv_dir):
144
+ print("❌ Project .venv was found but uvicorn/fastapi doesn't appear installed.")
145
+ print("💡 Run 'poetry install' to install dependencies.")
146
+ sys.exit(1)
147
+ else:
148
+ if not allow_global_runtime:
149
+ print("❌ Project environment not bootstrapped (no .venv found).")
150
+ print("💡 Run 'poetry install' to install dependencies.")
151
+ sys.exit(1)
152
+
153
+ rc = _run(sys.executable, "-m", "uvicorn", "src.main:app", "--host", host, "--port", str(port))
154
+ if rc != 0:
155
+ sys.exit(rc)
156
+ else:
157
+ print("❌ Unknown project type.")
158
+ sys.exit(1)
159
+
160
+
161
+ def build() -> None:
162
+ """Build project"""
163
+ _print_banner("📦", "Building project")
164
+ rc = _python_module("build")
165
+ if rc != 0:
166
+ sys.exit(rc)
167
+
168
+
169
+ def test() -> None:
170
+ """Run tests"""
171
+ _print_banner("🧪", "Running tests")
172
+ rc = _python_module("pytest", "-q")
173
+ if rc != 0:
174
+ sys.exit(rc)
175
+
176
+
177
+ def lint() -> None:
178
+ """Run linting"""
179
+ _print_banner("🔧", "Running lint")
180
+ root = Path.cwd()
181
+ targets = _python_code_targets(root)
182
+ rc = _python_module("ruff", "check", *targets)
183
+ if rc == 0:
184
+ rc = _python_module("black", "--check", *targets)
185
+ if rc != 0:
186
+ sys.exit(rc)
187
+
188
+
189
+ def format_code() -> None:
190
+ """Format code"""
191
+ _print_banner("✨", "Formatting")
192
+ root = Path.cwd()
193
+ targets = _python_code_targets(root)
194
+ rc = _python_module("ruff", "check", *targets, "--fix")
195
+ if rc == 0:
196
+ rc = _python_module("black", *targets)
197
+ if rc != 0:
198
+ sys.exit(rc)
199
+
200
+
201
+ def help_cmd() -> None:
202
+ """Show help"""
203
+ _print_banner("📚", "Project Commands")
204
+ print("Usage: rapidkit <command> [args...]\n")
205
+ print(" init 📦 Initialize project (install deps)")
206
+ print(" dev 🚀 Start development server")
207
+ print(" start ⚡ Start production server")
208
+ print(" build 📦 Build for production")
209
+ print(" test 🧪 Run tests")
210
+ print(" lint 🔧 Lint code")
211
+ print(" format ✨ Format code")
212
+ print(" help 📚 Show help")
213
+ print("")
214
+ print("💡 Note: This is a demo project. For full RapidKit features:")
215
+ print(" pipx install rapidkit")
216
+
217
+
218
+ def main():
219
+ if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help", "help"):
220
+ help_cmd()
221
+ return
222
+
223
+ command = sys.argv[1]
224
+ args = sys.argv[2:]
225
+
226
+ commands = {
227
+ "init": init,
228
+ "dev": dev,
229
+ "start": start,
230
+ "build": build,
231
+ "test": test,
232
+ "lint": lint,
233
+ "format": format_code,
234
+ "help": help_cmd,
235
+ }
236
+
237
+ if command not in commands:
238
+ print(f"❌ Unknown command: {command}")
239
+ help_cmd()
240
+ sys.exit(1)
241
+
242
+ if command in ("dev", "start"):
243
+ import argparse as _arg
244
+ _parser = _arg.ArgumentParser(prog=f"rapidkit {command}")
245
+ _parser.add_argument("-p", "--port", dest="port", type=int, default=8000)
246
+ _parser.add_argument("--host", dest="host", default="0.0.0.0")
247
+ _parser.add_argument("--allow-global-runtime", action="store_true", dest="allow_global_runtime")
248
+ _ns, _extra = _parser.parse_known_args(args)
249
+ commands[command](port=_ns.port, host=_ns.host, allow_global_runtime=_ns.allow_global_runtime)
250
+ else:
251
+ commands[command]()
252
+
253
+
254
+ if __name__ == "__main__":
255
+ main()
@@ -0,0 +1,6 @@
1
+ {
2
+ "kit_name": "fastapi.standard",
3
+ "profile": "fastapi/standard",
4
+ "created_at": "{{ created_at | default(now) }}",
5
+ "rapidkit_version": "npm-demo"
6
+ }
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ # Local RapidKit project launcher (npm demo template)
3
+ # This script provides local project commands for demo projects.
4
+
5
+ set -euo pipefail
6
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
7
+
8
+ # Detect project type
9
+ if [ -f "$ROOT_DIR/pyproject.toml" ]; then
10
+ PKG_TYPE="python"
11
+ elif [ -f "$ROOT_DIR/package.json" ]; then
12
+ PKG_TYPE="node"
13
+ else
14
+ PKG_TYPE="unknown"
15
+ fi
16
+
17
+ VENV_PY="${ROOT_DIR}/.venv/bin/python"
18
+ VENV_POETRY="${ROOT_DIR}/.venv/bin/poetry"
19
+
20
+ case "$PKG_TYPE" in
21
+ python)
22
+ # prefer project-local poetry
23
+ if [ -x "$VENV_POETRY" ]; then
24
+ if [ "${1:-}" = "init" ]; then
25
+ exec "$VENV_POETRY" "install"
26
+ else
27
+ exec "$VENV_POETRY" "run" "$@"
28
+ fi
29
+ fi
30
+
31
+ # then prefer poetry on PATH
32
+ if command -v poetry >/dev/null 2>&1; then
33
+ if [ "${1:-}" = "init" ]; then
34
+ exec poetry "install"
35
+ else
36
+ exec poetry "run" "$@"
37
+ fi
38
+ fi
39
+
40
+ # If venv python exists, try installing poetry into it
41
+ if [ -x "$VENV_PY" ]; then
42
+ echo "⚠️ Poetry not found. Installing poetry into project .venv..."
43
+ "$VENV_PY" -m pip install --upgrade pip >/dev/null 2>&1 || true
44
+ "$VENV_PY" -m pip install poetry >/dev/null 2>&1 || true
45
+ if [ -x "$VENV_POETRY" ]; then
46
+ if [ "${1:-}" = "init" ]; then
47
+ exec "$VENV_POETRY" "install"
48
+ else
49
+ exec "$VENV_POETRY" "run" "$@"
50
+ fi
51
+ else
52
+ echo "❌ Failed to locate poetry after installation."
53
+ exit 1
54
+ fi
55
+ fi
56
+
57
+ echo "❌ Poetry is not available and no project .venv exists."
58
+ echo "💡 Run: 'poetry install' to bootstrap the project, or install poetry: 'pip install poetry'"
59
+ exit 127
60
+ ;;
61
+
62
+ *)
63
+ echo "❌ Could not detect project type (pyproject.toml missing)."
64
+ echo "💡 This appears to be a demo project. Run 'poetry install' to set up."
65
+ exit 127
66
+ ;;
67
+ esac
@@ -5,17 +5,18 @@ A minimal FastAPI service generated with the **FastAPI Standard Kit**. All domai
5
5
  ## Quick start
6
6
 
7
7
  ```bash
8
- poetry install
9
- poetry run dev
8
+ rapidkit init # bootstrap the project (preferred)
9
+ rapidkit dev
10
10
  ```
11
11
 
12
12
  ## Available commands
13
13
 
14
14
  ```bash
15
- poetry run dev # 🚀 Start development server with hot reload
16
- poetry run start # Start production server
17
- poetry run build # 📦 Build project for production
18
- poetry run test # 🧪 Run tests
15
+ rapidkit init # 🔧 Install dependencies
16
+ rapidkit dev # 🚀 Start development server with hot reload
17
+ rapidkit start # Start production server
18
+ rapidkit test # 🧪 Run tests
19
+ rapidkit help # 📚 Show available commands
19
20
  ```
20
21
 
21
22
  ## Project layout
@@ -95,7 +95,8 @@ def dev() -> None:
95
95
  """🚀 Start development server with hot reload
96
96
 
97
97
  Usage:
98
- poetry run dev [options]
98
+ rapidkit dev [options] # preferred (project-aware)
99
+ # or: poetry run dev [options]
99
100
 
100
101
  Options:
101
102
  --port, -p PORT Server port (default: 8000)
@@ -103,9 +104,10 @@ def dev() -> None:
103
104
  --env, -e ENV Environment (default: development)
104
105
 
105
106
  Examples:
106
- poetry run dev
107
- poetry run dev --port 3000
108
- poetry run dev --host 127.0.0.1 --port 8080
107
+ rapidkit dev
108
+ rapidkit dev --port 3000
109
+ rapidkit dev --host 127.0.0.1 --port 8080
110
+ # or: poetry run dev --port 3000
109
111
  """
110
112
  _print_banner("🚀", "Starting development server with hot reload...")
111
113
  _print_banner("📁", f"Working directory: {Path.cwd()}")
@@ -175,7 +177,7 @@ def build() -> None:
175
177
  _print_error(f"Build failed: {e}")
176
178
  sys.exit(1)
177
179
  except FileNotFoundError:
178
- _print_error("Build tools not found. Install with: poetry install --group dev")
180
+ _print_error("Build tools not found. Install with: rapidkit init (or: poetry install --group dev)")
179
181
  sys.exit(1)
180
182
 
181
183
 
@@ -214,7 +216,7 @@ def test() -> None:
214
216
  sys.exit(result.returncode)
215
217
 
216
218
  except FileNotFoundError:
217
- _print_error("pytest not found. Install with: poetry install --group dev")
219
+ _print_error("pytest not found. Install with: rapidkit init (or: poetry install --group dev)")
218
220
  sys.exit(1)
219
221
 
220
222
 
@@ -247,7 +249,7 @@ def lint() -> None:
247
249
  _print_error("Linting failed! Run 'poetry run format' to fix issues.")
248
250
  sys.exit(1)
249
251
  except FileNotFoundError:
250
- _print_error("Linting tools not found. Install with: poetry install --group dev")
252
+ _print_error("Linting tools not found. Install with: rapidkit init (or: poetry install --group dev)")
251
253
  sys.exit(1)
252
254
 
253
255
 
@@ -280,7 +282,7 @@ def format() -> None:
280
282
  _print_error(f"Formatting failed: {e}")
281
283
  sys.exit(1)
282
284
  except FileNotFoundError:
283
- _print_error("Formatting tools not found. Install with: poetry install --group dev")
285
+ _print_error("Formatting tools not found. Install with: rapidkit init (or: poetry install --group dev)")
284
286
  sys.exit(1)
285
287
 
286
288
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from contextlib import asynccontextmanager
6
+ from typing import AsyncIterator
7
+
5
8
  from fastapi import FastAPI
6
9
  from fastapi.middleware.cors import CORSMiddleware
7
10
 
@@ -9,12 +12,24 @@ from fastapi.middleware.cors import CORSMiddleware
9
12
 
10
13
  from .routing import api_router
11
14
 
15
+
16
+ @asynccontextmanager
17
+ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
18
+ """Application lifespan context manager for startup/shutdown events."""
19
+ # Startup
20
+ # <<<inject:startup>>>
21
+ yield
22
+ # Shutdown
23
+ # <<<inject:shutdown>>>
24
+
25
+
12
26
  app = FastAPI(
13
27
  title="{{ project_name }}",
14
28
  description="{{ description }}",
15
29
  version="{{ app_version }}",
16
30
  docs_url="/docs",
17
31
  redoc_url="/redoc",
32
+ lifespan=lifespan,
18
33
  )
19
34
 
20
35
  app.add_middleware(
@@ -27,15 +42,3 @@ app.add_middleware(
27
42
 
28
43
  app.include_router(api_router, prefix="/api")
29
44
  # <<<inject:routes>>>
30
-
31
-
32
- @app.on_event("startup")
33
- async def _on_startup() -> None:
34
- """Execute startup hooks provided by modules."""
35
- # <<<inject:startup>>>
36
-
37
-
38
- @app.on_event("shutdown")
39
- async def _on_shutdown() -> None:
40
- """Execute shutdown hooks provided by modules."""
41
- # <<<inject:shutdown>>>
@@ -9,5 +9,5 @@ from .health import router as health_router
9
9
 
10
10
  api_router = APIRouter()
11
11
 
12
- api_router.include_router(health_router, prefix="/health", tags=["health"])
12
+ api_router.include_router(health_router)
13
13
  # <<<inject:router-mount>>>
@@ -4,10 +4,29 @@ from __future__ import annotations
4
4
 
5
5
  from fastapi import APIRouter
6
6
 
7
- router = APIRouter()
7
+ try:
8
+ from src.health.registry import (
9
+ list_registered_health_routes as _list_registered_health_routes,
10
+ )
11
+ except ImportError: # pragma: no cover - registry not generated yet
12
+ def _list_registered_health_routes(
13
+ prefix: str = "/api/health",
14
+ ) -> list[dict[str, str]]:
15
+ return []
16
+
17
+
18
+ router = APIRouter(prefix="/health", tags=["health"])
8
19
 
9
20
 
10
21
  @router.get("/", summary="Health check")
11
22
  async def heartbeat() -> dict[str, str]:
12
23
  """Return basic service heartbeat."""
24
+
13
25
  return {"status": "ok"}
26
+
27
+
28
+ @router.get("/modules", summary="Registered module health endpoints")
29
+ async def module_health_catalog() -> dict[str, list[dict[str, str]]]:
30
+ """Expose metadata for module-provided health routers."""
31
+
32
+ return {"routes": _list_registered_health_routes()}