Ctrl K
ComunidadGuías…

pyproject.toml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
[project]
name = "noeta"
version = "1.2.3"
description = "Open-source, Claude Code-compatible coding agent you can self-host on many workers: any worker continues any task, waits for approval hold no process. Terminal UI, CLI, ACP, HTTP API and Python SDK; Claude, DeepSeek or any OpenAI-compatible model."
readme = "README.md"
license = "Apache-2.0"
license-files = ["LICENSE"]
authors = [{ name = "The Noeta Authors", email = "initxy0@gmail.com" }]
keywords = ["ai-agent", "coding-agent", "ai-coding", "claude-code", "llm", "agentic-ai", "agent-sdk", "cli", "tui", "mcp", "acp", "anthropic", "claude", "deepseek", "openai", "self-hosted", "durable-execution", "event-sourcing", "human-in-the-loop", "multi-agent"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Environment :: Console",
    "Framework :: AsyncIO",
    "Intended Audience :: Developers",
    "Operating System :: POSIX :: Linux",
    "Operating System :: MacOS",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Topic :: Software Development",
    "Topic :: Software Development :: Code Generators",
    "Topic :: Scientific/Engineering :: Artificial Intelligence",
    "Typing :: Typed",
]
requires-python = ">=3.11"
dependencies = [
    "httpx[socks]>=0.28.1",
    "msgspec>=0.19",
    "pillow>=10.4",
    "pyyaml>=6.0",
]

[project.optional-dependencies]
postgres = ["psycopg[binary,pool]>=3.2"]
server = ["starlette>=0.40", "uvicorn>=0.30", "websockets>=12"]
acp = ["agent-client-protocol>=0.12,<0.13"]
keyring = ["keyring>=24"]
# The Workflow tool's embedded JavaScript engine (V8, about 80 MB installed).
workflow = ["mini-racer>=0.14,<0.15"]
# Released together, at the same version (docs/releasing.md).
tui = ["noeta-tui==1.2.3"]

[project.urls]
Homepage = "https://github.com/initxy/noeta"
Documentation = "https://initxy.github.io/noeta/"
Source = "https://github.com/initxy/noeta"
Issues = "https://github.com/initxy/noeta/issues"
Changelog = "https://github.com/initxy/noeta/blob/main/CHANGELOG.md"

[project.scripts]
noeta = "noeta.cli.main:main"

# Built-in plugins and backends register exactly as a third-party package
# would (noeta/core/registry.py).
[project.entry-points."noeta.plugins"]
ask_user = "noeta.plugins.ask_user:AskUser"
checkpoints = "noeta.plugins.checkpoints:Checkpoints"
compaction = "noeta.plugins.compaction:Compaction"
empty_reply = "noeta.plugins.empty_reply:EmptyReply"
environment = "noeta.plugins.environment:Environment"
findings = "noeta.plugins.findings:Findings"
fs = "noeta.plugins.fs:FileTools"
hooks = "noeta.plugins.hooks:Hooks"
instructions = "noeta.plugins.instructions:Instructions"
markdown_commands = "noeta.plugins.markdown_commands:MarkdownCommands"
mcp = "noeta.plugins.mcp:Mcp"
memory = "noeta.plugins.memory:Memory"
permissions = "noeta.plugins.permissions:Permissions"
plan_mode = "noeta.plugins.plan_mode:PlanMode"
project_status = "noeta.plugins.project_status:ProjectStatus"
budget = "noeta.plugins.budget:BudgetCaps"
quiet_nudge = "noeta.plugins.quiet_nudge:QuietNudge"
schedule = "noeta.plugins.schedule:Schedule"
shell = "noeta.plugins.shell:Shell"
skills = "noeta.plugins.skills:Skills"
structured_output = "noeta.plugins.structured_output:StructuredOutput"
title = "noeta.plugins.title:Title"
todos = "noeta.plugins.todos:Todos"
tool_search = "noeta.plugins.tool_search:ToolSearch"
subtasks = "noeta.plugins.subtasks:Subtasks"
web = "noeta.plugins.web:Web"
workflow = "noeta.plugins.workflow:Orchestration"

[project.entry-points."noeta.providers"]
anthropic = "noeta.backends.providers:anthropic_provider"
openai_compat = "noeta.backends.providers:openai_compat_provider"
openai_responses = "noeta.backends.providers:openai_responses_provider"

[project.entry-points."noeta.execenvs"]
local = "noeta.backends.execenvs.local:LocalExecEnv"
container = "noeta.backends.execenvs.container:open_env"

[project.entry-points."noeta.stores"]
sqlite = "noeta.backends.stores.sqlite:open_url"
postgres = "noeta.backends.stores.postgres:open_url"
postgresql = "noeta.backends.stores.postgres:open_url"

[dependency-groups]
dev = [
    "pytest>=8.0",
    "pytest-asyncio>=0.24",
    "pytest-cov>=5.0",
    "pytest-xdist>=3.8",
    "mypy>=1.10",
    "respx>=0.23.1",
    "ruff>=0.6",
    "import-linter>=2.0",
    "types-pyyaml>=6.0",
    "pytest-timeout>=2.3",
    # The workflow extra, so the Workflow tests run whatever `uv sync` is given.
    "mini-racer>=0.14,<0.15",
    # The terminal UI (tui/), so `make check` runs its tests and lints it.
    "noeta-tui",
]

[tool.uv.workspace]
members = ["tui"]

[tool.uv.sources]
noeta-tui = { workspace = true }

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

# The built web app (make web-bundle) is not in the repository but goes in
# both distributions, so a wheel built from the sdist carries it too.
[tool.hatch.build]
artifacts = ["/noeta/host/web_dist/"]

[tool.hatch.build.targets.wheel]
packages = ["noeta"]

# A local coding tool's settings are not part of the source distribution.
[tool.hatch.build.targets.sdist]
exclude = ["/.claude"]

[tool.mypy]
strict = true
files = ["noeta", "evals", "tui/src"]

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
addopts = "-ra -m 'not live and not cluster' --import-mode=importlib"
# A hung test fails with every thread's stack instead of stalling the run:
# faulthandler dumps the stacks at 120 s, pytest-timeout ends the test at 300 s.
faulthandler_timeout = 120
timeout = 300
timeout_method = "thread"
markers = [
    "live: needs a real model backend; opt in with -m live",
    "cluster: worker processes on Postgres under faults; run with make cluster",
]

[tool.coverage.run]
source = ["noeta"]
branch = true

[tool.ruff]
target-version = "py311"
line-length = 100

[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "UP", "RUF", "BLE"]
# RUF012: msgspec Struct defaults are copied per instance.
# UP007: module-level union aliases read better as Union[...] when long.
ignore = ["RUF012", "UP007"]

[tool.ruff.lint.isort]
known-first-party = ["noeta", "noeta_tui"]
# tui/tests' shared helpers, on its pytest pythonpath.
known-local-folder = ["support"]

[tool.ruff.lint.flake8-bugbear]
extend-immutable-calls = ["noeta.core.store.Patch", "Patch"]

[tool.ruff.lint.per-file-ignores]
"{tests,tui/tests}/**" = ["B008", "E501"]
# The terminal UI draws Claude Code's glyphs (❯ ⎿ ⏺ ✻).
"tui/**" = ["RUF001", "RUF002", "RUF003"]
# Dev tooling, not shipped: long table lines and prose are fine.
"{evals,bench,scripts}/**" = ["E501", "RUF002", "RUF003", "B905"]

[tool.importlinter]
root_packages = ["noeta", "noeta_tui"]

# The layers, top to bottom; a module may import only from layers below it.
[[tool.importlinter.contracts]]
name = "layers: cli > agents > plugins | backends | host > toolkit > core"
type = "layers"
layers = [
    "noeta.cli",
    "noeta.agents",
    "noeta.plugins | noeta.backends | noeta.host",
    "noeta.toolkit",
    "noeta.core",
]

# A plugin is written against the kernel only, so any one can be removed.
[[tool.importlinter.contracts]]
name = "plugins do not import each other"
type = "independence"
modules = ["noeta.plugins.*"]

# Front ends and protocol adapters reach plugins only through the kernel (settings by name, the
# question form, commands), so any plugin can be removed or replaced.
[[tool.importlinter.contracts]]
name = "front ends do not import plugins"
type = "forbidden"
source_modules = ["noeta.cli", "noeta.host"]
forbidden_modules = ["noeta.plugins"]

# The terminal UI (tui/) is a front end on noeta's host API: no plugin, no
# backend, nothing of the noeta command (which hands it what it needs).
[[tool.importlinter.contracts]]
name = "the terminal UI imports no plugin, backend, agent or the noeta command"
type = "forbidden"
source_modules = ["noeta_tui"]
forbidden_modules = ["noeta.plugins", "noeta.backends", "noeta.agents", "noeta.cli"]

[[tool.importlinter.contracts]]
name = "backends do not import each other"
type = "independence"
modules = ["noeta.backends.*"]

[[tool.importlinter.contracts]]
name = "the loop is pure: no runner, store or provider"
type = "forbidden"
source_modules = ["noeta.core.loop"]
forbidden_modules = ["noeta.core.runner", "noeta.core.store", "noeta.core.provider"]