toolang 0.2.0__tar.gz → 0.2.2__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.
- {toolang-0.2.0 → toolang-0.2.2}/PKG-INFO +1 -1
- {toolang-0.2.0 → toolang-0.2.2}/pyproject.toml +4 -4
- toolang-0.2.2/src/toolang/cli/__main__.py +8 -0
- toolang-0.2.2/src/toolang/cli/caps/__init__.py +5 -0
- toolang-0.2.2/src/toolang/cli/caps/__main__.py +8 -0
- toolang-0.2.0/src/toolang/cli/caps_main.py → toolang-0.2.2/src/toolang/cli/caps/app.py +3 -3
- toolang-0.2.0/src/toolang/cli/caps.py → toolang-0.2.2/src/toolang/cli/caps/commands.py +14 -14
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/cli/progress.py +7 -0
- toolang-0.2.2/src/toolang/cli/toolang/__init__.py +0 -0
- toolang-0.2.2/src/toolang/cli/toolang/__main__.py +8 -0
- toolang-0.2.0/src/toolang/cli/main.py → toolang-0.2.2/src/toolang/cli/toolang/app.py +83 -55
- toolang-0.2.2/src/toolang/cli/toolang/caps.py +13 -0
- toolang-0.2.2/src/toolang/cli/toolang/fmt.py +129 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/cli/utils.py +26 -17
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/config/log.py +21 -4
- toolang-0.2.2/src/toolang/program_format.py +851 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/up.py +11 -1
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_cli.py +243 -12
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_log_config.py +36 -0
- toolang-0.2.2/tests/test_program_format.py +359 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_run_queue_runner.py +46 -0
- {toolang-0.2.0 → toolang-0.2.2}/uv.lock +1 -1
- {toolang-0.2.0 → toolang-0.2.2}/.github/workflows/publish-pypi.yml +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/.gitignore +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/AGENTS.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/LICENSE +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/README.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/api.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/caps.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/chat.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/concepts.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/execution.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/ids.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/index.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/layout.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/models.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/plugins.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/prepared-lock.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/program.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/selectors.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/tasks.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/tools.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/docs/webui-jobs.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/alice.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/invoke-playground.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/invoke.fixed-model.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/invoke.openrouter-smoke.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/invoke.priority.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/invoke.simulated-history.memory.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/examples/invoke.simulated-history.none.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/reference/README.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/reference/build.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/reference/config.toml +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/reference/templates/index.html.jinja2 +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/reference/templates/module.html.jinja2 +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/agents.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/error.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/examples/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/examples/tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/protocols/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/protocols/channel.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/protocols/loop.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/protocols/model.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/protocols/sandbox.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/protocols/tool.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/channel.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/message.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/model.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/run.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/sandbox.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/types/tool.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/utils/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/utils/channels.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/utils/function_tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/utils/tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/base/utils/typer_tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/caps.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/channels/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/channels/telegram.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/cli/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/cli/invoke.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/cli/logo.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/common/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/common/ids.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/common/progress.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/common/template.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/registry.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/_streaming.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/chat.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/_shared.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/agent.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/caps.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/execution.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/jobs.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/inspect/router.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/manage/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/manage/caps.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/manage/jobs.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/router/manage/router.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/runner/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/trigger/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/trigger/poll.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/trigger/pulse.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/components/trigger/watch.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/config/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/config/env.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/config/log_spec.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/config/plugins.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/config/web.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/assembly.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/binding.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/context.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/db.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/detail.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/effective.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/events.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/execute.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/input.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/model.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/model_call.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/prompts/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/prompts/context.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/prompts/instruct.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/records.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/response.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/runner.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/execution/stream.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/loops/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/loops/basic.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/adapters/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/adapters/chat_completions.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/adapters/responses.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/config.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/discovery.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/errors.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/custom.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/deepseek.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/google.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/ollama.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/openai.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/providers/openrouter.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/resolution.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/models/views.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/plugin.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/program.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/sandboxes/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/sandboxes/docker.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/sandboxes/none.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/selectors.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/state/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/state/durable.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/state/live.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/state/prepared.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/state/program.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/state/pulse.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/agent.default.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/chore.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/prompt.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/psyche.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/service.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/service.stdio.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/skill.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/templates/task.default.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/__init__.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/agent_chat.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/agent_state.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/filesystem.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/registry.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/service_use.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/shell.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/views.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/tools/web_search.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/src/toolang/work.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/syntax.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/remote-prompt/rewrite.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/remote-psyche/reviewer.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/remote-service/github.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/remote-skill/pdf-processing/SKILL.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/remote-skill/pdf-processing/assets/template.md +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/sample.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/fixtures/source_only.too +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_agent_state_tool.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_channels.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_execution_template.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_ids.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_models.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_plugin_tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_prepared.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_program.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_sandboxes.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_selectors.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_typer_tools.py +0 -0
- {toolang-0.2.0 → toolang-0.2.2}/tests/test_work.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "toolang"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "A programming language and runtime for agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -54,9 +54,9 @@ Repository = "https://github.com/openhat-ai/toolang"
|
|
|
54
54
|
Issues = "https://github.com/openhat-ai/toolang/issues"
|
|
55
55
|
|
|
56
56
|
[project.scripts]
|
|
57
|
-
toolang = "toolang.cli.
|
|
58
|
-
too = "toolang.cli.
|
|
59
|
-
caps = "toolang.cli.
|
|
57
|
+
toolang = "toolang.cli.toolang.app:main"
|
|
58
|
+
too = "toolang.cli.toolang.app:main"
|
|
59
|
+
caps = "toolang.cli.caps.app:main"
|
|
60
60
|
|
|
61
61
|
[project.entry-points."toolang.tool"]
|
|
62
62
|
agent_chat = "toolang.tools.agent_chat:create_tool_set"
|
|
@@ -14,9 +14,9 @@ from typing import Annotated
|
|
|
14
14
|
import click
|
|
15
15
|
import typer
|
|
16
16
|
|
|
17
|
-
from
|
|
18
|
-
from .
|
|
19
|
-
from
|
|
17
|
+
from ...config.log import configure_logging
|
|
18
|
+
from .commands import CAP_KINDS, register_standalone_caps_commands
|
|
19
|
+
from ..utils import _OptionalPrefixAgentGroup, _toolang_root
|
|
20
20
|
|
|
21
21
|
_CLI_PREFIX_AGENT: str | None = None
|
|
22
22
|
CAP_TOP_LEVEL_COMMANDS = frozenset({"list", *CAP_KINDS})
|
|
@@ -12,8 +12,8 @@ import click
|
|
|
12
12
|
import typer
|
|
13
13
|
from typer.core import TyperGroup
|
|
14
14
|
|
|
15
|
-
from
|
|
16
|
-
from
|
|
15
|
+
from ...base.error import ToolangError
|
|
16
|
+
from ..utils import (
|
|
17
17
|
_OptionalPrefixAgentCommand,
|
|
18
18
|
_OptionalPrefixAgentGroup,
|
|
19
19
|
_OptionalPrefixAgentListCommand,
|
|
@@ -27,12 +27,12 @@ from .utils import (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
from
|
|
33
|
-
from
|
|
34
|
-
from
|
|
35
|
-
from
|
|
30
|
+
from ... import caps as cap_store
|
|
31
|
+
from ... import templates
|
|
32
|
+
from ...execution.records import UpdateKind
|
|
33
|
+
from ...components.trigger import watch as watch_feature
|
|
34
|
+
from ...state.prepared import PreparedEntry, PreparedState
|
|
35
|
+
from ..progress import CliProgress
|
|
36
36
|
|
|
37
37
|
CapKind = Literal["skill", "psyche", "prompt", "service"]
|
|
38
38
|
EntryKind = Literal["psyche", "skill", "service", "prompt", "task", "chore"]
|
|
@@ -462,7 +462,7 @@ def _make_add_cap_command(kind: CapKind, title: str) -> Callable[..., None]:
|
|
|
462
462
|
ctx: typer.Context,
|
|
463
463
|
ref: str = typer.Argument(..., help=f"{title} ref"),
|
|
464
464
|
) -> None:
|
|
465
|
-
from
|
|
465
|
+
from ..progress import as_progress_sink
|
|
466
466
|
|
|
467
467
|
visibility, agent_name = _target_visibility(ctx)
|
|
468
468
|
selected_agent = _context_agent(ctx)
|
|
@@ -691,11 +691,11 @@ def _all_cap_entries(
|
|
|
691
691
|
prepare: bool,
|
|
692
692
|
kinds: set[EntryKind],
|
|
693
693
|
) -> tuple[PreparedEntry, ...]:
|
|
694
|
-
from
|
|
694
|
+
from ...state.durable import scan_durable_state
|
|
695
695
|
|
|
696
696
|
durable = _wrap_user_error(scan_durable_state, toolang_root, agent_name)
|
|
697
697
|
if prepare and durable.program_source is not None:
|
|
698
|
-
from
|
|
698
|
+
from ..progress import as_progress_sink, make_cli_progress
|
|
699
699
|
|
|
700
700
|
progress = make_cli_progress(
|
|
701
701
|
prepare_summary_label="Resolved",
|
|
@@ -810,7 +810,7 @@ def _append_cap_update(
|
|
|
810
810
|
|
|
811
811
|
|
|
812
812
|
def _make_cap_write_progress() -> CliProgress:
|
|
813
|
-
from
|
|
813
|
+
from ..progress import make_cli_progress
|
|
814
814
|
|
|
815
815
|
return make_cli_progress(
|
|
816
816
|
prepare_summary_label="Resolved",
|
|
@@ -828,8 +828,8 @@ def _refresh_and_append_cap_update(
|
|
|
828
828
|
progress_total: int,
|
|
829
829
|
progress: CliProgress | None = None,
|
|
830
830
|
) -> None:
|
|
831
|
-
from
|
|
832
|
-
from
|
|
831
|
+
from ...state.durable import scan_durable_state
|
|
832
|
+
from ..progress import as_progress_sink
|
|
833
833
|
|
|
834
834
|
durable = _wrap_user_error(scan_durable_state, toolang_root, agent_name)
|
|
835
835
|
_wrap_user_error(
|
|
@@ -83,6 +83,7 @@ class CliProgress:
|
|
|
83
83
|
for line in self._lines():
|
|
84
84
|
print(line, file=self._stream)
|
|
85
85
|
self._print_summary()
|
|
86
|
+
self._reset_terminal_style()
|
|
86
87
|
|
|
87
88
|
def interrupt(self) -> None:
|
|
88
89
|
with self._lock:
|
|
@@ -290,6 +291,12 @@ class CliProgress:
|
|
|
290
291
|
for summary in summaries:
|
|
291
292
|
self._console.print(Text(summary, style="dim"))
|
|
292
293
|
|
|
294
|
+
def _reset_terminal_style(self) -> None:
|
|
295
|
+
if not self._console.is_terminal:
|
|
296
|
+
return
|
|
297
|
+
self._stream.write("\x1b[0m")
|
|
298
|
+
self._stream.flush()
|
|
299
|
+
|
|
293
300
|
def _live_text(self) -> Text:
|
|
294
301
|
text = Text()
|
|
295
302
|
agent_items = [item for item in self._items.values() if item.kind == "agent"]
|
|
File without changes
|
|
@@ -19,10 +19,9 @@ import click
|
|
|
19
19
|
import typer
|
|
20
20
|
from typer.core import TyperCommand, TyperGroup
|
|
21
21
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
from
|
|
25
|
-
from .utils import (
|
|
22
|
+
from ... import agents
|
|
23
|
+
from ...config.log import LoggingPlan, configure_logging, configure_logging_plan, resolve_agent_logging
|
|
24
|
+
from ..utils import (
|
|
26
25
|
_PrefixAgentWorkGroup,
|
|
27
26
|
_RequiredPrefixAgentCommand,
|
|
28
27
|
_RunAgentCommand,
|
|
@@ -39,22 +38,23 @@ from .utils import (
|
|
|
39
38
|
_required_prefix_agent,
|
|
40
39
|
_required_runtime_agent,
|
|
41
40
|
_runtime_environ_for_agent,
|
|
42
|
-
_runtime_components,
|
|
43
41
|
_runtime_value,
|
|
44
42
|
_toolang_root,
|
|
45
43
|
_ui_base_url,
|
|
46
44
|
_wait_for_started_status,
|
|
47
45
|
_wrap_user_error,
|
|
48
46
|
)
|
|
47
|
+
from .caps import CAP_KINDS, register_caps_commands
|
|
48
|
+
from .fmt import register_fmt_command
|
|
49
49
|
|
|
50
50
|
if TYPE_CHECKING:
|
|
51
|
-
from
|
|
52
|
-
from
|
|
53
|
-
from
|
|
54
|
-
from
|
|
55
|
-
from
|
|
56
|
-
from
|
|
57
|
-
from
|
|
51
|
+
from ... import caps as cap_store
|
|
52
|
+
from ... import templates
|
|
53
|
+
from ... import up as agent_up
|
|
54
|
+
from ...execution.records import UpdateKind
|
|
55
|
+
from ...state.prepared import PreparedLock, PreparedState
|
|
56
|
+
from .. import invoke as cli_invoke
|
|
57
|
+
from ..progress import CliProgress
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class _LazyModule:
|
|
@@ -105,6 +105,7 @@ TOP_LEVEL_COMMANDS = frozenset(
|
|
|
105
105
|
"remove",
|
|
106
106
|
"list",
|
|
107
107
|
"info",
|
|
108
|
+
"fmt",
|
|
108
109
|
"model",
|
|
109
110
|
"tool",
|
|
110
111
|
"channel",
|
|
@@ -399,6 +400,7 @@ def info_agent(
|
|
|
399
400
|
("Home", str(agents.agent_home(root, agent_name))),
|
|
400
401
|
("Caps", _info_caps_summary(root, agent_name)),
|
|
401
402
|
("Jobs", _info_jobs_summary(root, agent_name)),
|
|
403
|
+
("Tools", _info_tools_summary(root, agent_name)),
|
|
402
404
|
("Models", _info_models_summary(root, agent_name, runtime_state=runtime_state, running=status.status != "stopped")),
|
|
403
405
|
("Status", status_value),
|
|
404
406
|
]
|
|
@@ -408,16 +410,12 @@ def info_agent(
|
|
|
408
410
|
return
|
|
409
411
|
if status.sandbox:
|
|
410
412
|
rows.append(("Sandbox", status.sandbox))
|
|
411
|
-
if status.status == "running":
|
|
412
|
-
loops_text = _runtime_components(runtime_state)
|
|
413
|
-
if loops_text is not None:
|
|
414
|
-
rows.append(("Components", loops_text))
|
|
415
413
|
message = _runtime_value(runtime_state.get("message"))
|
|
416
414
|
pid_text = agents.runtime_pid_label(runtime_state)
|
|
417
415
|
if pid_text is not None and status.status != "stopped":
|
|
418
416
|
rows.append(("PID", pid_text))
|
|
419
|
-
if status.
|
|
420
|
-
rows.append(("API", status.
|
|
417
|
+
if status.endpoint:
|
|
418
|
+
rows.append(("API", status.endpoint))
|
|
421
419
|
if status.webui_url:
|
|
422
420
|
rows.append(("WebUI", status.webui_url))
|
|
423
421
|
if status.status == "running" and started_at != "-":
|
|
@@ -491,7 +489,7 @@ def run_agent(
|
|
|
491
489
|
typer.Option("--sandbox-child", hidden=True),
|
|
492
490
|
] = False,
|
|
493
491
|
) -> None:
|
|
494
|
-
from
|
|
492
|
+
from ..progress import as_progress_sink, make_cli_progress
|
|
495
493
|
|
|
496
494
|
selector = _required_runtime_agent(ctx, agent)
|
|
497
495
|
normalized_components = _normalize_component_option(components)
|
|
@@ -563,7 +561,7 @@ def _resolve_runtime_startup(
|
|
|
563
561
|
background: bool,
|
|
564
562
|
progress: CliProgress | None,
|
|
565
563
|
) -> _RuntimeStartup:
|
|
566
|
-
from
|
|
564
|
+
from ..progress import as_progress_sink
|
|
567
565
|
|
|
568
566
|
run_root = target.toolang_root
|
|
569
567
|
agent_name = target.agent_name
|
|
@@ -666,7 +664,7 @@ def start_agent(
|
|
|
666
664
|
typer.Option("--endpoint-host", help="Endpoint host name.", hidden=True),
|
|
667
665
|
] = None,
|
|
668
666
|
) -> None:
|
|
669
|
-
from
|
|
667
|
+
from ..progress import as_progress_sink, make_cli_progress
|
|
670
668
|
|
|
671
669
|
selector = _required_runtime_agent(ctx, agent)
|
|
672
670
|
parsed_selector = _wrap_user_error(agents.parse_agent_selector, selector)
|
|
@@ -709,7 +707,7 @@ def start_agent(
|
|
|
709
707
|
command = [
|
|
710
708
|
sys.executable,
|
|
711
709
|
"-m",
|
|
712
|
-
"toolang.cli.
|
|
710
|
+
"toolang.cli.toolang",
|
|
713
711
|
*agent_up.build_run_argv(launch.startup),
|
|
714
712
|
]
|
|
715
713
|
with log_path.open("ab") as stream:
|
|
@@ -801,7 +799,7 @@ def _info_caps_summary(toolang_root: Path, agent_name: str) -> str:
|
|
|
801
799
|
|
|
802
800
|
|
|
803
801
|
def _prepared_info_cap_counts(toolang_root: Path, agent_name: str) -> dict[str, int] | None:
|
|
804
|
-
from
|
|
802
|
+
from ...state.prepared import load_private_lock, load_shared_lock
|
|
805
803
|
|
|
806
804
|
try:
|
|
807
805
|
shared_lock = load_shared_lock(toolang_root)
|
|
@@ -834,7 +832,7 @@ def _prepared_lock_info_cap_counts(
|
|
|
834
832
|
|
|
835
833
|
|
|
836
834
|
def _prepare_info_cap_counts(toolang_root: Path, agent_name: str) -> dict[str, int]:
|
|
837
|
-
from
|
|
835
|
+
from ..progress import as_progress_sink, make_cli_progress
|
|
838
836
|
|
|
839
837
|
progress = make_cli_progress(show_materialize_summary=True)
|
|
840
838
|
try:
|
|
@@ -853,7 +851,7 @@ def _prepare_info_cap_counts(toolang_root: Path, agent_name: str) -> dict[str, i
|
|
|
853
851
|
|
|
854
852
|
|
|
855
853
|
def _info_jobs_summary(toolang_root: Path, agent_name: str) -> str:
|
|
856
|
-
from
|
|
854
|
+
from ... import work
|
|
857
855
|
|
|
858
856
|
chore_count = len(work.list_chores(toolang_root, agent_name))
|
|
859
857
|
task_count = len(work.list_tasks(toolang_root, agent_name))
|
|
@@ -873,19 +871,46 @@ def _info_models_summary(
|
|
|
873
871
|
if running:
|
|
874
872
|
raw_models = runtime_state.get("models")
|
|
875
873
|
if isinstance(raw_models, list):
|
|
876
|
-
|
|
874
|
+
selectors = tuple(
|
|
877
875
|
str(item).strip()
|
|
878
876
|
for item in raw_models
|
|
879
877
|
if isinstance(item, str) and item.strip()
|
|
880
878
|
)
|
|
881
|
-
if
|
|
882
|
-
return
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
879
|
+
if selectors:
|
|
880
|
+
return _model_count_summary(
|
|
881
|
+
toolang_root,
|
|
882
|
+
agent_name,
|
|
883
|
+
environ=dict(os.environ),
|
|
884
|
+
selectors=selectors,
|
|
885
|
+
)
|
|
886
|
+
selectors = agent_up.load_default_models(toolang_root, agent_name)
|
|
887
|
+
return _model_count_summary(
|
|
888
|
+
toolang_root,
|
|
889
|
+
agent_name,
|
|
890
|
+
environ=dict(os.environ),
|
|
891
|
+
selectors=selectors,
|
|
892
|
+
)
|
|
893
|
+
|
|
887
894
|
|
|
888
|
-
|
|
895
|
+
def _info_tools_summary(toolang_root: Path, agent_name: str) -> str:
|
|
896
|
+
rows = _tool_rows(toolang_root, dict(os.environ), agent_name=agent_name)
|
|
897
|
+
set_count = len({namespace for namespace, _tool, _description in rows})
|
|
898
|
+
return f"{len(rows)} {'tool' if len(rows) == 1 else 'tools'}, {set_count} {'set' if set_count == 1 else 'sets'}"
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
def _model_count_summary(
|
|
902
|
+
toolang_root: Path,
|
|
903
|
+
agent_name: str,
|
|
904
|
+
*,
|
|
905
|
+
environ: dict[str, str],
|
|
906
|
+
selectors: Sequence[str] = (),
|
|
907
|
+
) -> str:
|
|
908
|
+
rows = _model_rows(toolang_root, environ, agent_name=agent_name, model_selectors=selectors)
|
|
909
|
+
provider_count = len({provider for _model, provider, _details in rows})
|
|
910
|
+
return (
|
|
911
|
+
f"{len(rows)} {'model' if len(rows) == 1 else 'models'}, "
|
|
912
|
+
f"{provider_count} {'provider' if provider_count == 1 else 'providers'}"
|
|
913
|
+
)
|
|
889
914
|
|
|
890
915
|
|
|
891
916
|
model_app = typer.Typer(
|
|
@@ -936,8 +961,8 @@ def list_models(
|
|
|
936
961
|
),
|
|
937
962
|
] = None,
|
|
938
963
|
) -> None:
|
|
939
|
-
from
|
|
940
|
-
from
|
|
964
|
+
from ...models.errors import NO_AVAILABLE_MODELS_MESSAGE
|
|
965
|
+
from ...models.resolution import split_model_selectors
|
|
941
966
|
|
|
942
967
|
environ = dict(os.environ)
|
|
943
968
|
root = _toolang_root(None)
|
|
@@ -970,7 +995,7 @@ def list_model_providers() -> None:
|
|
|
970
995
|
|
|
971
996
|
@model_app.command("adapters", help="List available model API adapters.")
|
|
972
997
|
def list_model_adapters() -> None:
|
|
973
|
-
from
|
|
998
|
+
from ...models.views import available_model_adapters
|
|
974
999
|
|
|
975
1000
|
rows = [(name,) for name in available_model_adapters()]
|
|
976
1001
|
_echo_table(("ADAPTER",), rows)
|
|
@@ -988,7 +1013,7 @@ def list_tools(
|
|
|
988
1013
|
),
|
|
989
1014
|
] = None,
|
|
990
1015
|
) -> None:
|
|
991
|
-
from
|
|
1016
|
+
from ...tools.registry import split_tool_selectors
|
|
992
1017
|
|
|
993
1018
|
environ = dict(os.environ)
|
|
994
1019
|
root = _toolang_root(None)
|
|
@@ -1030,13 +1055,14 @@ def _model_rows(
|
|
|
1030
1055
|
root: Path,
|
|
1031
1056
|
environ: dict[str, str],
|
|
1032
1057
|
*,
|
|
1058
|
+
agent_name: str = "",
|
|
1033
1059
|
model_selectors: Sequence[str] = (),
|
|
1034
1060
|
) -> list[tuple[str, str, str]]:
|
|
1035
|
-
from
|
|
1036
|
-
from
|
|
1061
|
+
from ...models.config import load_model_aliases
|
|
1062
|
+
from ...models.views import model_list_rows
|
|
1037
1063
|
|
|
1038
|
-
providers = agent_up.load_model_providers(root,
|
|
1039
|
-
aliases = load_model_aliases(root,
|
|
1064
|
+
providers = agent_up.load_model_providers(root, agent_name)
|
|
1065
|
+
aliases = load_model_aliases(root, agent_name)
|
|
1040
1066
|
return model_list_rows(
|
|
1041
1067
|
providers=providers,
|
|
1042
1068
|
aliases=aliases,
|
|
@@ -1046,8 +1072,8 @@ def _model_rows(
|
|
|
1046
1072
|
|
|
1047
1073
|
|
|
1048
1074
|
def _model_provider_rows(root: Path, environ: dict[str, str]) -> list[tuple[str, str, str]]:
|
|
1049
|
-
from
|
|
1050
|
-
from
|
|
1075
|
+
from ...models.config import load_model_aliases, load_model_provider_configs
|
|
1076
|
+
from ...models.views import model_provider_rows
|
|
1051
1077
|
|
|
1052
1078
|
provider_configs = load_model_provider_configs(root, "")
|
|
1053
1079
|
providers = agent_up.load_model_providers(root, "")
|
|
@@ -1064,12 +1090,13 @@ def _tool_rows(
|
|
|
1064
1090
|
root: Path,
|
|
1065
1091
|
environ: dict[str, str],
|
|
1066
1092
|
*,
|
|
1093
|
+
agent_name: str = "",
|
|
1067
1094
|
tool_selectors: Sequence[str] = (),
|
|
1068
1095
|
) -> list[tuple[str, str, str]]:
|
|
1069
|
-
from
|
|
1070
|
-
from
|
|
1096
|
+
from ...config.plugins import load_tool_plugin_config
|
|
1097
|
+
from ...tools.views import tool_list_rows
|
|
1071
1098
|
|
|
1072
|
-
config = load_tool_plugin_config(root,
|
|
1099
|
+
config = load_tool_plugin_config(root, agent_name, environ=environ)
|
|
1073
1100
|
tools = agent_up.load_tool_plugins(config=config)
|
|
1074
1101
|
sources = _plugin_source_by_name("toolang.tool")
|
|
1075
1102
|
return tool_list_rows(
|
|
@@ -1209,7 +1236,7 @@ def _make_work_list_command(kind: WorkKind, title: str) -> Callable[..., None]:
|
|
|
1209
1236
|
typer.Option("--all", help="Include archived items."),
|
|
1210
1237
|
] = False,
|
|
1211
1238
|
) -> None:
|
|
1212
|
-
from
|
|
1239
|
+
from ... import work
|
|
1213
1240
|
|
|
1214
1241
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
1215
1242
|
root = _context_root(ctx)
|
|
@@ -1253,7 +1280,7 @@ def _make_new_work_command(kind: WorkKind, title: str) -> Callable[..., None]:
|
|
|
1253
1280
|
def new_work(
|
|
1254
1281
|
ctx: typer.Context,
|
|
1255
1282
|
) -> None:
|
|
1256
|
-
from
|
|
1283
|
+
from ... import work
|
|
1257
1284
|
|
|
1258
1285
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
1259
1286
|
text = click.edit(
|
|
@@ -1281,7 +1308,7 @@ def _make_edit_work_command(kind: WorkKind, title: str) -> Callable[..., None]:
|
|
|
1281
1308
|
ctx: typer.Context,
|
|
1282
1309
|
id: str = typer.Argument(..., help=f"{title} id", metavar="ID"),
|
|
1283
1310
|
) -> None:
|
|
1284
|
-
from
|
|
1311
|
+
from ... import work
|
|
1285
1312
|
|
|
1286
1313
|
job_id = id
|
|
1287
1314
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
@@ -1316,7 +1343,7 @@ def _make_pause_work_command(kind: WorkKind, title: str) -> Callable[..., None]:
|
|
|
1316
1343
|
ctx: typer.Context,
|
|
1317
1344
|
id: str = typer.Argument(..., help=f"{title} id", metavar="ID"),
|
|
1318
1345
|
) -> None:
|
|
1319
|
-
from
|
|
1346
|
+
from ... import work
|
|
1320
1347
|
|
|
1321
1348
|
job_id = id
|
|
1322
1349
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
@@ -1339,7 +1366,7 @@ def _make_resume_work_command(kind: WorkKind, title: str) -> Callable[..., None]
|
|
|
1339
1366
|
ctx: typer.Context,
|
|
1340
1367
|
id: str = typer.Argument(..., help=f"{title} id", metavar="ID"),
|
|
1341
1368
|
) -> None:
|
|
1342
|
-
from
|
|
1369
|
+
from ... import work
|
|
1343
1370
|
|
|
1344
1371
|
job_id = id
|
|
1345
1372
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
@@ -1362,7 +1389,7 @@ def _make_archive_work_command(kind: WorkKind, title: str) -> Callable[..., None
|
|
|
1362
1389
|
ctx: typer.Context,
|
|
1363
1390
|
id: str = typer.Argument(..., help=f"{title} id", metavar="ID"),
|
|
1364
1391
|
) -> None:
|
|
1365
|
-
from
|
|
1392
|
+
from ... import work
|
|
1366
1393
|
|
|
1367
1394
|
job_id = id
|
|
1368
1395
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
@@ -1389,7 +1416,7 @@ def _make_restore_work_command(kind: WorkKind, title: str) -> Callable[..., None
|
|
|
1389
1416
|
typer.Option("--inactive", help="Restore as inactive instead of active."),
|
|
1390
1417
|
] = False,
|
|
1391
1418
|
) -> None:
|
|
1392
|
-
from
|
|
1419
|
+
from ... import work
|
|
1393
1420
|
|
|
1394
1421
|
job_id = id
|
|
1395
1422
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
@@ -1414,7 +1441,7 @@ def _make_delete_work_command(kind: WorkKind, title: str) -> Callable[..., None]
|
|
|
1414
1441
|
ctx: typer.Context,
|
|
1415
1442
|
id: str = typer.Argument(..., help=f"{title} id", metavar="ID"),
|
|
1416
1443
|
) -> None:
|
|
1417
|
-
from
|
|
1444
|
+
from ... import work
|
|
1418
1445
|
|
|
1419
1446
|
job_id = id
|
|
1420
1447
|
agent_name = _required_prefix_agent(ctx, command_name=kind)
|
|
@@ -1457,7 +1484,8 @@ app.add_typer(model_app, name="model", no_args_is_help=True, rich_help_panel=RUN
|
|
|
1457
1484
|
app.add_typer(tool_app, name="tool", no_args_is_help=True, rich_help_panel=RUNTIME_COMMAND_PANEL)
|
|
1458
1485
|
app.add_typer(channel_app, name="channel", no_args_is_help=True, rich_help_panel=RUNTIME_COMMAND_PANEL)
|
|
1459
1486
|
app.add_typer(sandbox_app, name="sandbox", no_args_is_help=True, rich_help_panel=RUNTIME_COMMAND_PANEL)
|
|
1460
|
-
|
|
1487
|
+
register_fmt_command(app)
|
|
1488
|
+
register_caps_commands(app, rich_help_panel=CAPS_COMMAND_PANEL)
|
|
1461
1489
|
register_work_commands()
|
|
1462
1490
|
|
|
1463
1491
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Caps commands mounted under the main toolang CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from ..caps.commands import CAP_KINDS, register_toolang_caps_commands as _register_toolang_caps_commands
|
|
8
|
+
|
|
9
|
+
__all__ = ["CAP_KINDS", "register_caps_commands"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_caps_commands(app: typer.Typer, *, rich_help_panel: str | None = None) -> None:
|
|
13
|
+
_register_toolang_caps_commands(app, rich_help_panel=rich_help_panel)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Hidden formatter command for Toolang source files."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def register_fmt_command(app: typer.Typer) -> None:
|
|
15
|
+
app.command("fmt", help="Format .too files.", hidden=True, no_args_is_help=True)(fmt)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def fmt(
|
|
19
|
+
paths: Annotated[
|
|
20
|
+
list[Path] | None,
|
|
21
|
+
typer.Argument(help="File or directory paths to format."),
|
|
22
|
+
] = None,
|
|
23
|
+
check: Annotated[
|
|
24
|
+
bool,
|
|
25
|
+
typer.Option("--check", help="Exit non-zero if any file is not formatted."),
|
|
26
|
+
] = False,
|
|
27
|
+
tab_size: Annotated[
|
|
28
|
+
int,
|
|
29
|
+
typer.Option("--tab-size", help="Number of spaces per indentation level."),
|
|
30
|
+
] = 2,
|
|
31
|
+
stdin_filepath: Annotated[
|
|
32
|
+
Path | None,
|
|
33
|
+
typer.Option(
|
|
34
|
+
"--stdin-filepath",
|
|
35
|
+
help="Path label for stdin.",
|
|
36
|
+
),
|
|
37
|
+
] = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
from ...program_format import ToolangFormatError, format_source
|
|
40
|
+
|
|
41
|
+
if tab_size < 1:
|
|
42
|
+
raise click.ClickException("--tab-size must be at least 1")
|
|
43
|
+
|
|
44
|
+
def format_too_source(source: str) -> str:
|
|
45
|
+
return format_source(source, tab_size=tab_size)
|
|
46
|
+
|
|
47
|
+
path_args = paths or []
|
|
48
|
+
if any(str(path) == "-" for path in path_args) and len(path_args) > 1:
|
|
49
|
+
raise click.ClickException("'-' cannot be combined with other path arguments")
|
|
50
|
+
|
|
51
|
+
stdin_path_arg = _stdin_path_arg(path_args)
|
|
52
|
+
if stdin_filepath is not None or stdin_path_arg is not None:
|
|
53
|
+
if path_args and stdin_path_arg is None:
|
|
54
|
+
raise click.ClickException("--stdin-filepath can only be combined with '-'")
|
|
55
|
+
if check:
|
|
56
|
+
raise click.ClickException("--check cannot be combined with stdin formatting")
|
|
57
|
+
label = stdin_filepath or stdin_path_arg or Path("<stdin>")
|
|
58
|
+
_format_stdin(label, format_source=format_too_source, error_type=ToolangFormatError)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
source_paths = _collect_format_paths(path_args)
|
|
62
|
+
changed: list[Path] = []
|
|
63
|
+
for source_path in source_paths:
|
|
64
|
+
try:
|
|
65
|
+
source = source_path.read_text(encoding="utf-8")
|
|
66
|
+
except OSError as exc:
|
|
67
|
+
raise click.ClickException(f"{source_path}: {exc}") from exc
|
|
68
|
+
try:
|
|
69
|
+
formatted = format_too_source(source)
|
|
70
|
+
except ToolangFormatError as exc:
|
|
71
|
+
raise click.ClickException(f"{source_path}: {exc}") from exc
|
|
72
|
+
if formatted == source:
|
|
73
|
+
continue
|
|
74
|
+
changed.append(source_path)
|
|
75
|
+
if check:
|
|
76
|
+
continue
|
|
77
|
+
try:
|
|
78
|
+
source_path.write_text(formatted, encoding="utf-8")
|
|
79
|
+
except OSError as exc:
|
|
80
|
+
raise click.ClickException(f"{source_path}: {exc}") from exc
|
|
81
|
+
|
|
82
|
+
if check and changed:
|
|
83
|
+
for source_path in changed:
|
|
84
|
+
typer.echo(f"would reformat {source_path}")
|
|
85
|
+
raise typer.Exit(1)
|
|
86
|
+
if changed:
|
|
87
|
+
for source_path in changed:
|
|
88
|
+
typer.echo(f"formatted {source_path}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _format_stdin(
|
|
92
|
+
stdin_filepath: Path,
|
|
93
|
+
*,
|
|
94
|
+
format_source: Callable[[str], str],
|
|
95
|
+
error_type: type[Exception],
|
|
96
|
+
) -> None:
|
|
97
|
+
try:
|
|
98
|
+
formatted = format_source(sys.stdin.read())
|
|
99
|
+
except error_type as exc:
|
|
100
|
+
raise click.ClickException(f"{stdin_filepath}: {exc}") from exc
|
|
101
|
+
sys.stdout.write(formatted)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _stdin_path_arg(paths: list[Path]) -> Path | None:
|
|
105
|
+
if len(paths) == 1 and str(paths[0]) == "-":
|
|
106
|
+
return Path("<stdin>")
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _collect_format_paths(paths: list[Path]) -> list[Path]:
|
|
111
|
+
collected: list[Path] = []
|
|
112
|
+
seen: set[Path] = set()
|
|
113
|
+
for path in paths:
|
|
114
|
+
candidate = path.expanduser()
|
|
115
|
+
if candidate.is_dir():
|
|
116
|
+
candidates = sorted(item for item in candidate.rglob("*.too") if item.is_file())
|
|
117
|
+
elif candidate.is_file():
|
|
118
|
+
if candidate.suffix != ".too":
|
|
119
|
+
raise click.ClickException(f"not a .too file: {candidate}")
|
|
120
|
+
candidates = [candidate]
|
|
121
|
+
else:
|
|
122
|
+
raise click.ClickException(f"path not found: {candidate}")
|
|
123
|
+
for source_path in candidates:
|
|
124
|
+
resolved = source_path.resolve()
|
|
125
|
+
if resolved in seen:
|
|
126
|
+
continue
|
|
127
|
+
seen.add(resolved)
|
|
128
|
+
collected.append(source_path)
|
|
129
|
+
return collected
|