imbi-slackbot 2.8.0__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.
Files changed (37) hide show
  1. imbi_slackbot-2.8.0/.github/workflows/publish.yml +45 -0
  2. imbi_slackbot-2.8.0/.github/workflows/test.yml +49 -0
  3. imbi_slackbot-2.8.0/.gitignore +8 -0
  4. imbi_slackbot-2.8.0/.pre-commit-config.yaml +22 -0
  5. imbi_slackbot-2.8.0/.python-version +1 -0
  6. imbi_slackbot-2.8.0/LICENSE +28 -0
  7. imbi_slackbot-2.8.0/PKG-INFO +77 -0
  8. imbi_slackbot-2.8.0/README.md +59 -0
  9. imbi_slackbot-2.8.0/docs/slack-app-manifest.md +57 -0
  10. imbi_slackbot-2.8.0/justfile +51 -0
  11. imbi_slackbot-2.8.0/pyproject.toml +189 -0
  12. imbi_slackbot-2.8.0/src/imbi_slackbot/__init__.py +18 -0
  13. imbi_slackbot-2.8.0/src/imbi_slackbot/agent.py +148 -0
  14. imbi_slackbot-2.8.0/src/imbi_slackbot/app.py +91 -0
  15. imbi_slackbot-2.8.0/src/imbi_slackbot/app_status.py +41 -0
  16. imbi_slackbot-2.8.0/src/imbi_slackbot/client.py +73 -0
  17. imbi_slackbot-2.8.0/src/imbi_slackbot/identity.py +155 -0
  18. imbi_slackbot-2.8.0/src/imbi_slackbot/links.py +89 -0
  19. imbi_slackbot-2.8.0/src/imbi_slackbot/mcp.py +248 -0
  20. imbi_slackbot-2.8.0/src/imbi_slackbot/py.typed +0 -0
  21. imbi_slackbot-2.8.0/src/imbi_slackbot/settings.py +111 -0
  22. imbi_slackbot-2.8.0/src/imbi_slackbot/slack_handler.py +223 -0
  23. imbi_slackbot-2.8.0/src/imbi_slackbot/system_prompt.md +76 -0
  24. imbi_slackbot-2.8.0/src/imbi_slackbot/system_prompt.py +92 -0
  25. imbi_slackbot-2.8.0/tests/__init__.py +0 -0
  26. imbi_slackbot-2.8.0/tests/helpers.py +40 -0
  27. imbi_slackbot-2.8.0/tests/test_agent.py +161 -0
  28. imbi_slackbot-2.8.0/tests/test_app.py +51 -0
  29. imbi_slackbot-2.8.0/tests/test_client.py +49 -0
  30. imbi_slackbot-2.8.0/tests/test_identity.py +135 -0
  31. imbi_slackbot-2.8.0/tests/test_links.py +100 -0
  32. imbi_slackbot-2.8.0/tests/test_mcp.py +183 -0
  33. imbi_slackbot-2.8.0/tests/test_settings.py +64 -0
  34. imbi_slackbot-2.8.0/tests/test_slack_handler.py +301 -0
  35. imbi_slackbot-2.8.0/tests/test_system_prompt.py +59 -0
  36. imbi_slackbot-2.8.0/tests/test_version.py +11 -0
  37. imbi_slackbot-2.8.0/uv.lock +2494 -0
@@ -0,0 +1,45 @@
1
+ name: Publish to PyPI
2
+ on:
3
+ release:
4
+ types: [published]
5
+
6
+ permissions:
7
+ id-token: write
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v6
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v5
20
+
21
+ - name: Set up Python
22
+ run: uv python install 3.14
23
+
24
+ - name: Build
25
+ run: uv build
26
+
27
+ - name: Upload dist
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment: pypi
37
+ steps:
38
+ - name: Download dist
39
+ uses: actions/download-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ - name: Publish package
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,49 @@
1
+ name: "Run Tests"
2
+ on:
3
+ push:
4
+ branches: [ main ]
5
+ pull_request:
6
+ branches: [ main ]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ lint:
13
+ name: "Static Analysis"
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v5
18
+ - name: Install just
19
+ uses: extractions/setup-just@v2
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v7
22
+ with:
23
+ enable-cache: true
24
+ - name: Run lint checks
25
+ run: just lint
26
+
27
+ test:
28
+ name: "Automated Tests"
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - name: Checkout repository
32
+ uses: actions/checkout@v5
33
+ - name: Install just
34
+ uses: extractions/setup-just@v2
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v7
37
+ with:
38
+ enable-cache: true
39
+ python-version: "3.14"
40
+ - name: Run tests
41
+ env:
42
+ UV_FROZEN: "1"
43
+ run: just test
44
+ - name: Upload coverage reports
45
+ uses: codecov/codecov-action@v5
46
+ with:
47
+ token: ${{ secrets.CODECOV_TOKEN }}
48
+ files: "build/coverage.xml"
49
+ flags: unittests
@@ -0,0 +1,8 @@
1
+ .*
2
+ !/.coderabbit.yaml
3
+ !/.github
4
+ !/.gitignore
5
+ !/.pre-commit-config.yaml
6
+ !/.python-version
7
+ /build
8
+ /dist
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0 # unrelated to the pre-commit utility version
4
+ hooks:
5
+ - id: check-executables-have-shebangs
6
+ - id: check-json
7
+ - id: check-shebang-scripts-are-executable
8
+ - id: check-toml
9
+ - id: check-yaml
10
+ - id: end-of-file-fixer
11
+ - id: mixed-line-ending
12
+ - id: trailing-whitespace
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.14.14
15
+ hooks:
16
+ - id: ruff-check
17
+ args: [--fix]
18
+ - id: ruff-format
19
+ - repo: https://github.com/tombi-toml/tombi-pre-commit
20
+ rev: v0.7.27
21
+ hooks:
22
+ - id: tombi-format
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, AWeber
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: imbi-slackbot
3
+ Version: 2.8.0
4
+ Summary: Imbi Slack bot
5
+ Author-email: "Gavin M. Roy" <gavinr@aweber.com>
6
+ License-Expression: BSD-3-Clause
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.14
9
+ Requires-Dist: anthropic>=0.84.0
10
+ Requires-Dist: fastapi>=0.135.1
11
+ Requires-Dist: fastmcp>=3.0.0
12
+ Requires-Dist: httpx>=0.28.1
13
+ Requires-Dist: imbi-common[databases,mcp,server]==2.8.0
14
+ Requires-Dist: pydantic~=2.12
15
+ Requires-Dist: sentry-sdk>=2.48.0
16
+ Requires-Dist: slack-bolt>=1.21
17
+ Description-Content-Type: text/markdown
18
+
19
+ # imbi-slackbot
20
+
21
+ A Slack bot for the [Imbi](https://github.com/AWeber-Imbi/imbi) DevOps service
22
+ management platform. It behaves like the
23
+ [Imbi assistant](https://github.com/AWeber-Imbi/imbi-assistant) — giving Claude
24
+ live access to Imbi data and actions through tools — but speaks Slack and acts
25
+ *as the Slack user who asked*, so every answer and action respects that user's
26
+ Imbi permissions.
27
+
28
+ ## How It Works
29
+
30
+ The bot connects to Slack over [Socket Mode](https://api.slack.com/apis/socket-mode)
31
+ (an outbound websocket, so no public ingress is required). When a user mentions
32
+ the bot or DMs it, the bot:
33
+
34
+ 1. **Resolves the Slack user to an Imbi user.** It looks up the Slack user's
35
+ email (`users.info`), finds the matching active Imbi `User` in the graph, and
36
+ mints a short-lived per-user JWT access token signed with the shared
37
+ `IMBI_AUTH_JWT_SECRET` — exactly the token the Imbi API issues at login. The
38
+ resolution is cached with a TTL. Users with no matching Imbi account get a
39
+ friendly explanation instead.
40
+ 2. **Reconstructs the thread context** from Slack (`conversations.replies`) so
41
+ follow-up questions in a thread keep their history. Nothing is persisted
42
+ server-side; the Slack thread *is* the conversation.
43
+ 3. **Runs Claude with the Imbi toolset.** Tools are built at startup from the
44
+ Imbi API's `openapi.json` via an in-process
45
+ [FastMCP](https://github.com/jlowin/fastmcp) server, using the shared
46
+ `imbi_common.mcp` exclusion policy (auth/MFA/status/thumbnail and any
47
+ `x-imbi-ai-tool: false` operations are filtered out). The user's minted token
48
+ is forwarded on every tool call, so the API enforces their permissions.
49
+ 4. **Replies in-thread** with Claude's answer.
50
+
51
+ A tiny FastAPI app exposes `/status` (port 8004) for health checks; the Socket
52
+ Mode connection runs as a background task alongside it.
53
+
54
+ ## Configuration
55
+
56
+ | Environment variable | Purpose |
57
+ |---|---|
58
+ | `SLACK_BOT_TOKEN` | Slack bot token (`xoxb-…`) |
59
+ | `SLACK_APP_TOKEN` | Slack app-level token for Socket Mode (`xapp-…`) |
60
+ | `ANTHROPIC_API_KEY` | Anthropic API key (enables the bot when set) |
61
+ | `IMBI_AUTH_JWT_SECRET` | Shared HS256 secret used to mint per-user tokens |
62
+ | `IMBI_INTERNAL_API_URL` | In-cluster base URL of the Imbi API |
63
+ | `IMBI_UI_URL` | Public base URL of the Imbi UI, used to build deep links |
64
+ | `IMBI_INTERNAL_UI_URL` | In-cluster UI address for fetching `llms.txt` (e.g. the Caddy frontend); falls back to `IMBI_UI_URL` |
65
+ | `POSTGRES_URL` | Imbi graph (Apache AGE) connection URL |
66
+ | `IMBI_SLACKBOT_MODEL` | Claude model (default `claude-sonnet-4-6`) |
67
+
68
+ See `src/imbi_slackbot/settings.py` for the full set.
69
+
70
+ ## Development
71
+
72
+ ```bash
73
+ just setup # install deps + pre-commit hooks
74
+ just test # run the test suite with coverage
75
+ just lint # ruff + mypy + basedpyright
76
+ just serve # run the bot against .env
77
+ ```
@@ -0,0 +1,59 @@
1
+ # imbi-slackbot
2
+
3
+ A Slack bot for the [Imbi](https://github.com/AWeber-Imbi/imbi) DevOps service
4
+ management platform. It behaves like the
5
+ [Imbi assistant](https://github.com/AWeber-Imbi/imbi-assistant) — giving Claude
6
+ live access to Imbi data and actions through tools — but speaks Slack and acts
7
+ *as the Slack user who asked*, so every answer and action respects that user's
8
+ Imbi permissions.
9
+
10
+ ## How It Works
11
+
12
+ The bot connects to Slack over [Socket Mode](https://api.slack.com/apis/socket-mode)
13
+ (an outbound websocket, so no public ingress is required). When a user mentions
14
+ the bot or DMs it, the bot:
15
+
16
+ 1. **Resolves the Slack user to an Imbi user.** It looks up the Slack user's
17
+ email (`users.info`), finds the matching active Imbi `User` in the graph, and
18
+ mints a short-lived per-user JWT access token signed with the shared
19
+ `IMBI_AUTH_JWT_SECRET` — exactly the token the Imbi API issues at login. The
20
+ resolution is cached with a TTL. Users with no matching Imbi account get a
21
+ friendly explanation instead.
22
+ 2. **Reconstructs the thread context** from Slack (`conversations.replies`) so
23
+ follow-up questions in a thread keep their history. Nothing is persisted
24
+ server-side; the Slack thread *is* the conversation.
25
+ 3. **Runs Claude with the Imbi toolset.** Tools are built at startup from the
26
+ Imbi API's `openapi.json` via an in-process
27
+ [FastMCP](https://github.com/jlowin/fastmcp) server, using the shared
28
+ `imbi_common.mcp` exclusion policy (auth/MFA/status/thumbnail and any
29
+ `x-imbi-ai-tool: false` operations are filtered out). The user's minted token
30
+ is forwarded on every tool call, so the API enforces their permissions.
31
+ 4. **Replies in-thread** with Claude's answer.
32
+
33
+ A tiny FastAPI app exposes `/status` (port 8004) for health checks; the Socket
34
+ Mode connection runs as a background task alongside it.
35
+
36
+ ## Configuration
37
+
38
+ | Environment variable | Purpose |
39
+ |---|---|
40
+ | `SLACK_BOT_TOKEN` | Slack bot token (`xoxb-…`) |
41
+ | `SLACK_APP_TOKEN` | Slack app-level token for Socket Mode (`xapp-…`) |
42
+ | `ANTHROPIC_API_KEY` | Anthropic API key (enables the bot when set) |
43
+ | `IMBI_AUTH_JWT_SECRET` | Shared HS256 secret used to mint per-user tokens |
44
+ | `IMBI_INTERNAL_API_URL` | In-cluster base URL of the Imbi API |
45
+ | `IMBI_UI_URL` | Public base URL of the Imbi UI, used to build deep links |
46
+ | `IMBI_INTERNAL_UI_URL` | In-cluster UI address for fetching `llms.txt` (e.g. the Caddy frontend); falls back to `IMBI_UI_URL` |
47
+ | `POSTGRES_URL` | Imbi graph (Apache AGE) connection URL |
48
+ | `IMBI_SLACKBOT_MODEL` | Claude model (default `claude-sonnet-4-6`) |
49
+
50
+ See `src/imbi_slackbot/settings.py` for the full set.
51
+
52
+ ## Development
53
+
54
+ ```bash
55
+ just setup # install deps + pre-commit hooks
56
+ just test # run the test suite with coverage
57
+ just lint # ruff + mypy + basedpyright
58
+ just serve # run the bot against .env
59
+ ```
@@ -0,0 +1,57 @@
1
+ # Slack App Setup
2
+
3
+ `imbi-slackbot` connects to Slack over **Socket Mode**, so it needs no public
4
+ URL. Create a Slack app from the manifest below (Slack → *Your Apps* →
5
+ *Create New App* → *From an app manifest*), install it to the workspace, then
6
+ provide the two tokens to the service.
7
+
8
+ ## Tokens
9
+
10
+ | Env var | Where to find it |
11
+ |---|---|
12
+ | `SLACK_BOT_TOKEN` | *OAuth & Permissions* → *Bot User OAuth Token* (`xoxb-…`) after install |
13
+ | `SLACK_APP_TOKEN` | *Basic Information* → *App-Level Tokens* → create one with the `connections:write` scope (`xapp-…`) |
14
+
15
+ The bot also needs `ANTHROPIC_API_KEY`, the shared `IMBI_AUTH_JWT_SECRET`, and
16
+ `IMBI_INTERNAL_API_URL` / `POSTGRES_URL` (see the README).
17
+
18
+ ## Required scopes & why
19
+
20
+ - `app_mentions:read` — receive `@imbi` mentions in channels.
21
+ - `chat:write` — post replies.
22
+ - `users:read` + `users:read.email` — **the identity bridge**: resolve the
23
+ Slack user to their email, which is matched against the Imbi `User` record.
24
+ - `im:history`, `im:read` — read and respond in direct messages.
25
+ - `channels:history`, `groups:history` — read thread context
26
+ (`conversations.replies`) when mentioned in public/private channels.
27
+
28
+ ## Manifest
29
+
30
+ ```yaml
31
+ display_information:
32
+ name: Imbi
33
+ description: Query and manage Imbi from Slack
34
+ features:
35
+ bot_user:
36
+ display_name: imbi
37
+ always_online: true
38
+ oauth_config:
39
+ scopes:
40
+ bot:
41
+ - app_mentions:read
42
+ - chat:write
43
+ - users:read
44
+ - users:read.email
45
+ - im:history
46
+ - im:read
47
+ - channels:history
48
+ - groups:history
49
+ settings:
50
+ event_subscriptions:
51
+ bot_events:
52
+ - app_mention
53
+ - message.im
54
+ socket_mode_enabled: true
55
+ org_deploy_enabled: false
56
+ token_rotation_enabled: false
57
+ ```
@@ -0,0 +1,51 @@
1
+ [doc("Bootstrap the environment and run the bot in the foreground")]
2
+ [group("Testing")]
3
+ serve *ARGS: setup
4
+ uv run --env-file=.env imbi-slackbot serve {{ ARGS }}
5
+
6
+ [default]
7
+ [private]
8
+ ci: lint test
9
+
10
+ [doc("Set up your development environment")]
11
+ [group("Environment")]
12
+ setup:
13
+ uv sync --all-groups --all-extras --frozen
14
+ uv run pre-commit install --install-hooks --overwrite
15
+
16
+ [doc("Run tests")]
17
+ [group("Testing")]
18
+ test: setup
19
+ uv run pytest
20
+
21
+ [doc("Run linters")]
22
+ [group("Testing")]
23
+ lint: setup
24
+ uv run pre-commit run --all-files
25
+ uv run basedpyright
26
+ uv run mypy
27
+
28
+ [doc("Reformat code (optionally pass specific files)")]
29
+ [group("Development")]
30
+ format *FILES: setup
31
+ #!/usr/bin/env sh
32
+ set -ex
33
+ if [ "{{FILES}}" = '' ]; then
34
+ args='--all-files'
35
+ else
36
+ args='--files {{FILES}}'
37
+ fi
38
+ uv run pre-commit run ruff-format $args
39
+ uv run pre-commit run tombi-format $args
40
+
41
+ [doc("Remove runtime artifacts")]
42
+ [group("Environment")]
43
+ clean:
44
+ rm -f .coverage
45
+ rm -fR build
46
+
47
+ [confirm]
48
+ [doc("Remove caches, virtual env, and output files")]
49
+ [group("Environment")]
50
+ real-clean: clean
51
+ rm -fR .venv .*_cache dist
@@ -0,0 +1,189 @@
1
+ [project]
2
+ name = "imbi-slackbot"
3
+ version = "2.8.0"
4
+ description = "Imbi Slack bot"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = "BSD-3-Clause"
8
+ authors = [{ name = "Gavin M. Roy", email = "gavinr@aweber.com" }]
9
+ dependencies = [
10
+ "anthropic>=0.84.0",
11
+ "fastapi>=0.135.1",
12
+ "fastmcp>=3.0.0",
13
+ "httpx>=0.28.1",
14
+ "imbi-common[databases,mcp,server]==2.8.0",
15
+ "pydantic~=2.12",
16
+ "sentry-sdk>=2.48.0",
17
+ "slack-bolt>=1.21",
18
+ ]
19
+
20
+ [project.scripts]
21
+ imbi-slackbot = "imbi_slackbot.app:cli"
22
+
23
+ [dependency-groups]
24
+ dev = [
25
+ "basedpyright>=1.37.1",
26
+ "coverage[toml]>=7.13.1",
27
+ "mypy~=1.19.1",
28
+ "pre-commit>=4.5.1",
29
+ "pytest>=9.0.2",
30
+ "pytest-cov>=7.0.0",
31
+ "python-dotenv>=1.1.0",
32
+ "ruff~=0.14.6",
33
+ ]
34
+
35
+ [build-system]
36
+ requires = ["hatchling"]
37
+ build-backend = "hatchling.build"
38
+
39
+ [tool.coverage.report]
40
+ fail_under = 90
41
+ show_missing = true
42
+
43
+ [tool.coverage.run]
44
+ branch = true
45
+ source = ["src"]
46
+
47
+ [tool.mypy]
48
+ namespace_packages = true
49
+ show_error_codes = true
50
+ strict = true
51
+ packages = ["imbi_slackbot", "tests"]
52
+
53
+ [[tool.mypy.overrides]]
54
+ module = "tests.*"
55
+ disallow_untyped_defs = false
56
+ disallow_incomplete_defs = false
57
+ disallow_untyped_calls = false
58
+ disallow_any_expr = false
59
+ implicit_reexport = true
60
+ warn_return_any = false
61
+ disable_error_code = [
62
+ "attr-defined",
63
+ "call-arg",
64
+ "arg-type",
65
+ "type-arg",
66
+ "union-attr",
67
+ "index",
68
+ "var-annotated"
69
+ ]
70
+
71
+ [tool.pyright]
72
+ deprecateTypingAliases = true
73
+ reportMissingSuperCall = "hint"
74
+ typeCheckingMode = "strict"
75
+
76
+ [[tool.pyright.executionEnvironments]]
77
+ root = "src"
78
+ reportUnusedFunction = "warning"
79
+
80
+ [[tool.pyright.executionEnvironments]]
81
+ root = "tests"
82
+ reportUnusedFunction = "none"
83
+ reportUnusedVariable = "none"
84
+ reportPrivateUsage = "none"
85
+ reportAttributeAccessIssue = "none"
86
+ reportMissingParameterType = "none"
87
+ reportUnknownLambdaType = "none"
88
+ reportUnknownParameterType = "none"
89
+ reportUnknownVariableType = "none"
90
+ reportMissingTypeArgument = "none"
91
+ reportCallIssue = "none"
92
+ reportOptionalMemberAccess = "none"
93
+ reportOptionalSubscript = "none"
94
+ reportUnknownMemberType = "none"
95
+ reportArgumentType = "none"
96
+ reportUnknownArgumentType = "none"
97
+ extraPaths = ["src", "."]
98
+
99
+ [tool.pytest.ini_options]
100
+ addopts = [
101
+ "--cov=src/imbi_slackbot",
102
+ "--cov-report=xml:build/coverage.xml",
103
+ "--cov-report=term",
104
+ ]
105
+ testpaths = ["tests"]
106
+
107
+ [tool.ruff]
108
+ line-length = 79
109
+ target-version = "py314"
110
+
111
+ [tool.ruff.format]
112
+ quote-style = "single"
113
+
114
+ [tool.ruff.lint]
115
+ select = [
116
+ "A",
117
+ "ANN",
118
+ "ARG",
119
+ "ASYNC",
120
+ "B",
121
+ "BLE",
122
+ "C4",
123
+ "C90",
124
+ "DTZ",
125
+ "E",
126
+ "ERA",
127
+ "F",
128
+ "FLY",
129
+ "FURB",
130
+ "G",
131
+ "I",
132
+ "LOG",
133
+ "N",
134
+ "PIE",
135
+ "PL",
136
+ "PTH",
137
+ "Q",
138
+ "RET",
139
+ "RSE",
140
+ "RUF",
141
+ "S",
142
+ "SIM",
143
+ "T10",
144
+ "T20",
145
+ "TC",
146
+ "TRY",
147
+ "W",
148
+ ]
149
+ ignore = [
150
+ "ANN401",
151
+ "ARG002",
152
+ "PLR0913",
153
+ "PLR2004",
154
+ "TRY003",
155
+ "TRY400",
156
+ ]
157
+ flake8-quotes = { inline-quotes = "single" }
158
+
159
+ [tool.ruff.lint.flake8-type-checking]
160
+ runtime-evaluated-base-classes = ["pydantic.BaseModel"]
161
+
162
+ [tool.ruff.lint.mccabe]
163
+ max-complexity = 15
164
+
165
+ [tool.ruff.lint.per-file-ignores]
166
+ "src/imbi_slackbot/client.py" = ["PLW0603"]
167
+ "src/imbi_slackbot/identity.py" = ["PLW0603"]
168
+ "src/imbi_slackbot/links.py" = ["PLW0603"]
169
+ "src/imbi_slackbot/mcp.py" = ["PLW0603"]
170
+ "src/imbi_slackbot/settings.py" = ["PLW0603"]
171
+ "src/imbi_slackbot/slack_handler.py" = ["PLW0603"]
172
+ "src/imbi_slackbot/system_prompt.py" = ["PLW0603"]
173
+ "tests/**/*.py" = [
174
+ "ANN",
175
+ "ARG005",
176
+ "B018",
177
+ "N802",
178
+ "PLC0415",
179
+ "RUF059",
180
+ "S",
181
+ ]
182
+
183
+ [tool.uv]
184
+ exclude-newer = "7 days"
185
+ exclude-newer-package = { "imbi-common" = false }
186
+
187
+ [[tool.uv.index]]
188
+ url = "https://pypi.org/simple"
189
+ default = true
@@ -0,0 +1,18 @@
1
+ import re as _re
2
+ from importlib import metadata as _metadata
3
+
4
+ try:
5
+ version = _metadata.version('imbi-slackbot')
6
+ except _metadata.PackageNotFoundError:
7
+ version = '0.0.0'
8
+
9
+ version_info: list[int | str] = []
10
+ for _part in version.split('.'):
11
+ _match = _re.fullmatch(r'(\d+)(.*)', _part)
12
+ if _match is None:
13
+ version_info.append(_part)
14
+ else:
15
+ version_info.append(int(_match.group(1)))
16
+ if _match.group(2):
17
+ version_info.append(_match.group(2))
18
+ del _metadata, _re