quickscale-cli 0.61.0__tar.gz → 0.63.0__tar.gz

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.
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.1
2
+ Name: quickscale-cli
3
+ Version: 0.63.0
4
+ Summary: Command-line interface for QuickScale Django project generator
5
+ Home-page: https://github.com/Experto-AI/quickscale
6
+ License: Apache-2.0
7
+ Keywords: django,cli,project-generator,saas
8
+ Author: Experto AI
9
+ Author-email: victor@experto.ai
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: Django
13
+ Classifier: Framework :: Django :: 4.2
14
+ Classifier: Framework :: Django :: 5.0
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: Apache Software License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Dist: click (>=8.1.0,<9.0.0)
22
+ Requires-Dist: quickscale-core (>=0.63.0,<0.64.0)
23
+ Project-URL: Documentation, https://github.com/Experto-AI/quickscale/tree/main/docs
24
+ Project-URL: Repository, https://github.com/Experto-AI/quickscale
@@ -1,10 +1,9 @@
1
1
  [tool.poetry]
2
2
  name = "quickscale-cli"
3
- version = "0.61.0"
3
+ version = "0.63.0"
4
4
  description = "Command-line interface for QuickScale Django project generator"
5
5
  authors = ["Experto AI <victor@experto.ai>"]
6
6
  license = "Apache-2.0"
7
- readme = "README.md"
8
7
  keywords = ["django", "cli", "project-generator", "saas"]
9
8
  homepage = "https://github.com/Experto-AI/quickscale"
10
9
  repository = "https://github.com/Experto-AI/quickscale"
@@ -26,7 +25,7 @@ packages = [{include = "quickscale_cli", from = "src"}]
26
25
  [tool.poetry.dependencies]
27
26
  python = "^3.10"
28
27
  click = "^8.1.0"
29
- quickscale-core = "^0.61.0"
28
+ quickscale-core = "^0.63.0"
30
29
 
31
30
  [tool.poetry.group.dev.dependencies]
32
31
  pytest = "^7.4.0"
@@ -106,4 +105,6 @@ module = "tests.*"
106
105
  disallow_untyped_defs = false
107
106
  check_untyped_defs = false
108
107
 
109
-
108
+ [[tool.mypy.overrides]]
109
+ module = "quickscale_core.*"
110
+ ignore_missing_imports = true
@@ -1,2 +1,2 @@
1
1
  # Auto-generated by scripts/version_tool.sh
2
- __version__ = "0.61.0"
2
+ __version__ = "0.63.0"
@@ -0,0 +1,457 @@
1
+ """Module management commands for QuickScale CLI."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ import click
7
+
8
+ from quickscale_core.config import add_module, load_config, update_module_version
9
+ from quickscale_core.utils.git_utils import (
10
+ GitError,
11
+ check_remote_branch_exists,
12
+ is_git_repo,
13
+ is_working_directory_clean,
14
+ run_git_subtree_add,
15
+ run_git_subtree_pull,
16
+ run_git_subtree_push,
17
+ )
18
+
19
+ # Available modules
20
+ AVAILABLE_MODULES = ["auth", "billing", "teams"]
21
+
22
+
23
+ def configure_auth_module() -> dict[str, Any]:
24
+ """Interactive configuration for auth module"""
25
+ click.echo("\n⚙️ Configuring auth module...")
26
+ click.echo("Answer these questions to customize the authentication setup:\n")
27
+
28
+ config = {
29
+ "allow_registration": click.confirm("Enable user registration?", default=True),
30
+ "email_verification": click.prompt(
31
+ "Email verification",
32
+ type=click.Choice(["none", "optional", "mandatory"], case_sensitive=False),
33
+ default="none",
34
+ show_choices=True,
35
+ ),
36
+ "authentication_method": click.prompt(
37
+ "Authentication method",
38
+ type=click.Choice(["email", "username", "both"], case_sensitive=False),
39
+ default="email",
40
+ show_choices=True,
41
+ ),
42
+ }
43
+
44
+ return config
45
+
46
+
47
+ def apply_auth_configuration(project_path: Path, config: dict[str, Any]) -> None:
48
+ """Apply auth module configuration to project settings"""
49
+ settings_path = project_path / "settings.py"
50
+ urls_path = project_path / "urls.py"
51
+
52
+ if not settings_path.exists():
53
+ click.secho("⚠️ Warning: settings.py not found, skipping auto-configuration", fg="yellow")
54
+ return
55
+
56
+ # Read settings.py
57
+ with open(settings_path) as f:
58
+ settings_content = f.read()
59
+
60
+ # Check if already configured
61
+ if "quickscale_modules_auth" in settings_content:
62
+ click.echo("ℹ️ Auth module already configured in settings.py")
63
+ return
64
+
65
+ # Add required apps to INSTALLED_APPS
66
+ installed_apps_addition = """
67
+ # QuickScale Auth Module - Added by quickscale embed
68
+ INSTALLED_APPS += [
69
+ "django.contrib.sites", # Required by allauth
70
+ "allauth",
71
+ "allauth.account",
72
+ "quickscale_modules_auth",
73
+ ]
74
+
75
+ # Authentication Configuration
76
+ AUTHENTICATION_BACKENDS = [
77
+ "django.contrib.auth.backends.ModelBackend",
78
+ "allauth.account.auth_backends.AuthenticationBackend",
79
+ ]
80
+
81
+ # Custom User Model
82
+ AUTH_USER_MODEL = "quickscale_modules_auth.User"
83
+
84
+ # Site ID (required by django.contrib.sites)
85
+ SITE_ID = 1
86
+
87
+ # Allauth Settings
88
+ """
89
+
90
+ # Add configuration based on user choices
91
+ if config["authentication_method"] == "email":
92
+ installed_apps_addition += 'ACCOUNT_AUTHENTICATION_METHOD = "email"\n'
93
+ installed_apps_addition += "ACCOUNT_USERNAME_REQUIRED = False\n"
94
+ installed_apps_addition += "ACCOUNT_EMAIL_REQUIRED = True\n"
95
+ elif config["authentication_method"] == "username":
96
+ installed_apps_addition += 'ACCOUNT_AUTHENTICATION_METHOD = "username"\n'
97
+ installed_apps_addition += "ACCOUNT_USERNAME_REQUIRED = True\n"
98
+ installed_apps_addition += "ACCOUNT_EMAIL_REQUIRED = False\n"
99
+ else: # both
100
+ installed_apps_addition += 'ACCOUNT_AUTHENTICATION_METHOD = "email"\n'
101
+ installed_apps_addition += "ACCOUNT_USERNAME_REQUIRED = True\n"
102
+ installed_apps_addition += "ACCOUNT_EMAIL_REQUIRED = True\n"
103
+
104
+ installed_apps_addition += f'ACCOUNT_EMAIL_VERIFICATION = "{config["email_verification"]}"\n'
105
+ installed_apps_addition += f'ACCOUNT_ALLOW_REGISTRATION = {config["allow_registration"]}\n'
106
+ installed_apps_addition += (
107
+ 'ACCOUNT_ADAPTER = "quickscale_modules_auth.adapters.QuickscaleAccountAdapter"\n'
108
+ )
109
+ installed_apps_addition += (
110
+ 'ACCOUNT_SIGNUP_FORM_CLASS = "quickscale_modules_auth.forms.SignupForm"\n'
111
+ )
112
+ installed_apps_addition += 'LOGIN_REDIRECT_URL = "/accounts/profile/"\n'
113
+ installed_apps_addition += 'LOGOUT_REDIRECT_URL = "/"\n'
114
+ installed_apps_addition += "SESSION_COOKIE_AGE = 1209600 # 2 weeks\n"
115
+
116
+ # Append to settings.py
117
+ with open(settings_path, "a") as f:
118
+ f.write("\n" + installed_apps_addition)
119
+
120
+ click.secho(" ✅ Updated settings.py with auth configuration", fg="green")
121
+
122
+ # Update urls.py
123
+ if urls_path.exists():
124
+ with open(urls_path) as f:
125
+ urls_content = f.read()
126
+
127
+ if "quickscale_modules_auth" not in urls_content:
128
+ # Find urlpatterns and add auth URLs
129
+ if "urlpatterns = [" in urls_content:
130
+ urls_addition = (
131
+ ' path("accounts/", include("quickscale_modules_auth.urls")), # Auth URLs\n'
132
+ )
133
+ urls_content = urls_content.replace(
134
+ "urlpatterns = [", "urlpatterns = [\n" + urls_addition
135
+ )
136
+
137
+ with open(urls_path, "w") as f:
138
+ f.write(urls_content)
139
+
140
+ click.secho(" ✅ Updated urls.py with auth URLs", fg="green")
141
+
142
+ # Show configuration summary
143
+ click.echo("\n📋 Configuration applied:")
144
+ click.echo(f" • Registration: {'Enabled' if config['allow_registration'] else 'Disabled'}")
145
+ click.echo(f" • Email verification: {config['email_verification']}")
146
+ click.echo(f" • Authentication: {config['authentication_method']}")
147
+
148
+
149
+ MODULE_CONFIGURATORS = {
150
+ "auth": (configure_auth_module, apply_auth_configuration),
151
+ }
152
+
153
+
154
+ @click.command()
155
+ @click.option(
156
+ "--module",
157
+ required=True,
158
+ type=click.Choice(AVAILABLE_MODULES, case_sensitive=False),
159
+ help="Module name to embed",
160
+ )
161
+ @click.option(
162
+ "--remote",
163
+ default="https://github.com/Experto-AI/quickscale.git",
164
+ help="Git remote URL (default: QuickScale repository)",
165
+ )
166
+ def embed(module: str, remote: str) -> None:
167
+ r"""
168
+ Embed a QuickScale module into your project via git subtree.
169
+
170
+ \b
171
+ Examples:
172
+ quickscale embed --module auth
173
+ quickscale embed --module billing
174
+
175
+ \b
176
+ Available modules (v0.62.0 placeholders):
177
+ - auth: Authentication with django-allauth (placeholder)
178
+ - billing: Stripe integration with dj-stripe (placeholder)
179
+ - teams: Multi-tenancy and team management (placeholder)
180
+
181
+ \b
182
+ Note: In v0.62.0, modules contain only placeholder READMEs explaining
183
+ they're not yet implemented. Full implementations coming in v0.63.0+.
184
+ """
185
+ try:
186
+ # Validate git repository
187
+ if not is_git_repo():
188
+ click.secho("❌ Error: Not a git repository", fg="red", err=True)
189
+ click.echo("\n💡 Tip: Run 'git init' to initialize a git repository", err=True)
190
+ raise click.Abort()
191
+
192
+ # Check working directory is clean
193
+ if not is_working_directory_clean():
194
+ click.secho("❌ Error: Working directory has uncommitted changes", fg="red", err=True)
195
+ click.echo("\n💡 Tip: Commit or stash your changes before embedding modules", err=True)
196
+ raise click.Abort()
197
+
198
+ # Check if module already exists
199
+ module_path = Path.cwd() / "modules" / module
200
+ if module_path.exists():
201
+ click.secho(
202
+ f"❌ Error: Module '{module}' already exists at {module_path}", fg="red", err=True
203
+ )
204
+ click.echo("\n💡 Tip: Remove the existing module directory first", err=True)
205
+ raise click.Abort()
206
+
207
+ # Check if branch exists on remote
208
+ branch = f"splits/{module}-module"
209
+ click.echo(f"🔍 Checking if {branch} exists on remote...")
210
+
211
+ if not check_remote_branch_exists(remote, branch):
212
+ click.secho(
213
+ f"❌ Error: Module '{module}' is not yet implemented",
214
+ fg="red",
215
+ err=True,
216
+ )
217
+ click.echo(
218
+ f"\n💡 The '{module}' module infrastructure is ready but contains "
219
+ "only placeholder files.",
220
+ err=True,
221
+ )
222
+ click.echo(" Full implementation coming in v0.63.0+", err=True)
223
+ click.echo(f"\n📖 Branch '{branch}' does not exist on remote: {remote}", err=True)
224
+ raise click.Abort()
225
+
226
+ # Interactive module configuration (v0.63.0+)
227
+ config = {}
228
+ if module in MODULE_CONFIGURATORS:
229
+ configurator, applier = MODULE_CONFIGURATORS[module]
230
+ config = configurator()
231
+
232
+ # Embed module via git subtree
233
+ prefix = f"modules/{module}"
234
+ click.echo(f"\n📦 Embedding {module} module from {branch}...")
235
+
236
+ run_git_subtree_add(prefix=prefix, remote=remote, branch=branch, squash=True)
237
+
238
+ # Update configuration tracking
239
+ add_module(
240
+ module_name=module,
241
+ prefix=prefix,
242
+ branch=branch,
243
+ version="v0.63.0",
244
+ )
245
+
246
+ # Apply module-specific configuration
247
+ if module in MODULE_CONFIGURATORS and config:
248
+ _, applier = MODULE_CONFIGURATORS[module]
249
+ project_root = Path.cwd()
250
+ applier(project_root, config)
251
+
252
+ # Success message
253
+ click.secho(f"\n✅ Module '{module}' embedded successfully!", fg="green", bold=True)
254
+ click.echo(f" Location: {module_path}")
255
+ click.echo(f" Branch: {branch}")
256
+
257
+ # Module-specific next steps
258
+ click.echo("\n📋 Next steps:")
259
+ if module == "auth":
260
+ click.echo(f" 1. Review module code in modules/{module}/")
261
+ click.echo(" 2. Run migrations: python manage.py migrate")
262
+ click.echo(" 3. Create superuser (optional): python manage.py createsuperuser")
263
+ click.echo(" 4. Visit http://localhost:8000/accounts/login/")
264
+ click.echo("\n� Documentation: modules/auth/README.md")
265
+ else:
266
+ click.echo(f" 1. Review the module code in modules/{module}/")
267
+ click.echo(f" 2. Follow setup instructions in modules/{module}/README.md")
268
+ click.echo(" 3. Run migrations: python manage.py migrate")
269
+
270
+ except GitError as e:
271
+ click.secho(f"❌ Git error: {e}", fg="red", err=True)
272
+ raise click.Abort()
273
+ except Exception as e:
274
+ click.secho(f"❌ Unexpected error: {e}", fg="red", err=True)
275
+ raise click.Abort()
276
+
277
+
278
+ @click.command()
279
+ @click.option(
280
+ "--no-preview",
281
+ is_flag=True,
282
+ help="Skip diff preview before updating",
283
+ )
284
+ def update(no_preview: bool) -> None:
285
+ r"""
286
+ Update all installed QuickScale modules to their latest versions.
287
+
288
+ \b
289
+ Examples:
290
+ quickscale update # Update with diff preview
291
+ quickscale update --no-preview # Update without preview
292
+
293
+ \b
294
+ This command:
295
+ - Reads installed modules from .quickscale/config.yml
296
+ - Updates ONLY modules you've explicitly installed
297
+ - Shows a diff preview before updating (unless --no-preview)
298
+ - Updates the installed version in config after successful update
299
+ """
300
+ try:
301
+ # Validate git repository
302
+ if not is_git_repo():
303
+ click.secho("❌ Error: Not a git repository", fg="red", err=True)
304
+ click.echo("\n💡 Tip: This command must be run from a git repository", err=True)
305
+ raise click.Abort()
306
+
307
+ # Check working directory is clean
308
+ if not is_working_directory_clean():
309
+ click.secho("❌ Error: Working directory has uncommitted changes", fg="red", err=True)
310
+ click.echo("\n💡 Tip: Commit or stash your changes before updating modules", err=True)
311
+ raise click.Abort()
312
+
313
+ # Load configuration
314
+ config = load_config()
315
+
316
+ if not config.modules:
317
+ click.secho("✅ No modules installed. Nothing to update.", fg="green")
318
+ click.echo("\n💡 Tip: Install modules with 'quickscale embed --module <name>'")
319
+ return
320
+
321
+ # Show installed modules
322
+ click.echo(f"📦 Found {len(config.modules)} installed module(s):")
323
+ for name, info in config.modules.items():
324
+ click.echo(f" - {name} ({info.installed_version})")
325
+
326
+ if not no_preview:
327
+ click.echo("\n🔍 Preview mode: Changes will be shown before updating")
328
+
329
+ # Confirm update
330
+ if not click.confirm("\n❓ Continue with update?"):
331
+ click.echo("❌ Update cancelled")
332
+ return
333
+
334
+ # Update each module
335
+ for name, info in config.modules.items():
336
+ click.echo(f"\n📥 Updating {name} module...")
337
+
338
+ try:
339
+ output = run_git_subtree_pull(
340
+ prefix=info.prefix,
341
+ remote=config.default_remote,
342
+ branch=info.branch,
343
+ squash=True,
344
+ )
345
+
346
+ # Update version in config
347
+ update_module_version(name, "v0.62.0") # Placeholder version
348
+
349
+ click.secho(f"✅ Updated {name} successfully", fg="green")
350
+
351
+ if output and not no_preview:
352
+ click.echo("\n📋 Changes summary:")
353
+ click.echo(output[:500]) # Show first 500 chars
354
+
355
+ except GitError as e:
356
+ click.secho(f"❌ Failed to update {name}: {e}", fg="red", err=True)
357
+ click.echo(f"💡 Tip: Check for conflicts in modules/{name}/", err=True)
358
+ continue
359
+
360
+ click.secho("\n🎉 Module update complete!", fg="green", bold=True)
361
+
362
+ except GitError as e:
363
+ click.secho(f"❌ Git error: {e}", fg="red", err=True)
364
+ raise click.Abort()
365
+ except Exception as e:
366
+ click.secho(f"❌ Unexpected error: {e}", fg="red", err=True)
367
+ raise click.Abort()
368
+
369
+
370
+ @click.command()
371
+ @click.option(
372
+ "--module",
373
+ required=True,
374
+ type=click.Choice(AVAILABLE_MODULES, case_sensitive=False),
375
+ help="Module name to push changes for",
376
+ )
377
+ @click.option(
378
+ "--branch",
379
+ help="Feature branch name (default: feature/<module>-improvements)",
380
+ )
381
+ @click.option(
382
+ "--remote",
383
+ default="https://github.com/Experto-AI/quickscale.git",
384
+ help="Git remote URL (default: QuickScale repository)",
385
+ )
386
+ def push(module: str, branch: str, remote: str) -> None:
387
+ r"""
388
+ Push your local module changes to a feature branch for contribution.
389
+
390
+ \b
391
+ Examples:
392
+ quickscale push --module auth
393
+ quickscale push --module auth --branch feature/fix-email-validation
394
+
395
+ \b
396
+ Workflow:
397
+ 1. This command pushes your changes to a feature branch
398
+ 2. You'll need to create a pull request manually on GitHub
399
+ 3. Maintainers review and merge to main branch
400
+ 4. Auto-split updates the module's split branch
401
+
402
+ \b
403
+ Note: You must have write access to the repository to push.
404
+ For external contributions, fork the repository first.
405
+ """
406
+ try:
407
+ # Validate git repository
408
+ if not is_git_repo():
409
+ click.secho("❌ Error: Not a git repository", fg="red", err=True)
410
+ raise click.Abort()
411
+
412
+ # Check if module is installed
413
+ config = load_config()
414
+ if module not in config.modules:
415
+ click.secho(f"❌ Error: Module '{module}' is not installed", fg="red", err=True)
416
+ click.echo(
417
+ f"\n💡 Tip: Install the module first with 'quickscale embed --module {module}'",
418
+ err=True,
419
+ )
420
+ raise click.Abort()
421
+
422
+ module_info = config.modules[module]
423
+
424
+ # Default branch name
425
+ if not branch:
426
+ branch = f"feature/{module}-improvements"
427
+
428
+ # Show what will be pushed
429
+ click.echo(f"📤 Preparing to push changes for module: {module}")
430
+ click.echo(f" Local prefix: {module_info.prefix}")
431
+ click.echo(f" Target branch: {branch}")
432
+ click.echo(f" Remote: {remote}")
433
+
434
+ # Confirm push
435
+ if not click.confirm("\n❓ Continue with push?"):
436
+ click.echo("❌ Push cancelled")
437
+ return
438
+
439
+ # Push subtree
440
+ click.echo(f"\n🚀 Pushing to {branch}...")
441
+ run_git_subtree_push(prefix=module_info.prefix, remote=remote, branch=branch)
442
+
443
+ # Success message
444
+ click.secho("\n✅ Changes pushed successfully!", fg="green", bold=True)
445
+ click.echo("\n📋 Next steps:")
446
+ click.echo(" 1. Create a pull request on GitHub:")
447
+ click.echo(f" https://github.com/Experto-AI/quickscale/pull/new/{branch}")
448
+ click.echo(" 2. Describe your changes and submit for review")
449
+ click.echo(" 3. After merge, the split branch will auto-update")
450
+
451
+ except GitError as e:
452
+ click.secho(f"❌ Git error: {e}", fg="red", err=True)
453
+ click.echo("\n💡 Tip: Make sure you have write access to the repository", err=True)
454
+ raise click.Abort()
455
+ except Exception as e:
456
+ click.secho(f"❌ Unexpected error: {e}", fg="red", err=True)
457
+ raise click.Abort()
@@ -8,13 +8,14 @@ import quickscale_cli
8
8
  import quickscale_core
9
9
  from quickscale_cli.commands.deployment_commands import deploy
10
10
  from quickscale_cli.commands.development_commands import down, logs, manage, ps, shell, up
11
+ from quickscale_cli.commands.module_commands import embed, push, update
11
12
  from quickscale_core.generator import ProjectGenerator
12
13
 
13
14
 
14
15
  class InitCommand(click.Command):
15
16
  """Custom init command with enhanced error messages."""
16
17
 
17
- def parse_args(self, ctx, args):
18
+ def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
18
19
  """Override parse_args to provide better error messages."""
19
20
  try:
20
21
  return super().parse_args(ctx, args)
@@ -54,6 +55,11 @@ cli.add_command(ps)
54
55
  # Register deployment commands
55
56
  cli.add_command(deploy)
56
57
 
58
+ # Register module management commands
59
+ cli.add_command(embed)
60
+ cli.add_command(update)
61
+ cli.add_command(push)
62
+
57
63
 
58
64
  @cli.command(cls=InitCommand)
59
65
  @click.argument("project_name", required=True, metavar="PROJECT_NAME")
@@ -64,7 +70,7 @@ cli.add_command(deploy)
64
70
  help="Theme to use for the project (default: starter_html)",
65
71
  )
66
72
  def init(project_name: str, theme: str) -> None:
67
- """
73
+ r"""
68
74
  Generate a new Django project with production-ready configurations.
69
75
 
70
76
  \b
@@ -24,7 +24,8 @@ def get_railway_cli_version() -> str | None:
24
24
  """
25
25
  Get the current Railway CLI version string.
26
26
 
27
- Returns:
27
+ Returns
28
+ -------
28
29
  Version string (e.g., "4.0.0") or None if not installed or error
29
30
 
30
31
  """
@@ -51,7 +52,8 @@ def install_railway_cli() -> bool:
51
52
  """
52
53
  Install Railway CLI via npm.
53
54
 
54
- Returns:
55
+ Returns
56
+ -------
55
57
  True if installation successful, False otherwise
56
58
 
57
59
  """
@@ -71,7 +73,8 @@ def upgrade_railway_cli() -> bool:
71
73
  """
72
74
  Upgrade Railway CLI to the latest version via npm.
73
75
 
74
- Returns:
76
+ Returns
77
+ -------
75
78
  True if upgrade successful, False otherwise
76
79
 
77
80
  """
@@ -93,7 +96,8 @@ def login_railway_cli_browserless() -> bool:
93
96
 
94
97
  This will prompt the user to visit a URL and enter a pairing code.
95
98
 
96
- Returns:
99
+ Returns
100
+ -------
97
101
  True if login successful, False otherwise
98
102
 
99
103
  """
@@ -207,11 +211,13 @@ def run_railway_command(
207
211
  Execute Railway CLI command with error handling.
208
212
 
209
213
  Args:
214
+ ----
210
215
  args: Command arguments to pass to railway CLI
211
216
  timeout: Command timeout in seconds
212
217
  interactive: If True, run command interactively without capturing output
213
218
 
214
219
  Returns:
220
+ -------
215
221
  CompletedProcess result
216
222
 
217
223
  """
@@ -247,6 +253,7 @@ def set_railway_variable(key: str, value: str, service: str | None = None) -> bo
247
253
  Set Railway environment variable.
248
254
 
249
255
  Args:
256
+ ----
250
257
  key: Environment variable name
251
258
  value: Environment variable value
252
259
  service: Service name to set variable for (optional)
@@ -272,10 +279,12 @@ def set_railway_variables_batch(
272
279
  triggering a deployment for each variable.
273
280
 
274
281
  Args:
282
+ ----
275
283
  variables: Dictionary of environment variable names and values
276
284
  service: Service name to set variables for (optional)
277
285
 
278
286
  Returns:
287
+ -------
279
288
  Tuple of (success, failed_keys)
280
289
  - success: True if all variables were set successfully
281
290
  - failed_keys: List of variable keys that failed to set
@@ -327,9 +336,11 @@ def get_deployment_url(service: str | None = None) -> str | None:
327
336
  Get deployment URL from Railway project.
328
337
 
329
338
  Args:
339
+ ----
330
340
  service: Service name (unused - kept for compatibility)
331
341
 
332
342
  Note:
343
+ ----
333
344
  Railway CLI v4's `status` command does not accept --service flag.
334
345
  This function returns the first deployment URL found.
335
346
 
@@ -356,9 +367,11 @@ def get_app_service_name(project_name: str | None = None) -> str:
356
367
  Determine the app service name for Railway deployment.
357
368
 
358
369
  Args:
370
+ ----
359
371
  project_name: Project name (defaults to current directory name)
360
372
 
361
373
  Returns:
374
+ -------
362
375
  Service name to use for the app
363
376
 
364
377
  """
@@ -376,9 +389,11 @@ def generate_railway_domain(service: str) -> str | None:
376
389
  Generate Railway public domain for a service.
377
390
 
378
391
  Args:
392
+ ----
379
393
  service: Service name to generate domain for
380
394
 
381
395
  Returns:
396
+ -------
382
397
  Generated domain URL or None if failed
383
398
 
384
399
  """
@@ -412,7 +427,8 @@ def check_uncommitted_changes() -> tuple[bool, str]:
412
427
  """
413
428
  Check if there are uncommitted changes in the Git repository.
414
429
 
415
- Returns:
430
+ Returns
431
+ -------
416
432
  Tuple of (has_changes, status_output)
417
433
  - has_changes: True if there are uncommitted changes
418
434
  - status_output: Git status output string
@@ -438,7 +454,8 @@ def verify_railway_json() -> tuple[bool, str]:
438
454
  """
439
455
  Verify that railway.json exists and is valid JSON.
440
456
 
441
- Returns:
457
+ Returns
458
+ -------
442
459
  Tuple of (is_valid, error_message)
443
460
  - is_valid: True if file exists and is valid JSON
444
461
  - error_message: Error description if validation fails, empty string otherwise
@@ -463,7 +480,8 @@ def verify_dockerfile() -> tuple[bool, str]:
463
480
  """
464
481
  Verify that Dockerfile exists in the project root.
465
482
 
466
- Returns:
483
+ Returns
484
+ -------
467
485
  Tuple of (exists, error_message)
468
486
  - exists: True if Dockerfile exists
469
487
  - error_message: Error description if check fails, empty string otherwise
@@ -487,7 +505,8 @@ def verify_railway_dependencies() -> tuple[bool, list[str]]:
487
505
  - dj-database-url (Database URL parsing)
488
506
  - whitenoise (Static file serving)
489
507
 
490
- Returns:
508
+ Returns
509
+ -------
491
510
  Tuple of (all_present, missing_deps)
492
511
  - all_present: True if all required dependencies are present
493
512
  - missing_deps: List of missing dependency names
@@ -520,7 +539,8 @@ def check_poetry_lock_consistency() -> tuple[bool, str]:
520
539
  """
521
540
  Check if poetry.lock is consistent with pyproject.toml.
522
541
 
523
- Returns:
542
+ Returns
543
+ -------
524
544
  Tuple of (is_consistent, message)
525
545
  - is_consistent: True if poetry.lock is consistent with pyproject.toml
526
546
  - message: Status message
@@ -557,7 +577,8 @@ def fix_poetry_lock() -> tuple[bool, str]:
557
577
  """
558
578
  Fix poetry.lock by running poetry lock --no-update.
559
579
 
560
- Returns:
580
+ Returns
581
+ -------
561
582
  Tuple of (success, message)
562
583
  - success: True if lock file was fixed successfully
563
584
  - message: Status message
@@ -586,9 +607,11 @@ def get_railway_variables(service: str | None = None) -> dict[str, str] | None:
586
607
  Get all environment variables for a Railway service.
587
608
 
588
609
  Args:
610
+ ----
589
611
  service: Service name (optional)
590
612
 
591
613
  Returns:
614
+ -------
592
615
  Dictionary of variable names and values, or None if failed
593
616
 
594
617
  """
@@ -625,9 +648,11 @@ def link_database_to_service(service: str) -> tuple[bool, str]:
625
648
  This creates a DATABASE_URL reference variable that points to the PostgreSQL service.
626
649
 
627
650
  Args:
651
+ ----
628
652
  service: App service name to link database to
629
653
 
630
654
  Returns:
655
+ -------
631
656
  Tuple of (success, message)
632
657
  - success: True if link was created successfully
633
658
  - message: Status or error message
@@ -1,206 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: quickscale-cli
3
- Version: 0.61.0
4
- Summary: Command-line interface for QuickScale Django project generator
5
- Home-page: https://github.com/Experto-AI/quickscale
6
- License: Apache-2.0
7
- Keywords: django,cli,project-generator,saas
8
- Author: Experto AI
9
- Author-email: victor@experto.ai
10
- Requires-Python: >=3.10,<4.0
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Framework :: Django
13
- Classifier: Framework :: Django :: 4.2
14
- Classifier: Framework :: Django :: 5.0
15
- Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: Apache Software License
17
- Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Requires-Dist: click (>=8.1.0,<9.0.0)
22
- Requires-Dist: quickscale-core (>=0.61.0,<0.62.0)
23
- Project-URL: Documentation, https://github.com/Experto-AI/quickscale/tree/main/docs
24
- Project-URL: Repository, https://github.com/Experto-AI/quickscale
25
- Description-Content-Type: text/markdown
26
-
27
- # 🚀 QuickScale
28
-
29
- <!--
30
- README.md - User-Focused Introduction
31
-
32
- PURPOSE: This file serves as the first contact point for users, developers, and evaluators visiting the QuickScale project.
33
-
34
- CONTENT GUIDELINES:
35
- - Keep content user-facing and accessible to newcomers
36
- - Focus on "what" and "how to get started" rather than "why" or technical details
37
- - Include quick examples and development workflows
38
- - Avoid deep architectural explanations (those belong in DECISIONS.md)
39
- - Avoid competitive analysis or strategic context (those belong in QUICKSCALE.md)
40
- - Maximum length: ~200 lines to ensure quick readability
41
- - Link to other documents for detailed information
42
-
43
- TARGET AUDIENCE: New users, potential adopters, GitHub visitors, developers evaluating QuickScale
44
- -->
45
-
46
- ---
47
-
48
- ## QuickScale: Compose your Django SaaS.
49
-
50
- QuickScale is a **composable Django framework** for building client SaaS applications. Start with a stable core, add reusable modules, customize themes, and deploy faster—while maintaining the flexibility to create commercial extensions and build a community ecosystem.
51
-
52
- ---
53
-
54
- ## What is QuickScale?
55
-
56
- QuickScale is a **Django project generator** that creates production-ready SaaS applications with one command. Designed for **solo developers and development agencies**, it gives you:
57
-
58
- - **Production-ready foundations**: Docker, PostgreSQL, testing, CI/CD, and security out-of-the-box
59
- - **One-command deployment**: Deploy to Railway with `quickscale deploy railway`
60
- - **Full ownership**: Generated projects are 100% yours to customize—no vendor lock-in
61
- - **Standardized stack**: Build multiple client projects faster with consistent best practices
62
-
63
- 🧭 **Future Vision**: QuickScale will evolve to support reusable modules and themes. Today it's a personal toolkit; tomorrow it becomes a community platform when demand emerges. [Read the full evolution strategy](./docs/overview/quickscale.md#evolution-strategy-personal-toolkit-first).
64
-
65
- ## Documentation Guide
66
-
67
- **Start here for your needs:**
68
- - 📖 **New user?** You're in the right place. This README shows you what QuickScale is and how to get started.
69
- - 🔧 **Need commands?** See [user_manual.md](./docs/technical/user_manual.md) for all commands and workflows
70
- - 🚀 **Deploying to Railway?** See [railway.md](./docs/deployment/railway.md) for Railway deployment guide
71
- - 📋 **Planning a feature?** Check [decisions.md](./docs/technical/decisions.md) for the authoritative MVP scope and technical rules
72
- - 🗓️ **Timeline & tasks?** See [roadmap.md](./docs/technical/roadmap.md)
73
- - 🏗️ **Project structure?** See [scaffolding.md](./docs/technical/scaffolding.md) for complete directory layouts
74
- - 🎯 **Why QuickScale?** See [quickscale.md](./docs/overview/quickscale.md) for competitive positioning
75
-
76
- **Quick Reference:**
77
- - **MVP** = Phase 1 (Personal Toolkit)
78
- - **Post-MVP** = Phase 2+ (Modules & Themes)
79
- - **Generated Project** = Output of `quickscale init`
80
-
81
- See [decisions.md - Glossary section](./docs/technical/decisions.md#document-responsibilities-short) for complete terminology and Single Source of Truth reference
82
-
83
-
84
- ### Primary Use Cases (MVP):
85
- - **Solo Developer**: Build client projects faster with production-ready foundations
86
- - **Development Agency**: Standardize your tech stack across client engagements
87
-
88
- ### Future Use Cases (Post-MVP):
89
- - **Commercial Extension Developer**: Create and sell premium modules/themes
90
- - **Open Source Contributor**: Extend the ecosystem with modules and themes
91
-
92
- ### Development Flow (MVP)
93
- 1. `quickscale init myapp`
94
- - Generates the minimal Django starter described in the MVP Feature Matrix
95
- - Ships with standalone `settings.py` by default; there is NO automatic settings inheritance. Advanced users who manually embed `quickscale_core` via git subtree may opt-in to inherit from `quickscale_core.settings` (see [decisions.md](./docs/technical/decisions.md#mvp-feature-matrix-authoritative)).
96
- - **Optional**: Embed `quickscale_core` via git subtree after generation; follow the [Personal Toolkit workflow](./docs/technical/decisions.md#integration-note-personal-toolkit-git-subtree) for canonical commands and helper roadmap
97
- 2. Add your custom Django apps and features
98
- 3. Adopt optional inheritance or module extraction patterns only when you embed the core; the rules and best practices stay centralized in `DECISIONS.md`
99
- 4. Build your unique client application
100
- 5. Deploy to Railway with `quickscale deploy railway` (or use standard Django deployment)
101
-
102
- ℹ️ QuickScale's MVP centers on the personal toolkit workflow. Extraction patterns, module packaging, and subtree helper command plans stay documented in `docs/technical/decisions.md` so this README can stay concise.
103
-
104
- 🔎 **Scope note**: The [MVP Feature Matrix](./docs/technical/decisions.md#mvp-feature-matrix-authoritative) is the single source of truth for what's in or out.
105
-
106
- ### What You Get
107
-
108
- Running `quickscale init myapp` generates a **production-ready Django project** with:
109
-
110
- - ✅ **Docker** setup (development + production)
111
- - ✅ **PostgreSQL** configuration
112
- - ✅ **Environment-based** settings (dev/prod split)
113
- - ✅ **Security** best practices (SECRET_KEY, ALLOWED_HOSTS, etc.)
114
- - ✅ **Testing** infrastructure (pytest + factory_boy)
115
- - ✅ **CI/CD** pipeline (GitHub Actions)
116
- - ✅ **Code quality** hooks (ruff format + ruff check)
117
- - ✅ **Poetry** for dependency management
118
- - ✅ **One-Command Deployment**: Deploy to Railway with `quickscale deploy railway` - fully automated setup
119
-
120
- **See the complete project structure:** [scaffolding.md - Generated Project Output](./docs/technical/scaffolding.md#5-generated-project-output)
121
-
122
- The generated project is **yours to own and modify** - no vendor lock-in, just Django best practices.
123
-
124
- ## Why QuickScale vs. Alternatives?
125
-
126
- ✅ **Faster than Cookiecutter** - One command vs. 30+ interactive prompts
127
- ✅ **More flexible than SaaS Pegasus** - Open source with full code ownership ($0 vs. $349+)
128
- ✅ **Simpler than building from scratch** - Production-ready in 5 minutes vs. days of setup
129
- ✅ **Railway deployment automation** - Competitors require manual platform configuration
130
-
131
- **QuickScale is a development accelerator**, not a complete solution. You start with production-ready foundations and build your unique client application on top.
132
-
133
- See [competitive_analysis.md](./docs/overview/competitive_analysis.md) for detailed comparison with SaaS Pegasus and Cookiecutter.
134
-
135
- ---
136
-
137
-
138
- ## Quick Start
139
-
140
- ```bash
141
- # Install QuickScale globally
142
- ./scripts/install_global.sh
143
-
144
- # Create your first project
145
- quickscale init myapp
146
- cd myapp
147
-
148
- # Or choose a specific theme (v0.61.0+)
149
- # quickscale init myapp --theme starter_html # Default HTML theme
150
- # quickscale init myapp --theme starter_htmx # HTMX theme (coming in v0.67.0)
151
- # quickscale init myapp --theme starter_react # React theme (coming in v0.68.0)
152
- ```
153
-
154
- **Choose your development workflow:**
155
-
156
- ### Option 1: Docker (Recommended for production parity)
157
-
158
- ```bash
159
- # Start all services (web + database)
160
- quickscale up
161
-
162
- # Run migrations
163
- quickscale manage migrate
164
-
165
- # Create superuser
166
- quickscale manage createsuperuser
167
-
168
- # View logs
169
- quickscale logs -f web
170
-
171
- # Open a shell in the container
172
- quickscale shell
173
-
174
- # Stop services
175
- quickscale down
176
- ```
177
-
178
- **Visit http://localhost:8000** - Your app is running in Docker with PostgreSQL!
179
-
180
- ### Option 2: Native Poetry (Simpler for quick testing)
181
-
182
- ```bash
183
- # Install dependencies
184
- poetry install
185
-
186
- # Run migrations
187
- poetry run python manage.py migrate
188
-
189
- # Start development server
190
- poetry run python manage.py runserver
191
- ```
192
-
193
- **Visit http://localhost:8000** - Your app is running natively!
194
-
195
- **For complete command reference and workflows**, see the [user_manual.md](./docs/technical/user_manual.md).
196
-
197
- ## Learn More
198
-
199
- - **[decisions.md](./docs/technical/decisions.md)** - Technical specifications and implementation rules
200
- - **[quickscale.md](./docs/overview/quickscale.md)** - Strategic vision and competitive positioning
201
- - **[competitive_analysis.md](./docs/overview/competitive_analysis.md)** - Comparison vs SaaS Pegasus and alternatives
202
- - **[roadmap.md](./docs/technical/roadmap.md)** - Development roadmap and implementation plan
203
- - **[user_manual.md](./docs/technical/user_manual.md)** - Commands and workflows
204
- - **[contributing.md](./docs/contrib/contributing.md)** - Development workflow and coding standards
205
-
206
-
@@ -1,179 +0,0 @@
1
- # 🚀 QuickScale
2
-
3
- <!--
4
- README.md - User-Focused Introduction
5
-
6
- PURPOSE: This file serves as the first contact point for users, developers, and evaluators visiting the QuickScale project.
7
-
8
- CONTENT GUIDELINES:
9
- - Keep content user-facing and accessible to newcomers
10
- - Focus on "what" and "how to get started" rather than "why" or technical details
11
- - Include quick examples and development workflows
12
- - Avoid deep architectural explanations (those belong in DECISIONS.md)
13
- - Avoid competitive analysis or strategic context (those belong in QUICKSCALE.md)
14
- - Maximum length: ~200 lines to ensure quick readability
15
- - Link to other documents for detailed information
16
-
17
- TARGET AUDIENCE: New users, potential adopters, GitHub visitors, developers evaluating QuickScale
18
- -->
19
-
20
- ---
21
-
22
- ## QuickScale: Compose your Django SaaS.
23
-
24
- QuickScale is a **composable Django framework** for building client SaaS applications. Start with a stable core, add reusable modules, customize themes, and deploy faster—while maintaining the flexibility to create commercial extensions and build a community ecosystem.
25
-
26
- ---
27
-
28
- ## What is QuickScale?
29
-
30
- QuickScale is a **Django project generator** that creates production-ready SaaS applications with one command. Designed for **solo developers and development agencies**, it gives you:
31
-
32
- - **Production-ready foundations**: Docker, PostgreSQL, testing, CI/CD, and security out-of-the-box
33
- - **One-command deployment**: Deploy to Railway with `quickscale deploy railway`
34
- - **Full ownership**: Generated projects are 100% yours to customize—no vendor lock-in
35
- - **Standardized stack**: Build multiple client projects faster with consistent best practices
36
-
37
- 🧭 **Future Vision**: QuickScale will evolve to support reusable modules and themes. Today it's a personal toolkit; tomorrow it becomes a community platform when demand emerges. [Read the full evolution strategy](./docs/overview/quickscale.md#evolution-strategy-personal-toolkit-first).
38
-
39
- ## Documentation Guide
40
-
41
- **Start here for your needs:**
42
- - 📖 **New user?** You're in the right place. This README shows you what QuickScale is and how to get started.
43
- - 🔧 **Need commands?** See [user_manual.md](./docs/technical/user_manual.md) for all commands and workflows
44
- - 🚀 **Deploying to Railway?** See [railway.md](./docs/deployment/railway.md) for Railway deployment guide
45
- - 📋 **Planning a feature?** Check [decisions.md](./docs/technical/decisions.md) for the authoritative MVP scope and technical rules
46
- - 🗓️ **Timeline & tasks?** See [roadmap.md](./docs/technical/roadmap.md)
47
- - 🏗️ **Project structure?** See [scaffolding.md](./docs/technical/scaffolding.md) for complete directory layouts
48
- - 🎯 **Why QuickScale?** See [quickscale.md](./docs/overview/quickscale.md) for competitive positioning
49
-
50
- **Quick Reference:**
51
- - **MVP** = Phase 1 (Personal Toolkit)
52
- - **Post-MVP** = Phase 2+ (Modules & Themes)
53
- - **Generated Project** = Output of `quickscale init`
54
-
55
- See [decisions.md - Glossary section](./docs/technical/decisions.md#document-responsibilities-short) for complete terminology and Single Source of Truth reference
56
-
57
-
58
- ### Primary Use Cases (MVP):
59
- - **Solo Developer**: Build client projects faster with production-ready foundations
60
- - **Development Agency**: Standardize your tech stack across client engagements
61
-
62
- ### Future Use Cases (Post-MVP):
63
- - **Commercial Extension Developer**: Create and sell premium modules/themes
64
- - **Open Source Contributor**: Extend the ecosystem with modules and themes
65
-
66
- ### Development Flow (MVP)
67
- 1. `quickscale init myapp`
68
- - Generates the minimal Django starter described in the MVP Feature Matrix
69
- - Ships with standalone `settings.py` by default; there is NO automatic settings inheritance. Advanced users who manually embed `quickscale_core` via git subtree may opt-in to inherit from `quickscale_core.settings` (see [decisions.md](./docs/technical/decisions.md#mvp-feature-matrix-authoritative)).
70
- - **Optional**: Embed `quickscale_core` via git subtree after generation; follow the [Personal Toolkit workflow](./docs/technical/decisions.md#integration-note-personal-toolkit-git-subtree) for canonical commands and helper roadmap
71
- 2. Add your custom Django apps and features
72
- 3. Adopt optional inheritance or module extraction patterns only when you embed the core; the rules and best practices stay centralized in `DECISIONS.md`
73
- 4. Build your unique client application
74
- 5. Deploy to Railway with `quickscale deploy railway` (or use standard Django deployment)
75
-
76
- ℹ️ QuickScale's MVP centers on the personal toolkit workflow. Extraction patterns, module packaging, and subtree helper command plans stay documented in `docs/technical/decisions.md` so this README can stay concise.
77
-
78
- 🔎 **Scope note**: The [MVP Feature Matrix](./docs/technical/decisions.md#mvp-feature-matrix-authoritative) is the single source of truth for what's in or out.
79
-
80
- ### What You Get
81
-
82
- Running `quickscale init myapp` generates a **production-ready Django project** with:
83
-
84
- - ✅ **Docker** setup (development + production)
85
- - ✅ **PostgreSQL** configuration
86
- - ✅ **Environment-based** settings (dev/prod split)
87
- - ✅ **Security** best practices (SECRET_KEY, ALLOWED_HOSTS, etc.)
88
- - ✅ **Testing** infrastructure (pytest + factory_boy)
89
- - ✅ **CI/CD** pipeline (GitHub Actions)
90
- - ✅ **Code quality** hooks (ruff format + ruff check)
91
- - ✅ **Poetry** for dependency management
92
- - ✅ **One-Command Deployment**: Deploy to Railway with `quickscale deploy railway` - fully automated setup
93
-
94
- **See the complete project structure:** [scaffolding.md - Generated Project Output](./docs/technical/scaffolding.md#5-generated-project-output)
95
-
96
- The generated project is **yours to own and modify** - no vendor lock-in, just Django best practices.
97
-
98
- ## Why QuickScale vs. Alternatives?
99
-
100
- ✅ **Faster than Cookiecutter** - One command vs. 30+ interactive prompts
101
- ✅ **More flexible than SaaS Pegasus** - Open source with full code ownership ($0 vs. $349+)
102
- ✅ **Simpler than building from scratch** - Production-ready in 5 minutes vs. days of setup
103
- ✅ **Railway deployment automation** - Competitors require manual platform configuration
104
-
105
- **QuickScale is a development accelerator**, not a complete solution. You start with production-ready foundations and build your unique client application on top.
106
-
107
- See [competitive_analysis.md](./docs/overview/competitive_analysis.md) for detailed comparison with SaaS Pegasus and Cookiecutter.
108
-
109
- ---
110
-
111
-
112
- ## Quick Start
113
-
114
- ```bash
115
- # Install QuickScale globally
116
- ./scripts/install_global.sh
117
-
118
- # Create your first project
119
- quickscale init myapp
120
- cd myapp
121
-
122
- # Or choose a specific theme (v0.61.0+)
123
- # quickscale init myapp --theme starter_html # Default HTML theme
124
- # quickscale init myapp --theme starter_htmx # HTMX theme (coming in v0.67.0)
125
- # quickscale init myapp --theme starter_react # React theme (coming in v0.68.0)
126
- ```
127
-
128
- **Choose your development workflow:**
129
-
130
- ### Option 1: Docker (Recommended for production parity)
131
-
132
- ```bash
133
- # Start all services (web + database)
134
- quickscale up
135
-
136
- # Run migrations
137
- quickscale manage migrate
138
-
139
- # Create superuser
140
- quickscale manage createsuperuser
141
-
142
- # View logs
143
- quickscale logs -f web
144
-
145
- # Open a shell in the container
146
- quickscale shell
147
-
148
- # Stop services
149
- quickscale down
150
- ```
151
-
152
- **Visit http://localhost:8000** - Your app is running in Docker with PostgreSQL!
153
-
154
- ### Option 2: Native Poetry (Simpler for quick testing)
155
-
156
- ```bash
157
- # Install dependencies
158
- poetry install
159
-
160
- # Run migrations
161
- poetry run python manage.py migrate
162
-
163
- # Start development server
164
- poetry run python manage.py runserver
165
- ```
166
-
167
- **Visit http://localhost:8000** - Your app is running natively!
168
-
169
- **For complete command reference and workflows**, see the [user_manual.md](./docs/technical/user_manual.md).
170
-
171
- ## Learn More
172
-
173
- - **[decisions.md](./docs/technical/decisions.md)** - Technical specifications and implementation rules
174
- - **[quickscale.md](./docs/overview/quickscale.md)** - Strategic vision and competitive positioning
175
- - **[competitive_analysis.md](./docs/overview/competitive_analysis.md)** - Comparison vs SaaS Pegasus and alternatives
176
- - **[roadmap.md](./docs/technical/roadmap.md)** - Development roadmap and implementation plan
177
- - **[user_manual.md](./docs/technical/user_manual.md)** - Commands and workflows
178
- - **[contributing.md](./docs/contrib/contributing.md)** - Development workflow and coding standards
179
-