forgex-cli 0.1.1__tar.gz → 0.1.3__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.
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/PKG-INFO +1 -1
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/__init__.py +1 -1
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/config.py +82 -12
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/repl.py +15 -18
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/.github/workflows/ci.yml +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/.github/workflows/release.yml +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/.gitignore +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/CHANGELOG.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/LICENSE +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/README.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/SECURITY.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/agents.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/architecture.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/configuration.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/decisions.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/providers.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/quickstart.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/sandbox.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/docs/security.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/examples/demo-project/README.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/examples/demo-project/pyproject.toml +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/examples/demo-project/src/demo/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/examples/demo-project/src/demo/calc.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/examples/demo-project/tests/test_calc.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/pyproject.toml +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/__main__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/architect.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/base.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/builder.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/ensemble.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/expert_coder.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/agents/supervisor.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/app.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/build_views.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/build.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/checkpoint.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/context.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/diff.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/do_cmd.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/doctor.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/fix.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/init.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/logs.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/models.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/plan.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/review.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/rollback.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/run.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/status.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/supervise.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/task.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/tools.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/commands/verify.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/context_memory.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/intent.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/progress.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/runtime.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/session.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/cli/ui.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/config/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/config/loader.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/config/schema.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/context/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/context/chunker.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/context/dependency_graph.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/context/indexer.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/context/manager.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/context/retriever.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/base.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/capabilities.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/factory.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/groq.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/local.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/openai_compatible.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/registry.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/models/router.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/checkpoints.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/engine.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/events.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/lockfile.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/retry.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/state.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/stores.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/orchestration/workflow.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/project.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/prompts/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/prompts/architect.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/prompts/builder.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/prompts/expert_coder.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/prompts/reviewer.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/prompts/supervisor.md +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/research/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/research/citations.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/research/engine.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/research/sources.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/sandbox/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/sandbox/executor.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/sandbox/permissions.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/sandbox/policies.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/agent.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/checkpoint.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/error.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/event.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/plan.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/project.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/research.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/result.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/schemas/task.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/storage/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/storage/database.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/storage/migrations.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/storage/repositories.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/base.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/dependencies.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/filesystem.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/git.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/project.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/search.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/terminal.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/testing.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/tools/verification.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/utils/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/utils/logging.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/utils/paths.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/src/forgex/utils/redact.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/conftest.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/__init__.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_agents_architect.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_builder_agent.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_checkpoints.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_build.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_checkpoint.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_do.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_models.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_plan.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_review.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_surface.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_task.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_tools.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_cli_verify.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_config.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_config_env.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_config_setup.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_context_agents.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_context_manager.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_context_memory.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_engine.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_ensemble.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_events.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_executor.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_expert_coder.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_fix.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_indexer.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_init.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_intent.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_large_repo.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_offline.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_packaging.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_paths.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_permissions.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_plan_schemas.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_policies.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_project.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_providers.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_recovery.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_redact.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_registry.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_repl.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_research.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_retriever.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_retry.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_router.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_run_cli.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_runtime.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_security.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_session.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_state.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_storage.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_stores.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_supervisor.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_tasks.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_tools_dependencies.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_tools_filesystem.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_tools_git.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_tools_search.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_tools_terminal.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_verification.py +0 -0
- {forgex_cli-0.1.1 → forgex_cli-0.1.3}/tests/unit/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forgex-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: FORGEX — multi-agent AI software engineering. Plan. Build. Refine. Verify.
|
|
5
5
|
Project-URL: Homepage, https://github.com/B-max12/Forgex
|
|
6
6
|
Project-URL: Repository, https://github.com/B-max12/Forgex
|
|
@@ -145,11 +145,43 @@ def _test_provider_connectivity(provider: str, base_url: str | None, api_key: st
|
|
|
145
145
|
return False, str(exc)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
def _strip_quotes(value: str) -> str:
|
|
149
|
+
"""Strip surrounding single or double quotes from user input."""
|
|
150
|
+
v = value.strip()
|
|
151
|
+
if len(v) >= 2 and v[0] == v[-1] and v[0] in ("'", '"'):
|
|
152
|
+
return v[1:-1]
|
|
153
|
+
return v
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _fetch_models(base_url: str, api_key: str | None) -> list[str]:
|
|
157
|
+
"""Fetch the list of available model IDs from an OpenAI-compatible /models endpoint."""
|
|
158
|
+
import httpx
|
|
159
|
+
|
|
160
|
+
url = base_url.rstrip("/") + "/models"
|
|
161
|
+
headers: dict[str, str] = {}
|
|
162
|
+
if api_key:
|
|
163
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
164
|
+
try:
|
|
165
|
+
with httpx.Client(timeout=8.0) as client:
|
|
166
|
+
resp = client.get(url, headers=headers)
|
|
167
|
+
if resp.status_code == 200:
|
|
168
|
+
data = resp.json()
|
|
169
|
+
models = data.get("data", [])
|
|
170
|
+
return sorted([m["id"] for m in models if isinstance(m, dict) and "id" in m])
|
|
171
|
+
except Exception:
|
|
172
|
+
pass
|
|
173
|
+
return []
|
|
174
|
+
|
|
175
|
+
|
|
148
176
|
@app.command("setup")
|
|
149
177
|
def setup_command(
|
|
150
178
|
ctx: typer.Context,
|
|
151
179
|
) -> None:
|
|
152
180
|
"""Interactive setup wizard for provider credentials and defaults."""
|
|
181
|
+
from rich.console import Console
|
|
182
|
+
from rich.table import Table
|
|
183
|
+
|
|
184
|
+
console = Console()
|
|
153
185
|
ui = ui_from(ctx)
|
|
154
186
|
ui.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
155
187
|
ui.text("FORGEX Configuration Setup Wizard — Welcome!")
|
|
@@ -165,37 +197,74 @@ def setup_command(
|
|
|
165
197
|
base_url = None
|
|
166
198
|
api_key_env: str | None = "FORGEX_GROQ_API_KEY"
|
|
167
199
|
api_key = None
|
|
200
|
+
provider_name = prov
|
|
168
201
|
|
|
169
202
|
if prov == "groq":
|
|
170
203
|
provider_name = "groq"
|
|
171
|
-
|
|
172
|
-
|
|
204
|
+
base_url = "https://api.groq.com/openai/v1"
|
|
205
|
+
api_key = _strip_quotes(Prompt.ask("Enter your Groq API key", password=True, default=""))
|
|
173
206
|
elif prov == "openai":
|
|
174
207
|
provider_name = "openai_compatible"
|
|
175
208
|
api_key_env = "FORGEX_OPENAI_API_KEY"
|
|
176
|
-
|
|
177
|
-
|
|
209
|
+
base_url = "https://api.openai.com/v1"
|
|
210
|
+
api_key = _strip_quotes(Prompt.ask("Enter your OpenAI API key", password=True, default=""))
|
|
178
211
|
elif prov == "local":
|
|
179
212
|
provider_name = "local"
|
|
180
|
-
base_url = Prompt.ask("Enter local OpenAI-compatible endpoint", default="http://localhost:11434/v1")
|
|
213
|
+
base_url = _strip_quotes(Prompt.ask("Enter local OpenAI-compatible endpoint", default="http://localhost:11434/v1"))
|
|
181
214
|
api_key_env = None
|
|
182
|
-
default_model = Prompt.ask("Enter model name", default="llama3.1:8b")
|
|
183
215
|
else:
|
|
184
|
-
provider_name = Prompt.ask("Enter custom provider name", default="custom")
|
|
185
|
-
base_url = Prompt.ask("Enter base URL", default="http://localhost:8080/v1")
|
|
216
|
+
provider_name = _strip_quotes(Prompt.ask("Enter custom provider name", default="custom"))
|
|
217
|
+
base_url = _strip_quotes(Prompt.ask("Enter base URL (no surrounding quotes)", default="http://localhost:8080/v1"))
|
|
186
218
|
api_key_env = f"FORGEX_{provider_name.upper()}_API_KEY"
|
|
187
|
-
api_key = Prompt.ask("Enter API key (if required)", password=True, default="")
|
|
188
|
-
default_model = Prompt.ask("Enter default model", default="qwen2.5:14b")
|
|
219
|
+
api_key = _strip_quotes(Prompt.ask("Enter API key (if required)", password=True, default=""))
|
|
189
220
|
|
|
190
221
|
# Health check verification
|
|
222
|
+
effective_url = base_url or ""
|
|
191
223
|
if api_key or (prov == "local" and base_url):
|
|
192
|
-
ui.text(f"
|
|
224
|
+
ui.text(f"\nTesting connectivity to {provider_name}...")
|
|
193
225
|
ok, detail = _test_provider_connectivity(provider_name, base_url, api_key)
|
|
194
226
|
if ok:
|
|
195
227
|
ui.ok(f"Provider connection verified: {detail}")
|
|
196
228
|
else:
|
|
197
229
|
ui.warn(f"Provider test: {detail} (proceeding; test with `forgex doctor --ping`)")
|
|
198
230
|
|
|
231
|
+
# Fetch and display available models
|
|
232
|
+
default_model = "llama-3.3-70b-versatile" if prov == "groq" else (
|
|
233
|
+
"gpt-4o-mini" if prov == "openai" else "llama3.1:8b"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if effective_url:
|
|
237
|
+
ui.text(f"\nFetching available models from {provider_name}...")
|
|
238
|
+
available = _fetch_models(effective_url, api_key)
|
|
239
|
+
if available:
|
|
240
|
+
table = Table(title="Available Models", show_header=True, header_style="bold cyan", border_style="dim")
|
|
241
|
+
table.add_column("#", style="dim", width=4)
|
|
242
|
+
table.add_column("Model ID", style="green")
|
|
243
|
+
for i, m in enumerate(available, 1):
|
|
244
|
+
table.add_row(str(i), m)
|
|
245
|
+
console.print(table)
|
|
246
|
+
|
|
247
|
+
choice = Prompt.ask(
|
|
248
|
+
f"Select model (enter number 1-{len(available)} or type a model name)",
|
|
249
|
+
default="1",
|
|
250
|
+
)
|
|
251
|
+
choice = choice.strip()
|
|
252
|
+
try:
|
|
253
|
+
idx = int(choice)
|
|
254
|
+
if 1 <= idx <= len(available):
|
|
255
|
+
default_model = available[idx - 1]
|
|
256
|
+
else:
|
|
257
|
+
default_model = choice
|
|
258
|
+
except ValueError:
|
|
259
|
+
default_model = _strip_quotes(choice)
|
|
260
|
+
|
|
261
|
+
ui.ok(f"Selected model: {default_model}")
|
|
262
|
+
else:
|
|
263
|
+
ui.warn("Could not fetch model list from provider.")
|
|
264
|
+
default_model = _strip_quotes(Prompt.ask("Enter default model name", default=default_model))
|
|
265
|
+
else:
|
|
266
|
+
default_model = _strip_quotes(Prompt.ask("Enter default model name", default=default_model))
|
|
267
|
+
|
|
199
268
|
models = {
|
|
200
269
|
"architect": default_model,
|
|
201
270
|
"builder": default_model,
|
|
@@ -205,7 +274,7 @@ def setup_command(
|
|
|
205
274
|
|
|
206
275
|
if Confirm.ask("Customize models for specific agent roles?", default=False):
|
|
207
276
|
for role in ("architect", "builder", "expert_coder", "supervisor"):
|
|
208
|
-
models[role] = Prompt.ask(f"Model for {role}", default=default_model)
|
|
277
|
+
models[role] = _strip_quotes(Prompt.ask(f"Model for {role}", default=default_model))
|
|
209
278
|
|
|
210
279
|
use_sandbox = Confirm.ask("Enable Docker sandbox for command execution?", default=False)
|
|
211
280
|
mode = Prompt.ask("Approval mode for commands and network", choices=["auto", "ask", "safe"], default="ask")
|
|
@@ -223,3 +292,4 @@ def setup_command(
|
|
|
223
292
|
g_dir = global_config_path().parent
|
|
224
293
|
apply_setup(answers, global_dir=g_dir)
|
|
225
294
|
ui.ok(f"Setup complete! Config written to {g_dir}")
|
|
295
|
+
|
|
@@ -168,11 +168,12 @@ class ForgexREPL:
|
|
|
168
168
|
|
|
169
169
|
def _run_setup_wizard(self) -> None:
|
|
170
170
|
"""Run the setup wizard directly (not through CliRunner) so it can read real stdin."""
|
|
171
|
+
import click
|
|
171
172
|
from forgex.cli.commands.config import setup_command
|
|
172
173
|
|
|
173
|
-
# Build a minimal typer.Context so ui_from() can work
|
|
174
174
|
try:
|
|
175
|
-
|
|
175
|
+
# Create a plain click Context — setup_command only needs ctx.obj for ui_from()
|
|
176
|
+
ctx = click.Context(click.Command("setup"))
|
|
176
177
|
ctx.obj = {"verbose": False, "quiet": False, "json": False, "plain": False}
|
|
177
178
|
setup_command(ctx)
|
|
178
179
|
self.session.refresh()
|
|
@@ -341,30 +342,26 @@ class ForgexREPL:
|
|
|
341
342
|
break
|
|
342
343
|
|
|
343
344
|
def _run_cli_command(self, args: list[str]) -> bool:
|
|
344
|
-
"""Invoke a Typer CLI command within the REPL session.
|
|
345
|
+
"""Invoke a Typer CLI command within the REPL session.
|
|
346
|
+
|
|
347
|
+
Uses subprocess to run each command so that:
|
|
348
|
+
1. Each command gets a fresh event loop (fixes 'Event loop is closed')
|
|
349
|
+
2. Interactive prompts can read real stdin
|
|
350
|
+
3. Rich output goes directly to the terminal
|
|
351
|
+
"""
|
|
345
352
|
# Interactive commands that need real stdin must bypass CliRunner
|
|
346
353
|
if args == ["config", "setup"]:
|
|
347
354
|
self._run_setup_wizard()
|
|
348
355
|
return True
|
|
349
356
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
from forgex.cli.app import app
|
|
357
|
+
import subprocess
|
|
353
358
|
|
|
354
|
-
runner = CliRunner()
|
|
355
359
|
success = False
|
|
356
360
|
try:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
sys.stdout.flush()
|
|
362
|
-
if result.exit_code != 0 and result.exception:
|
|
363
|
-
if isinstance(result.exception, ForgexError):
|
|
364
|
-
self.console.print(f"[red]✗[/red] {result.exception}")
|
|
365
|
-
elif not isinstance(result.exception, SystemExit):
|
|
366
|
-
self.console.print(f"[red]Error:[/red] {result.exception}")
|
|
367
|
-
success = result.exit_code == 0
|
|
361
|
+
# Run as a subprocess so each command gets a fresh Python/asyncio state
|
|
362
|
+
cmd = [sys.executable, "-m", "forgex", *args]
|
|
363
|
+
result = subprocess.run(cmd, cwd=os.getcwd())
|
|
364
|
+
success = result.returncode == 0
|
|
368
365
|
except ForgexError as exc:
|
|
369
366
|
self.console.print(f"[red]✗[/red] {exc}")
|
|
370
367
|
except KeyboardInterrupt:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|