devs-cli 0.1.2__tar.gz → 0.1.4__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.
- {devs_cli-0.1.2/devs_cli.egg-info → devs_cli-0.1.4}/PKG-INFO +3 -3
- {devs_cli-0.1.2 → devs_cli-0.1.4}/README.md +2 -2
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/cli.py +128 -122
- {devs_cli-0.1.2 → devs_cli-0.1.4/devs_cli.egg-info}/PKG-INFO +3 -3
- {devs_cli-0.1.2 → devs_cli-0.1.4}/pyproject.toml +2 -2
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_cli.py +32 -23
- {devs_cli-0.1.2 → devs_cli-0.1.4}/LICENSE +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/__init__.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/config.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/core/__init__.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/core/integration.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/exceptions.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs/utils/__init__.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs_cli.egg-info/SOURCES.txt +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs_cli.egg-info/dependency_links.txt +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs_cli.egg-info/entry_points.txt +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs_cli.egg-info/requires.txt +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/devs_cli.egg-info/top_level.txt +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/setup.cfg +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_cli_clean.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_cli_misc.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_cli_start.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_cli_stop.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_cli_vscode.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_container_manager.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_e2e.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_integration.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_live_mode.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_project.py +0 -0
- {devs_cli-0.1.2 → devs_cli-0.1.4}/tests/test_workspace_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devs-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: DevContainer Management Tool - Manage multiple named devcontainers for any project
|
|
5
5
|
Author: Dan Lester
|
|
6
6
|
License-Expression: MIT
|
|
@@ -81,9 +81,9 @@ devs start frontend --env DEBUG=true --env API_URL=http://localhost:3000
|
|
|
81
81
|
devs claude frontend "Fix the tests" --env NODE_ENV=test
|
|
82
82
|
|
|
83
83
|
# Set up Claude authentication (once per host)
|
|
84
|
-
devs claude
|
|
84
|
+
devs claude --auth
|
|
85
85
|
# Or with API key
|
|
86
|
-
devs claude
|
|
86
|
+
devs claude --auth --api-key <YOUR_API_KEY>
|
|
87
87
|
|
|
88
88
|
# Clean up when done
|
|
89
89
|
devs stop frontend backend
|
|
@@ -40,9 +40,9 @@ devs start frontend --env DEBUG=true --env API_URL=http://localhost:3000
|
|
|
40
40
|
devs claude frontend "Fix the tests" --env NODE_ENV=test
|
|
41
41
|
|
|
42
42
|
# Set up Claude authentication (once per host)
|
|
43
|
-
devs claude
|
|
43
|
+
devs claude --auth
|
|
44
44
|
# Or with API key
|
|
45
|
-
devs claude
|
|
45
|
+
devs claude --auth --api-key <YOUR_API_KEY>
|
|
46
46
|
|
|
47
47
|
# Clean up when done
|
|
48
48
|
devs stop frontend backend
|
|
@@ -328,67 +328,80 @@ def shell(dev_name: str, live: bool, env: tuple, debug: bool) -> None:
|
|
|
328
328
|
|
|
329
329
|
|
|
330
330
|
@cli.command()
|
|
331
|
-
@click.argument('dev_name')
|
|
332
|
-
@click.argument('prompt')
|
|
331
|
+
@click.argument('dev_name', required=False)
|
|
332
|
+
@click.argument('prompt', required=False)
|
|
333
|
+
@click.option('--auth', is_flag=True, help='Set up Claude authentication for devcontainers')
|
|
334
|
+
@click.option('--api-key', help='Claude API key to authenticate with (use with --auth)')
|
|
333
335
|
@click.option('--reset-workspace', is_flag=True, help='Reset workspace contents before execution')
|
|
334
336
|
@click.option('--live', is_flag=True, help='Start container with current directory mounted as workspace')
|
|
335
337
|
@click.option('--env', multiple=True, help='Environment variables to pass to container (format: VAR=value)')
|
|
336
338
|
@debug_option
|
|
337
|
-
def claude(dev_name: str, prompt: str, reset_workspace: bool, live: bool, env: tuple, debug: bool) -> None:
|
|
338
|
-
"""Execute Claude CLI in devcontainer.
|
|
339
|
-
|
|
339
|
+
def claude(dev_name: str, prompt: str, auth: bool, api_key: str, reset_workspace: bool, live: bool, env: tuple, debug: bool) -> None:
|
|
340
|
+
"""Execute Claude CLI in devcontainer or set up authentication.
|
|
341
|
+
|
|
340
342
|
DEV_NAME: Development environment name
|
|
341
343
|
PROMPT: Prompt to send to Claude
|
|
342
|
-
|
|
344
|
+
|
|
343
345
|
Example: devs claude sally "Summarize this codebase"
|
|
344
346
|
Example: devs claude sally "Fix the tests" --reset-workspace
|
|
345
347
|
Example: devs claude sally "Fix the tests" --live # Run with current directory
|
|
346
348
|
Example: devs claude sally "Start the server" --env QUART_PORT=5001
|
|
349
|
+
Example: devs claude --auth # Interactive authentication
|
|
350
|
+
Example: devs claude --auth --api-key <YOUR_KEY> # API key authentication
|
|
347
351
|
"""
|
|
352
|
+
# Handle authentication mode
|
|
353
|
+
if auth:
|
|
354
|
+
_handle_claude_auth(api_key=api_key, debug=debug)
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
# Validate required arguments for execution mode
|
|
358
|
+
if not dev_name or not prompt:
|
|
359
|
+
raise click.UsageError("DEV_NAME and PROMPT are required unless using --auth")
|
|
360
|
+
|
|
348
361
|
check_dependencies()
|
|
349
362
|
project = get_project()
|
|
350
|
-
|
|
363
|
+
|
|
351
364
|
# Load environment variables from DEVS.yml and merge with CLI --env flags
|
|
352
365
|
devs_env = DevsConfigLoader.load_env_vars(dev_name, project.info.name)
|
|
353
366
|
cli_env = parse_env_vars(env) if env else {}
|
|
354
367
|
extra_env = merge_env_vars(devs_env, cli_env) if devs_env or cli_env else None
|
|
355
|
-
|
|
368
|
+
|
|
356
369
|
if extra_env:
|
|
357
370
|
console.print(f"🔧 Environment variables: {', '.join(f'{k}={v}' for k, v in extra_env.items())}")
|
|
358
|
-
|
|
371
|
+
|
|
359
372
|
container_manager = ContainerManager(project, config)
|
|
360
373
|
workspace_manager = WorkspaceManager(project, config)
|
|
361
|
-
|
|
374
|
+
|
|
362
375
|
try:
|
|
363
376
|
# Ensure workspace exists (handles live mode and reset internally)
|
|
364
377
|
workspace_dir = workspace_manager.create_workspace(dev_name, reset_contents=reset_workspace, live=live)
|
|
365
378
|
# Ensure container is running
|
|
366
379
|
container_manager.ensure_container_running(
|
|
367
|
-
dev_name=dev_name,
|
|
368
|
-
workspace_dir=workspace_dir,
|
|
369
|
-
force_rebuild=False,
|
|
370
|
-
debug=debug,
|
|
371
|
-
live=live,
|
|
380
|
+
dev_name=dev_name,
|
|
381
|
+
workspace_dir=workspace_dir,
|
|
382
|
+
force_rebuild=False,
|
|
383
|
+
debug=debug,
|
|
384
|
+
live=live,
|
|
372
385
|
extra_env=extra_env
|
|
373
386
|
)
|
|
374
|
-
|
|
387
|
+
|
|
375
388
|
# Execute Claude
|
|
376
389
|
console.print(f"🤖 Executing Claude in {dev_name}...")
|
|
377
390
|
if reset_workspace and not live:
|
|
378
391
|
console.print("🗑️ Workspace contents reset")
|
|
379
392
|
console.print(f"📝 Prompt: {prompt}")
|
|
380
393
|
console.print("")
|
|
381
|
-
|
|
394
|
+
|
|
382
395
|
success, output, error = container_manager.exec_claude(
|
|
383
396
|
dev_name=dev_name,
|
|
384
|
-
workspace_dir=workspace_dir,
|
|
385
|
-
prompt=prompt,
|
|
386
|
-
debug=debug,
|
|
387
|
-
stream=True,
|
|
388
|
-
live=live,
|
|
397
|
+
workspace_dir=workspace_dir,
|
|
398
|
+
prompt=prompt,
|
|
399
|
+
debug=debug,
|
|
400
|
+
stream=True,
|
|
401
|
+
live=live,
|
|
389
402
|
extra_env=extra_env
|
|
390
403
|
)
|
|
391
|
-
|
|
404
|
+
|
|
392
405
|
console.print("") # Add spacing after streamed output
|
|
393
406
|
if success:
|
|
394
407
|
console.print("✅ Claude execution completed")
|
|
@@ -399,12 +412,103 @@ def claude(dev_name: str, prompt: str, reset_workspace: bool, live: bool, env: t
|
|
|
399
412
|
console.print("🚫 Error:")
|
|
400
413
|
console.print(error)
|
|
401
414
|
sys.exit(1)
|
|
402
|
-
|
|
415
|
+
|
|
403
416
|
except (ContainerError, WorkspaceError) as e:
|
|
404
417
|
console.print(f"❌ Error executing Claude in {dev_name}: {e}")
|
|
405
418
|
sys.exit(1)
|
|
406
419
|
|
|
407
420
|
|
|
421
|
+
def _handle_claude_auth(api_key: str, debug: bool) -> None:
|
|
422
|
+
"""Handle Claude authentication setup.
|
|
423
|
+
|
|
424
|
+
This configures Claude authentication that will be shared across
|
|
425
|
+
all devcontainers for this project. The authentication is stored
|
|
426
|
+
on the host and bind-mounted into containers.
|
|
427
|
+
"""
|
|
428
|
+
try:
|
|
429
|
+
# Ensure Claude config directory exists
|
|
430
|
+
config.ensure_directories()
|
|
431
|
+
|
|
432
|
+
console.print("🔐 Setting up Claude authentication...")
|
|
433
|
+
console.print(f" Configuration will be saved to: {config.claude_config_dir}")
|
|
434
|
+
|
|
435
|
+
if api_key:
|
|
436
|
+
# Set API key directly using Claude CLI
|
|
437
|
+
console.print(" Using provided API key...")
|
|
438
|
+
|
|
439
|
+
# Set CLAUDE_CONFIG_DIR to our config directory and run auth with API key
|
|
440
|
+
env = os.environ.copy()
|
|
441
|
+
env['CLAUDE_CONFIG_DIR'] = str(config.claude_config_dir)
|
|
442
|
+
|
|
443
|
+
cmd = ['claude', 'auth', '--key', api_key]
|
|
444
|
+
|
|
445
|
+
if debug:
|
|
446
|
+
console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
|
|
447
|
+
console.print(f"[dim]CLAUDE_CONFIG_DIR: {config.claude_config_dir}[/dim]")
|
|
448
|
+
|
|
449
|
+
result = subprocess.run(
|
|
450
|
+
cmd,
|
|
451
|
+
env=env,
|
|
452
|
+
capture_output=True,
|
|
453
|
+
text=True
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
if result.returncode != 0:
|
|
457
|
+
error_msg = result.stderr or result.stdout or "Unknown error"
|
|
458
|
+
raise Exception(f"Claude authentication failed: {error_msg}")
|
|
459
|
+
|
|
460
|
+
else:
|
|
461
|
+
# Interactive authentication
|
|
462
|
+
console.print(" Starting interactive authentication...")
|
|
463
|
+
console.print(" Follow the prompts to authenticate with Claude")
|
|
464
|
+
console.print("")
|
|
465
|
+
|
|
466
|
+
# Set CLAUDE_CONFIG_DIR to our config directory
|
|
467
|
+
env = os.environ.copy()
|
|
468
|
+
env['CLAUDE_CONFIG_DIR'] = str(config.claude_config_dir)
|
|
469
|
+
|
|
470
|
+
cmd = ['claude', 'auth']
|
|
471
|
+
|
|
472
|
+
if debug:
|
|
473
|
+
console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
|
|
474
|
+
console.print(f"[dim]CLAUDE_CONFIG_DIR: {config.claude_config_dir}[/dim]")
|
|
475
|
+
|
|
476
|
+
# Run interactively
|
|
477
|
+
result = subprocess.run(
|
|
478
|
+
cmd,
|
|
479
|
+
env=env,
|
|
480
|
+
check=False
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
if result.returncode != 0:
|
|
484
|
+
raise Exception("Claude authentication was cancelled or failed")
|
|
485
|
+
|
|
486
|
+
console.print("")
|
|
487
|
+
console.print("✅ Claude authentication configured successfully!")
|
|
488
|
+
console.print(f" Configuration saved to: {config.claude_config_dir}")
|
|
489
|
+
console.print(" This authentication will be shared across all devcontainers")
|
|
490
|
+
console.print("")
|
|
491
|
+
console.print("💡 You can now use Claude in any devcontainer:")
|
|
492
|
+
console.print(" devs claude <dev-name> 'Your prompt here'")
|
|
493
|
+
|
|
494
|
+
except FileNotFoundError:
|
|
495
|
+
console.print("❌ Claude CLI not found on host machine")
|
|
496
|
+
console.print("")
|
|
497
|
+
console.print("Please install Claude CLI first:")
|
|
498
|
+
console.print(" npm install -g @anthropic-ai/claude-cli")
|
|
499
|
+
console.print("")
|
|
500
|
+
console.print("Note: Claude needs to be installed on the host machine")
|
|
501
|
+
console.print(" for authentication. It's already available in containers.")
|
|
502
|
+
sys.exit(1)
|
|
503
|
+
|
|
504
|
+
except Exception as e:
|
|
505
|
+
console.print(f"❌ Failed to configure Claude authentication: {e}")
|
|
506
|
+
if debug:
|
|
507
|
+
import traceback
|
|
508
|
+
console.print(traceback.format_exc())
|
|
509
|
+
sys.exit(1)
|
|
510
|
+
|
|
511
|
+
|
|
408
512
|
@cli.command()
|
|
409
513
|
@click.argument('dev_name')
|
|
410
514
|
@click.option('--reset-workspace', is_flag=True, help='Reset workspace contents before execution')
|
|
@@ -492,104 +596,6 @@ def runtests(dev_name: str, reset_workspace: bool, live: bool, env: tuple, debug
|
|
|
492
596
|
sys.exit(1)
|
|
493
597
|
|
|
494
598
|
|
|
495
|
-
@cli.command('claude-auth')
|
|
496
|
-
@click.option('--api-key', help='Claude API key to authenticate with')
|
|
497
|
-
@debug_option
|
|
498
|
-
def claude_auth(api_key: str, debug: bool) -> None:
|
|
499
|
-
"""Set up Claude authentication for devcontainers.
|
|
500
|
-
|
|
501
|
-
This configures Claude authentication that will be shared across
|
|
502
|
-
all devcontainers for this project. The authentication is stored
|
|
503
|
-
on the host and bind-mounted into containers.
|
|
504
|
-
|
|
505
|
-
Example: devs claude-auth
|
|
506
|
-
Example: devs claude-auth --api-key <YOUR_API_KEY>
|
|
507
|
-
"""
|
|
508
|
-
|
|
509
|
-
try:
|
|
510
|
-
# Ensure Claude config directory exists
|
|
511
|
-
config.ensure_directories()
|
|
512
|
-
|
|
513
|
-
console.print("🔐 Setting up Claude authentication...")
|
|
514
|
-
console.print(f" Configuration will be saved to: {config.claude_config_dir}")
|
|
515
|
-
|
|
516
|
-
if api_key:
|
|
517
|
-
# Set API key directly using Claude CLI
|
|
518
|
-
console.print(" Using provided API key...")
|
|
519
|
-
|
|
520
|
-
# Set CLAUDE_CONFIG_DIR to our config directory and run auth with API key
|
|
521
|
-
env = os.environ.copy()
|
|
522
|
-
env['CLAUDE_CONFIG_DIR'] = str(config.claude_config_dir)
|
|
523
|
-
|
|
524
|
-
cmd = ['claude', 'auth', '--key', api_key]
|
|
525
|
-
|
|
526
|
-
if debug:
|
|
527
|
-
console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
|
|
528
|
-
console.print(f"[dim]CLAUDE_CONFIG_DIR: {config.claude_config_dir}[/dim]")
|
|
529
|
-
|
|
530
|
-
result = subprocess.run(
|
|
531
|
-
cmd,
|
|
532
|
-
env=env,
|
|
533
|
-
capture_output=True,
|
|
534
|
-
text=True
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
if result.returncode != 0:
|
|
538
|
-
error_msg = result.stderr or result.stdout or "Unknown error"
|
|
539
|
-
raise Exception(f"Claude authentication failed: {error_msg}")
|
|
540
|
-
|
|
541
|
-
else:
|
|
542
|
-
# Interactive authentication
|
|
543
|
-
console.print(" Starting interactive authentication...")
|
|
544
|
-
console.print(" Follow the prompts to authenticate with Claude")
|
|
545
|
-
console.print("")
|
|
546
|
-
|
|
547
|
-
# Set CLAUDE_CONFIG_DIR to our config directory
|
|
548
|
-
env = os.environ.copy()
|
|
549
|
-
env['CLAUDE_CONFIG_DIR'] = str(config.claude_config_dir)
|
|
550
|
-
|
|
551
|
-
cmd = ['claude', 'auth']
|
|
552
|
-
|
|
553
|
-
if debug:
|
|
554
|
-
console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
|
|
555
|
-
console.print(f"[dim]CLAUDE_CONFIG_DIR: {config.claude_config_dir}[/dim]")
|
|
556
|
-
|
|
557
|
-
# Run interactively
|
|
558
|
-
result = subprocess.run(
|
|
559
|
-
cmd,
|
|
560
|
-
env=env,
|
|
561
|
-
check=False
|
|
562
|
-
)
|
|
563
|
-
|
|
564
|
-
if result.returncode != 0:
|
|
565
|
-
raise Exception("Claude authentication was cancelled or failed")
|
|
566
|
-
|
|
567
|
-
console.print("")
|
|
568
|
-
console.print("✅ Claude authentication configured successfully!")
|
|
569
|
-
console.print(f" Configuration saved to: {config.claude_config_dir}")
|
|
570
|
-
console.print(" This authentication will be shared across all devcontainers")
|
|
571
|
-
console.print("")
|
|
572
|
-
console.print("💡 You can now use Claude in any devcontainer:")
|
|
573
|
-
console.print(" devs claude <dev-name> 'Your prompt here'")
|
|
574
|
-
|
|
575
|
-
except FileNotFoundError:
|
|
576
|
-
console.print("❌ Claude CLI not found on host machine")
|
|
577
|
-
console.print("")
|
|
578
|
-
console.print("Please install Claude CLI first:")
|
|
579
|
-
console.print(" npm install -g @anthropic-ai/claude-cli")
|
|
580
|
-
console.print("")
|
|
581
|
-
console.print("Note: Claude needs to be installed on the host machine")
|
|
582
|
-
console.print(" for authentication. It's already available in containers.")
|
|
583
|
-
sys.exit(1)
|
|
584
|
-
|
|
585
|
-
except Exception as e:
|
|
586
|
-
console.print(f"❌ Failed to configure Claude authentication: {e}")
|
|
587
|
-
if debug:
|
|
588
|
-
import traceback
|
|
589
|
-
console.print(traceback.format_exc())
|
|
590
|
-
sys.exit(1)
|
|
591
|
-
|
|
592
|
-
|
|
593
599
|
@cli.command()
|
|
594
600
|
@click.option('--all-projects', is_flag=True, help='List containers for all projects')
|
|
595
601
|
def list(all_projects: bool) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devs-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: DevContainer Management Tool - Manage multiple named devcontainers for any project
|
|
5
5
|
Author: Dan Lester
|
|
6
6
|
License-Expression: MIT
|
|
@@ -81,9 +81,9 @@ devs start frontend --env DEBUG=true --env API_URL=http://localhost:3000
|
|
|
81
81
|
devs claude frontend "Fix the tests" --env NODE_ENV=test
|
|
82
82
|
|
|
83
83
|
# Set up Claude authentication (once per host)
|
|
84
|
-
devs claude
|
|
84
|
+
devs claude --auth
|
|
85
85
|
# Or with API key
|
|
86
|
-
devs claude
|
|
86
|
+
devs claude --auth --api-key <YOUR_API_KEY>
|
|
87
87
|
|
|
88
88
|
# Clean up when done
|
|
89
89
|
devs stop frontend backend
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devs-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "DevContainer Management Tool - Manage multiple named devcontainers for any project"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -65,7 +65,7 @@ line-length = 88
|
|
|
65
65
|
target-version = ['py38']
|
|
66
66
|
|
|
67
67
|
[tool.mypy]
|
|
68
|
-
python_version = "
|
|
68
|
+
python_version = "3.8"
|
|
69
69
|
warn_return_any = true
|
|
70
70
|
warn_unused_configs = true
|
|
71
71
|
disallow_untyped_defs = true
|
|
@@ -123,31 +123,32 @@ class TestCLI:
|
|
|
123
123
|
assert result.exit_code != 0
|
|
124
124
|
assert "Missing argument" in result.output
|
|
125
125
|
|
|
126
|
-
def
|
|
127
|
-
"""Test claude
|
|
126
|
+
def test_claude_command_help(self):
|
|
127
|
+
"""Test claude command help."""
|
|
128
128
|
runner = CliRunner()
|
|
129
|
-
result = runner.invoke(cli, ['claude
|
|
130
|
-
|
|
129
|
+
result = runner.invoke(cli, ['claude', '--help'])
|
|
130
|
+
|
|
131
131
|
assert result.exit_code == 0
|
|
132
|
-
assert "
|
|
132
|
+
assert "Execute Claude CLI in devcontainer or set up authentication" in result.output
|
|
133
|
+
assert "--auth" in result.output
|
|
133
134
|
assert "--api-key" in result.output
|
|
134
|
-
|
|
135
|
+
|
|
135
136
|
@patch('devs.cli.subprocess.run')
|
|
136
137
|
@patch('devs.cli.config')
|
|
137
138
|
def test_claude_auth_with_api_key(self, mock_config, mock_subprocess):
|
|
138
|
-
"""Test claude
|
|
139
|
+
"""Test claude --auth command with API key."""
|
|
139
140
|
# Setup mocks
|
|
140
141
|
mock_config.claude_config_dir = '/tmp/test-claude-config'
|
|
141
142
|
mock_config.ensure_directories = Mock()
|
|
142
143
|
mock_subprocess.return_value.returncode = 0
|
|
143
|
-
|
|
144
|
+
|
|
144
145
|
runner = CliRunner()
|
|
145
|
-
result = runner.invoke(cli, ['claude
|
|
146
|
-
|
|
146
|
+
result = runner.invoke(cli, ['claude', '--auth', '--api-key', 'test-key-123'])
|
|
147
|
+
|
|
147
148
|
assert result.exit_code == 0
|
|
148
149
|
assert "Setting up Claude authentication" in result.output
|
|
149
150
|
assert "Claude authentication configured successfully" in result.output
|
|
150
|
-
|
|
151
|
+
|
|
151
152
|
# Verify subprocess was called with correct arguments
|
|
152
153
|
mock_subprocess.assert_called_once()
|
|
153
154
|
call_args = mock_subprocess.call_args
|
|
@@ -155,43 +156,51 @@ class TestCLI:
|
|
|
155
156
|
assert 'auth' in call_args[0][0]
|
|
156
157
|
assert '--key' in call_args[0][0]
|
|
157
158
|
assert 'test-key-123' in call_args[0][0]
|
|
158
|
-
|
|
159
|
+
|
|
159
160
|
@patch('devs.cli.subprocess.run')
|
|
160
161
|
@patch('devs.cli.config')
|
|
161
162
|
def test_claude_auth_interactive(self, mock_config, mock_subprocess):
|
|
162
|
-
"""Test claude
|
|
163
|
+
"""Test claude --auth command in interactive mode."""
|
|
163
164
|
# Setup mocks
|
|
164
165
|
mock_config.claude_config_dir = '/tmp/test-claude-config'
|
|
165
166
|
mock_config.ensure_directories = Mock()
|
|
166
167
|
mock_subprocess.return_value.returncode = 0
|
|
167
|
-
|
|
168
|
+
|
|
168
169
|
runner = CliRunner()
|
|
169
|
-
result = runner.invoke(cli, ['claude
|
|
170
|
-
|
|
170
|
+
result = runner.invoke(cli, ['claude', '--auth'])
|
|
171
|
+
|
|
171
172
|
assert result.exit_code == 0
|
|
172
173
|
assert "Setting up Claude authentication" in result.output
|
|
173
174
|
assert "Starting interactive authentication" in result.output
|
|
174
175
|
assert "Claude authentication configured successfully" in result.output
|
|
175
|
-
|
|
176
|
+
|
|
176
177
|
# Verify subprocess was called for interactive auth
|
|
177
178
|
mock_subprocess.assert_called_once()
|
|
178
179
|
call_args = mock_subprocess.call_args
|
|
179
180
|
assert 'claude' in call_args[0][0]
|
|
180
181
|
assert 'auth' in call_args[0][0]
|
|
181
182
|
assert '--key' not in call_args[0][0]
|
|
182
|
-
|
|
183
|
+
|
|
183
184
|
@patch('devs.cli.subprocess.run')
|
|
184
185
|
@patch('devs.cli.config')
|
|
185
186
|
def test_claude_auth_command_not_found(self, mock_config, mock_subprocess):
|
|
186
|
-
"""Test claude
|
|
187
|
+
"""Test claude --auth when claude CLI is not installed."""
|
|
187
188
|
# Setup mocks
|
|
188
189
|
mock_config.claude_config_dir = '/tmp/test-claude-config'
|
|
189
190
|
mock_config.ensure_directories = Mock()
|
|
190
191
|
mock_subprocess.side_effect = FileNotFoundError()
|
|
191
|
-
|
|
192
|
+
|
|
192
193
|
runner = CliRunner()
|
|
193
|
-
result = runner.invoke(cli, ['claude
|
|
194
|
-
|
|
194
|
+
result = runner.invoke(cli, ['claude', '--auth'])
|
|
195
|
+
|
|
195
196
|
assert result.exit_code == 1
|
|
196
197
|
assert "Claude CLI not found" in result.output
|
|
197
|
-
assert "npm install -g @anthropic-ai/claude-cli" in result.output
|
|
198
|
+
assert "npm install -g @anthropic-ai/claude-cli" in result.output
|
|
199
|
+
|
|
200
|
+
def test_claude_missing_args(self):
|
|
201
|
+
"""Test claude command without required args (not using --auth)."""
|
|
202
|
+
runner = CliRunner()
|
|
203
|
+
result = runner.invoke(cli, ['claude'])
|
|
204
|
+
|
|
205
|
+
assert result.exit_code != 0
|
|
206
|
+
assert "DEV_NAME and PROMPT are required unless using --auth" in result.output
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|