tigerharness 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.
Files changed (170) hide show
  1. tigerharness-0.1.0/.gitignore +13 -0
  2. tigerharness-0.1.0/CONTRIBUTING.md +140 -0
  3. tigerharness-0.1.0/LICENSE +21 -0
  4. tigerharness-0.1.0/PKG-INFO +191 -0
  5. tigerharness-0.1.0/README.md +146 -0
  6. tigerharness-0.1.0/docs/DESIGN.md +154 -0
  7. tigerharness-0.1.0/docs/slack-bridge.md +102 -0
  8. tigerharness-0.1.0/docs/task-runner.md +166 -0
  9. tigerharness-0.1.0/docs/tiger-memory.md +143 -0
  10. tigerharness-0.1.0/examples/env.example +21 -0
  11. tigerharness-0.1.0/examples/personas/researcher.md +19 -0
  12. tigerharness-0.1.0/examples/tiger-memory.config.yaml +41 -0
  13. tigerharness-0.1.0/pyproject.toml +98 -0
  14. tigerharness-0.1.0/skills/assign-task/SKILL.md +85 -0
  15. tigerharness-0.1.0/skills/lab-notebook-quarter-roll/SKILL.md +26 -0
  16. tigerharness-0.1.0/skills/slack-notify/SKILL.md +53 -0
  17. tigerharness-0.1.0/skills/tiger-memory-drill-down/SKILL.md +45 -0
  18. tigerharness-0.1.0/skills/tiger-memory-search/SKILL.md +28 -0
  19. tigerharness-0.1.0/src/tigerharness/__init__.py +9 -0
  20. tigerharness-0.1.0/src/tigerharness/agent_sdk/README.md +231 -0
  21. tigerharness-0.1.0/src/tigerharness/agent_sdk/__init__.py +131 -0
  22. tigerharness-0.1.0/src/tigerharness/agent_sdk/backends/__init__.py +5 -0
  23. tigerharness-0.1.0/src/tigerharness/agent_sdk/backends/_base.py +82 -0
  24. tigerharness-0.1.0/src/tigerharness/agent_sdk/backends/anthropic_sdk.py +567 -0
  25. tigerharness-0.1.0/src/tigerharness/agent_sdk/backends/claude_p.py +657 -0
  26. tigerharness-0.1.0/src/tigerharness/agent_sdk/backends/openai_sdk.py +43 -0
  27. tigerharness-0.1.0/src/tigerharness/agent_sdk/docs/HANDOFF.md +539 -0
  28. tigerharness-0.1.0/src/tigerharness/agent_sdk/docs/agent_sdk_comparison.md +727 -0
  29. tigerharness-0.1.0/src/tigerharness/agent_sdk/errors.py +38 -0
  30. tigerharness-0.1.0/src/tigerharness/agent_sdk/examples/__init__.py +0 -0
  31. tigerharness-0.1.0/src/tigerharness/agent_sdk/examples/basic.py +27 -0
  32. tigerharness-0.1.0/src/tigerharness/agent_sdk/examples/builtin_tools.py +58 -0
  33. tigerharness-0.1.0/src/tigerharness/agent_sdk/examples/multi_turn.py +36 -0
  34. tigerharness-0.1.0/src/tigerharness/agent_sdk/examples/streaming.py +59 -0
  35. tigerharness-0.1.0/src/tigerharness/agent_sdk/factory.py +86 -0
  36. tigerharness-0.1.0/src/tigerharness/agent_sdk/retry.py +125 -0
  37. tigerharness-0.1.0/src/tigerharness/agent_sdk/types.py +353 -0
  38. tigerharness-0.1.0/src/tigerharness/cli.py +60 -0
  39. tigerharness-0.1.0/src/tigerharness/init.py +182 -0
  40. tigerharness-0.1.0/src/tigerharness/py.typed +0 -0
  41. tigerharness-0.1.0/src/tigerharness/slack_bridge/__init__.py +5 -0
  42. tigerharness-0.1.0/src/tigerharness/slack_bridge/__main__.py +89 -0
  43. tigerharness-0.1.0/src/tigerharness/slack_bridge/bridge.py +346 -0
  44. tigerharness-0.1.0/src/tigerharness/slack_bridge/config.py +91 -0
  45. tigerharness-0.1.0/src/tigerharness/slack_bridge/downloader.py +163 -0
  46. tigerharness-0.1.0/src/tigerharness/slack_bridge/notify.py +337 -0
  47. tigerharness-0.1.0/src/tigerharness/slack_bridge/persistence.py +104 -0
  48. tigerharness-0.1.0/src/tigerharness/task_runner/__init__.py +6 -0
  49. tigerharness-0.1.0/src/tigerharness/task_runner/__main__.py +7 -0
  50. tigerharness-0.1.0/src/tigerharness/task_runner/cli.py +587 -0
  51. tigerharness-0.1.0/src/tigerharness/task_runner/notifier.py +307 -0
  52. tigerharness-0.1.0/src/tigerharness/task_runner/personas.py +213 -0
  53. tigerharness-0.1.0/src/tigerharness/task_runner/registry.py +211 -0
  54. tigerharness-0.1.0/src/tigerharness/task_runner/runner.py +1051 -0
  55. tigerharness-0.1.0/src/tigerharness/task_runner/stuck_watchdog.py +567 -0
  56. tigerharness-0.1.0/src/tigerharness/tiger_memory/__init__.py +11 -0
  57. tigerharness-0.1.0/src/tigerharness/tiger_memory/briefing.py +356 -0
  58. tigerharness-0.1.0/src/tigerharness/tiger_memory/cli.py +158 -0
  59. tigerharness-0.1.0/src/tigerharness/tiger_memory/config.py +336 -0
  60. tigerharness-0.1.0/src/tigerharness/tiger_memory/drill.py +390 -0
  61. tigerharness-0.1.0/src/tigerharness/tiger_memory/embedders.py +135 -0
  62. tigerharness-0.1.0/src/tigerharness/tiger_memory/frontmatter.py +65 -0
  63. tigerharness-0.1.0/src/tigerharness/tiger_memory/lifecycle.py +951 -0
  64. tigerharness-0.1.0/src/tigerharness/tiger_memory/must_memorize.py +372 -0
  65. tigerharness-0.1.0/src/tigerharness/tiger_memory/rag.py +204 -0
  66. tigerharness-0.1.0/src/tigerharness/tiger_memory/sources/__init__.py +27 -0
  67. tigerharness-0.1.0/src/tigerharness/tiger_memory/sources/base.py +40 -0
  68. tigerharness-0.1.0/src/tigerharness/tiger_memory/sources/claude_transcript.py +310 -0
  69. tigerharness-0.1.0/src/tigerharness/tiger_memory/sources/docs.py +97 -0
  70. tigerharness-0.1.0/src/tigerharness/tiger_memory/state.py +123 -0
  71. tigerharness-0.1.0/src/tigerharness/tiger_memory/store.py +330 -0
  72. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/__init__.py +19 -0
  73. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/anthropic.py +115 -0
  74. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/base.py +45 -0
  75. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/mock.py +28 -0
  76. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/daily_rollup.md +20 -0
  77. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/detailed_summary.md +40 -0
  78. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/longer_memory.md +24 -0
  79. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/monthly_rollup.md +30 -0
  80. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/must_memorize_extract.md +41 -0
  81. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/short_summary.md +30 -0
  82. tigerharness-0.1.0/src/tigerharness/tiger_memory/summarizers/prompts/default/v1/weekly_rollup.md +27 -0
  83. tigerharness-0.1.0/src/tigerharness/tiger_memory/templates/briefing_readme.md +71 -0
  84. tigerharness-0.1.0/tests/__init__.py +0 -0
  85. tigerharness-0.1.0/tests/agent_sdk/__init__.py +0 -0
  86. tigerharness-0.1.0/tests/agent_sdk/_helpers.py +30 -0
  87. tigerharness-0.1.0/tests/agent_sdk/conftest.py +295 -0
  88. tigerharness-0.1.0/tests/agent_sdk/test_anthropic_sdk.py +1029 -0
  89. tigerharness-0.1.0/tests/agent_sdk/test_base.py +175 -0
  90. tigerharness-0.1.0/tests/agent_sdk/test_claude_p.py +889 -0
  91. tigerharness-0.1.0/tests/agent_sdk/test_errors.py +46 -0
  92. tigerharness-0.1.0/tests/agent_sdk/test_examples.py +22 -0
  93. tigerharness-0.1.0/tests/agent_sdk/test_factory.py +111 -0
  94. tigerharness-0.1.0/tests/agent_sdk/test_retry.py +174 -0
  95. tigerharness-0.1.0/tests/agent_sdk/test_stub_backends.py +29 -0
  96. tigerharness-0.1.0/tests/agent_sdk/test_types.py +313 -0
  97. tigerharness-0.1.0/tests/slack_bridge/__init__.py +0 -0
  98. tigerharness-0.1.0/tests/slack_bridge/test_bridge.py +280 -0
  99. tigerharness-0.1.0/tests/slack_bridge/test_bridge_coverage.py +140 -0
  100. tigerharness-0.1.0/tests/slack_bridge/test_bridge_extra.py +123 -0
  101. tigerharness-0.1.0/tests/slack_bridge/test_config.py +74 -0
  102. tigerharness-0.1.0/tests/slack_bridge/test_downloader.py +150 -0
  103. tigerharness-0.1.0/tests/slack_bridge/test_main_coverage.py +157 -0
  104. tigerharness-0.1.0/tests/slack_bridge/test_main_extra.py +62 -0
  105. tigerharness-0.1.0/tests/slack_bridge/test_notify.py +334 -0
  106. tigerharness-0.1.0/tests/slack_bridge/test_notify_extra.py +214 -0
  107. tigerharness-0.1.0/tests/slack_bridge/test_persistence.py +70 -0
  108. tigerharness-0.1.0/tests/slack_bridge/test_persistence_extra.py +33 -0
  109. tigerharness-0.1.0/tests/task_runner/__init__.py +0 -0
  110. tigerharness-0.1.0/tests/task_runner/test_cli.py +270 -0
  111. tigerharness-0.1.0/tests/task_runner/test_cli_coverage.py +161 -0
  112. tigerharness-0.1.0/tests/task_runner/test_cli_extra.py +148 -0
  113. tigerharness-0.1.0/tests/task_runner/test_notifier.py +381 -0
  114. tigerharness-0.1.0/tests/task_runner/test_notifier_coverage.py +96 -0
  115. tigerharness-0.1.0/tests/task_runner/test_notifier_extra.py +191 -0
  116. tigerharness-0.1.0/tests/task_runner/test_personas.py +140 -0
  117. tigerharness-0.1.0/tests/task_runner/test_registry.py +144 -0
  118. tigerharness-0.1.0/tests/task_runner/test_runner.py +647 -0
  119. tigerharness-0.1.0/tests/task_runner/test_runner_classify_close.py +67 -0
  120. tigerharness-0.1.0/tests/task_runner/test_runner_coverage.py +274 -0
  121. tigerharness-0.1.0/tests/task_runner/test_stuck_watchdog.py +881 -0
  122. tigerharness-0.1.0/tests/task_runner/test_stuck_watchdog_coverage.py +547 -0
  123. tigerharness-0.1.0/tests/task_runner/test_stuck_watchdog_integration.py +584 -0
  124. tigerharness-0.1.0/tests/test_branch_partials.py +251 -0
  125. tigerharness-0.1.0/tests/test_cli.py +61 -0
  126. tigerharness-0.1.0/tests/test_coverage_final2.py +254 -0
  127. tigerharness-0.1.0/tests/test_coverage_final3.py +359 -0
  128. tigerharness-0.1.0/tests/test_entry_points.py +21 -0
  129. tigerharness-0.1.0/tests/test_final_coverage.py +312 -0
  130. tigerharness-0.1.0/tests/test_init.py +112 -0
  131. tigerharness-0.1.0/tests/test_main_modules.py +64 -0
  132. tigerharness-0.1.0/tests/tiger_memory/__init__.py +0 -0
  133. tigerharness-0.1.0/tests/tiger_memory/conftest.py +66 -0
  134. tigerharness-0.1.0/tests/tiger_memory/test_anthropic_summarizer.py +117 -0
  135. tigerharness-0.1.0/tests/tiger_memory/test_briefing.py +123 -0
  136. tigerharness-0.1.0/tests/tiger_memory/test_briefing_coverage.py +148 -0
  137. tigerharness-0.1.0/tests/tiger_memory/test_briefing_read_filter.py +157 -0
  138. tigerharness-0.1.0/tests/tiger_memory/test_briefing_rebuild.py +215 -0
  139. tigerharness-0.1.0/tests/tiger_memory/test_briefing_rebuild_polish.py +155 -0
  140. tigerharness-0.1.0/tests/tiger_memory/test_briefing_stale.py +155 -0
  141. tigerharness-0.1.0/tests/tiger_memory/test_claude_transcript_extra.py +219 -0
  142. tigerharness-0.1.0/tests/tiger_memory/test_cli.py +175 -0
  143. tigerharness-0.1.0/tests/tiger_memory/test_config.py +146 -0
  144. tigerharness-0.1.0/tests/tiger_memory/test_coverage_push.py +289 -0
  145. tigerharness-0.1.0/tests/tiger_memory/test_docs_coverage.py +80 -0
  146. tigerharness-0.1.0/tests/tiger_memory/test_docs_source.py +123 -0
  147. tigerharness-0.1.0/tests/tiger_memory/test_drill.py +183 -0
  148. tigerharness-0.1.0/tests/tiger_memory/test_drill_coverage2.py +234 -0
  149. tigerharness-0.1.0/tests/tiger_memory/test_drill_extra.py +354 -0
  150. tigerharness-0.1.0/tests/tiger_memory/test_embedders.py +90 -0
  151. tigerharness-0.1.0/tests/tiger_memory/test_embedders_extra.py +85 -0
  152. tigerharness-0.1.0/tests/tiger_memory/test_frontmatter.py +33 -0
  153. tigerharness-0.1.0/tests/tiger_memory/test_frontmatter_extra.py +58 -0
  154. tigerharness-0.1.0/tests/tiger_memory/test_lifecycle.py +190 -0
  155. tigerharness-0.1.0/tests/tiger_memory/test_lifecycle_coverage.py +268 -0
  156. tigerharness-0.1.0/tests/tiger_memory/test_lifecycle_coverage2.py +384 -0
  157. tigerharness-0.1.0/tests/tiger_memory/test_lifecycle_full.py +608 -0
  158. tigerharness-0.1.0/tests/tiger_memory/test_must_memorize.py +188 -0
  159. tigerharness-0.1.0/tests/tiger_memory/test_must_memorize_extra.py +268 -0
  160. tigerharness-0.1.0/tests/tiger_memory/test_rag.py +176 -0
  161. tigerharness-0.1.0/tests/tiger_memory/test_remaining_coverage.py +293 -0
  162. tigerharness-0.1.0/tests/tiger_memory/test_sources.py +120 -0
  163. tigerharness-0.1.0/tests/tiger_memory/test_state.py +153 -0
  164. tigerharness-0.1.0/tests/tiger_memory/test_store.py +165 -0
  165. tigerharness-0.1.0/tests/tiger_memory/test_store_coverage.py +160 -0
  166. tigerharness-0.1.0/tests/tiger_memory/test_store_extra.py +166 -0
  167. tigerharness-0.1.0/tests/tiger_memory/test_store_lock.py +99 -0
  168. tigerharness-0.1.0/tests/tiger_memory/test_summarizers.py +30 -0
  169. tigerharness-0.1.0/tests/tiger_memory/test_transcript_coverage.py +76 -0
  170. tigerharness-0.1.0/tests/tiger_memory/test_v05_pass2.py +127 -0
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .env
9
+ *.lock
10
+ .pytest_cache/
11
+ .coverage
12
+ htmlcov/
13
+ htmlcov/
@@ -0,0 +1,140 @@
1
+ # Contributing to tigerharness
2
+
3
+ ## Development setup
4
+
5
+ ```bash
6
+ git clone https://github.com/DingyuZhou/TigerHarness.git
7
+ cd tigerharness
8
+ uv sync --extra all
9
+ ```
10
+
11
+ ## Running tests
12
+
13
+ ```bash
14
+ # All tests
15
+ uv run pytest
16
+
17
+ # With coverage report
18
+ uv run pytest --cov=tigerharness --cov-report=term-missing
19
+
20
+ # Single module
21
+ uv run pytest tests/tiger_memory/test_lifecycle_full.py -v
22
+ ```
23
+
24
+ Coverage threshold: **98.5%** (enforced in `pyproject.toml`). Current: **98.60%** (1088 tests).
25
+
26
+ ## Project structure
27
+
28
+ ```
29
+ src/tigerharness/
30
+ __init__.py Top-level package
31
+ cli.py Unified CLI entry point
32
+ init.py Project scaffolding (tigerharness init)
33
+ py.typed PEP 561 type stub marker
34
+ task_runner/ Iterative task execution
35
+ cli.py CLI subcommands (assign, list, cancel, logs)
36
+ runner.py Core iteration loop
37
+ notifier.py Slack DM notifications
38
+ personas.py Config-driven persona registry
39
+ registry.py Task state persistence
40
+ slack_bridge/ Slack Socket Mode bridge
41
+ bridge.py Event handler + dispatch
42
+ config.py Env-var-driven config loader
43
+ downloader.py File attachment download
44
+ notify.py Outbound DM/file CLI
45
+ persistence.py Thread -> session mapping
46
+ tiger_memory/ Persistent memory management
47
+ cli.py CLI (init, rebuild, search, drill, pin)
48
+ config.py YAML config loader
49
+ lifecycle.py Bootstrap / rebuild / resummarize engine
50
+ briefing.py Layered briefing rebuild
51
+ drill.py Read commands (drill, tree, raw, search)
52
+ store.py On-disk store + atomic write + locking
53
+ must_memorize.py Scored memo table with decay
54
+ rag.py Embedding-based semantic search
55
+ embedders.py Pluggable embedding backends
56
+ frontmatter.py YAML frontmatter parser/writer
57
+ sources/ Source adapters (claude_code, docs)
58
+ summarizers/ Summarizer backends (anthropic, mock)
59
+ templates/ Briefing README template
60
+ tests/
61
+ task_runner/ Task runner tests
62
+ slack_bridge/ Slack bridge tests
63
+ tiger_memory/ Tiger memory tests
64
+ test_main_modules.py __main__.py entrypoint tests
65
+ examples/
66
+ tiger-memory.config.yaml Sample memory config (annotated)
67
+ personas/researcher.md Sample persona prompt
68
+ env.example Slack bridge env template
69
+ docs/
70
+ DESIGN.md Architecture decisions + migration notes
71
+ task-runner.md Task runner module README
72
+ slack-bridge.md Slack bridge module README
73
+ tiger-memory.md Tiger memory module README
74
+ skills/ Claude Code SKILL.md definitions
75
+ ```
76
+
77
+ ## Adding a new module
78
+
79
+ 1. Create `src/tigerharness/<module>/` with `__init__.py`.
80
+ 2. Add tests in `tests/<module>/`.
81
+ 3. If it has CLI commands, add a `cli.py` and wire into `src/tigerharness/cli.py`.
82
+ 4. If it has optional dependencies, add an extra in `pyproject.toml`.
83
+ 5. Write a module README in `docs/<module>.md`.
84
+ 6. Run `uv run pytest --cov=tigerharness --cov-report=term-missing` and verify coverage.
85
+
86
+ ## Adding a custom persona
87
+
88
+ 1. Create a markdown file in your personas directory (e.g., `personas/analyst.md`).
89
+ 2. Set `TIGERHARNESS_PERSONAS_DIR` to point to that directory.
90
+ 3. Use `python -m tigerharness.task_runner assign --to analyst --prompt "..."`.
91
+
92
+ See `examples/personas/researcher.md` for a template.
93
+
94
+ ## Adding a custom memory backend
95
+
96
+ Tiger-memory's summarizer is pluggable:
97
+
98
+ 1. Subclass `tigerharness.tiger_memory.summarizers.base.Summarizer`.
99
+ 2. Implement `summarize(prompt, max_words) -> str` and `cost_estimate_usd(...)`.
100
+ 3. Register in `_build_summarizer()` in `lifecycle.py` (or propose a plugin hook).
101
+
102
+ ## Code style
103
+
104
+ - Python 3.11+
105
+ - Type hints on all public APIs
106
+ - Docstrings on all modules and public functions
107
+ - No hardcoded paths -- everything via env vars or config
108
+ - Tests use `tmp_path` fixtures for filesystem isolation
109
+ - Mock external services (Slack, Anthropic, subprocess) in tests
110
+
111
+ ## Commit conventions
112
+
113
+ ```
114
+ <prefix>: <imperative summary, 72 chars max>
115
+
116
+ <body>
117
+ ```
118
+
119
+ Prefix: `feat:`, `fix:`, `test:`, `docs:`, `refactor:`
120
+
121
+ ## Running a single sub-package
122
+
123
+ ```bash
124
+ # Task runner
125
+ python -m tigerharness.task_runner --help
126
+
127
+ # Slack bridge (daemon)
128
+ python -m tigerharness.slack_bridge
129
+
130
+ # Tiger memory
131
+ tiger-memory --help
132
+ # or: python -m tigerharness.tiger_memory.cli --help
133
+ ```
134
+
135
+ ## Building
136
+
137
+ ```bash
138
+ uv build
139
+ # Creates dist/tigerharness-0.1.0-py3-none-any.whl
140
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tigerharness 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.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: tigerharness
3
+ Version: 0.1.0
4
+ Summary: A generic Claude Code agent harness: iterative task execution, Slack integration, and persistent memory management.
5
+ Project-URL: Repository, https://github.com/DingyuZhou/TigerHarness
6
+ Project-URL: Issues, https://github.com/DingyuZhou/TigerHarness/issues
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: agent,anthropic,claude,harness,memory,task-runner
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.11
19
+ Provides-Extra: all
20
+ Requires-Dist: aiohttp>=3.9; extra == 'all'
21
+ Requires-Dist: claude-agent-sdk>=0.1.0; extra == 'all'
22
+ Requires-Dist: fastembed>=0.4; extra == 'all'
23
+ Requires-Dist: openai>=1.0; extra == 'all'
24
+ Requires-Dist: python-dotenv>=1.0; extra == 'all'
25
+ Requires-Dist: pyyaml>=6.0; extra == 'all'
26
+ Requires-Dist: slack-bolt<2.0,>=1.18; extra == 'all'
27
+ Requires-Dist: sqlite-vec>=0.1; extra == 'all'
28
+ Provides-Extra: anthropic
29
+ Requires-Dist: claude-agent-sdk>=0.1.0; extra == 'anthropic'
30
+ Provides-Extra: memory
31
+ Requires-Dist: pyyaml>=6.0; extra == 'memory'
32
+ Provides-Extra: memory-rag
33
+ Requires-Dist: fastembed>=0.4; extra == 'memory-rag'
34
+ Requires-Dist: pyyaml>=6.0; extra == 'memory-rag'
35
+ Requires-Dist: sqlite-vec>=0.1; extra == 'memory-rag'
36
+ Provides-Extra: memory-rag-openai
37
+ Requires-Dist: openai>=1.0; extra == 'memory-rag-openai'
38
+ Requires-Dist: pyyaml>=6.0; extra == 'memory-rag-openai'
39
+ Requires-Dist: sqlite-vec>=0.1; extra == 'memory-rag-openai'
40
+ Provides-Extra: slack
41
+ Requires-Dist: aiohttp>=3.9; extra == 'slack'
42
+ Requires-Dist: python-dotenv>=1.0; extra == 'slack'
43
+ Requires-Dist: slack-bolt<2.0,>=1.18; extra == 'slack'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # tigerharness
47
+
48
+ A generic Claude Code agent harness: iterative task execution, Slack
49
+ integration, and persistent memory management.
50
+
51
+ ## Sub-packages
52
+
53
+ | Package | Description |
54
+ |---|---|
55
+ | `tigerharness.agent_sdk` | Backend-agnostic agent SDK. Same caller code, swappable runtimes: `claude -p` subprocess, Anthropic's `claude-agent-sdk`, OpenAI's `openai-agents` (planned). |
56
+ | `tigerharness.task_runner` | Fire-and-forget iterative task execution. Drives a persona through N Claude turns with periodic `/compact`. |
57
+ | `tigerharness.slack_bridge` | Slack Socket Mode bridge. Forwards DMs to a `claude -p` backend and posts replies back to the thread. |
58
+ | `tigerharness.tiger_memory` | Persistent agent memory: archive, journal, briefing with lazy rebuild, kind-decay must-memorize, and drill-down. |
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ # Core (claude -p backend only; Claude Code CLI must be on PATH)
64
+ pip install tigerharness
65
+
66
+ # With the official claude-agent-sdk backend
67
+ pip install tigerharness[anthropic]
68
+
69
+ # With Slack bridge support
70
+ pip install tigerharness[slack]
71
+
72
+ # With memory support
73
+ pip install tigerharness[memory]
74
+
75
+ # With memory + RAG (local embeddings, free)
76
+ pip install tigerharness[memory-rag]
77
+
78
+ # Everything
79
+ pip install tigerharness[all]
80
+ ```
81
+
82
+ Or with uv:
83
+ ```bash
84
+ uv add tigerharness
85
+ uv add tigerharness --extra all
86
+ ```
87
+
88
+ ## Quick start
89
+
90
+ ### Scaffold a new project
91
+
92
+ ```bash
93
+ # Create config files in the current directory
94
+ tigerharness init --name researcher
95
+
96
+ # Or with tiger-memory support
97
+ tigerharness init --name researcher --memory
98
+
99
+ # This creates:
100
+ # personas/researcher.md -- edit your agent's instructions
101
+ # .env -- fill in Slack tokens
102
+ # tiger-memory.config.yaml -- (only with --memory)
103
+ ```
104
+
105
+ ### Task runner
106
+
107
+ ```bash
108
+ # 1. Point tigerharness at your personas directory
109
+ export TIGERHARNESS_PERSONAS_DIR=./personas
110
+
111
+ # 2. Assign a task (5 iterations)
112
+ python -m tigerharness.task_runner assign \
113
+ --to researcher \
114
+ --prompt "Research the latest developments in solar energy" \
115
+ --iters 5
116
+
117
+ # 3. Check status
118
+ python -m tigerharness.task_runner list
119
+
120
+ # 4. View logs
121
+ python -m tigerharness.task_runner logs <task-id>
122
+ ```
123
+
124
+ ### Slack bridge
125
+
126
+ ```bash
127
+ # 1. Fill in your Slack tokens in .env (from api.slack.com)
128
+ # SLACK_APP_TOKEN=xapp-...
129
+ # SLACK_BOT_TOKEN=xoxb-...
130
+ # ALLOWED_SLACK_USER_IDS=U0123ABC
131
+
132
+ # 2. Run the bridge
133
+ python -m tigerharness.slack_bridge
134
+ ```
135
+
136
+ ### Tiger memory
137
+
138
+ ```bash
139
+ # 1. Copy the example config
140
+ cp examples/tiger-memory.config.yaml tiger-memory.config.yaml
141
+ # Edit: set store.root and sources.project_path
142
+
143
+ # 2. Initialize the memory store
144
+ tiger-memory --config tiger-memory.config.yaml init
145
+
146
+ # 3. Bootstrap (one-time backfill from existing transcripts)
147
+ tiger-memory --config tiger-memory.config.yaml bootstrap --dry-run
148
+ tiger-memory --config tiger-memory.config.yaml bootstrap
149
+
150
+ # 4. Rebuild (incremental, run after each session)
151
+ tiger-memory --config tiger-memory.config.yaml rebuild
152
+
153
+ # 5. Search memory
154
+ tiger-memory --config tiger-memory.config.yaml search "solar energy"
155
+
156
+ # 6. Pin a must-memorize fact
157
+ tiger-memory --config tiger-memory.config.yaml pin "Prefers solar over wind"
158
+ ```
159
+
160
+ ## Configuration
161
+
162
+ All paths are resolved from environment variables -- no hardcoded paths.
163
+
164
+ | Variable | Default | Description |
165
+ |---|---|---|
166
+ | `TIGERHARNESS_STATE_DIR` | `~/.local/state/tigerharness-tasks/` | Task runner state directory |
167
+ | `TIGERHARNESS_PERSONAS_DIR` | (none) | Directory containing `<name>.md` prompt files |
168
+ | `TIGERHARNESS_SLACK_ENV` | `.env` | Path to slack-bridge .env file |
169
+ | `TIGERHARNESS_AGENT_CWD` | `.` | Working directory for the Claude agent |
170
+ | `TIGERHARNESS_AGENT_PROMPT` | (none) | Path to the agent's system prompt |
171
+ | `TIGERHARNESS_SLACK_BRIDGE_DIR` | (none) | Path to slack-bridge service dir (for notify CLI) |
172
+ | `TIGERHARNESS_ATTACHMENT_DIR` | `/tmp/slack-attachments` | Where to stage downloaded files |
173
+ | `TIGER_MEMORY_CONFIG` | (none) | Path to tiger-memory YAML config |
174
+ | `TIGER_MEMORY_CLI` | (none) | Path to tiger-memory CLI binary |
175
+
176
+ ## Examples
177
+
178
+ See [`examples/`](examples/) for sample configs:
179
+ - [`tiger-memory.config.yaml`](examples/tiger-memory.config.yaml) -- annotated memory config
180
+ - [`personas/researcher.md`](examples/personas/researcher.md) -- sample persona prompt
181
+ - [`env.example`](examples/env.example) -- Slack bridge env template
182
+
183
+ ## Requirements
184
+
185
+ - Python 3.11+
186
+ - For the default `claude_p` backend: the [Claude Code CLI](https://docs.claude.com/en/docs/claude-code) (`claude`) on `PATH`.
187
+ - For the `anthropic_sdk` backend: install with `[anthropic]` extra; pulls in [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk/).
188
+
189
+ ## License
190
+
191
+ MIT
@@ -0,0 +1,146 @@
1
+ # tigerharness
2
+
3
+ A generic Claude Code agent harness: iterative task execution, Slack
4
+ integration, and persistent memory management.
5
+
6
+ ## Sub-packages
7
+
8
+ | Package | Description |
9
+ |---|---|
10
+ | `tigerharness.agent_sdk` | Backend-agnostic agent SDK. Same caller code, swappable runtimes: `claude -p` subprocess, Anthropic's `claude-agent-sdk`, OpenAI's `openai-agents` (planned). |
11
+ | `tigerharness.task_runner` | Fire-and-forget iterative task execution. Drives a persona through N Claude turns with periodic `/compact`. |
12
+ | `tigerharness.slack_bridge` | Slack Socket Mode bridge. Forwards DMs to a `claude -p` backend and posts replies back to the thread. |
13
+ | `tigerharness.tiger_memory` | Persistent agent memory: archive, journal, briefing with lazy rebuild, kind-decay must-memorize, and drill-down. |
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ # Core (claude -p backend only; Claude Code CLI must be on PATH)
19
+ pip install tigerharness
20
+
21
+ # With the official claude-agent-sdk backend
22
+ pip install tigerharness[anthropic]
23
+
24
+ # With Slack bridge support
25
+ pip install tigerharness[slack]
26
+
27
+ # With memory support
28
+ pip install tigerharness[memory]
29
+
30
+ # With memory + RAG (local embeddings, free)
31
+ pip install tigerharness[memory-rag]
32
+
33
+ # Everything
34
+ pip install tigerharness[all]
35
+ ```
36
+
37
+ Or with uv:
38
+ ```bash
39
+ uv add tigerharness
40
+ uv add tigerharness --extra all
41
+ ```
42
+
43
+ ## Quick start
44
+
45
+ ### Scaffold a new project
46
+
47
+ ```bash
48
+ # Create config files in the current directory
49
+ tigerharness init --name researcher
50
+
51
+ # Or with tiger-memory support
52
+ tigerharness init --name researcher --memory
53
+
54
+ # This creates:
55
+ # personas/researcher.md -- edit your agent's instructions
56
+ # .env -- fill in Slack tokens
57
+ # tiger-memory.config.yaml -- (only with --memory)
58
+ ```
59
+
60
+ ### Task runner
61
+
62
+ ```bash
63
+ # 1. Point tigerharness at your personas directory
64
+ export TIGERHARNESS_PERSONAS_DIR=./personas
65
+
66
+ # 2. Assign a task (5 iterations)
67
+ python -m tigerharness.task_runner assign \
68
+ --to researcher \
69
+ --prompt "Research the latest developments in solar energy" \
70
+ --iters 5
71
+
72
+ # 3. Check status
73
+ python -m tigerharness.task_runner list
74
+
75
+ # 4. View logs
76
+ python -m tigerharness.task_runner logs <task-id>
77
+ ```
78
+
79
+ ### Slack bridge
80
+
81
+ ```bash
82
+ # 1. Fill in your Slack tokens in .env (from api.slack.com)
83
+ # SLACK_APP_TOKEN=xapp-...
84
+ # SLACK_BOT_TOKEN=xoxb-...
85
+ # ALLOWED_SLACK_USER_IDS=U0123ABC
86
+
87
+ # 2. Run the bridge
88
+ python -m tigerharness.slack_bridge
89
+ ```
90
+
91
+ ### Tiger memory
92
+
93
+ ```bash
94
+ # 1. Copy the example config
95
+ cp examples/tiger-memory.config.yaml tiger-memory.config.yaml
96
+ # Edit: set store.root and sources.project_path
97
+
98
+ # 2. Initialize the memory store
99
+ tiger-memory --config tiger-memory.config.yaml init
100
+
101
+ # 3. Bootstrap (one-time backfill from existing transcripts)
102
+ tiger-memory --config tiger-memory.config.yaml bootstrap --dry-run
103
+ tiger-memory --config tiger-memory.config.yaml bootstrap
104
+
105
+ # 4. Rebuild (incremental, run after each session)
106
+ tiger-memory --config tiger-memory.config.yaml rebuild
107
+
108
+ # 5. Search memory
109
+ tiger-memory --config tiger-memory.config.yaml search "solar energy"
110
+
111
+ # 6. Pin a must-memorize fact
112
+ tiger-memory --config tiger-memory.config.yaml pin "Prefers solar over wind"
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ All paths are resolved from environment variables -- no hardcoded paths.
118
+
119
+ | Variable | Default | Description |
120
+ |---|---|---|
121
+ | `TIGERHARNESS_STATE_DIR` | `~/.local/state/tigerharness-tasks/` | Task runner state directory |
122
+ | `TIGERHARNESS_PERSONAS_DIR` | (none) | Directory containing `<name>.md` prompt files |
123
+ | `TIGERHARNESS_SLACK_ENV` | `.env` | Path to slack-bridge .env file |
124
+ | `TIGERHARNESS_AGENT_CWD` | `.` | Working directory for the Claude agent |
125
+ | `TIGERHARNESS_AGENT_PROMPT` | (none) | Path to the agent's system prompt |
126
+ | `TIGERHARNESS_SLACK_BRIDGE_DIR` | (none) | Path to slack-bridge service dir (for notify CLI) |
127
+ | `TIGERHARNESS_ATTACHMENT_DIR` | `/tmp/slack-attachments` | Where to stage downloaded files |
128
+ | `TIGER_MEMORY_CONFIG` | (none) | Path to tiger-memory YAML config |
129
+ | `TIGER_MEMORY_CLI` | (none) | Path to tiger-memory CLI binary |
130
+
131
+ ## Examples
132
+
133
+ See [`examples/`](examples/) for sample configs:
134
+ - [`tiger-memory.config.yaml`](examples/tiger-memory.config.yaml) -- annotated memory config
135
+ - [`personas/researcher.md`](examples/personas/researcher.md) -- sample persona prompt
136
+ - [`env.example`](examples/env.example) -- Slack bridge env template
137
+
138
+ ## Requirements
139
+
140
+ - Python 3.11+
141
+ - For the default `claude_p` backend: the [Claude Code CLI](https://docs.claude.com/en/docs/claude-code) (`claude`) on `PATH`.
142
+ - For the `anthropic_sdk` backend: install with `[anthropic]` extra; pulls in [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk/).
143
+
144
+ ## License
145
+
146
+ MIT
@@ -0,0 +1,154 @@
1
+ # tigerharness Design Document
2
+
3
+ ## Overview
4
+
5
+ tigerharness is a standalone, open-source Python package that provides a
6
+ generic Claude Code agent harness: iterative task execution, Slack
7
+ integration, and persistent memory management. Extracted from the
8
+ tigerleap workspace's `services/` directory.
9
+
10
+ ## Package structure decision: single package with sub-modules
11
+
12
+ **Chosen: monolithic package with sub-modules** (not a uv workspace /
13
+ monorepo with separate packages).
14
+
15
+ Rationale:
16
+ 1. All three services share `agent-sdk` as a core dependency.
17
+ 2. `task-runner` already cross-references `slack-bridge` for notifications.
18
+ 3. A single `pip install tigerharness` is simpler for adopters than
19
+ coordinating three separate packages.
20
+ 4. Optional extras (`[slack]`, `[memory]`, `[memory-rag]`) let users
21
+ install only what they need.
22
+
23
+ ```
24
+ tigerharness/
25
+ pyproject.toml # single package, extras for slack/memory/rag
26
+ src/
27
+ tigerharness/
28
+ __init__.py
29
+ task_runner/ # iterative task execution loop
30
+ slack_bridge/ # Slack Socket Mode bridge to Claude
31
+ tiger_memory/ # persistent memory: archive, journal, briefing
32
+ tests/
33
+ task_runner/
34
+ slack_bridge/
35
+ tiger_memory/
36
+ skills/ # Claude Code skill definitions (SKILL.md files)
37
+ assign-task/
38
+ slack-notify/
39
+ lab-notebook-quarter-roll/
40
+ tiger-memory-drill-down/
41
+ tiger-memory-search/
42
+ docs/
43
+ DESIGN.md # this file
44
+ task-runner.md
45
+ slack-bridge.md
46
+ tiger-memory.md
47
+ ```
48
+
49
+ ## Decoupling strategy
50
+
51
+ ### 1. task-runner decoupling
52
+
53
+ | Original coupling | Resolution |
54
+ |---|---|
55
+ | `WORKSPACE_ROOT = Path(__file__).parents[3]` in personas.py | Replace with config-driven persona registry. Personas defined in a YAML/Python config the user provides, not hardcoded. |
56
+ | `tigerleap-tasks` state dir name | Rename to `tigerharness-tasks` (configurable via env). |
57
+ | `_BRIDGE_ENV_PATH` hardcoded parents[3] path | Resolve `.env` from `SLACK_BRIDGE_ENV` env var or colocated `.env` |
58
+ | `_SLACK_THREAD_NOTICE` hardcoded path `/home/tigerleap/projects/...` | Template that uses `TIGERHARNESS_SLACK_BRIDGE_DIR` or auto-discovers |
59
+ | Persona system prompt files at `personas/<role>.md` | Configurable `TIGERHARNESS_PERSONAS_DIR` or pass prompt text directly |
60
+ | Hardcoded 5 persona names (sai, chief, scout, quartermaster, inquisitor) | Ship as example/default config; users register their own personas |
61
+
62
+ ### 2. slack-bridge decoupling
63
+
64
+ | Original coupling | Resolution |
65
+ |---|---|
66
+ | `_WORKSPACE_ROOT = Path(__file__).parents[3]` | Remove; all paths from env/config |
67
+ | `_SAI_PROMPT_PATH = _WORKSPACE_ROOT / "personas" / "sai.md"` | `TIGERHARNESS_PERSONA_PROMPT` env var or config file |
68
+ | `load_dotenv(… / "sandbox" / "slack-bridge" / ".env")` | Standard dotenv from CWD or explicit path |
69
+ | `tiger-memory` venv path hardcoded | `TIGER_MEMORY_CLI` env var or PATH lookup |
70
+
71
+ ### 3. tiger-memory decoupling
72
+
73
+ Already fully config-driven! No tigerleap references in code.
74
+ Only change: move it into the `tigerharness` namespace.
75
+
76
+ ## Dependency map
77
+
78
+ ```
79
+ tigerharness (core)
80
+ requires: agent-sdk (will be vendored or declared as git dep initially)
81
+
82
+ tigerharness[slack]
83
+ adds: slack-bolt, aiohttp, python-dotenv
84
+
85
+ tigerharness[memory]
86
+ adds: pyyaml
87
+
88
+ tigerharness[memory-rag]
89
+ adds: fastembed, sqlite-vec
90
+
91
+ tigerharness[memory-rag-openai]
92
+ adds: openai, sqlite-vec
93
+ ```
94
+
95
+ ## Configuration model
96
+
97
+ All hardcoded paths are replaced with a layered config:
98
+
99
+ 1. **Environment variables** (highest priority):
100
+ - `TIGERHARNESS_STATE_DIR` — XDG state (default: `~/.local/state/tigerharness/`)
101
+ - `TIGERHARNESS_PERSONAS_DIR` — directory of `<name>.md` prompt files
102
+ - `TIGERHARNESS_SLACK_BRIDGE_DIR` — where slack-bridge runs (for notify CLI)
103
+ - `TIGER_MEMORY_CONFIG` — path to tiger-memory YAML config
104
+
105
+ 2. **Config file** (`tigerharness.yaml` or per-service configs)
106
+
107
+ 3. **Sensible defaults** that work out of the box for a fresh install.
108
+
109
+ ## Persona system redesign
110
+
111
+ The original has 5 hardcoded personas. The new design:
112
+
113
+ ```python
114
+ # User provides a personas.yaml or Python dict:
115
+ personas:
116
+ - name: assistant
117
+ aliases: [ai, helper]
118
+ cwd: /path/to/project
119
+ prompt_file: personas/assistant.md # relative to TIGERHARNESS_PERSONAS_DIR
120
+ permission_mode: bypassPermissions
121
+ disallowed_tools: ["Bash(sudo:*)"]
122
+ ```
123
+
124
+ A `register_persona()` API and CLI `--persona-config` flag allow
125
+ runtime configuration. The package ships with NO default personas
126
+ (unlike tigerleap's 5) — users define their own.
127
+
128
+ ## Skills extraction
129
+
130
+ Skills are Claude Code `.claude/skills/` SKILL.md files. They contain
131
+ no executable code — just documentation for Claude to follow. We:
132
+
133
+ 1. Copy them into `tigerharness/skills/`
134
+ 2. Replace all hardcoded paths with generic placeholders
135
+ 3. Add an install script / docs explaining how to symlink them into a
136
+ project's `.claude/skills/`
137
+
138
+ ## Test strategy
139
+
140
+ - All existing tests migrate with minimal changes (mock paths, remove
141
+ tigerleap assumptions).
142
+ - New tests fill gaps in:
143
+ - Persona config loading (new YAML-driven system)
144
+ - Config resolution (env var precedence)
145
+ - Integration between task-runner and slack-bridge notify
146
+ - Target: 100% line coverage via `pytest-cov`.
147
+
148
+ ## Migration path for tigerleap
149
+
150
+ After tigerharness is stable:
151
+ 1. `tigerleap` adds `tigerharness` as a dependency
152
+ 2. `tigerleap/services/` becomes thin wrappers (or is deleted)
153
+ 3. A `tigerharness.yaml` at tigerleap root configures the personas,
154
+ paths, and memory