devops-bot-sdk 1.7.6__tar.gz → 1.7.20__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.
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/PKG-INFO +1 -1
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/devops_bot_sdk.egg-info/SOURCES.txt +12 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/cli.py +530 -0
- devops_bot_sdk-1.7.20/sdk/faqs.py +62 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/helpdesk_kb.md +59 -2
- devops_bot_sdk-1.7.20/sdk/instructions_template.py +374 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ipc/handlers.py +20 -4
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/log_colors.py +47 -6
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/platform_compat.py +5 -4
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/run_auto.py +59 -5
- devops_bot_sdk-1.7.20/sdk/scope_plan.py +148 -0
- devops_bot_sdk-1.7.20/sdk/ticket_bulk.py +214 -0
- devops_bot_sdk-1.7.20/sdk/ticket_plan.py +571 -0
- devops_bot_sdk-1.7.20/sdk/ticket_pools.py +272 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/tickets.py +36 -5
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/api.py +184 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/models.py +78 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/service.py +173 -23
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/app.js +597 -2
- devops_bot_sdk-1.7.20/sdk/ui/static/faqs.html +37 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/help.html +2 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/styles.css +87 -2
- devops_bot_sdk-1.7.20/sdk/ui/static/tickets.html +331 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/updater.py +22 -0
- devops_bot_sdk-1.7.20/tests/test_faqs.py +46 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_infographic_data.py +1 -1
- devops_bot_sdk-1.7.20/tests/test_instructions_template.py +158 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_log_colors_backlog.py +55 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_mcp_route_contract.py +9 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_parallel_tickets.py +1 -1
- devops_bot_sdk-1.7.20/tests/test_ticket_bulk.py +194 -0
- devops_bot_sdk-1.7.20/tests/test_ticket_plan.py +262 -0
- devops_bot_sdk-1.7.20/tests/test_ticket_pools.py +252 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_update_timer.py +121 -3
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_windows_task_paths.py +3 -0
- devops_bot_sdk-1.7.6/sdk/ui/static/tickets.html +0 -120
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/README.md +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/pyproject.toml +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agent_sharing.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/claude_usage.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/client.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/config.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/custom_agents.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/deferred_tickets.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/fallback_handoff.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/help_actions.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/helpdesk.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/model_policy.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/router.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/schedule_tickets.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/test.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ticket_board.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/vps_identity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/setup.cfg +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_claude_runs.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_live_tracking.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_crud_cli.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_sharing.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_approval_park.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_auto_ticket_queue.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_claude_usage_gate.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_create_safe_retry.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_deferred_tickets.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_fallback_cli.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_fallback_cli_upgrade.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_help_actions.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_help_attachments.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_help_questions.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_helpdesk.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_limit_fallback_handoff.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_model_policy.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_panel_help.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_panel_ticket_schedules.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_schedule_tickets.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_scheduler_transient_retry.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ticket_board.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ticket_cli.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ticket_schedule_and_agent.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_updater_ignores_cwd.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_vps_only_reporting.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_windows_panel_task.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_windows_stop_and_pidfile.py +0 -0
- {devops_bot_sdk-1.7.6 → devops_bot_sdk-1.7.20}/tests/test_write_hook.py +0 -0
|
@@ -21,6 +21,7 @@ sdk/departments.py
|
|
|
21
21
|
sdk/estimator.py
|
|
22
22
|
sdk/exceptions.py
|
|
23
23
|
sdk/fallback_handoff.py
|
|
24
|
+
sdk/faqs.py
|
|
24
25
|
sdk/firewall.py
|
|
25
26
|
sdk/formats.py
|
|
26
27
|
sdk/git_ops.py
|
|
@@ -31,6 +32,7 @@ sdk/graphify.py
|
|
|
31
32
|
sdk/help_actions.py
|
|
32
33
|
sdk/helpdesk.py
|
|
33
34
|
sdk/helpdesk_kb.md
|
|
35
|
+
sdk/instructions_template.py
|
|
34
36
|
sdk/intake.py
|
|
35
37
|
sdk/journal.py
|
|
36
38
|
sdk/local_exec.py
|
|
@@ -49,6 +51,7 @@ sdk/router.py
|
|
|
49
51
|
sdk/run_auto.py
|
|
50
52
|
sdk/schedule_tickets.py
|
|
51
53
|
sdk/scheduler.py
|
|
54
|
+
sdk/scope_plan.py
|
|
52
55
|
sdk/self_report.py
|
|
53
56
|
sdk/self_report_local.py
|
|
54
57
|
sdk/sse.py
|
|
@@ -58,7 +61,10 @@ sdk/task_types.py
|
|
|
58
61
|
sdk/test.py
|
|
59
62
|
sdk/test_pipeline.py
|
|
60
63
|
sdk/ticket_board.py
|
|
64
|
+
sdk/ticket_bulk.py
|
|
61
65
|
sdk/ticket_fields.py
|
|
66
|
+
sdk/ticket_plan.py
|
|
67
|
+
sdk/ticket_pools.py
|
|
62
68
|
sdk/tickets.py
|
|
63
69
|
sdk/updater.py
|
|
64
70
|
sdk/url_access.py
|
|
@@ -144,6 +150,7 @@ sdk/ui/static/app.js
|
|
|
144
150
|
sdk/ui/static/chat.html
|
|
145
151
|
sdk/ui/static/custom-agents.html
|
|
146
152
|
sdk/ui/static/default-agents.html
|
|
153
|
+
sdk/ui/static/faqs.html
|
|
147
154
|
sdk/ui/static/help.html
|
|
148
155
|
sdk/ui/static/index.html
|
|
149
156
|
sdk/ui/static/report.html
|
|
@@ -193,6 +200,7 @@ tests/test_epic_session_isolation.py
|
|
|
193
200
|
tests/test_estimator.py
|
|
194
201
|
tests/test_fallback_cli.py
|
|
195
202
|
tests/test_fallback_cli_upgrade.py
|
|
203
|
+
tests/test_faqs.py
|
|
196
204
|
tests/test_firewall_open_on_update.py
|
|
197
205
|
tests/test_formats.py
|
|
198
206
|
tests/test_git_action.py
|
|
@@ -207,6 +215,7 @@ tests/test_help_attachments.py
|
|
|
207
215
|
tests/test_help_questions.py
|
|
208
216
|
tests/test_helpdesk.py
|
|
209
217
|
tests/test_infographic_data.py
|
|
218
|
+
tests/test_instructions_template.py
|
|
210
219
|
tests/test_intake_job_outcome.py
|
|
211
220
|
tests/test_intake_plan_review.py
|
|
212
221
|
tests/test_journal.py
|
|
@@ -245,8 +254,11 @@ tests/test_swagger_command.py
|
|
|
245
254
|
tests/test_task_file_and_plan_run.py
|
|
246
255
|
tests/test_task_file_format_doc.py
|
|
247
256
|
tests/test_ticket_board.py
|
|
257
|
+
tests/test_ticket_bulk.py
|
|
248
258
|
tests/test_ticket_cli.py
|
|
249
259
|
tests/test_ticket_fields.py
|
|
260
|
+
tests/test_ticket_plan.py
|
|
261
|
+
tests/test_ticket_pools.py
|
|
250
262
|
tests/test_ticket_run_resolve_by_id.py
|
|
251
263
|
tests/test_ticket_schedule_and_agent.py
|
|
252
264
|
tests/test_ticket_speed_fixes.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.20
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -34,7 +34,7 @@ Rules:
|
|
|
34
34
|
- All data egress through submit_webhook only
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "1.7.
|
|
37
|
+
__version__ = "1.7.20" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
38
38
|
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
39
39
|
|
|
40
40
|
from sdk.client import BackendClient
|
|
@@ -202,6 +202,9 @@ def main() -> None:
|
|
|
202
202
|
if noun == "tickets":
|
|
203
203
|
_ticket_list(rest)
|
|
204
204
|
return
|
|
205
|
+
if noun == "pool" or noun == "pools":
|
|
206
|
+
_pool_command(rest)
|
|
207
|
+
return
|
|
205
208
|
|
|
206
209
|
if noun == "agentd":
|
|
207
210
|
_agentd_command(rest)
|
|
@@ -267,6 +270,7 @@ def _print_usage() -> None:
|
|
|
267
270
|
print(" agentos agent <set-key NAME KEY[=VALUE]…|keys NAME|rm-key NAME KEY…> # persistent API keys (custom agents)")
|
|
268
271
|
print(" agentos ticket <list|create|update KEY_OR_ID|delete KEY_OR_ID|help>")
|
|
269
272
|
print(" agentos tickets [--status S] [--project KEY] [--all] [--json] # how many To Do / In Progress / … and which")
|
|
273
|
+
print(" agentos pool <list|show|create|add|remove|move|order|set|run|delete|help> # ticket pools: up to 50 tickets run together, in order")
|
|
270
274
|
print(" agentos integration <add PATH|test NAME|list|rm NAME>")
|
|
271
275
|
print(" agentos default-agents list [--department-id UUID] # read-only; agents are created in the web app")
|
|
272
276
|
print(" agentos agentd <start [--foreground]|status>")
|
|
@@ -1203,6 +1207,20 @@ def _ticket_usage() -> None:
|
|
|
1203
1207
|
print(" [--instructions TEXT | --instructions-file PATH] [--priority P] [--status S]")
|
|
1204
1208
|
print(" [--points N] [--assignee EMAIL] [--type TYPE]")
|
|
1205
1209
|
print(" agentos ticket show KEY_OR_ID [--json] # summary, status, description, humanInstructions")
|
|
1210
|
+
print(" agentos ticket bulk-update KEY… | --pool NAME [--status S] [--priority P] [--type T] [--points N]")
|
|
1211
|
+
print(" [--assignee EMAIL] [--description TEXT|--description-file PATH] [--instructions TEXT|--instructions-file PATH]")
|
|
1212
|
+
print(" [--description-mode M] [--instructions-mode M] [--clear-description] [--clear-instructions]")
|
|
1213
|
+
print(" # one or a few fields on up to 50 tickets; M: append (default) | prepend | replace")
|
|
1214
|
+
print(" agentos ticket instructions KEY… | --pool NAME (--text TEXT | --file PATH)")
|
|
1215
|
+
print(" [--replace | --prepend | --clear] # Human Instructions on up to 50 tickets at once (default: append)")
|
|
1216
|
+
print(" agentos ticket bulk-create PLAN.md|PLAN.json [--project KEY] [--instructions TEXT | --instructions-file PATH]")
|
|
1217
|
+
print(" [--type T] [--priority P] [--pool NAME | --module-pools] [--dry-run] [--example]")
|
|
1218
|
+
print(" # many tickets from one plan: modules, cross-module, Epics → Stories, Tasks, Bugs")
|
|
1219
|
+
print(" agentos ticket placeholders [--template KIND] # {ticket_key} {summary} {slug}… filled per ticket; KIND: development|content|research|design|general")
|
|
1220
|
+
print(" agentos ticket gen-instructions [KEY… | --pool NAME] [--kind KIND] [--notes TEXT] [--from-file PATH] [-o FILE]")
|
|
1221
|
+
print(" # the AI writes Human Instructions for any kind of work (picks it, or --kind), using the placeholders")
|
|
1222
|
+
print(" agentos ticket scope-to-plan SCOPE.md [--project KEY] [--modules \"A, B\"] [-o PLAN.md]")
|
|
1223
|
+
print(" # the AI drafts that plan from a detailed scope document (nothing is created)")
|
|
1206
1224
|
print(" agentos ticket delete KEY_OR_ID [--yes] # asks to confirm unless --yes")
|
|
1207
1225
|
print()
|
|
1208
1226
|
print(" TYPE: Epic|Story|Task|Bug|Sub-task (default Task) P: Lowest|Low|Medium|High|Highest (default Medium)")
|
|
@@ -1242,6 +1260,500 @@ def _parse_ticket_flags(args: list[str], *, creating: bool) -> dict[str, str]:
|
|
|
1242
1260
|
return fields
|
|
1243
1261
|
|
|
1244
1262
|
|
|
1263
|
+
_POOL_USAGE = """usage: agentos pool <command> — up to 50 tickets you run together, in your order
|
|
1264
|
+
|
|
1265
|
+
agentos pool list # every pool, its size and last run
|
|
1266
|
+
agentos pool show NAME # run order, each ticket's summary/status, instructions
|
|
1267
|
+
agentos pool create NAME KEY… [--instructions TEXT] [--parallel N] # new pool (keys in run order)
|
|
1268
|
+
agentos pool add NAME KEY… [--at POS] # add tickets (at the end, or at position POS)
|
|
1269
|
+
agentos pool remove NAME KEY… # take tickets out
|
|
1270
|
+
agentos pool move NAME KEY POS # move one ticket to position POS (1 = first)
|
|
1271
|
+
agentos pool order NAME KEY… # these first, in this order; the rest follow
|
|
1272
|
+
agentos pool set NAME [--instructions TEXT | --clear-instructions] [--parallel N] [--rename NEW]
|
|
1273
|
+
agentos pool run NAME [--parallel N] # run it now (= agentos-auto pool NAME)
|
|
1274
|
+
agentos pool delete NAME # remove the pool (tickets are untouched)
|
|
1275
|
+
|
|
1276
|
+
--instructions: applies to EVERY ticket in the pool, ahead of each ticket's own
|
|
1277
|
+
Human Instructions. --parallel: tickets at once, 1–8 (default 3) — never two on
|
|
1278
|
+
the same repo, so same-repo tickets still run in your order.
|
|
1279
|
+
"""
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
def _pool_opts(args: list[str], valued: tuple[str, ...], bare: tuple[str, ...] = ()) -> tuple[list[str], dict]:
|
|
1283
|
+
"""Split ``args`` into positionals and ``{flag: value}`` (bare flags → True)."""
|
|
1284
|
+
pos: list[str] = []
|
|
1285
|
+
opts: dict = {}
|
|
1286
|
+
i = 0
|
|
1287
|
+
while i < len(args):
|
|
1288
|
+
a = args[i]
|
|
1289
|
+
if a in valued:
|
|
1290
|
+
if i + 1 >= len(args):
|
|
1291
|
+
print(f"error: expected a value after {a}")
|
|
1292
|
+
sys.exit(1)
|
|
1293
|
+
opts[a] = args[i + 1]
|
|
1294
|
+
i += 2
|
|
1295
|
+
continue
|
|
1296
|
+
if a in bare:
|
|
1297
|
+
opts[a] = True
|
|
1298
|
+
elif a.startswith("--"):
|
|
1299
|
+
print(f"error: unknown option {a} — see --help")
|
|
1300
|
+
sys.exit(1)
|
|
1301
|
+
else:
|
|
1302
|
+
pos.append(a)
|
|
1303
|
+
i += 1
|
|
1304
|
+
return pos, opts
|
|
1305
|
+
|
|
1306
|
+
|
|
1307
|
+
def _pool_print(pool: dict, info: dict | None = None) -> None:
|
|
1308
|
+
from sdk import log_colors
|
|
1309
|
+
paint = log_colors.colorize if log_colors.enabled() else (lambda x: x)
|
|
1310
|
+
print(f"pool {pool['name']} — {len(pool['keys'])} ticket(s), up to {pool.get('parallel')} at once"
|
|
1311
|
+
+ (f", last run {pool['last_run_at']}" if pool.get("last_run_at") else ""))
|
|
1312
|
+
width = max(len(k) for k in pool["keys"]) if pool["keys"] else 0
|
|
1313
|
+
for n, k in enumerate(pool["keys"], 1):
|
|
1314
|
+
row = (info or {}).get(k)
|
|
1315
|
+
tail = f" {row['summary'][:70]} → {row['status']}" if row else (" (not found on the board)" if info else "")
|
|
1316
|
+
print(paint(f" {n:>2}. {k.ljust(width)}{tail}"))
|
|
1317
|
+
if pool.get("instructions"):
|
|
1318
|
+
print(" instructions (every ticket):")
|
|
1319
|
+
for line in pool["instructions"].splitlines():
|
|
1320
|
+
print(f" {line}")
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
def _pool_command(args: list[str]) -> None:
|
|
1324
|
+
"""`agentos pool …` — ticket pools (sdk/ticket_pools.py)."""
|
|
1325
|
+
from sdk import ticket_pools as TP
|
|
1326
|
+
|
|
1327
|
+
sub, rest = (args[0], args[1:]) if args else ("list", [])
|
|
1328
|
+
if sub in ("-h", "--help", "help"):
|
|
1329
|
+
print(_POOL_USAGE, end="")
|
|
1330
|
+
return
|
|
1331
|
+
|
|
1332
|
+
def _parallel(opts: dict) -> int | None:
|
|
1333
|
+
return int(opts["--parallel"]) if str(opts.get("--parallel", "")).isdigit() else (
|
|
1334
|
+
None if "--parallel" not in opts else -1)
|
|
1335
|
+
|
|
1336
|
+
try:
|
|
1337
|
+
if sub in ("list", "ls"):
|
|
1338
|
+
pools = TP.list_pools()
|
|
1339
|
+
if not pools:
|
|
1340
|
+
print("no pools yet — `agentos pool create NAME KEY… [--instructions TEXT]`")
|
|
1341
|
+
return
|
|
1342
|
+
for p in pools:
|
|
1343
|
+
print(f" {p['name']:<24} {len(p['keys']):>2} ticket(s) ×{p.get('parallel')}"
|
|
1344
|
+
f" {'instructions' if p.get('instructions') else '':<12}"
|
|
1345
|
+
f" {('last run ' + p['last_run_at']) if p.get('last_run_at') else 'never run'}")
|
|
1346
|
+
return
|
|
1347
|
+
if sub == "show":
|
|
1348
|
+
if not rest:
|
|
1349
|
+
print("usage: agentos pool show NAME")
|
|
1350
|
+
sys.exit(1)
|
|
1351
|
+
pool = TP.get(" ".join(rest))
|
|
1352
|
+
info = None
|
|
1353
|
+
try:
|
|
1354
|
+
from sdk.client import BackendClient
|
|
1355
|
+
info = asyncio.run(TP.describe(BackendClient.from_config(), pool["keys"])) or None
|
|
1356
|
+
except Exception: # noqa: BLE001 — no token / no backend: show the keys alone
|
|
1357
|
+
info = None
|
|
1358
|
+
_pool_print(pool, info)
|
|
1359
|
+
return
|
|
1360
|
+
if sub == "create":
|
|
1361
|
+
pos, opts = _pool_opts(rest, ("--instructions", "--parallel"))
|
|
1362
|
+
if len(pos) < 2:
|
|
1363
|
+
print("usage: agentos pool create NAME KEY… [--instructions TEXT] [--parallel N]")
|
|
1364
|
+
sys.exit(1)
|
|
1365
|
+
pool = TP.create(pos[0], pos[1:], opts.get("--instructions", ""), _parallel(opts))
|
|
1366
|
+
shown = f'"{pool["name"]}"' if " " in pool["name"] else pool["name"]
|
|
1367
|
+
print(f"✓ created pool {pool['name']} with {len(pool['keys'])} ticket(s) — "
|
|
1368
|
+
f"run it: agentos pool run {shown}")
|
|
1369
|
+
return
|
|
1370
|
+
if sub == "add":
|
|
1371
|
+
pos, opts = _pool_opts(rest, ("--at",))
|
|
1372
|
+
if len(pos) < 2:
|
|
1373
|
+
print("usage: agentos pool add NAME KEY… [--at POS]")
|
|
1374
|
+
sys.exit(1)
|
|
1375
|
+
pool = TP.add(pos[0], pos[1:], int(opts["--at"]) if "--at" in opts else None)
|
|
1376
|
+
elif sub in ("remove", "rm"):
|
|
1377
|
+
if len(rest) < 2:
|
|
1378
|
+
print("usage: agentos pool remove NAME KEY…")
|
|
1379
|
+
sys.exit(1)
|
|
1380
|
+
pool = TP.remove(rest[0], rest[1:])
|
|
1381
|
+
elif sub == "move":
|
|
1382
|
+
if len(rest) != 3 or not rest[2].isdigit():
|
|
1383
|
+
print("usage: agentos pool move NAME KEY POS")
|
|
1384
|
+
sys.exit(1)
|
|
1385
|
+
pool = TP.move(rest[0], rest[1], int(rest[2]))
|
|
1386
|
+
elif sub == "order":
|
|
1387
|
+
if len(rest) < 2:
|
|
1388
|
+
print("usage: agentos pool order NAME KEY…")
|
|
1389
|
+
sys.exit(1)
|
|
1390
|
+
pool = TP.order(rest[0], rest[1:])
|
|
1391
|
+
elif sub == "set":
|
|
1392
|
+
pos, opts = _pool_opts(rest, ("--instructions", "--parallel", "--rename"),
|
|
1393
|
+
("--clear-instructions",))
|
|
1394
|
+
if len(pos) != 1 or not opts:
|
|
1395
|
+
print("usage: agentos pool set NAME [--instructions TEXT | --clear-instructions] "
|
|
1396
|
+
"[--parallel N] [--rename NEW]")
|
|
1397
|
+
sys.exit(1)
|
|
1398
|
+
instr = "" if opts.get("--clear-instructions") else opts.get("--instructions")
|
|
1399
|
+
pool = TP.update(pos[0], instructions=instr, parallel=_parallel(opts),
|
|
1400
|
+
rename=opts.get("--rename"))
|
|
1401
|
+
elif sub == "delete":
|
|
1402
|
+
if not rest:
|
|
1403
|
+
print("usage: agentos pool delete NAME")
|
|
1404
|
+
sys.exit(1)
|
|
1405
|
+
pool = TP.delete(" ".join(rest))
|
|
1406
|
+
print(f"✓ deleted pool {pool['name']} (its {len(pool['keys'])} tickets are untouched)")
|
|
1407
|
+
return
|
|
1408
|
+
elif sub == "run":
|
|
1409
|
+
pos, opts = _pool_opts(rest, ("--parallel", "--workspace-root"), ("--foreground", "-f"))
|
|
1410
|
+
if not pos:
|
|
1411
|
+
print("usage: agentos pool run NAME [--parallel N]")
|
|
1412
|
+
sys.exit(1)
|
|
1413
|
+
from sdk import run_auto
|
|
1414
|
+
argv = ["agentos-auto", "pool", " ".join(pos)]
|
|
1415
|
+
for f in ("--parallel", "--workspace-root"):
|
|
1416
|
+
if f in opts:
|
|
1417
|
+
argv += [f, opts[f]]
|
|
1418
|
+
argv += [f for f in ("--foreground", "-f") if opts.get(f)]
|
|
1419
|
+
sys.argv = argv
|
|
1420
|
+
run_auto.main()
|
|
1421
|
+
return
|
|
1422
|
+
else:
|
|
1423
|
+
print(f"unknown pool command {sub!r}\n")
|
|
1424
|
+
print(_POOL_USAGE, end="")
|
|
1425
|
+
sys.exit(1)
|
|
1426
|
+
except TP.PoolError as exc:
|
|
1427
|
+
print(f"error: {exc}")
|
|
1428
|
+
sys.exit(1)
|
|
1429
|
+
_pool_print(pool)
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
def _read_text_arg(opts: dict, text_flag: str, file_flag: str) -> str | None:
|
|
1433
|
+
"""--text / --file twins; exits 1 on both or an unreadable file."""
|
|
1434
|
+
if text_flag in opts and file_flag in opts:
|
|
1435
|
+
print(f"error: pass {text_flag} or {file_flag}, not both")
|
|
1436
|
+
sys.exit(1)
|
|
1437
|
+
if file_flag in opts:
|
|
1438
|
+
try:
|
|
1439
|
+
return Path(opts[file_flag]).expanduser().read_text(encoding="utf-8")
|
|
1440
|
+
except OSError as exc:
|
|
1441
|
+
print(f"error: cannot read {opts[file_flag]}: {exc}")
|
|
1442
|
+
sys.exit(1)
|
|
1443
|
+
return opts.get(text_flag)
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
def _ticket_bulk_instructions(args: list[str]) -> None:
|
|
1447
|
+
"""`agentos ticket instructions KEY… (--text T | --file P) [--replace|--prepend|--clear]`."""
|
|
1448
|
+
from sdk import ticket_bulk
|
|
1449
|
+
from sdk.client import BackendClient
|
|
1450
|
+
|
|
1451
|
+
if not args or args[0] in ("-h", "--help", "help"):
|
|
1452
|
+
print("usage: agentos ticket instructions KEY… | --pool NAME (--text TEXT | --file PATH)")
|
|
1453
|
+
print(" [--replace | --prepend | --clear]")
|
|
1454
|
+
print(" Writes Human Instructions on every ticket given (at most 50). Default: APPEND to")
|
|
1455
|
+
print(" what each ticket already has (a block it already contains is not added twice).")
|
|
1456
|
+
sys.exit(0 if args else 1)
|
|
1457
|
+
keys, opts = _pool_opts(args, ("--text", "--file", "--pool"), ("--replace", "--prepend", "--clear", "--append"))
|
|
1458
|
+
modes = [m for m in ("replace", "prepend", "clear", "append") if opts.get("--" + m)]
|
|
1459
|
+
if len(modes) > 1:
|
|
1460
|
+
print("error: choose one of --append, --prepend, --replace, --clear")
|
|
1461
|
+
sys.exit(1)
|
|
1462
|
+
mode = modes[0] if modes else "append"
|
|
1463
|
+
text = _read_text_arg(opts, "--text", "--file") or ""
|
|
1464
|
+
if mode != "clear" and not text.strip():
|
|
1465
|
+
print("error: give the instructions with --text TEXT or --file PATH (or --clear)")
|
|
1466
|
+
sys.exit(1)
|
|
1467
|
+
keys = _bulk_keys(keys, opts.get("--pool"))
|
|
1468
|
+
res = asyncio.run(ticket_bulk.set_instructions(BackendClient.from_config(), keys, text, mode))
|
|
1469
|
+
if "updated" not in res:
|
|
1470
|
+
print(f"error: {res['error']}")
|
|
1471
|
+
sys.exit(1)
|
|
1472
|
+
verb = {"append": "appended to", "prepend": "prepended to", "replace": "replaced on",
|
|
1473
|
+
"clear": "cleared on"}[mode]
|
|
1474
|
+
_print_bulk_result(res, f"instructions {verb}")
|
|
1475
|
+
sys.exit(0 if res["ok"] else 1)
|
|
1476
|
+
|
|
1477
|
+
|
|
1478
|
+
def _bulk_keys(keys: list[str], pool: str | None) -> list[str]:
|
|
1479
|
+
from sdk import ticket_pools
|
|
1480
|
+
if pool:
|
|
1481
|
+
try:
|
|
1482
|
+
keys = keys + [k for k in ticket_pools.get(pool)["keys"] if k not in keys]
|
|
1483
|
+
except ticket_pools.PoolError as exc:
|
|
1484
|
+
print(f"error: {exc}")
|
|
1485
|
+
sys.exit(1)
|
|
1486
|
+
if not keys:
|
|
1487
|
+
print("error: which tickets? — give keys, or --pool NAME")
|
|
1488
|
+
sys.exit(1)
|
|
1489
|
+
return keys
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
def _print_bulk_result(res: dict, verb: str) -> None:
|
|
1493
|
+
if res["updated"]:
|
|
1494
|
+
print(f"✓ {verb} {len(res['updated'])} ticket(s): {', '.join(res['updated'])}")
|
|
1495
|
+
if res["unchanged"]:
|
|
1496
|
+
print(f" unchanged (already had it): {', '.join(res['unchanged'])}")
|
|
1497
|
+
for p in res.get("partial") or []:
|
|
1498
|
+
print(f"⚠ {p['key']}: the backend accepted but did NOT apply {', '.join(p['not_applied'])}")
|
|
1499
|
+
for f in res["failed"]:
|
|
1500
|
+
print(f"✗ {f['key']}: {f['error']}")
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
def _ticket_bulk_update(args: list[str]) -> None:
|
|
1504
|
+
"""`agentos ticket bulk-update KEY…|--pool NAME --status S --priority P …`."""
|
|
1505
|
+
from sdk import ticket_bulk
|
|
1506
|
+
from sdk.client import BackendClient
|
|
1507
|
+
|
|
1508
|
+
if not args or args[0] in ("-h", "--help", "help"):
|
|
1509
|
+
print("usage: agentos ticket bulk-update KEY… | --pool NAME [one or more fields]")
|
|
1510
|
+
print(" --status S --priority P --type T --points N --assignee EMAIL")
|
|
1511
|
+
print(" --description TEXT | --description-file PATH [--description-mode append|prepend|replace]")
|
|
1512
|
+
print(" --instructions TEXT | --instructions-file PATH [--instructions-mode append|prepend|replace]")
|
|
1513
|
+
print(" --clear-description --clear-instructions")
|
|
1514
|
+
print(" The same change on every ticket given (at most 50); tickets that already match are")
|
|
1515
|
+
print(" left alone. Text fields APPEND by default.")
|
|
1516
|
+
sys.exit(0 if args else 1)
|
|
1517
|
+
valued = ("--pool", "--status", "--priority", "--type", "--points", "--assignee", "--description",
|
|
1518
|
+
"--description-file", "--instructions", "--instructions-file", "--description-mode",
|
|
1519
|
+
"--instructions-mode")
|
|
1520
|
+
keys, opts = _pool_opts(args, valued, ("--clear-description", "--clear-instructions"))
|
|
1521
|
+
keys = _bulk_keys(keys, opts.get("--pool"))
|
|
1522
|
+
fields: dict = {k: opts[f] for f, k in (("--status", "status"), ("--priority", "priority"),
|
|
1523
|
+
("--type", "type"), ("--points", "points"),
|
|
1524
|
+
("--assignee", "assigneeEmail")) if f in opts}
|
|
1525
|
+
modes: dict = {}
|
|
1526
|
+
for name, wire in (("description", "description"), ("instructions", "humanInstructions")):
|
|
1527
|
+
text = _read_text_arg(opts, f"--{name}", f"--{name}-file")
|
|
1528
|
+
if opts.get(f"--clear-{name}"):
|
|
1529
|
+
if text is not None:
|
|
1530
|
+
print(f"error: --clear-{name} and --{name} together make no sense")
|
|
1531
|
+
sys.exit(1)
|
|
1532
|
+
modes[wire] = "clear"
|
|
1533
|
+
elif text is not None:
|
|
1534
|
+
fields[wire] = text
|
|
1535
|
+
modes[wire] = opts.get(f"--{name}-mode", "append")
|
|
1536
|
+
if modes[wire] == "clear":
|
|
1537
|
+
print(f"error: use --clear-{name} to clear it")
|
|
1538
|
+
sys.exit(1)
|
|
1539
|
+
if not fields and not modes:
|
|
1540
|
+
print("error: nothing to update — pass at least one field (see --help)")
|
|
1541
|
+
sys.exit(1)
|
|
1542
|
+
res = asyncio.run(ticket_bulk.update_many(BackendClient.from_config(), keys, fields, modes))
|
|
1543
|
+
if "updated" not in res:
|
|
1544
|
+
print(f"error: {res['error']}")
|
|
1545
|
+
sys.exit(1)
|
|
1546
|
+
_print_bulk_result(res, "updated " + ", ".join(res["fields"]) + " on")
|
|
1547
|
+
sys.exit(0 if res["ok"] else 1)
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
def _pname(name: str) -> str:
|
|
1551
|
+
return f'"{name}"' if " " in name else name
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
def _print_plan_tree(res: dict) -> None:
|
|
1555
|
+
"""Dry-run view: modules → Epics → Stories, with types, statuses and instructions."""
|
|
1556
|
+
rows = res["tickets"]
|
|
1557
|
+
c = res.get("counts") or {}
|
|
1558
|
+
print(f"would create {len(rows)} ticket(s) in {res['project']}: {c.get('epic', 0)} epic(s), "
|
|
1559
|
+
f"{c.get('story', 0)} story/child ticket(s), {c.get('ticket', 0)} standalone")
|
|
1560
|
+
current = object()
|
|
1561
|
+
for i, t in enumerate(rows):
|
|
1562
|
+
if t["module"] != current:
|
|
1563
|
+
current = t["module"]
|
|
1564
|
+
m = next((x for x in res.get("modules") or [] if x["name"] == current), None)
|
|
1565
|
+
print(f"\n{current or '(no module)'}" + (" — instructions: " + m["instructions"].splitlines()[0][:70]
|
|
1566
|
+
if m and m.get("instructions") else ""))
|
|
1567
|
+
indent = " " if t["parent"] is not None else " "
|
|
1568
|
+
kind = t.get("type") or "Task"
|
|
1569
|
+
extra = [x for x in (t.get("priority"), f"{t['points']} pts" if t.get("points") else "",
|
|
1570
|
+
f"→ {t['status']}" if t.get("status") else "") if x]
|
|
1571
|
+
note = f" ({t['children']} children; set Done so the loop won't re-decompose it)" if t["kind"] == "epic" and t["children"] \
|
|
1572
|
+
else " (no children: the loop will decompose it)" if t["kind"] == "epic" else ""
|
|
1573
|
+
print(f"{indent}{i + 1:>3}. {kind:<8} {t['summary']}" + (f" [{' · '.join(extra)}]" if extra else "") + note)
|
|
1574
|
+
for w in res.get("warnings") or []:
|
|
1575
|
+
print(f"warning: {w}")
|
|
1576
|
+
for p in res.get("pools") or []:
|
|
1577
|
+
print(f"pool {p['name']}: {p['count']} ticket(s)")
|
|
1578
|
+
|
|
1579
|
+
|
|
1580
|
+
def _ticket_bulk_create(args: list[str]) -> None:
|
|
1581
|
+
"""`agentos ticket bulk-create PLAN [--project KEY] [--instructions …] [--pool NAME|--module-pools] [--dry-run]`."""
|
|
1582
|
+
from sdk import ticket_plan
|
|
1583
|
+
|
|
1584
|
+
if args and args[0] == "--example":
|
|
1585
|
+
print(ticket_plan.EXAMPLE_PLAN, end="")
|
|
1586
|
+
return
|
|
1587
|
+
if not args or args[0] in ("-h", "--help", "help"):
|
|
1588
|
+
print("usage: agentos ticket bulk-create PLAN.md|PLAN.json [--project KEY]")
|
|
1589
|
+
print(" [--instructions TEXT | --instructions-file PATH] [--type T] [--priority P]")
|
|
1590
|
+
print(" [--pool NAME] [--module-pools [--pool-prefix TEXT]] [--no-module-prefix] [--dry-run]")
|
|
1591
|
+
print(" Sections: `# Module: NAME` (its Instructions: apply to its tickets — e.g. the repo),")
|
|
1592
|
+
print(" `# Cross-module: A, B` (inherits every named module's instructions), `## Epic: T` with")
|
|
1593
|
+
print(" `### Story: T` children, standalone `## Task:` / `## Bug:`. Simple plans: one ticket per `## Summary`.")
|
|
1594
|
+
print(" An Epic created with children is set Done (the loop won't decompose it again).")
|
|
1595
|
+
print(f" At most {ticket_plan.MAX_PLAN} tickets. --pool: one pool of every runnable ticket; --module-pools:")
|
|
1596
|
+
print(" one pool per module (+ cross-module). Summaries get a [Module] prefix unless --no-module-prefix.")
|
|
1597
|
+
print(" Example: agentos ticket bulk-create --example > plan.md From a scope document: agentos ticket scope-to-plan")
|
|
1598
|
+
sys.exit(0 if args else 1)
|
|
1599
|
+
pos, opts = _pool_opts(args, ("--project", "--instructions", "--instructions-file", "--type",
|
|
1600
|
+
"--priority", "--pool", "--pool-prefix"),
|
|
1601
|
+
("--dry-run", "--yes", "--module-pools", "--no-module-prefix"))
|
|
1602
|
+
if len(pos) != 1:
|
|
1603
|
+
print("error: give exactly one plan file (use - to read stdin)")
|
|
1604
|
+
sys.exit(1)
|
|
1605
|
+
try:
|
|
1606
|
+
text = sys.stdin.read() if pos[0] == "-" else Path(pos[0]).expanduser().read_text(encoding="utf-8")
|
|
1607
|
+
except OSError as exc:
|
|
1608
|
+
print(f"error: cannot read {pos[0]}: {exc}")
|
|
1609
|
+
sys.exit(1)
|
|
1610
|
+
shared = _read_text_arg(opts, "--instructions", "--instructions-file") or ""
|
|
1611
|
+
defaults = {k: opts[f] for f, k in (("--type", "type"), ("--priority", "priority")) if opts.get(f)}
|
|
1612
|
+
|
|
1613
|
+
from sdk.client import BackendClient
|
|
1614
|
+
client = BackendClient.from_config() if not opts.get("--dry-run") else None
|
|
1615
|
+
res = asyncio.run(ticket_plan.create_from_plan(
|
|
1616
|
+
client, text, project=opts.get("--project", ""), instructions=shared, defaults=defaults,
|
|
1617
|
+
pool=opts.get("--pool", ""), module_pools=bool(opts.get("--module-pools")),
|
|
1618
|
+
pool_prefix=opts.get("--pool-prefix", ""), module_prefix=not opts.get("--no-module-prefix"),
|
|
1619
|
+
dry_run=bool(opts.get("--dry-run"))))
|
|
1620
|
+
if res.get("errors") or ("created" not in res and not res.get("dry_run")):
|
|
1621
|
+
for e in res.get("errors") or [res.get("error", "failed")]:
|
|
1622
|
+
print(f"error: {e}")
|
|
1623
|
+
sys.exit(1)
|
|
1624
|
+
if res.get("dry_run"):
|
|
1625
|
+
_print_plan_tree(res)
|
|
1626
|
+
print("nothing created (--dry-run).")
|
|
1627
|
+
return
|
|
1628
|
+
for c in res["created"]:
|
|
1629
|
+
under = (f" (parent {c['parent_key']} named in its description — not linked)" if c.get("parent_unlinked")
|
|
1630
|
+
else f" (under {c['parent_key']})" if c.get("parent_key") else "")
|
|
1631
|
+
print(f"✓ {c['key'] or c['id']} {c['summary']}{under}")
|
|
1632
|
+
for n in res.get("notes") or []:
|
|
1633
|
+
print(f"note: {n}")
|
|
1634
|
+
for f in res["failed"]:
|
|
1635
|
+
print(f"✗ {f['summary']}: {f['error']}")
|
|
1636
|
+
for p in res.get("pools") or []:
|
|
1637
|
+
print(f"✓ pool {p['name']} — {len(p['keys'])} ticket(s), in plan order (run it: agentos pool run {_pname(p['name'])})")
|
|
1638
|
+
for e in res.get("pool_errors") or []:
|
|
1639
|
+
print(f"warning: pool not created: {e}")
|
|
1640
|
+
sys.exit(0 if res["ok"] else 1)
|
|
1641
|
+
|
|
1642
|
+
|
|
1643
|
+
def _ticket_placeholders(args: list[str]) -> None:
|
|
1644
|
+
"""`agentos ticket placeholders [--template]`."""
|
|
1645
|
+
from sdk import instructions_template as itpl
|
|
1646
|
+
if "--template" in args:
|
|
1647
|
+
rest = [a for a in args if a != "--template"]
|
|
1648
|
+
kind = (rest[0] if rest else "development").lower()
|
|
1649
|
+
if kind not in itpl.TEMPLATES:
|
|
1650
|
+
print(f"error: kind must be one of {', '.join(itpl.TEMPLATES)}")
|
|
1651
|
+
sys.exit(1)
|
|
1652
|
+
print(itpl.TEMPLATES[kind][1], end="")
|
|
1653
|
+
return
|
|
1654
|
+
print("Placeholders in Human Instructions — written once, filled for each ticket:")
|
|
1655
|
+
for n, d in itpl.PLACEHOLDERS.items():
|
|
1656
|
+
print(f" {{{n}}}".ljust(18) + d)
|
|
1657
|
+
print(" ({{name}} works too; other braces are left alone. Filled on ticket create/update,")
|
|
1658
|
+
print(" bulk-update, instructions, bulk-create and at pool run time.)")
|
|
1659
|
+
print("Standard formats with placeholders — for every kind of work, not only code:")
|
|
1660
|
+
for k, (label, _) in itpl.TEMPLATES.items():
|
|
1661
|
+
print(f" agentos ticket placeholders --template {k}".ljust(58) + f"# {label}")
|
|
1662
|
+
print("({branch} is for code work; content, research, design and general tasks use an Output Location folder.)")
|
|
1663
|
+
|
|
1664
|
+
|
|
1665
|
+
def _ticket_gen_instructions(args: list[str]) -> None:
|
|
1666
|
+
"""`agentos ticket gen-instructions [KEY…|--pool NAME] [--notes T] [--from-file P] [-o FILE]`."""
|
|
1667
|
+
from sdk import instructions_template as itpl
|
|
1668
|
+
if args and args[0] in ("-h", "--help", "help"):
|
|
1669
|
+
print("usage: agentos ticket gen-instructions [KEY… | --pool NAME] [--kind KIND] [--notes TEXT] [--from-file PATH] [-o FILE]")
|
|
1670
|
+
print(" The local AI writes Human Instructions for these tickets — development, content,")
|
|
1671
|
+
print(" research, design or general work (it picks from the tickets, or --kind) — using")
|
|
1672
|
+
print(" placeholders ({ticket_key}, {summary}, {slug}…) so ONE text fits them all.")
|
|
1673
|
+
print(" --from-file: improve existing instructions. Apply with:")
|
|
1674
|
+
print(" agentos ticket instructions KEY… --file OUT --replace")
|
|
1675
|
+
return
|
|
1676
|
+
keys, opts = _pool_opts(args, ("--notes", "--from-file", "--pool", "--kind", "-o", "--output"), ())
|
|
1677
|
+
kind = (opts.get("--kind") or "auto").lower()
|
|
1678
|
+
if kind != "auto" and kind not in itpl.TEMPLATES:
|
|
1679
|
+
print(f"error: --kind must be auto or one of {', '.join(itpl.TEMPLATES)}")
|
|
1680
|
+
sys.exit(1)
|
|
1681
|
+
keys = [k.upper() for k in keys]
|
|
1682
|
+
if opts.get("--pool"):
|
|
1683
|
+
keys = _bulk_keys(keys, opts["--pool"])
|
|
1684
|
+
existing = _read_text_arg(opts, "--existing", "--from-file") or ""
|
|
1685
|
+
rows: list[dict] = []
|
|
1686
|
+
if keys:
|
|
1687
|
+
from sdk.client import BackendClient
|
|
1688
|
+
recs = asyncio.run(BackendClient.from_config().list_tasks_by_keys(keys)) or []
|
|
1689
|
+
by = {str(r.get("jiraIssueKey") or "").upper(): r for r in recs}
|
|
1690
|
+
rows = [{"key": k, "summary": str(by.get(k, {}).get("summary") or ""),
|
|
1691
|
+
"description": str(by.get(k, {}).get("description") or "")} for k in keys]
|
|
1692
|
+
print("generating with the local AI…", flush=True)
|
|
1693
|
+
res = asyncio.run(itpl.generate(tickets=rows, notes=opts.get("--notes", ""), existing=existing, kind=kind))
|
|
1694
|
+
if not res.get("ok"):
|
|
1695
|
+
print(f"error: {res.get('error')}")
|
|
1696
|
+
sys.exit(1)
|
|
1697
|
+
out = opts.get("-o") or opts.get("--output")
|
|
1698
|
+
if out:
|
|
1699
|
+
Path(out).expanduser().write_text(res["instructions"], encoding="utf-8")
|
|
1700
|
+
print(f"✓ written to {out}")
|
|
1701
|
+
if keys:
|
|
1702
|
+
print(f"apply: agentos ticket instructions {' '.join(keys)} --file {out} --replace")
|
|
1703
|
+
else:
|
|
1704
|
+
print(res["instructions"], end="")
|
|
1705
|
+
if res.get("unknown"):
|
|
1706
|
+
print(f"warning: not placeholders: {', '.join('{' + n + '}' for n in res['unknown'])}")
|
|
1707
|
+
|
|
1708
|
+
|
|
1709
|
+
def _ticket_scope_to_plan(args: list[str]) -> None:
|
|
1710
|
+
"""`agentos ticket scope-to-plan SCOPE [--project KEY] [--modules "A,B"] [--notes TEXT] [-o PLAN.md]`."""
|
|
1711
|
+
from sdk import scope_plan
|
|
1712
|
+
|
|
1713
|
+
if not args or args[0] in ("-h", "--help", "help"):
|
|
1714
|
+
print("usage: agentos ticket scope-to-plan SCOPE.md|SCOPE.txt [--project KEY] [--modules \"A, B\"]")
|
|
1715
|
+
print(" [--notes TEXT] [-o PLAN.md]")
|
|
1716
|
+
print(" The local AI reads a detailed scope document and drafts a ticket plan: a `# Module:` per")
|
|
1717
|
+
print(" module (with its repo when the scope names one), `# Cross-module:` for work spanning")
|
|
1718
|
+
print(" modules, Epics with User Stories (User Story format), Tasks and Bugs. Nothing is created:")
|
|
1719
|
+
print(" review/edit the plan, then `agentos ticket bulk-create PLAN.md --dry-run` and create.")
|
|
1720
|
+
sys.exit(0 if args else 1)
|
|
1721
|
+
pos, opts = _pool_opts(args, ("--project", "--modules", "--notes", "-o", "--output"), ())
|
|
1722
|
+
if len(pos) != 1:
|
|
1723
|
+
print("error: give exactly one scope document (use - to read stdin)")
|
|
1724
|
+
sys.exit(1)
|
|
1725
|
+
try:
|
|
1726
|
+
src = None if pos[0] == "-" else Path(pos[0]).expanduser()
|
|
1727
|
+
scope = sys.stdin.read() if src is None else src.read_text(encoding="utf-8")
|
|
1728
|
+
except (OSError, UnicodeDecodeError) as exc:
|
|
1729
|
+
print(f"error: cannot read {pos[0]} as text: {exc} (save it as .md or .txt)")
|
|
1730
|
+
sys.exit(1)
|
|
1731
|
+
# Default: next to the scope document, `<name>.plan.md`.
|
|
1732
|
+
out = Path(opts.get("-o") or opts.get("--output") or
|
|
1733
|
+
(src.with_name(src.stem + ".plan.md") if src else Path("scope.plan.md"))).expanduser()
|
|
1734
|
+
if out.exists():
|
|
1735
|
+
print(f"error: {out} already exists — pass -o NEW.md")
|
|
1736
|
+
sys.exit(1)
|
|
1737
|
+
modules = [m.strip() for m in (opts.get("--modules") or "").split(",") if m.strip()]
|
|
1738
|
+
print("drafting the plan with the local AI — a large scope can take several minutes…", flush=True)
|
|
1739
|
+
res = asyncio.run(scope_plan.draft_plan(scope, project=opts.get("--project", ""), modules=modules,
|
|
1740
|
+
notes=opts.get("--notes", ""),
|
|
1741
|
+
scope_name=src.name if src else "scope.md"))
|
|
1742
|
+
if not res.get("plan"):
|
|
1743
|
+
print(f"error: {res.get('error')}")
|
|
1744
|
+
sys.exit(1)
|
|
1745
|
+
out.write_text(res["plan"], encoding="utf-8")
|
|
1746
|
+
s = res["summary"]
|
|
1747
|
+
print(f"✓ plan written to {out}: {s['modules']} module(s), {s['cross_module']} cross-module section(s), "
|
|
1748
|
+
f"{s['epic']} epic(s), {s['story']} stories, {s['ticket']} standalone ticket(s)")
|
|
1749
|
+
for e in res.get("errors") or []:
|
|
1750
|
+
print(f" needs fixing: {e}")
|
|
1751
|
+
for w in res.get("warnings") or []:
|
|
1752
|
+
print(f" warning: {w}")
|
|
1753
|
+
print(f"next: review/edit it, then agentos ticket bulk-create {_pname(str(out))} --dry-run")
|
|
1754
|
+
sys.exit(0 if res.get("ok") and not res.get("errors") else 1)
|
|
1755
|
+
|
|
1756
|
+
|
|
1245
1757
|
def _ticket_list(args: list[str]) -> None:
|
|
1246
1758
|
"""`agentos ticket list` / `agentos tickets` — how many in each status, and which."""
|
|
1247
1759
|
import json as _json
|
|
@@ -1309,6 +1821,24 @@ def _ticket_command(args: list[str]) -> None:
|
|
|
1309
1821
|
if verb == "list":
|
|
1310
1822
|
_ticket_list(args[1:])
|
|
1311
1823
|
return
|
|
1824
|
+
if verb == "instructions":
|
|
1825
|
+
_ticket_bulk_instructions(args[1:])
|
|
1826
|
+
return
|
|
1827
|
+
if verb == "bulk-update":
|
|
1828
|
+
_ticket_bulk_update(args[1:])
|
|
1829
|
+
return
|
|
1830
|
+
if verb in ("bulk-create", "plan"):
|
|
1831
|
+
_ticket_bulk_create(args[1:])
|
|
1832
|
+
return
|
|
1833
|
+
if verb == "scope-to-plan":
|
|
1834
|
+
_ticket_scope_to_plan(args[1:])
|
|
1835
|
+
return
|
|
1836
|
+
if verb == "placeholders":
|
|
1837
|
+
_ticket_placeholders(args[1:])
|
|
1838
|
+
return
|
|
1839
|
+
if verb == "gen-instructions":
|
|
1840
|
+
_ticket_gen_instructions(args[1:])
|
|
1841
|
+
return
|
|
1312
1842
|
if verb not in {"create", "update", "show", "delete"}:
|
|
1313
1843
|
_ticket_usage()
|
|
1314
1844
|
sys.exit(1)
|