anythink 0.1.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.
- anythink-0.1.0/.github/workflows/ci.yml +107 -0
- anythink-0.1.0/.github/workflows/publish.yml +59 -0
- anythink-0.1.0/.gitignore +36 -0
- anythink-0.1.0/CHANGELOG.md +88 -0
- anythink-0.1.0/CLAUDE.md +110 -0
- anythink-0.1.0/CONTRIBUTING.md +35 -0
- anythink-0.1.0/LICENSE +21 -0
- anythink-0.1.0/PKG-INFO +290 -0
- anythink-0.1.0/README.md +206 -0
- anythink-0.1.0/anythink_app_description.md +733 -0
- anythink-0.1.0/pyproject.toml +134 -0
- anythink-0.1.0/src/anythink/__init__.py +10 -0
- anythink-0.1.0/src/anythink/app/__init__.py +1 -0
- anythink-0.1.0/src/anythink/app/chat.py +231 -0
- anythink-0.1.0/src/anythink/app/context.py +75 -0
- anythink-0.1.0/src/anythink/cli.py +333 -0
- anythink-0.1.0/src/anythink/commands/__init__.py +1 -0
- anythink-0.1.0/src/anythink/commands/base.py +36 -0
- anythink-0.1.0/src/anythink/commands/handlers.py +376 -0
- anythink-0.1.0/src/anythink/commands/registry.py +71 -0
- anythink-0.1.0/src/anythink/config/__init__.py +1 -0
- anythink-0.1.0/src/anythink/config/manager.py +155 -0
- anythink-0.1.0/src/anythink/config/models.py +102 -0
- anythink-0.1.0/src/anythink/config/personas.py +93 -0
- anythink-0.1.0/src/anythink/config/schema.py +26 -0
- anythink-0.1.0/src/anythink/exceptions.py +57 -0
- anythink-0.1.0/src/anythink/files/__init__.py +1 -0
- anythink-0.1.0/src/anythink/files/reader.py +161 -0
- anythink-0.1.0/src/anythink/keys/__init__.py +1 -0
- anythink-0.1.0/src/anythink/keys/manager.py +97 -0
- anythink-0.1.0/src/anythink/plugins/__init__.py +1 -0
- anythink-0.1.0/src/anythink/plugins/manager.py +71 -0
- anythink-0.1.0/src/anythink/plugins/models.py +17 -0
- anythink-0.1.0/src/anythink/providers/__init__.py +25 -0
- anythink-0.1.0/src/anythink/providers/anthropic.py +138 -0
- anythink-0.1.0/src/anythink/providers/base.py +128 -0
- anythink-0.1.0/src/anythink/providers/cohere.py +123 -0
- anythink-0.1.0/src/anythink/providers/gemini.py +152 -0
- anythink-0.1.0/src/anythink/providers/groq.py +121 -0
- anythink-0.1.0/src/anythink/providers/lm_studio.py +28 -0
- anythink-0.1.0/src/anythink/providers/mistral.py +124 -0
- anythink-0.1.0/src/anythink/providers/ollama.py +130 -0
- anythink-0.1.0/src/anythink/providers/openai.py +150 -0
- anythink-0.1.0/src/anythink/providers/registry.py +67 -0
- anythink-0.1.0/src/anythink/py.typed +0 -0
- anythink-0.1.0/src/anythink/search/__init__.py +1 -0
- anythink-0.1.0/src/anythink/search/base.py +35 -0
- anythink-0.1.0/src/anythink/search/duckduckgo.py +58 -0
- anythink-0.1.0/src/anythink/search/registry.py +48 -0
- anythink-0.1.0/src/anythink/search/serpapi.py +67 -0
- anythink-0.1.0/src/anythink/session/__init__.py +1 -0
- anythink-0.1.0/src/anythink/session/manager.py +82 -0
- anythink-0.1.0/src/anythink/session/models.py +115 -0
- anythink-0.1.0/src/anythink/ui/__init__.py +1 -0
- anythink-0.1.0/src/anythink/ui/banner.py +28 -0
- anythink-0.1.0/src/anythink/ui/console.py +31 -0
- anythink-0.1.0/src/anythink/ui/input.py +30 -0
- anythink-0.1.0/src/anythink/ui/renderer.py +59 -0
- anythink-0.1.0/src/anythink/ui/status.py +46 -0
- anythink-0.1.0/src/anythink/ui/theme.py +80 -0
- anythink-0.1.0/tests/__init__.py +0 -0
- anythink-0.1.0/tests/conftest.py +39 -0
- anythink-0.1.0/tests/test_app/__init__.py +0 -0
- anythink-0.1.0/tests/test_app/test_chat.py +565 -0
- anythink-0.1.0/tests/test_app/test_context.py +84 -0
- anythink-0.1.0/tests/test_cli.py +390 -0
- anythink-0.1.0/tests/test_commands/__init__.py +0 -0
- anythink-0.1.0/tests/test_commands/test_base.py +40 -0
- anythink-0.1.0/tests/test_commands/test_handlers.py +706 -0
- anythink-0.1.0/tests/test_commands/test_registry.py +144 -0
- anythink-0.1.0/tests/test_config/__init__.py +0 -0
- anythink-0.1.0/tests/test_config/test_manager.py +149 -0
- anythink-0.1.0/tests/test_config/test_models.py +96 -0
- anythink-0.1.0/tests/test_config/test_personas.py +75 -0
- anythink-0.1.0/tests/test_files/__init__.py +0 -0
- anythink-0.1.0/tests/test_files/test_reader.py +202 -0
- anythink-0.1.0/tests/test_init.py +16 -0
- anythink-0.1.0/tests/test_keys/__init__.py +0 -0
- anythink-0.1.0/tests/test_keys/test_manager.py +125 -0
- anythink-0.1.0/tests/test_plugins/__init__.py +0 -0
- anythink-0.1.0/tests/test_plugins/test_manager.py +171 -0
- anythink-0.1.0/tests/test_plugins/test_models.py +40 -0
- anythink-0.1.0/tests/test_providers/__init__.py +0 -0
- anythink-0.1.0/tests/test_providers/conftest.py +60 -0
- anythink-0.1.0/tests/test_providers/test_anthropic.py +83 -0
- anythink-0.1.0/tests/test_providers/test_base.py +123 -0
- anythink-0.1.0/tests/test_providers/test_groq.py +80 -0
- anythink-0.1.0/tests/test_providers/test_ollama.py +170 -0
- anythink-0.1.0/tests/test_providers/test_openai.py +64 -0
- anythink-0.1.0/tests/test_providers/test_providers_no_sdk.py +254 -0
- anythink-0.1.0/tests/test_providers/test_registry.py +79 -0
- anythink-0.1.0/tests/test_search/__init__.py +0 -0
- anythink-0.1.0/tests/test_search/test_base.py +23 -0
- anythink-0.1.0/tests/test_search/test_duckduckgo.py +115 -0
- anythink-0.1.0/tests/test_search/test_registry.py +101 -0
- anythink-0.1.0/tests/test_search/test_serpapi.py +116 -0
- anythink-0.1.0/tests/test_session/__init__.py +0 -0
- anythink-0.1.0/tests/test_session/test_manager.py +157 -0
- anythink-0.1.0/tests/test_session/test_models.py +126 -0
- anythink-0.1.0/tests/test_ui/__init__.py +0 -0
- anythink-0.1.0/tests/test_ui/test_banner.py +41 -0
- anythink-0.1.0/tests/test_ui/test_console.py +57 -0
- anythink-0.1.0/tests/test_ui/test_renderer.py +89 -0
- anythink-0.1.0/tests/test_ui/test_status.py +76 -0
- anythink-0.1.0/tests/test_ui/test_theme.py +75 -0
- anythink-0.1.0/ultraplan_anythink_ai.md +230 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-typecheck:
|
|
11
|
+
name: Lint & Type Check
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.11"
|
|
19
|
+
cache: pip
|
|
20
|
+
|
|
21
|
+
- name: Install dev dependencies
|
|
22
|
+
run: pip install -e ".[dev]"
|
|
23
|
+
|
|
24
|
+
- name: Lint (ruff)
|
|
25
|
+
run: ruff check src/
|
|
26
|
+
|
|
27
|
+
- name: Format check (black)
|
|
28
|
+
run: black --check src/ tests/
|
|
29
|
+
|
|
30
|
+
- name: Type check (mypy)
|
|
31
|
+
run: mypy src/anythink
|
|
32
|
+
|
|
33
|
+
security:
|
|
34
|
+
name: Security Scan
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- uses: actions/setup-python@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.11"
|
|
42
|
+
cache: pip
|
|
43
|
+
|
|
44
|
+
- name: Install dev dependencies
|
|
45
|
+
run: pip install -e ".[dev]"
|
|
46
|
+
|
|
47
|
+
- name: Security scan (bandit)
|
|
48
|
+
run: bandit -r src/anythink -c pyproject.toml
|
|
49
|
+
|
|
50
|
+
test:
|
|
51
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
strategy:
|
|
54
|
+
fail-fast: false
|
|
55
|
+
matrix:
|
|
56
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
57
|
+
env:
|
|
58
|
+
PYTHON_KEYRING_BACKEND: keyrings.alt.file.PlaintextKeyring
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
- uses: actions/setup-python@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: ${{ matrix.python-version }}
|
|
65
|
+
cache: pip
|
|
66
|
+
|
|
67
|
+
- name: Install with dev dependencies
|
|
68
|
+
run: pip install -e ".[dev]"
|
|
69
|
+
|
|
70
|
+
- name: Run tests with coverage
|
|
71
|
+
run: pytest tests/ -v --cov-report=xml
|
|
72
|
+
|
|
73
|
+
- name: Upload coverage report
|
|
74
|
+
if: matrix.python-version == '3.11'
|
|
75
|
+
uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: coverage-report
|
|
78
|
+
path: coverage.xml
|
|
79
|
+
retention-days: 7
|
|
80
|
+
|
|
81
|
+
build:
|
|
82
|
+
name: Build & Verify Package
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
needs: [lint-typecheck, security, test]
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
|
|
88
|
+
- uses: actions/setup-python@v5
|
|
89
|
+
with:
|
|
90
|
+
python-version: "3.11"
|
|
91
|
+
cache: pip
|
|
92
|
+
|
|
93
|
+
- name: Install build tools
|
|
94
|
+
run: pip install build twine
|
|
95
|
+
|
|
96
|
+
- name: Build distribution
|
|
97
|
+
run: python -m build
|
|
98
|
+
|
|
99
|
+
- name: Verify distribution metadata
|
|
100
|
+
run: twine check dist/*
|
|
101
|
+
|
|
102
|
+
- name: Upload dist artifacts
|
|
103
|
+
uses: actions/upload-artifact@v4
|
|
104
|
+
with:
|
|
105
|
+
name: dist
|
|
106
|
+
path: dist/
|
|
107
|
+
retention-days: 7
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build & Verify
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
env:
|
|
13
|
+
PYTHON_KEYRING_BACKEND: keyrings.alt.file.PlaintextKeyring
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.11"
|
|
20
|
+
cache: pip
|
|
21
|
+
|
|
22
|
+
- name: Install with dev dependencies
|
|
23
|
+
run: pip install -e ".[dev]"
|
|
24
|
+
|
|
25
|
+
- name: Run full test suite
|
|
26
|
+
run: pytest tests/ -v --cov-fail-under=80
|
|
27
|
+
|
|
28
|
+
- name: Install build tools
|
|
29
|
+
run: pip install build twine
|
|
30
|
+
|
|
31
|
+
- name: Build distribution
|
|
32
|
+
run: python -m build
|
|
33
|
+
|
|
34
|
+
- name: Verify distribution metadata
|
|
35
|
+
run: twine check dist/*
|
|
36
|
+
|
|
37
|
+
- name: Upload dist artifacts
|
|
38
|
+
uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
|
|
43
|
+
publish:
|
|
44
|
+
name: Publish to PyPI
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
needs: build
|
|
47
|
+
environment: pypi
|
|
48
|
+
permissions:
|
|
49
|
+
id-token: write # required for OIDC trusted publishing
|
|
50
|
+
contents: read
|
|
51
|
+
steps:
|
|
52
|
+
- name: Download dist artifacts
|
|
53
|
+
uses: actions/download-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
|
|
58
|
+
- name: Publish to PyPI
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Byte-compiled / optimized / cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging / build
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
wheels/
|
|
12
|
+
*.egg
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
.env
|
|
19
|
+
|
|
20
|
+
# Test / coverage / type-check caches
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.mypy_cache/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
htmlcov/
|
|
27
|
+
coverage.xml
|
|
28
|
+
|
|
29
|
+
# IDE / editor
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
*.swp
|
|
33
|
+
|
|
34
|
+
# OS
|
|
35
|
+
.DS_Store
|
|
36
|
+
Thumbs.db
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Anythink are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-06-19
|
|
10
|
+
|
|
11
|
+
Initial release of Anythink — a universal, AI-powered CLI chatbot.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
#### Core & Configuration
|
|
16
|
+
- `AnythinkError` exception hierarchy: `ConfigError`, `ProviderError` (+ `AuthenticationError`, `RateLimitError`, `ProviderUnavailableError`, `ModelNotFoundError`), `SessionError`, `KeychainError`, `PluginError`, `SearchError`, `FileError`
|
|
17
|
+
- `AppConfig` frozen dataclass with XDG Base Directory compliant storage
|
|
18
|
+
- `ConfigManager` for loading/saving `config.yaml`; `Paths` helper resolving all XDG dirs
|
|
19
|
+
- 4 built-in color themes: **Midnight** (default), **Aurora**, **Ember**, **Arctic**
|
|
20
|
+
|
|
21
|
+
#### LLM Providers
|
|
22
|
+
- `BaseProvider` ABC with `stream_chat`, `list_models`, `test_connection`, `supports_vision`, `requires_api_key`
|
|
23
|
+
- Built-in providers: Groq, Google Gemini, OpenAI, Anthropic, Mistral, Cohere, Ollama, LM Studio
|
|
24
|
+
- Optional extras: `pip install anythink[groq|gemini|openai|anthropic|mistral|cohere|all]`
|
|
25
|
+
- Provider registry via Python `anythink.providers` entry points
|
|
26
|
+
|
|
27
|
+
#### Terminal UI
|
|
28
|
+
- ASCII logo startup banner (version, model alias, provider)
|
|
29
|
+
- `StreamRenderer`: real-time token streaming with Rich markup
|
|
30
|
+
- `ContextStatusBar`: live token usage bar with color thresholds (green → yellow → orange → red)
|
|
31
|
+
- `make_console` / `make_prompt_session`: theme-aware terminal I/O
|
|
32
|
+
|
|
33
|
+
#### Chat Loop
|
|
34
|
+
- Interactive async chat loop with slash command dispatch and legacy `exit`/`quit` shortcuts
|
|
35
|
+
- Persona / system prompt management: `/persona <name>|clear`
|
|
36
|
+
- Approaching-context-limit warnings at 60 % / 85 % / 95 % thresholds
|
|
37
|
+
|
|
38
|
+
#### API Key Management
|
|
39
|
+
- OS keychain storage via `keyring` (macOS Keychain, Linux Secret Service, Windows Credential Manager)
|
|
40
|
+
- YAML index file tracks configured providers (keyring has no list-by-service API)
|
|
41
|
+
- CLI: `anythink keys add|show|update|delete|test <provider>`
|
|
42
|
+
|
|
43
|
+
#### Model Aliases
|
|
44
|
+
- `ModelAlias` + `ModelRegistry` backed by `models.yaml`
|
|
45
|
+
- Friendly user-defined names for provider/model pairs with context-window sizes and vision flags
|
|
46
|
+
- CLI: `anythink model add|list|remove`
|
|
47
|
+
|
|
48
|
+
#### Session Management
|
|
49
|
+
- `Session` + `SessionManager` with JSON-backed storage in `$XDG_DATA_HOME/anythink/sessions/`
|
|
50
|
+
- Automatic session saving after each conversation (configurable via `session_autosave`)
|
|
51
|
+
- Session search by name or ID prefix
|
|
52
|
+
- `/session save|load|list|delete|rename` slash commands
|
|
53
|
+
|
|
54
|
+
#### Slash Command System
|
|
55
|
+
- `CommandRegistry` with entry-point discovery (`anythink.slash_commands`)
|
|
56
|
+
- `CommandResult` pattern (`should_exit`, `message`, `error`) drives the chat loop
|
|
57
|
+
- Built-in commands: `/help`, `/clear`, `/history`, `/tokens`, `/model`, `/persona`, `/session`, `/file`, `/image`, `/files`, `/search`, `/plugins`, `/exit`, `/quit`
|
|
58
|
+
|
|
59
|
+
#### File Input & Multimodal Support
|
|
60
|
+
- `TextAttachment` (≤ 1 MB, UTF-8): `.py`, `.js`, `.ts`, `.go`, `.rs`, `.json`, `.yaml`, `.md`, `.txt`, and 20+ more extensions
|
|
61
|
+
- `ImageAttachment` (≤ 10 MB): PNG, JPEG, WEBP, GIF with automatic MIME-type mapping
|
|
62
|
+
- `read_file()` auto-detects type by extension; `read_text_file()` / `read_image_file()` for explicit type
|
|
63
|
+
- Multimodal messages: `TextPart` (file header + content) + `ImagePart` + `TextPart` (user text) via `ChatMessage.content: list[ContentPart]`
|
|
64
|
+
- `ChatState.pending_attachments` cleared after each send
|
|
65
|
+
|
|
66
|
+
#### Agentic Web Search
|
|
67
|
+
- `BaseSearchBackend` ABC; `SearchResult` dataclass
|
|
68
|
+
- `DuckDuckGoSearch`: free, lazy-imports `duckduckgo-search` (`pip install anythink[search]`)
|
|
69
|
+
- `SerpAPISearch`: async `httpx` client, Google results via SerpAPI (requires key)
|
|
70
|
+
- `SearchRegistry` with entry-point discovery and `get_available(preferred)` fallback
|
|
71
|
+
- Auto-search mode: `/search on|off` injects results as `TextPart` before each user message
|
|
72
|
+
- One-off search: `/search <query>` shows title / URL / snippet in terminal
|
|
73
|
+
- `AppContext.search_registry` wired at startup (SerpAPI key looked up from keychain)
|
|
74
|
+
|
|
75
|
+
#### Plugin Architecture
|
|
76
|
+
- `PluginInfo` dataclass: name, version, description, author, entry_point_groups, homepage
|
|
77
|
+
- `PluginManager.list_plugins()` discovers packages contributing to any `anythink.*` entry point group via distribution metadata
|
|
78
|
+
- `PluginManager.install(pkg)` / `remove(pkg)` wrap `pip` via `subprocess`
|
|
79
|
+
- `/plugins list|info|install|remove` slash commands
|
|
80
|
+
- CLI: `anythink plugins list|info|install|remove`
|
|
81
|
+
|
|
82
|
+
#### CI/CD
|
|
83
|
+
- GitHub Actions `ci.yml`: lint (ruff), format check (black), type check (mypy), security scan (bandit), test matrix (Python 3.11 / 3.12 / 3.13), coverage XML upload, build + `twine check`
|
|
84
|
+
- GitHub Actions `publish.yml`: tests → build → `twine check` → PyPI via OIDC trusted publishing (no stored secrets)
|
|
85
|
+
- 80 % minimum test coverage enforced (`--cov-fail-under=80`)
|
|
86
|
+
- 526 tests across 40+ test files
|
|
87
|
+
|
|
88
|
+
[0.1.0]: https://github.com/dhineshtheprogrammer/anythink_ai/releases/tag/v0.1.0
|
anythink-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Anythink is a universal, AI-powered CLI chatbot (`pip install anythink`) built in Python 3.11+. It is currently at **early implementation stage** — only the CLI skeleton, config schema/manager, and exception hierarchy exist. The detailed feature spec lives in `anythink_app_description.md`.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install for development (editable, with all dev tools)
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
|
|
15
|
+
# Run the CLI
|
|
16
|
+
anythink
|
|
17
|
+
anythink --version
|
|
18
|
+
anythink keys list
|
|
19
|
+
anythink model list
|
|
20
|
+
|
|
21
|
+
# Lint
|
|
22
|
+
ruff check src/
|
|
23
|
+
|
|
24
|
+
# Format check (never auto-format in CI; use --check locally too)
|
|
25
|
+
black --check src/ tests/
|
|
26
|
+
|
|
27
|
+
# Type check (strict mypy, src/anythink only)
|
|
28
|
+
mypy src/anythink
|
|
29
|
+
|
|
30
|
+
# Security scan
|
|
31
|
+
bandit -r src/anythink -c pyproject.toml
|
|
32
|
+
|
|
33
|
+
# Tests (must set keyring backend to avoid OS keychain prompts)
|
|
34
|
+
PYTHON_KEYRING_BACKEND=keyrings.alt.file.PlaintextKeyring pytest tests/ -v
|
|
35
|
+
|
|
36
|
+
# Run a single test file
|
|
37
|
+
PYTHON_KEYRING_BACKEND=keyrings.alt.file.PlaintextKeyring pytest tests/test_cli.py -v
|
|
38
|
+
|
|
39
|
+
# Build PyPI distribution
|
|
40
|
+
python -m build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Coverage minimum is 80% (enforced via `--cov-fail-under=80` in `pyproject.toml`). `src/anythink/ui/input.py` is excluded from coverage.
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
### Source layout
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
src/anythink/
|
|
51
|
+
__init__.py # exposes __version__
|
|
52
|
+
cli.py # Typer app — entry point (anythink command)
|
|
53
|
+
exceptions.py # full exception hierarchy
|
|
54
|
+
config/
|
|
55
|
+
schema.py # AppConfig frozen dataclass + VALID_THEMES
|
|
56
|
+
manager.py # ConfigManager (load/save/validate) + Paths (XDG)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Plugin system (entry points)
|
|
60
|
+
|
|
61
|
+
New providers, search backends, and slash commands are registered via Python entry points in `pyproject.toml` — the core never changes to add capabilities:
|
|
62
|
+
|
|
63
|
+
- `anythink.providers` → each provider class (e.g. `GroqProvider`)
|
|
64
|
+
- `anythink.search_backends` → search backend classes
|
|
65
|
+
- `anythink.slash_commands` → registration functions
|
|
66
|
+
|
|
67
|
+
Provider SDKs are optional extras: `pip install anythink[groq]`, `[gemini]`, `[openai]`, `[anthropic]`, `[mistral]`, `[cohere]`, `[all]`.
|
|
68
|
+
|
|
69
|
+
### Configuration & storage (XDG)
|
|
70
|
+
|
|
71
|
+
All data follows XDG Base Directory Specification. Key paths are resolved in `config/manager.py:_resolve_paths()`:
|
|
72
|
+
|
|
73
|
+
| File/Dir | Location |
|
|
74
|
+
|---|---|
|
|
75
|
+
| Main config | `$XDG_CONFIG_HOME/anythink/config.yaml` |
|
|
76
|
+
| Model aliases | `$XDG_CONFIG_HOME/anythink/models.yaml` |
|
|
77
|
+
| Personas | `$XDG_CONFIG_HOME/anythink/personas.yaml` |
|
|
78
|
+
| Plugins | `$XDG_CONFIG_HOME/anythink/plugins.yaml` |
|
|
79
|
+
| Sessions | `$XDG_DATA_HOME/anythink/sessions/` |
|
|
80
|
+
| Logs | `$XDG_STATE_HOME/anythink/logs/` |
|
|
81
|
+
|
|
82
|
+
Defaults without env vars: `~/.config/anythink/`, `~/.local/share/anythink/`, etc.
|
|
83
|
+
|
|
84
|
+
`ConfigManager.is_configured()` checks for the existence of `config.yaml`. `ConfigManager.load()` returns an `AppConfig` frozen dataclass.
|
|
85
|
+
|
|
86
|
+
### Exception hierarchy
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
AnythinkError
|
|
90
|
+
ConfigError
|
|
91
|
+
ProviderError
|
|
92
|
+
AuthenticationError
|
|
93
|
+
RateLimitError
|
|
94
|
+
ProviderUnavailableError
|
|
95
|
+
ModelNotFoundError
|
|
96
|
+
SessionError
|
|
97
|
+
KeychainError
|
|
98
|
+
PluginError
|
|
99
|
+
SearchError
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`AnythinkError` carries both an internal `message` and a `user_message` (displayed in the terminal). `ProviderError` also carries the `provider` name.
|
|
103
|
+
|
|
104
|
+
### CLI structure
|
|
105
|
+
|
|
106
|
+
The Typer app in `cli.py` has two sub-apps: `keys_app` and `model_app`. The root callback (`main`) invokes `ConfigManager.is_configured()` before starting a chat session. All sub-commands beyond the initial skeleton are stubs referencing the implementation phase.
|
|
107
|
+
|
|
108
|
+
### Testing conventions
|
|
109
|
+
|
|
110
|
+
The `xdg_dirs` fixture in `tests/conftest.py` redirects all four XDG env vars to `tmp_path` subdirectories. Every test that touches the filesystem should use this fixture (or `config_manager` which builds on it) to avoid touching `~/.config`. The `PYTHON_KEYRING_BACKEND=keyrings.alt.file.PlaintextKeyring` env var must be set for any test that indirectly imports keyring code.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Contributing to Anythink
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing!
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/dhineshtheprogrammer/anythink_ai
|
|
9
|
+
cd anythink_ai
|
|
10
|
+
pip install -e ".[dev]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Running Tests
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pytest tests/ -v
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Code Style
|
|
20
|
+
|
|
21
|
+
- **Ruff** for linting: `ruff check src/`
|
|
22
|
+
- **Black** for formatting: `black src/ tests/`
|
|
23
|
+
- **Mypy** for type checking: `mypy src/anythink`
|
|
24
|
+
|
|
25
|
+
## Submitting Changes
|
|
26
|
+
|
|
27
|
+
1. Fork the repository
|
|
28
|
+
2. Create a feature branch from `main`
|
|
29
|
+
3. Make your changes with tests
|
|
30
|
+
4. Ensure all CI checks pass
|
|
31
|
+
5. Open a pull request
|
|
32
|
+
|
|
33
|
+
## Plugin Development
|
|
34
|
+
|
|
35
|
+
See the documentation on creating Anythink plugins (provider, search backend, slash command, file handler, or tool plugins) via Python entry points.
|
anythink-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dhineshtheprogrammer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|