indieclaw 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.
- indieclaw-0.1.0/.bandit +2 -0
- indieclaw-0.1.0/.env.example +4 -0
- indieclaw-0.1.0/.github/workflows/ci.yml +43 -0
- indieclaw-0.1.0/.github/workflows/python-doctor.yml +54 -0
- indieclaw-0.1.0/.github/workflows/release.yml +93 -0
- indieclaw-0.1.0/.gitignore +11 -0
- indieclaw-0.1.0/.python-version +1 -0
- indieclaw-0.1.0/CHANGELOG.md +15 -0
- indieclaw-0.1.0/CLAUDE.md +67 -0
- indieclaw-0.1.0/LICENSE +21 -0
- indieclaw-0.1.0/Makefile +44 -0
- indieclaw-0.1.0/PKG-INFO +117 -0
- indieclaw-0.1.0/README.md +94 -0
- indieclaw-0.1.0/deploy.sh +13 -0
- indieclaw-0.1.0/docs/index.html +265 -0
- indieclaw-0.1.0/indieclaw/__init__.py +6 -0
- indieclaw-0.1.0/indieclaw/agent.py +446 -0
- indieclaw-0.1.0/indieclaw/auth.py +36 -0
- indieclaw-0.1.0/indieclaw/browser.py +210 -0
- indieclaw-0.1.0/indieclaw/claude_code.py +651 -0
- indieclaw-0.1.0/indieclaw/config.py +72 -0
- indieclaw-0.1.0/indieclaw/daemon.py +81 -0
- indieclaw-0.1.0/indieclaw/doctor.py +419 -0
- indieclaw-0.1.0/indieclaw/handlers.py +620 -0
- indieclaw-0.1.0/indieclaw/handlers_commands.py +279 -0
- indieclaw-0.1.0/indieclaw/main.py +491 -0
- indieclaw-0.1.0/indieclaw/prompt_builder.py +100 -0
- indieclaw-0.1.0/indieclaw/py.typed +0 -0
- indieclaw-0.1.0/indieclaw/scheduler.py +296 -0
- indieclaw-0.1.0/indieclaw/session_state.py +78 -0
- indieclaw-0.1.0/indieclaw/setup.py +403 -0
- indieclaw-0.1.0/indieclaw/setup_services.py +210 -0
- indieclaw-0.1.0/indieclaw/skills.py +17 -0
- indieclaw-0.1.0/indieclaw/subconscious.py +100 -0
- indieclaw-0.1.0/indieclaw/tool_loader.py +136 -0
- indieclaw-0.1.0/indieclaw/tools.py +131 -0
- indieclaw-0.1.0/indieclaw/tools_browser.py +90 -0
- indieclaw-0.1.0/indieclaw/tools_sdk.py +525 -0
- indieclaw-0.1.0/indieclaw/tui.py +287 -0
- indieclaw-0.1.0/indieclaw/version.py +126 -0
- indieclaw-0.1.0/indieclaw/watchdog.sh +113 -0
- indieclaw-0.1.0/indieclaw/workspace.py +97 -0
- indieclaw-0.1.0/install.sh +73 -0
- indieclaw-0.1.0/pyproject.toml +65 -0
- indieclaw-0.1.0/pyrightconfig.json +5 -0
- indieclaw-0.1.0/templates/AGENT.md +89 -0
- indieclaw-0.1.0/templates/MEMORY.md +16 -0
- indieclaw-0.1.0/templates/SOUL.md +34 -0
- indieclaw-0.1.0/templates/SUBCONSCIOUS.md +37 -0
- indieclaw-0.1.0/templates/USER.md +21 -0
- indieclaw-0.1.0/templates/crons.yaml +18 -0
- indieclaw-0.1.0/templates/skills/cli-learning/SKILL.md +51 -0
- indieclaw-0.1.0/templates/skills/handover/SKILL.md +21 -0
- indieclaw-0.1.0/templates/skills/skill-creation/SKILL.md +22 -0
- indieclaw-0.1.0/templates/skills/tool-building/SKILL.md +77 -0
- indieclaw-0.1.0/tests/test_agent.py +367 -0
- indieclaw-0.1.0/tests/test_auth.py +87 -0
- indieclaw-0.1.0/tests/test_config.py +187 -0
- indieclaw-0.1.0/tests/test_daemon.py +112 -0
- indieclaw-0.1.0/tests/test_doctor.py +551 -0
- indieclaw-0.1.0/tests/test_handlers.py +586 -0
- indieclaw-0.1.0/tests/test_scheduler.py +146 -0
- indieclaw-0.1.0/tests/test_session_state.py +95 -0
- indieclaw-0.1.0/tests/test_skills.py +58 -0
- indieclaw-0.1.0/tests/test_subconscious.py +126 -0
- indieclaw-0.1.0/tests/test_tool_loader.py +188 -0
- indieclaw-0.1.0/tests/test_tools.py +53 -0
- indieclaw-0.1.0/tests/test_tools_sdk.py +341 -0
- indieclaw-0.1.0/tests/test_version.py +85 -0
- indieclaw-0.1.0/tests/test_workspace.py +52 -0
- indieclaw-0.1.0/uv.lock +1686 -0
indieclaw-0.1.0/.bandit
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
strategy:
|
|
7
|
+
matrix:
|
|
8
|
+
python-version: ["3.12", "3.13"]
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
- uses: astral-sh/setup-uv@v4
|
|
12
|
+
- run: uv python install ${{ matrix.python-version }}
|
|
13
|
+
- run: uv sync --dev
|
|
14
|
+
- run: uv run pytest tests/ -v
|
|
15
|
+
|
|
16
|
+
auto-release:
|
|
17
|
+
needs: test
|
|
18
|
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push' && contains(github.event.head_commit.message, 'Bump version')
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: write
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Extract version
|
|
26
|
+
id: version
|
|
27
|
+
run: |
|
|
28
|
+
VERSION=$(python3 -c "import re; print(re.search(r'version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))")
|
|
29
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
30
|
+
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
31
|
+
|
|
32
|
+
- name: Create tag and release
|
|
33
|
+
env:
|
|
34
|
+
GH_TOKEN: ${{ github.token }}
|
|
35
|
+
run: |
|
|
36
|
+
TAG="${{ steps.version.outputs.tag }}"
|
|
37
|
+
if gh release view "$TAG" >/dev/null 2>&1; then
|
|
38
|
+
echo "Release $TAG already exists, skipping."
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
git tag "$TAG"
|
|
42
|
+
git push origin "$TAG"
|
|
43
|
+
gh release create "$TAG" --generate-notes -t "$TAG"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Python Doctor
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
health-check:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install python-doctor
|
|
17
|
+
run: pipx install python-doctor
|
|
18
|
+
|
|
19
|
+
- name: Run python-doctor
|
|
20
|
+
id: doctor
|
|
21
|
+
run: |
|
|
22
|
+
score=$(python-doctor . --score 2>/dev/null || echo "0")
|
|
23
|
+
echo "score=$score" >> "$GITHUB_OUTPUT"
|
|
24
|
+
echo "Python Doctor score: $score/100"
|
|
25
|
+
|
|
26
|
+
- name: Determine badge color
|
|
27
|
+
id: color
|
|
28
|
+
run: |
|
|
29
|
+
score=${{ steps.doctor.outputs.score }}
|
|
30
|
+
if [ "$score" -ge 90 ]; then
|
|
31
|
+
echo "color=brightgreen" >> "$GITHUB_OUTPUT"
|
|
32
|
+
elif [ "$score" -ge 75 ]; then
|
|
33
|
+
echo "color=green" >> "$GITHUB_OUTPUT"
|
|
34
|
+
elif [ "$score" -ge 50 ]; then
|
|
35
|
+
echo "color=yellow" >> "$GITHUB_OUTPUT"
|
|
36
|
+
else
|
|
37
|
+
echo "color=red" >> "$GITHUB_OUTPUT"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
- name: Update badge in README
|
|
41
|
+
run: |
|
|
42
|
+
score=${{ steps.doctor.outputs.score }}
|
|
43
|
+
color=${{ steps.color.outputs.color }}
|
|
44
|
+
encoded_score=$(echo "${score}/100" | sed 's|/|%2F|g')
|
|
45
|
+
sed -i "s|python--doctor-[0-9]*%2F100-[a-z]*|python--doctor-${encoded_score}-${color}|" README.md
|
|
46
|
+
|
|
47
|
+
- name: Commit if changed
|
|
48
|
+
run: |
|
|
49
|
+
git diff --quiet README.md && exit 0
|
|
50
|
+
git config user.name "github-actions[bot]"
|
|
51
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
52
|
+
git add README.md
|
|
53
|
+
git commit -m "ci: update python-doctor badge to ${{ steps.doctor.outputs.score }}/100"
|
|
54
|
+
git push
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build binary (${{ matrix.os }} ${{ matrix.arch }})
|
|
11
|
+
runs-on: ${{ matrix.runner }}
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
include:
|
|
15
|
+
- os: linux
|
|
16
|
+
arch: x86_64
|
|
17
|
+
runner: ubuntu-latest
|
|
18
|
+
- os: linux
|
|
19
|
+
arch: arm64
|
|
20
|
+
runner: ubuntu-24.04-arm
|
|
21
|
+
- os: macos
|
|
22
|
+
arch: arm64
|
|
23
|
+
runner: macos-latest
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- uses: astral-sh/setup-uv@v4
|
|
29
|
+
|
|
30
|
+
- name: Install Python
|
|
31
|
+
run: uv python install 3.12
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies + PyInstaller
|
|
34
|
+
run: uv sync && uv pip install pyinstaller
|
|
35
|
+
|
|
36
|
+
- name: Build binary
|
|
37
|
+
run: |
|
|
38
|
+
uv run pyinstaller \
|
|
39
|
+
--onefile \
|
|
40
|
+
--name indieclaw \
|
|
41
|
+
--hidden-import indieclaw \
|
|
42
|
+
--collect-all indieclaw \
|
|
43
|
+
--collect-all claude_agent_sdk \
|
|
44
|
+
indieclaw/main.py
|
|
45
|
+
|
|
46
|
+
- name: Rename binary
|
|
47
|
+
run: |
|
|
48
|
+
mv dist/indieclaw dist/indieclaw-${{ matrix.os }}-${{ matrix.arch }}
|
|
49
|
+
|
|
50
|
+
- name: Upload artifact
|
|
51
|
+
uses: actions/upload-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: indieclaw-${{ matrix.os }}-${{ matrix.arch }}
|
|
54
|
+
path: dist/indieclaw-${{ matrix.os }}-${{ matrix.arch }}
|
|
55
|
+
|
|
56
|
+
pypi:
|
|
57
|
+
name: Publish to PyPI
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write
|
|
61
|
+
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- uses: astral-sh/setup-uv@v4
|
|
66
|
+
|
|
67
|
+
- name: Install Python
|
|
68
|
+
run: uv python install 3.12
|
|
69
|
+
|
|
70
|
+
- name: Build package
|
|
71
|
+
run: uv build
|
|
72
|
+
|
|
73
|
+
- name: Publish to PyPI
|
|
74
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
75
|
+
|
|
76
|
+
release:
|
|
77
|
+
name: Create GitHub release
|
|
78
|
+
needs: [build, pypi]
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
permissions:
|
|
81
|
+
contents: write
|
|
82
|
+
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/download-artifact@v4
|
|
85
|
+
with:
|
|
86
|
+
path: dist
|
|
87
|
+
merge-multiple: true
|
|
88
|
+
|
|
89
|
+
- name: Create GitHub release
|
|
90
|
+
uses: softprops/action-gh-release@v2
|
|
91
|
+
with:
|
|
92
|
+
files: dist/indieclaw-*
|
|
93
|
+
generate_release_notes: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to IndieClaw will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-03-28
|
|
6
|
+
|
|
7
|
+
- Initial release as IndieClaw
|
|
8
|
+
- Self-hosted personal AI agent with Telegram integration
|
|
9
|
+
- Cron scheduler with configurable delivery
|
|
10
|
+
- Skills system with progressive disclosure
|
|
11
|
+
- Subconscious reflection engine
|
|
12
|
+
- Dynamic tool loading
|
|
13
|
+
- Browser automation
|
|
14
|
+
- Streaming responses
|
|
15
|
+
- fix: respect deliver_to: '' in cron jobs to prevent double delivery
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uv sync # install dev deps
|
|
7
|
+
uv run pytest # run tests (always run full suite before pushing)
|
|
8
|
+
uv run pytest tests/test_agent.py::test_run_returns_string # single test
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
Telegram bot wrapping `claude-agent-sdk`. State lives in `~/.indieclaw/` (not in this repo).
|
|
14
|
+
|
|
15
|
+
**Request flow:** Telegram message → `handlers.py` → `agent.run(chat_id, msg)` → short-lived `ClaudeSDKClient` (context manager) → tool loop → reply via Telegram.
|
|
16
|
+
|
|
17
|
+
**Session lifecycle:** Short-lived client per message. Session state persists via SDK's JSONL files; `resume=session_id` reconnects to the same conversation. `_session_ids[chat_id]` tracks the last session ID. Reset by `/reset` (clears session ID) or model/effort change. Context compaction handled natively by the SDK.
|
|
18
|
+
|
|
19
|
+
**Subagents:** Native SDK `AgentDefinition` (`task-runner`) for long autonomous tasks. Claude spawns them via the built-in `Agent` tool. No custom task registry.
|
|
20
|
+
|
|
21
|
+
**System prompt** (`prompt_builder.py`): Built per client creation. Order: workspace context → SOUL.md → USER.md → skills list → MEMORY.md. Current timestamp prepended to user message for cache-friendliness.
|
|
22
|
+
|
|
23
|
+
## Key files
|
|
24
|
+
|
|
25
|
+
| File | What it does |
|
|
26
|
+
|------|-------------|
|
|
27
|
+
| `agent.py` | Short-lived SDK client per message, `run()`, `run_streaming()`, native subagents, hooks |
|
|
28
|
+
| `handlers.py` | Telegram command/message handlers, typing loop, reactions, `/update` |
|
|
29
|
+
| `tools_sdk.py` | Built-in SDK tools (telegram, browser, search_sessions, etc.) |
|
|
30
|
+
| `tool_loader.py` | Hot-loads `~/.indieclaw/tools/*.py` as SDK tools |
|
|
31
|
+
| `browser.py` | Playwright browser manager (lazy singleton, per-chat contexts) |
|
|
32
|
+
| `main.py` | CLI entrypoint, bot wiring, startup/shutdown hooks |
|
|
33
|
+
| `version.py` | Shared version utilities: `local_version()`, `check_remote_version()`, `get_update_summary()` |
|
|
34
|
+
|
|
35
|
+
## Tools
|
|
36
|
+
|
|
37
|
+
**Built-in** (5): `Bash`, `Read`, `Write`, `WebSearch`, `WebFetch`
|
|
38
|
+
|
|
39
|
+
**SDK tools** (`tools_sdk.py`): `telegram_send`, `telegram_edit`, `telegram_send_file`, `telegram_send_voice`, `telegram_react`, `self_restart`, `self_update`, `update_config`, `read_skill`, `search_sessions`, `browse`, `browser_click`, `browser_type`, `browser_screenshot`, `browser_eval`, `test_tool`, `deploy_tool`, `disable_tool`, `update_subconscious`, `reflect`
|
|
40
|
+
|
|
41
|
+
**Dynamic tools** (`~/.indieclaw/tools/*.py`): Must export `SCHEMA` (OpenAI function schema dict) and `execute()`. All params arrive as strings — always coerce defensively.
|
|
42
|
+
|
|
43
|
+
## Things that bite you
|
|
44
|
+
|
|
45
|
+
- **`/update` handler is separate from `self_update` tool.** Both call `uv tool install --upgrade` then SIGTERM for systemd restart. Version logic is shared via `version.py` — don't duplicate.
|
|
46
|
+
- **Tool wrapper casts ALL params to strings.** Every tool (built-in and dynamic) must defensively coerce: `int()`, `json.loads()` with try/except. Never trust types.
|
|
47
|
+
- **System prompt is built once per client, not per message.** Don't add per-message dynamic content to `_system_prompt()` — it breaks prompt caching. Put ephemeral info in the user message instead.
|
|
48
|
+
- **Dynamic tools are loaded module-level** via `reload_dynamic_tools()`, called at the start of each `run()`. Adding/modifying tools no longer resets sessions.
|
|
49
|
+
- **Edited messages are reprocessed.** `on_message` handles both `update.message` and `update.edited_message`. Use `update.edited_message or update.message` to get the right one.
|
|
50
|
+
- **Test mocks need `edited_message = None`.** MagicMock is truthy — tests that create mock Updates must explicitly set `update.edited_message = None` or the handler picks up the mock as an edit.
|
|
51
|
+
- **`importlib.metadata` doesn't work in uv tool installs.** `importlib.metadata.version("indieclaw")` throws `PackageNotFoundError` when installed via `uv tool install`. Use `version.local_version()` which falls back to parsing `uv tool list` output, then `pyproject.toml`.
|
|
52
|
+
- **Always end-to-end test before pushing, not just unit tests.** Unit tests passing doesn't mean the feature works in the real environment. For version checks, runtime metadata, or anything environment-dependent — verify the actual function output in a quick `uv run python -c "..."` smoke test.
|
|
53
|
+
|
|
54
|
+
## What makes code maintainable
|
|
55
|
+
|
|
56
|
+
- **Reduce layers a reader has to trace.** Flat is better than deep. If understanding a change requires jumping through 3+ files or call frames, collapse the indirection. Prefer direct calls over abstract dispatch when there's only one use case.
|
|
57
|
+
- **Reduce state a reader has to hold in their head.** Short functions, small scopes, no hidden side effects. A reader should be able to understand a function from its signature and body alone — without needing to track what mutated elsewhere.
|
|
58
|
+
|
|
59
|
+
## Conventions
|
|
60
|
+
|
|
61
|
+
- **TDD: write tests first, then implement.** Write a failing test that captures the expected behavior, then write the code to make it pass. This catches environment mismatches and edge cases before they ship.
|
|
62
|
+
- **Release:** `make release V=x.y.z` — updates `pyproject.toml` version, generates `CHANGELOG.md` from commits since last tag, runs `uv lock`, commits, and creates annotated git tag. Then `git push && git push --tags`.
|
|
63
|
+
- Never add co-author lines or "Generated with Claude Code" to commits.
|
|
64
|
+
- Run full test suite before pushing — `uv run pytest`.
|
|
65
|
+
- Keep `CUSTOM_TOOLS` list at the bottom of `tools_sdk.py` in sync when adding tools.
|
|
66
|
+
- Browser tools use lazy imports (`from .browser import BrowserManager`) to avoid importing Playwright at module load.
|
|
67
|
+
- **Be lean with context.** Read files with `offset`/`limit` when you only need a section. Batch related shell commands (commit + push + upgrade + restart) into one chained call. Don't re-read files you just wrote. Use parallel tool calls. Skip exploratory reads when you already know the structure.
|
indieclaw-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 IndieClaw Contributors
|
|
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.
|
indieclaw-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# indieclaw release automation
|
|
2
|
+
# Usage: make release V=0.8.0
|
|
3
|
+
|
|
4
|
+
CURRENT_VERSION := $(shell python3 -c "import re; print(re.search(r'version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))")
|
|
5
|
+
|
|
6
|
+
.PHONY: release version
|
|
7
|
+
|
|
8
|
+
version:
|
|
9
|
+
@echo $(CURRENT_VERSION)
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
ifndef V
|
|
13
|
+
$(error Usage: make release V=x.y.z (current: $(CURRENT_VERSION)))
|
|
14
|
+
endif
|
|
15
|
+
@echo "Releasing indieclaw $(CURRENT_VERSION) → $(V)"
|
|
16
|
+
@# Update version in pyproject.toml
|
|
17
|
+
sed -i 's/^version = "$(CURRENT_VERSION)"/version = "$(V)"/' pyproject.toml
|
|
18
|
+
@echo " pyproject.toml updated"
|
|
19
|
+
@# Insert new version entry after the header in CHANGELOG.md
|
|
20
|
+
@DATE=$$(date +%Y-%m-%d); \
|
|
21
|
+
PREV_TAG=$$(git describe --tags --abbrev=0 2>/dev/null || echo ""); \
|
|
22
|
+
if [ -n "$$PREV_TAG" ]; then \
|
|
23
|
+
COMMITS=$$(git log $$PREV_TAG..HEAD --oneline --no-merges | grep -v "bump version\|Bump version"); \
|
|
24
|
+
else \
|
|
25
|
+
COMMITS=$$(git log --oneline --no-merges -20); \
|
|
26
|
+
fi; \
|
|
27
|
+
BODY=$$(echo "$$COMMITS" | sed 's/^[a-f0-9]* /- /'); \
|
|
28
|
+
{ head -3 CHANGELOG.md; \
|
|
29
|
+
echo ""; \
|
|
30
|
+
echo "## [$(V)] - $$DATE"; \
|
|
31
|
+
echo ""; \
|
|
32
|
+
echo "$$BODY"; \
|
|
33
|
+
echo ""; \
|
|
34
|
+
tail -n +4 CHANGELOG.md; \
|
|
35
|
+
} > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md
|
|
36
|
+
@echo " CHANGELOG.md updated"
|
|
37
|
+
@# Lock file
|
|
38
|
+
uv lock 2>/dev/null || true
|
|
39
|
+
@# Stage, commit, tag
|
|
40
|
+
git add pyproject.toml CHANGELOG.md uv.lock
|
|
41
|
+
git commit -m "release: v$(V)"
|
|
42
|
+
git tag -a "v$(V)" -m "v$(V)"
|
|
43
|
+
@echo ""
|
|
44
|
+
@echo "Done. Tagged v$(V). Push with: git push && git push --tags"
|
indieclaw-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: indieclaw
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Self-hosted Telegram AI agent. 10 tools, skills, subconscious, CLI learning.
|
|
5
|
+
Author: IndieClaw Contributors
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
9
|
+
Requires-Dist: apscheduler>=3.10.0
|
|
10
|
+
Requires-Dist: claude-agent-sdk>=0.1.44
|
|
11
|
+
Requires-Dist: edge-tts>=6.1.0
|
|
12
|
+
Requires-Dist: loguru>=0.7.0
|
|
13
|
+
Requires-Dist: playwright>=1.40.0
|
|
14
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
15
|
+
Requires-Dist: python-telegram-bot>=20.0
|
|
16
|
+
Requires-Dist: pyyaml>=6.0
|
|
17
|
+
Requires-Dist: questionary>=2.0.0
|
|
18
|
+
Requires-Dist: requests>=2.32.0
|
|
19
|
+
Requires-Dist: rich>=13.0
|
|
20
|
+
Requires-Dist: textual>=0.70
|
|
21
|
+
Requires-Dist: typer>=0.24.1
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# IndieClaw
|
|
25
|
+
|
|
26
|
+
[](https://github.com/saikatkumardey/python-doctor)
|
|
27
|
+
|
|
28
|
+
Personal AI agent on Telegram. Self-hosted, powered by Claude.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
curl -fsSL https://raw.githubusercontent.com/saikatkumardey/indieclaw/main/install.sh | bash
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or manually:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install uv
|
|
40
|
+
uv tool install git+https://github.com/saikatkumardey/indieclaw
|
|
41
|
+
indieclaw setup
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Setup asks for your Telegram bot token, user ID, and Claude auth. Get a bot token from [@BotFather](https://t.me/BotFather) and your user ID from [@userinfobot](https://t.me/userinfobot).
|
|
45
|
+
|
|
46
|
+
## Run
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
indieclaw start # background daemon
|
|
50
|
+
indieclaw start -f # foreground
|
|
51
|
+
indieclaw chat # interactive TUI (no Telegram needed)
|
|
52
|
+
indieclaw logs -f # stream logs
|
|
53
|
+
indieclaw stop
|
|
54
|
+
indieclaw restart
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`indieclaw chat` launches a terminal UI for chatting directly with your agent — useful for testing, debugging, or when you don't want to go through Telegram.
|
|
58
|
+
|
|
59
|
+
On Linux with systemd, `indieclaw setup` installs and starts the service automatically.
|
|
60
|
+
|
|
61
|
+
## Commands
|
|
62
|
+
|
|
63
|
+
| Command | |
|
|
64
|
+
|---------|--|
|
|
65
|
+
| `/status` | Model, tools, token usage |
|
|
66
|
+
| `/model` | Switch Claude model |
|
|
67
|
+
| `/effort` | Switch thinking effort |
|
|
68
|
+
| `/reset` | Clear conversation history |
|
|
69
|
+
| `/restart` | Restart the bot |
|
|
70
|
+
| `/update` | Pull latest and restart |
|
|
71
|
+
| `/cc <prompt>` | Live Claude Code session (streaming) |
|
|
72
|
+
|
|
73
|
+
## Workspace
|
|
74
|
+
|
|
75
|
+
Everything lives in `~/.indieclaw/`:
|
|
76
|
+
|
|
77
|
+
| File | Purpose |
|
|
78
|
+
|------|---------|
|
|
79
|
+
| `SOUL.md` | Agent identity and instructions |
|
|
80
|
+
| `USER.md` | Your profile (name, timezone, preferences) |
|
|
81
|
+
| `MEMORY.md` | Persistent memory across sessions |
|
|
82
|
+
| `crons.yaml` | Scheduled jobs |
|
|
83
|
+
| `skills/*/SKILL.md` | Skill docs injected into system prompt |
|
|
84
|
+
| `tools/*.py` | Custom tools — hot-loaded, no restart needed |
|
|
85
|
+
| `sessions/*.jsonl` | Conversation logs |
|
|
86
|
+
| `handover.md` | State snapshot across restarts |
|
|
87
|
+
| `subconscious.yaml` | Background reflection threads |
|
|
88
|
+
|
|
89
|
+
Override the workspace path: `INDIECLAW_HOME=/path/to/dir`
|
|
90
|
+
|
|
91
|
+
## Subconscious
|
|
92
|
+
|
|
93
|
+
A background reflection loop that runs every 2 hours. The agent reviews open threads, recent conversations, and memory — then decides whether to act (send a message, spawn a task) or stay quiet.
|
|
94
|
+
|
|
95
|
+
Threads are tracked in `~/.indieclaw/subconscious.yaml`. The agent can add, resolve, or keep threads across cycles. Disable it or change the interval in `~/.indieclaw/config.yaml`:
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
subconscious_enabled: false
|
|
99
|
+
subconscious_interval_hours: 4
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Custom tools
|
|
103
|
+
|
|
104
|
+
Drop a `.py` file in `~/.indieclaw/tools/` with a `SCHEMA` dict and `execute()` function. Available on the next message, no restart needed.
|
|
105
|
+
|
|
106
|
+
## Update
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
indieclaw update # from terminal
|
|
110
|
+
/update # from Telegram
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Saves a handover note before restarting and picks up where it left off.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# IndieClaw
|
|
2
|
+
|
|
3
|
+
[](https://github.com/saikatkumardey/python-doctor)
|
|
4
|
+
|
|
5
|
+
Personal AI agent on Telegram. Self-hosted, powered by Claude.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
curl -fsSL https://raw.githubusercontent.com/saikatkumardey/indieclaw/main/install.sh | bash
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or manually:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install uv
|
|
17
|
+
uv tool install git+https://github.com/saikatkumardey/indieclaw
|
|
18
|
+
indieclaw setup
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Setup asks for your Telegram bot token, user ID, and Claude auth. Get a bot token from [@BotFather](https://t.me/BotFather) and your user ID from [@userinfobot](https://t.me/userinfobot).
|
|
22
|
+
|
|
23
|
+
## Run
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
indieclaw start # background daemon
|
|
27
|
+
indieclaw start -f # foreground
|
|
28
|
+
indieclaw chat # interactive TUI (no Telegram needed)
|
|
29
|
+
indieclaw logs -f # stream logs
|
|
30
|
+
indieclaw stop
|
|
31
|
+
indieclaw restart
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`indieclaw chat` launches a terminal UI for chatting directly with your agent — useful for testing, debugging, or when you don't want to go through Telegram.
|
|
35
|
+
|
|
36
|
+
On Linux with systemd, `indieclaw setup` installs and starts the service automatically.
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
| Command | |
|
|
41
|
+
|---------|--|
|
|
42
|
+
| `/status` | Model, tools, token usage |
|
|
43
|
+
| `/model` | Switch Claude model |
|
|
44
|
+
| `/effort` | Switch thinking effort |
|
|
45
|
+
| `/reset` | Clear conversation history |
|
|
46
|
+
| `/restart` | Restart the bot |
|
|
47
|
+
| `/update` | Pull latest and restart |
|
|
48
|
+
| `/cc <prompt>` | Live Claude Code session (streaming) |
|
|
49
|
+
|
|
50
|
+
## Workspace
|
|
51
|
+
|
|
52
|
+
Everything lives in `~/.indieclaw/`:
|
|
53
|
+
|
|
54
|
+
| File | Purpose |
|
|
55
|
+
|------|---------|
|
|
56
|
+
| `SOUL.md` | Agent identity and instructions |
|
|
57
|
+
| `USER.md` | Your profile (name, timezone, preferences) |
|
|
58
|
+
| `MEMORY.md` | Persistent memory across sessions |
|
|
59
|
+
| `crons.yaml` | Scheduled jobs |
|
|
60
|
+
| `skills/*/SKILL.md` | Skill docs injected into system prompt |
|
|
61
|
+
| `tools/*.py` | Custom tools — hot-loaded, no restart needed |
|
|
62
|
+
| `sessions/*.jsonl` | Conversation logs |
|
|
63
|
+
| `handover.md` | State snapshot across restarts |
|
|
64
|
+
| `subconscious.yaml` | Background reflection threads |
|
|
65
|
+
|
|
66
|
+
Override the workspace path: `INDIECLAW_HOME=/path/to/dir`
|
|
67
|
+
|
|
68
|
+
## Subconscious
|
|
69
|
+
|
|
70
|
+
A background reflection loop that runs every 2 hours. The agent reviews open threads, recent conversations, and memory — then decides whether to act (send a message, spawn a task) or stay quiet.
|
|
71
|
+
|
|
72
|
+
Threads are tracked in `~/.indieclaw/subconscious.yaml`. The agent can add, resolve, or keep threads across cycles. Disable it or change the interval in `~/.indieclaw/config.yaml`:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
subconscious_enabled: false
|
|
76
|
+
subconscious_interval_hours: 4
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Custom tools
|
|
80
|
+
|
|
81
|
+
Drop a `.py` file in `~/.indieclaw/tools/` with a `SCHEMA` dict and `execute()` function. Available on the next message, no restart needed.
|
|
82
|
+
|
|
83
|
+
## Update
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
indieclaw update # from terminal
|
|
87
|
+
/update # from Telegram
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Saves a handover note before restarting and picks up where it left off.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Deploy indieclaw: reinstall and restart service.
|
|
3
|
+
set -e
|
|
4
|
+
cd "$(dirname "$0")"
|
|
5
|
+
uv tool uninstall indieclaw 2>/dev/null || true
|
|
6
|
+
uv tool install .
|
|
7
|
+
if systemctl is-active --quiet indieclaw 2>/dev/null; then
|
|
8
|
+
systemctl restart indieclaw && echo "Service restarted."
|
|
9
|
+
elif systemctl cat indieclaw &>/dev/null 2>&1; then
|
|
10
|
+
systemctl start indieclaw && echo "Service started."
|
|
11
|
+
else
|
|
12
|
+
echo "No systemd service found. Run: indieclaw setup (to generate the service file), then: indieclaw start"
|
|
13
|
+
fi
|