iac-code 0.3.0__tar.gz → 0.3.1__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.
- {iac_code-0.3.0 → iac_code-0.3.1}/PKG-INFO +15 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/README.md +14 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/pyproject.toml +3 -2
- iac_code-0.3.1/src/iac_code/__init__.py +2 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/client.py +45 -11
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/base.py +10 -2
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/slash_registry.py +1 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/agent/agent_loop.py +47 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/cli/headless.py +54 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/cli/main.py +37 -4
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/cli/output_formats.py +6 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/clear.py +1 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/registry.py +8 -4
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/config.py +28 -9
- iac_code-0.3.1/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +317 -180
- iac_code-0.3.1/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +319 -180
- iac_code-0.3.1/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +317 -180
- iac_code-0.3.1/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +311 -180
- iac_code-0.3.1/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +317 -180
- iac_code-0.3.1/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +311 -180
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/memory/memory_manager.py +51 -16
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/registry.py +1 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/agent_factory.py +1 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/permissions/loader.py +11 -5
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/session_storage.py +8 -4
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/client.py +1 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/config.py +9 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/fallback.py +4 -2
- iac_code-0.3.1/src/iac_code/services/update_checker.py +567 -0
- iac_code-0.3.1/src/iac_code/skills/auto_trigger.py +115 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/__init__.py +2 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/SKILL.md +4 -2
- iac_code-0.3.1/src/iac_code/skills/bundled/iac_aliyun/__init__.py +20 -0
- iac_code-0.3.1/src/iac_code/skills/bundled/iac_aliyun/auto_trigger.py +88 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/discovery.py +42 -26
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/frontmatter.py +4 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/skill_definition.py +4 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/base.py +9 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/aliyun_api.py +5 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_client.py +5 -0
- iac_code-0.3.1/src/iac_code/tools/cloud/aliyun/user_agent.py +23 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/edit_file.py +10 -2
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/read_file.py +3 -0
- iac_code-0.3.1/src/iac_code/tools/result_storage.py +64 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/write_file.py +7 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/banner.py +37 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/input_history.py +36 -6
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/prompt_input.py +18 -7
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/raw_input.py +58 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/raw_input_win.py +51 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/renderer.py +13 -2
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/repl.py +208 -9
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/spinner.py +0 -2
- iac_code-0.3.1/src/iac_code/ui/suggestions/skill_provider.py +47 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/token_extractor.py +12 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/types.py +3 -3
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/file_security.py +14 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/store.py +4 -2
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/json_utils.py +38 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/log.py +6 -4
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/project_paths.py +55 -32
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code.egg-info/PKG-INFO +15 -1
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code.egg-info/SOURCES.txt +5 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code.egg-info/requires.txt +3 -2
- iac_code-0.3.0/src/iac_code/__init__.py +0 -2
- iac_code-0.3.0/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.0/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.0/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.0/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.0/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.0/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.0/src/iac_code/skills/bundled/iac_aliyun/__init__.py +0 -16
- iac_code-0.3.0/src/iac_code/tools/result_storage.py +0 -39
- {iac_code-0.3.0 → iac_code-0.3.1}/LICENSE +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/MANIFEST.in +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/setup.cfg +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/setup.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/agent_card.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/app.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/artifacts.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/events.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/executor.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/exposure.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/metrics.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/parts.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/persistence.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/push.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/push_queue.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/push_secrets.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/push_worker.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/router.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/signing.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/task_store.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transport.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/dispatcher.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/grpc.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/grpc_jsonrpc.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/http.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/redis_streams.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/stdio.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/unix.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/transports/websocket.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/a2a/types.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/convert.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/http_sse.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/mcp.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/metrics.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/server.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/session.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/state.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/tools.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/types.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/acp/version.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/agent/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/agent/agent_tool.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/agent/agent_types.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/agent/message.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/agent/system_prompt.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/cli/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/cli/install_git_bash.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/auth.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/compact.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/debug.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/effort.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/exit.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/help.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/model.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/resume.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/commands/tasks.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/i18n/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/memory/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/memory/memory_tools.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/anthropic_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/azure_openai_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/base.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/dashscope_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/deepseek_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/gemini_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/kimi_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/lmstudio_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/manager.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/minimax_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/modelscope_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/ollama_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/openai_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/openrouter_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/retry.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/siliconflow_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/stream_watchdog.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/thinking.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/volcengine_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/providers/zhipu_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/capabilities/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/capabilities/auto_detect.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/capabilities/multimodal.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/cloud_credentials.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/context_manager.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/permissions/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/permissions/pipeline.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/permissions/storage.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/providers/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/providers/aliyun.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/qwenpaw_source.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/session_index.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/attributes.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/constants.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/content_serializer.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/events.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/identity.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/metrics.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/names.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/sanitize.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/sink.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/tracing.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/telemetry/types.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/token_budget.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/services/token_counter.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/ecs.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/oss.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/rds.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/redis.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/slb.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/vpc.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/ros-template.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameters.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/terraform-template.md +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/scripts/tf2ros.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/bundled/simplify.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/listing.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/loader.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/processor.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/renderer.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/skills/skill_tool.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/state/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/state/app_state.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tasks/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tasks/notification_queue.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tasks/task_state.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tasks/task_tools.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/bash_tool.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/command_parser.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/mode_validation.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/path_validation.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/permissions.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/readonly_commands.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/rule_matching.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/bash/safety_checks.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/aliyun_doc_search.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/api_hooks.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/endpoints.yml +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/hooks/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_parameters.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_validate.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_stack.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_stack_instances.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_yaml.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/base_api.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/base_stack.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/registry.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/cloud/types.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/glob.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/grep.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/list_files.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/tool_executor.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/tools/web_fetch.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/types/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/types/permissions.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/types/skill_source.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/types/stream_events.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/dialog.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/divider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/fuzzy_picker.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/progress_bar.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/search_box.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/select.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/status_icon.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/components/tabs.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/in_place_render.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/key_event.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/core/screen.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/dialogs/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/dialogs/global_search.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/dialogs/history_search.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/dialogs/model_picker.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/dialogs/quick_open.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/dialogs/resume_picker.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/keybindings/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/keybindings/manager.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/aggregator.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/command_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/directory_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/file_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/suggestions/shell_history_provider.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/ui/transcript_view.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/background_housekeeping.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/cleanup.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/console.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/__init__.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/clipboard.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/format_detect.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/pasted_content.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/processor.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/image/resizer.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/platform.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/signals.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/tool_input_parser.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code/utils/windows_paths.py +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code.egg-info/dependency_links.txt +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code.egg-info/entry_points.txt +0 -0
- {iac_code-0.3.0 → iac_code-0.3.1}/src/iac_code.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: iac_code
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Your AI-powered Infrastructure as Code assistant
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -74,6 +74,20 @@ Reading from stdin is also supported:
|
|
|
74
74
|
echo "Create an OSS Bucket" | iac-code --prompt -
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
## Contributing
|
|
78
|
+
|
|
79
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
make install # install dependencies and pre-commit hooks
|
|
83
|
+
make dev # run in debug mode
|
|
84
|
+
make test # run tests
|
|
85
|
+
make lint # run linters
|
|
86
|
+
make format # format code
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
See the [Contributing Guide](https://aliyun.github.io/iac-code/getting-started/contributing) for details.
|
|
90
|
+
|
|
77
91
|
## Contact Us
|
|
78
92
|
|
|
79
93
|
| [DingTalk](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ubm/77U7qRh/STFZUNBP26X4PNg2z6+uhiPcLGtDNfU=&_dt_no_comment=1&origin=11) | [Discord](https://discord.gg/qECFuFBwF) |
|
|
@@ -55,6 +55,20 @@ Reading from stdin is also supported:
|
|
|
55
55
|
echo "Create an OSS Bucket" | iac-code --prompt -
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Contributing
|
|
59
|
+
|
|
60
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
make install # install dependencies and pre-commit hooks
|
|
64
|
+
make dev # run in debug mode
|
|
65
|
+
make test # run tests
|
|
66
|
+
make lint # run linters
|
|
67
|
+
make format # format code
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
See the [Contributing Guide](https://aliyun.github.io/iac-code/getting-started/contributing) for details.
|
|
71
|
+
|
|
58
72
|
## Contact Us
|
|
59
73
|
|
|
60
74
|
| [DingTalk](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ubm/77U7qRh/STFZUNBP26X4PNg2z6+uhiPcLGtDNfU=&_dt_no_comment=1&origin=11) | [Discord](https://discord.gg/qECFuFBwF) |
|
|
@@ -21,6 +21,7 @@ dependencies = [
|
|
|
21
21
|
"pyperclip>=1.8.0",
|
|
22
22
|
"openai>=1.50",
|
|
23
23
|
"httpx>=0.27.0",
|
|
24
|
+
"packaging>=24.0",
|
|
24
25
|
"tiktoken>=0.7.0",
|
|
25
26
|
"jsonschema>=4.20",
|
|
26
27
|
"alibabacloud-ros20190910>=3.0.0",
|
|
@@ -32,8 +33,8 @@ dependencies = [
|
|
|
32
33
|
"pillow==12.2.0",
|
|
33
34
|
"cryptography>=42.0",
|
|
34
35
|
"keyring>=25.0",
|
|
35
|
-
"tree-sitter>=0.
|
|
36
|
-
"tree-sitter-bash>=0.
|
|
36
|
+
"tree-sitter>=0.25,<0.26",
|
|
37
|
+
"tree-sitter-bash>=0.25,<0.26",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
[project.optional-dependencies]
|
|
@@ -7,6 +7,7 @@ from time import monotonic
|
|
|
7
7
|
from typing import Any, AsyncIterator
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
10
|
+
from a2a.types import Role
|
|
10
11
|
|
|
11
12
|
from iac_code.a2a.signing import AgentCardSignature, agent_card_signature_jwks_url, verify_agent_card_dict
|
|
12
13
|
from iac_code.a2a.transport import A2AAuthConfig, A2ATransportBinding, UnsupportedA2ATransportError, headers_for_auth
|
|
@@ -31,19 +32,52 @@ class A2AClientResponse:
|
|
|
31
32
|
if not isinstance(result, dict):
|
|
32
33
|
return ""
|
|
33
34
|
text = result.get("text")
|
|
34
|
-
if isinstance(text, str):
|
|
35
|
+
if isinstance(text, str) and text:
|
|
35
36
|
return text
|
|
36
37
|
status = result.get("status")
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
if isinstance(status, dict):
|
|
39
|
+
extracted = _extract_parts_text(status.get("message"))
|
|
40
|
+
if extracted:
|
|
41
|
+
return extracted
|
|
42
|
+
task = result.get("task")
|
|
43
|
+
if isinstance(task, dict):
|
|
44
|
+
task_status = task.get("status")
|
|
45
|
+
if isinstance(task_status, dict):
|
|
46
|
+
extracted = _extract_parts_text(task_status.get("message"))
|
|
47
|
+
if extracted:
|
|
48
|
+
return extracted
|
|
49
|
+
history = task.get("history")
|
|
50
|
+
if isinstance(history, list):
|
|
51
|
+
for entry in reversed(history):
|
|
52
|
+
extracted = _extract_agent_entry_text(entry)
|
|
53
|
+
if extracted:
|
|
54
|
+
return extracted
|
|
55
|
+
return ""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_AGENT_ROLE_NAME = Role.Name(Role.ROLE_AGENT)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _extract_agent_entry_text(entry: Any) -> str:
|
|
62
|
+
if not isinstance(entry, dict) or entry.get("role") != _AGENT_ROLE_NAME:
|
|
63
|
+
return ""
|
|
64
|
+
return _extract_parts_text(entry)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _extract_parts_text(message: Any) -> str:
|
|
68
|
+
if not isinstance(message, dict):
|
|
69
|
+
return ""
|
|
70
|
+
parts = message.get("parts")
|
|
71
|
+
if not isinstance(parts, list):
|
|
72
|
+
return ""
|
|
73
|
+
pieces: list[str] = []
|
|
74
|
+
for part in parts:
|
|
75
|
+
if not isinstance(part, dict):
|
|
76
|
+
continue
|
|
77
|
+
value = part.get("text")
|
|
78
|
+
if isinstance(value, str):
|
|
79
|
+
pieces.append(value)
|
|
80
|
+
return "".join(pieces)
|
|
47
81
|
|
|
48
82
|
|
|
49
83
|
class A2AClient:
|
|
@@ -9,7 +9,7 @@ from urllib.parse import urlparse
|
|
|
9
9
|
from iac_code.a2a.transport import A2ATransportBinding, UnsupportedA2ATransportError
|
|
10
10
|
from iac_code.i18n import _
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
SUPPORTED_TRANSPORTS = frozenset({"http", "stdio", "unix", "websocket", "grpc", "grpc-jsonrpc", "redis-streams"})
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class A2ATransportConfigError(ValueError):
|
|
@@ -159,8 +159,16 @@ def select_binding(bindings: Sequence[A2ATransportBinding]) -> A2ATransportBindi
|
|
|
159
159
|
raise UnsupportedA2ATransportError(f"No runnable A2A transport found. Candidate bindings: {names}")
|
|
160
160
|
|
|
161
161
|
|
|
162
|
+
def validate_transport_supported(transport: str) -> None:
|
|
163
|
+
"""Raise ValueError if the transport name is not recognised."""
|
|
164
|
+
if transport not in SUPPORTED_TRANSPORTS:
|
|
165
|
+
supported = ", ".join(sorted(SUPPORTED_TRANSPORTS))
|
|
166
|
+
raise ValueError(f"Unsupported transport '{transport}'. Supported values: {supported}")
|
|
167
|
+
|
|
168
|
+
|
|
162
169
|
def validate_transport_for_platform(transport: str) -> None:
|
|
163
|
-
"""Raise
|
|
170
|
+
"""Raise an error if the transport is unsupported or unavailable on the current platform."""
|
|
171
|
+
validate_transport_supported(transport)
|
|
164
172
|
if transport == "unix" and sys.platform == "win32":
|
|
165
173
|
raise RuntimeError(
|
|
166
174
|
_(
|
|
@@ -91,7 +91,7 @@ class ACPSlashRegistry:
|
|
|
91
91
|
async def _handle_clear(self, agent_loop) -> str:
|
|
92
92
|
"""Clear the agent_loop conversation history."""
|
|
93
93
|
try:
|
|
94
|
-
agent_loop.
|
|
94
|
+
agent_loop.reset()
|
|
95
95
|
except Exception as exc:
|
|
96
96
|
logger.warning("ACP /clear failed: %s", exc)
|
|
97
97
|
return _("Clear failed: {error}").format(error=exc)
|
|
@@ -32,6 +32,7 @@ from iac_code.types.stream_events import (
|
|
|
32
32
|
ToolResultEvent,
|
|
33
33
|
ToolUseEndEvent,
|
|
34
34
|
ToolUseStartEvent,
|
|
35
|
+
Usage,
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
|
|
@@ -69,6 +70,7 @@ class AgentLoop:
|
|
|
69
70
|
cwd: str | None = None,
|
|
70
71
|
permission_context: Any = None, # ToolPermissionContext
|
|
71
72
|
permission_context_getter: Any = None, # Callable[[], ToolPermissionContext | None]
|
|
73
|
+
auto_trigger_skills: list[Any] | None = None,
|
|
72
74
|
) -> None:
|
|
73
75
|
self._provider_manager = provider_manager
|
|
74
76
|
self.system_prompt = system_prompt
|
|
@@ -79,6 +81,8 @@ class AgentLoop:
|
|
|
79
81
|
self._cwd = cwd or os.getcwd()
|
|
80
82
|
self._permission_context = permission_context
|
|
81
83
|
self._permission_context_getter = permission_context_getter
|
|
84
|
+
self._auto_trigger_skills = auto_trigger_skills or []
|
|
85
|
+
self._auto_loaded_skills: set[str] = set()
|
|
82
86
|
self._current_git_branch: str | None = None
|
|
83
87
|
|
|
84
88
|
model_name = ""
|
|
@@ -224,6 +228,7 @@ class AgentLoop:
|
|
|
224
228
|
# between turns (user runs git checkout via Bash tool), but
|
|
225
229
|
# is treated as stable within a single in-flight request.
|
|
226
230
|
self._refresh_git_branch()
|
|
231
|
+
await self._apply_auto_triggers(user_input)
|
|
227
232
|
self.context_manager.add_user_message(user_input)
|
|
228
233
|
if self._session_storage:
|
|
229
234
|
from iac_code.agent.message import Message
|
|
@@ -535,6 +540,46 @@ class AgentLoop:
|
|
|
535
540
|
self.context_manager.add_raw_message(msg)
|
|
536
541
|
if result.context_modifier is not None:
|
|
537
542
|
self._apply_context_modifier(result.context_modifier)
|
|
543
|
+
else:
|
|
544
|
+
yield MessageEndEvent(stop_reason="max_turns", usage=Usage())
|
|
545
|
+
|
|
546
|
+
async def _apply_auto_triggers(self, user_input: str | list[ContentBlock]) -> None:
|
|
547
|
+
if not self._auto_trigger_skills:
|
|
548
|
+
return
|
|
549
|
+
if all(command.name in self._auto_loaded_skills for command in self._auto_trigger_skills):
|
|
550
|
+
return
|
|
551
|
+
prompt_text = self._auto_trigger_text(user_input)
|
|
552
|
+
if not prompt_text:
|
|
553
|
+
return
|
|
554
|
+
|
|
555
|
+
from iac_code.skills.auto_trigger import process_auto_triggered_skills
|
|
556
|
+
|
|
557
|
+
results = await process_auto_triggered_skills(
|
|
558
|
+
prompt_text,
|
|
559
|
+
self._auto_trigger_skills,
|
|
560
|
+
loaded_skill_names=self._auto_loaded_skills,
|
|
561
|
+
context_messages=self.context_manager.get_messages(),
|
|
562
|
+
session_id=self._session_id,
|
|
563
|
+
)
|
|
564
|
+
for result in results:
|
|
565
|
+
for msg in result.new_messages:
|
|
566
|
+
injected = self.context_manager.add_raw_message(msg)
|
|
567
|
+
if self._session_storage:
|
|
568
|
+
self._session_storage.append(
|
|
569
|
+
self._cwd,
|
|
570
|
+
self._session_id,
|
|
571
|
+
injected,
|
|
572
|
+
git_branch=self._current_git_branch,
|
|
573
|
+
)
|
|
574
|
+
if result.context_modifier is not None:
|
|
575
|
+
self._apply_context_modifier(result.context_modifier)
|
|
576
|
+
|
|
577
|
+
@staticmethod
|
|
578
|
+
def _auto_trigger_text(user_input: str | list[ContentBlock]) -> str:
|
|
579
|
+
if isinstance(user_input, str):
|
|
580
|
+
return user_input
|
|
581
|
+
parts = [block.text for block in user_input if isinstance(block, TextBlock)]
|
|
582
|
+
return " ".join(part for part in parts if part).strip()
|
|
538
583
|
|
|
539
584
|
def _apply_context_modifier(self, modifier: Any) -> None:
|
|
540
585
|
"""Apply a context modifier from a ToolResult to the current execution context."""
|
|
@@ -642,6 +687,7 @@ class AgentLoop:
|
|
|
642
687
|
|
|
643
688
|
self._session_id = session_id
|
|
644
689
|
self._current_git_branch = None
|
|
690
|
+
self._auto_loaded_skills.clear()
|
|
645
691
|
self.context_manager.reset()
|
|
646
692
|
if resume_messages:
|
|
647
693
|
self.context_manager.load_messages(resume_messages)
|
|
@@ -663,6 +709,7 @@ class AgentLoop:
|
|
|
663
709
|
self._current_git_branch = None
|
|
664
710
|
|
|
665
711
|
def reset(self) -> None:
|
|
712
|
+
self._auto_loaded_skills.clear()
|
|
666
713
|
self.context_manager.reset()
|
|
667
714
|
|
|
668
715
|
def get_context_usage(self) -> dict:
|
|
@@ -24,6 +24,11 @@ from iac_code.types.stream_events import (
|
|
|
24
24
|
ErrorEvent,
|
|
25
25
|
MessageEndEvent,
|
|
26
26
|
PermissionRequestEvent,
|
|
27
|
+
StackInstancesProgressEvent,
|
|
28
|
+
StackProgressEvent,
|
|
29
|
+
SubAgentToolEvent,
|
|
30
|
+
ToolResultEvent,
|
|
31
|
+
ToolUseStartEvent,
|
|
27
32
|
)
|
|
28
33
|
from iac_code.utils.background_housekeeping import start_background_housekeeping
|
|
29
34
|
|
|
@@ -33,6 +38,47 @@ EXIT_MAX_TURNS = 2
|
|
|
33
38
|
__all__ = ["HeadlessRunner", "logger"]
|
|
34
39
|
|
|
35
40
|
|
|
41
|
+
class _ProgressWriter:
|
|
42
|
+
"""Write human-readable headless progress to stderr."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, stream: IO[str]) -> None:
|
|
45
|
+
self._stream = stream
|
|
46
|
+
|
|
47
|
+
def handle(self, event: Any) -> None:
|
|
48
|
+
line: str | None = None
|
|
49
|
+
if isinstance(event, ToolUseStartEvent):
|
|
50
|
+
line = _("Tool started: {}").format(event.name)
|
|
51
|
+
elif isinstance(event, ToolResultEvent):
|
|
52
|
+
if event.is_error:
|
|
53
|
+
line = _("Tool failed: {}").format(event.tool_name)
|
|
54
|
+
else:
|
|
55
|
+
line = _("Tool finished: {}").format(event.tool_name)
|
|
56
|
+
elif isinstance(event, SubAgentToolEvent):
|
|
57
|
+
if event.is_done:
|
|
58
|
+
if event.is_error:
|
|
59
|
+
line = _("Child tool failed: {}").format(event.child_tool_name)
|
|
60
|
+
else:
|
|
61
|
+
line = _("Child tool finished: {}").format(event.child_tool_name)
|
|
62
|
+
else:
|
|
63
|
+
line = _("Child tool started: {}").format(event.child_tool_name)
|
|
64
|
+
elif isinstance(event, StackProgressEvent):
|
|
65
|
+
line = _("Stack {}: {} ({:.1f}%)").format(
|
|
66
|
+
event.stack_name,
|
|
67
|
+
event.status,
|
|
68
|
+
event.progress_percentage,
|
|
69
|
+
)
|
|
70
|
+
elif isinstance(event, StackInstancesProgressEvent):
|
|
71
|
+
line = _("Stack group {}: {} ({}%)").format(
|
|
72
|
+
event.stack_group_name,
|
|
73
|
+
event.status,
|
|
74
|
+
event.progress_percentage,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if line is not None:
|
|
78
|
+
self._stream.write(line + "\n")
|
|
79
|
+
self._stream.flush()
|
|
80
|
+
|
|
81
|
+
|
|
36
82
|
class HeadlessRunner:
|
|
37
83
|
"""Run a single prompt headlessly, auto-approving all permission requests."""
|
|
38
84
|
|
|
@@ -45,6 +91,8 @@ class HeadlessRunner:
|
|
|
45
91
|
cli_allowed_tools: list[str] | None = None,
|
|
46
92
|
cli_disallowed_tools: list[str] | None = None,
|
|
47
93
|
cli_permission_mode: str | None = None,
|
|
94
|
+
verbose: bool = False,
|
|
95
|
+
progress_stream: IO[str] | None = None,
|
|
48
96
|
) -> None:
|
|
49
97
|
self._model = model
|
|
50
98
|
self._output_format = output_format
|
|
@@ -53,6 +101,8 @@ class HeadlessRunner:
|
|
|
53
101
|
self._cli_allowed_tools = cli_allowed_tools
|
|
54
102
|
self._cli_disallowed_tools = cli_disallowed_tools
|
|
55
103
|
self._cli_permission_mode = cli_permission_mode
|
|
104
|
+
self._verbose = verbose
|
|
105
|
+
self._progress_stream = progress_stream or sys.stderr
|
|
56
106
|
|
|
57
107
|
def _print_provider_not_configured(self, exc: Exception) -> None:
|
|
58
108
|
logger.error("Provider not configured: {}", exc)
|
|
@@ -91,6 +141,7 @@ class HeadlessRunner:
|
|
|
91
141
|
|
|
92
142
|
agent_loop = self._create_agent_loop()
|
|
93
143
|
writer = create_writer(self._output_format, self._output_stream)
|
|
144
|
+
progress_writer = _ProgressWriter(self._progress_stream) if self._verbose else None
|
|
94
145
|
|
|
95
146
|
has_error = False
|
|
96
147
|
hit_max_turns = False
|
|
@@ -118,6 +169,9 @@ class HeadlessRunner:
|
|
|
118
169
|
if isinstance(event, MessageEndEvent) and event.stop_reason == "max_turns":
|
|
119
170
|
hit_max_turns = True
|
|
120
171
|
|
|
172
|
+
if progress_writer is not None:
|
|
173
|
+
progress_writer.handle(event)
|
|
174
|
+
|
|
121
175
|
writer.handle(event)
|
|
122
176
|
except ProviderNotConfiguredError as exc:
|
|
123
177
|
self._print_provider_not_configured(exc)
|
|
@@ -84,6 +84,7 @@ def main(
|
|
|
84
84
|
output_format: str = typer.Option("text", "--output-format", help=_("Output format: text, json, stream-json")),
|
|
85
85
|
max_turns: int = typer.Option(100, "--max-turns", help=_("Maximum agent turns in headless mode")),
|
|
86
86
|
debug: bool = typer.Option(False, "--debug", "-d", help=_("Enable debug logging")),
|
|
87
|
+
verbose: bool = typer.Option(False, "--verbose", help=_("Show headless progress on stderr")),
|
|
87
88
|
version: bool = typer.Option(False, "--version", "-v", "-V", is_eager=True, help=_("Show version and exit")),
|
|
88
89
|
resume: str = typer.Option("", "--resume", "-r", help=_("Resume a session by ID")),
|
|
89
90
|
continue_session: bool = typer.Option(False, "--continue", "-c", help=_("Resume the most recent session")),
|
|
@@ -150,6 +151,18 @@ def main(
|
|
|
150
151
|
typer.echo(_("Error: --resume and --continue cannot be used together."), err=True)
|
|
151
152
|
raise typer.Exit(1)
|
|
152
153
|
|
|
154
|
+
fmt = None
|
|
155
|
+
if prompt:
|
|
156
|
+
from iac_code.cli.output_formats import OutputFormat
|
|
157
|
+
|
|
158
|
+
normalized_output_format = (output_format or "text").strip().lower()
|
|
159
|
+
try:
|
|
160
|
+
fmt = OutputFormat(normalized_output_format)
|
|
161
|
+
except ValueError as exc:
|
|
162
|
+
valid = ", ".join(item.value for item in OutputFormat)
|
|
163
|
+
typer.echo(_("Invalid --output-format '{}'. Valid values: {}").format(output_format, valid), err=True)
|
|
164
|
+
raise typer.Exit(1) from exc
|
|
165
|
+
|
|
153
166
|
# Priority: CLI parameter > saved config > default
|
|
154
167
|
if not model:
|
|
155
168
|
try:
|
|
@@ -159,10 +172,21 @@ def main(
|
|
|
159
172
|
raise typer.Exit(1)
|
|
160
173
|
|
|
161
174
|
if prompt:
|
|
175
|
+
assert fmt is not None
|
|
176
|
+
|
|
162
177
|
# Read from stdin if prompt is "-"
|
|
163
178
|
if prompt == "-":
|
|
164
179
|
prompt = sys.stdin.read().strip()
|
|
165
180
|
|
|
181
|
+
if permission_mode:
|
|
182
|
+
from iac_code.services.permissions.loader import parse_cli_permission_mode
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
parse_cli_permission_mode(permission_mode)
|
|
186
|
+
except ValueError as exc:
|
|
187
|
+
typer.echo(str(exc), err=True)
|
|
188
|
+
raise typer.Exit(1) from exc
|
|
189
|
+
|
|
166
190
|
# Headless mode: generate session_id for logging only
|
|
167
191
|
session_id = str(uuid.uuid4())
|
|
168
192
|
setup_logging(session_id=session_id, debug=debug)
|
|
@@ -175,7 +199,7 @@ def main(
|
|
|
175
199
|
Events.SESSION_STARTED,
|
|
176
200
|
{
|
|
177
201
|
"headless": True,
|
|
178
|
-
"output_format":
|
|
202
|
+
"output_format": fmt.value,
|
|
179
203
|
},
|
|
180
204
|
)
|
|
181
205
|
add_metric(Metrics.SESSION_COUNT, 1, {})
|
|
@@ -215,9 +239,7 @@ def main(
|
|
|
215
239
|
return await coro
|
|
216
240
|
|
|
217
241
|
from iac_code.cli.headless import HeadlessRunner
|
|
218
|
-
from iac_code.cli.output_formats import OutputFormat
|
|
219
242
|
|
|
220
|
-
fmt = OutputFormat(output_format)
|
|
221
243
|
cli_allowed = [s.strip() for s in allowed_tools.split(",") if s.strip()] if allowed_tools else None
|
|
222
244
|
cli_disallowed = [s.strip() for s in disallowed_tools.split(",") if s.strip()] if disallowed_tools else None
|
|
223
245
|
try:
|
|
@@ -228,6 +250,7 @@ def main(
|
|
|
228
250
|
cli_allowed_tools=cli_allowed,
|
|
229
251
|
cli_disallowed_tools=cli_disallowed,
|
|
230
252
|
cli_permission_mode=permission_mode or None,
|
|
253
|
+
verbose=verbose,
|
|
231
254
|
)
|
|
232
255
|
exit_code = asyncio.run(_run_with_handler(runner.run(prompt)))
|
|
233
256
|
except _QwenPawError as exc:
|
|
@@ -573,7 +596,11 @@ def a2a(
|
|
|
573
596
|
),
|
|
574
597
|
) -> None:
|
|
575
598
|
"""Run iac-code as an A2A 1.0 server."""
|
|
576
|
-
|
|
599
|
+
try:
|
|
600
|
+
config = _load_a2a_config(config_path) if config_path else {}
|
|
601
|
+
except Exception as exc:
|
|
602
|
+
typer.echo(str(exc), err=True)
|
|
603
|
+
raise typer.Exit(1) from exc
|
|
577
604
|
host = _a2a_config_value(ctx, config, "host", host)
|
|
578
605
|
port = _a2a_config_value(ctx, config, "port", port)
|
|
579
606
|
transport = _a2a_config_value(ctx, config, "transport", transport)
|
|
@@ -789,6 +816,8 @@ def a2a_call(
|
|
|
789
816
|
route = _a2a_client_route_specs(ctx, config, route)
|
|
790
817
|
route_name = _a2a_config_value(ctx, config, "route_name", route_name)
|
|
791
818
|
cwd = _a2a_config_value(ctx, config, "cwd", cwd)
|
|
819
|
+
if cwd in ("", "."):
|
|
820
|
+
cwd = str(Path.cwd())
|
|
792
821
|
context_id = _a2a_config_value(ctx, config, "context_id", context_id)
|
|
793
822
|
timeout = _a2a_config_value(ctx, config, "timeout", timeout)
|
|
794
823
|
stream = _a2a_config_value(ctx, config, "stream", stream)
|
|
@@ -809,6 +838,10 @@ def a2a_call(
|
|
|
809
838
|
require_card_signature=require_card_signature,
|
|
810
839
|
)
|
|
811
840
|
if not url:
|
|
841
|
+
if not route and not route_name:
|
|
842
|
+
raise ValueError(
|
|
843
|
+
"url is required. Provide --url or url in --config, or configure --route/--route-name."
|
|
844
|
+
)
|
|
812
845
|
from iac_code.a2a.router import A2ARouter
|
|
813
846
|
|
|
814
847
|
selected = A2ARouter([_parse_a2a_route_spec(value) for value in route]).resolve(
|
|
@@ -81,12 +81,17 @@ class JsonWriter:
|
|
|
81
81
|
entry["result"] = event.result
|
|
82
82
|
entry["is_error"] = event.is_error
|
|
83
83
|
elif isinstance(event, MessageEndEvent):
|
|
84
|
-
|
|
84
|
+
usage = {
|
|
85
85
|
"input_tokens": event.usage.input_tokens,
|
|
86
86
|
"output_tokens": event.usage.output_tokens,
|
|
87
87
|
"cache_creation_input_tokens": event.usage.cache_creation_input_tokens,
|
|
88
88
|
"cache_read_input_tokens": event.usage.cache_read_input_tokens,
|
|
89
89
|
}
|
|
90
|
+
is_empty_synthetic_max_turns = (
|
|
91
|
+
event.stop_reason == "max_turns" and self._usage is not None and not any(usage.values())
|
|
92
|
+
)
|
|
93
|
+
if not is_empty_synthetic_max_turns:
|
|
94
|
+
self._usage = usage
|
|
90
95
|
elif isinstance(event, ErrorEvent):
|
|
91
96
|
self._error = event.error
|
|
92
97
|
|
|
@@ -12,7 +12,7 @@ async def clear_command(context=None, **kwargs) -> str:
|
|
|
12
12
|
if context and hasattr(context, "repl"):
|
|
13
13
|
agent_loop = getattr(context.repl, "_agent_loop", None)
|
|
14
14
|
if agent_loop:
|
|
15
|
-
agent_loop.
|
|
15
|
+
agent_loop.reset()
|
|
16
16
|
if hasattr(context.repl, "_command_log"):
|
|
17
17
|
context.repl._command_log.clear()
|
|
18
18
|
|
|
@@ -240,12 +240,16 @@ class CommandRegistry:
|
|
|
240
240
|
return None
|
|
241
241
|
|
|
242
242
|
def is_command(self, text: str) -> bool:
|
|
243
|
-
"""Check if text is a command"""
|
|
244
|
-
return text.startswith("/")
|
|
243
|
+
"""Check if text is a command (``/``) or skill (``$``) invocation."""
|
|
244
|
+
return text.startswith(("/", "$"))
|
|
245
245
|
|
|
246
246
|
def parse(self, text: str) -> tuple[str, list[str]]:
|
|
247
|
-
"""Parse command text, return (command name, argument list)
|
|
248
|
-
|
|
247
|
+
"""Parse command text, return (command name, argument list).
|
|
248
|
+
|
|
249
|
+
Accepts both the ``/`` trigger (commands + skills) and the ``$``
|
|
250
|
+
trigger (skills only).
|
|
251
|
+
"""
|
|
252
|
+
parts = text.lstrip("/$").split()
|
|
249
253
|
name = parts[0] if parts else ""
|
|
250
254
|
args = parts[1:] if len(parts) > 1 else []
|
|
251
255
|
return name, args
|
|
@@ -15,6 +15,9 @@ from typing import Any
|
|
|
15
15
|
|
|
16
16
|
import yaml
|
|
17
17
|
|
|
18
|
+
from iac_code.i18n import _
|
|
19
|
+
from iac_code.utils.file_security import ensure_private_dir, ensure_private_file
|
|
20
|
+
|
|
18
21
|
# Default LLM model used when no model is saved in settings
|
|
19
22
|
DEFAULT_MODEL = "qwen3.7-max"
|
|
20
23
|
|
|
@@ -47,8 +50,9 @@ def _load_yaml(path: Path) -> dict[str, Any]:
|
|
|
47
50
|
|
|
48
51
|
def _save_yaml(path: Path, data: dict[str, Any]) -> None:
|
|
49
52
|
"""Write *data* to a YAML file, creating parent directories as needed."""
|
|
50
|
-
path.parent
|
|
53
|
+
ensure_private_dir(path.parent)
|
|
51
54
|
path.write_text(yaml.dump(data, default_flow_style=False, allow_unicode=True), encoding="utf-8")
|
|
55
|
+
ensure_private_file(path)
|
|
52
56
|
|
|
53
57
|
|
|
54
58
|
# ---------------------------------------------------------------------------
|
|
@@ -56,15 +60,29 @@ def _save_yaml(path: Path, data: dict[str, Any]) -> None:
|
|
|
56
60
|
# ---------------------------------------------------------------------------
|
|
57
61
|
|
|
58
62
|
|
|
63
|
+
def _normalize_provider_lookup_name(value: str) -> str:
|
|
64
|
+
"""Normalize provider display names and keys for user-facing lookup."""
|
|
65
|
+
return value.lower().replace(" ", "").replace("-", "").replace("_", "")
|
|
66
|
+
|
|
67
|
+
|
|
59
68
|
def _build_provider_name_to_key() -> dict[str, str]:
|
|
60
69
|
from iac_code.providers.registry import PROVIDER_REGISTRY
|
|
61
70
|
|
|
62
71
|
mapping: dict[str, str] = {}
|
|
72
|
+
|
|
73
|
+
def _add_alias(alias: str, provider_key: str) -> None:
|
|
74
|
+
normalized = _normalize_provider_lookup_name(alias)
|
|
75
|
+
existing = mapping.get(normalized)
|
|
76
|
+
if existing is not None and existing != provider_key:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"Ambiguous provider alias {alias!r}: normalized form {normalized!r} "
|
|
79
|
+
f"maps to both {existing!r} and {provider_key!r}"
|
|
80
|
+
)
|
|
81
|
+
mapping[normalized] = provider_key
|
|
82
|
+
|
|
63
83
|
for desc in PROVIDER_REGISTRY.values():
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
key_norm = desc.key.lower().replace("_", "").replace("-", "")
|
|
67
|
-
mapping[key_norm] = desc.key
|
|
84
|
+
_add_alias(desc.name, desc.key)
|
|
85
|
+
_add_alias(desc.key, desc.key)
|
|
68
86
|
return mapping
|
|
69
87
|
|
|
70
88
|
|
|
@@ -139,11 +157,13 @@ def _get_env_overrides() -> dict[str, str | None]:
|
|
|
139
157
|
provider_raw = _read("IAC_CODE_PROVIDER")
|
|
140
158
|
provider_key: str | None = None
|
|
141
159
|
if provider_raw is not None:
|
|
142
|
-
key = _PROVIDER_NAME_TO_KEY.get(provider_raw
|
|
160
|
+
key = _PROVIDER_NAME_TO_KEY.get(_normalize_provider_lookup_name(provider_raw))
|
|
143
161
|
if key is None:
|
|
144
162
|
valid = ", ".join(_PROVIDER_CANONICAL_NAMES)
|
|
145
163
|
raise ValueError(
|
|
146
|
-
|
|
164
|
+
_("Invalid IAC_CODE_PROVIDER value: {!r}. Valid values (case-insensitive): {}").format(
|
|
165
|
+
provider_raw, valid
|
|
166
|
+
)
|
|
147
167
|
)
|
|
148
168
|
provider_key = key
|
|
149
169
|
|
|
@@ -248,8 +268,7 @@ def get_config_dir() -> Path:
|
|
|
248
268
|
caching).
|
|
249
269
|
"""
|
|
250
270
|
config_dir = _resolve_config_dir()
|
|
251
|
-
config_dir
|
|
252
|
-
return config_dir
|
|
271
|
+
return ensure_private_dir(config_dir)
|
|
253
272
|
|
|
254
273
|
|
|
255
274
|
def get_credentials_path() -> Path:
|
|
Binary file
|