threadkeeper 0.13.0__tar.gz → 0.13.1__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.
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/PKG-INFO +6 -4
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/README.md +4 -3
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/pyproject.toml +2 -1
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_menubar_app.py +33 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/README.md +4 -3
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/menubar_app.py +32 -9
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper.egg-info/PKG-INFO +6 -4
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper.egg-info/requires.txt +1 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/LICENSE +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/setup.cfg +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_adapters.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_agent_status.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_auto_update.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_brief_footprint.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_brief_sections.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_candidate_reviewer.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_config_settings.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_core_memory.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_curator.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dashboard.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_delegated_search.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic_feed_tools.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic_miner.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic_observation_resolve.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic_recompute.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic_tier.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_dialectic_validator.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_error_paths.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_evolve_applier.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_evolve_apply_2.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_evolve_apply_3.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_evolve_daemon.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_extract_daemon.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_extract_dedup.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_i18n_multilang.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_identity.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_ingest_status.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_lessons.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_memory_guard.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_missed_spawns.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_nudges.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_onnx_embeddings.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_panel.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_probe_daemon.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_process_health.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_search_fts_punctuation.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_shadow_review.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_skill_hint.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_skill_passive_tier.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_skill_tier.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_skill_use_parser.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_skill_watcher.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_skills.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_budget.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_codex_stdin.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_config.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_hint.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_reap.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_slim.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_spawn_wrap.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_thread_janitor.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_threads.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_tools_smoke.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_validate_threads.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_vec_search.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/tests/test_verify_ingest.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/__init__.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/_mcp.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/_setup.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/_spawn_wrap.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/__init__.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/_hook_helpers.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/antigravity.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/base.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/claude_code.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/claude_desktop.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/codex.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/copilot.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/gemini.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/adapters/vscode.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/agent_status.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/Info.plist +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/ThreadKeeperAgentStatus.swift +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/build.sh +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/install.sh +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/auto_update.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/brief.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/candidate_reviewer.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/config.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/curator.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/db.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/dialectic_miner.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/dialectic_validator.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/embeddings.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/evolve_applier.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/evolve_daemon.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/extract_daemon.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/helpers.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/i18n.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/identity.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/ingest.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/lessons.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/memory_guard.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/migrate_embeddings.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/nudges.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/probe_daemon.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/process_health.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/review_prompts.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/search_proxy.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/server.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/shadow_review.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/skill_watcher.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/spawn_budget.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/spawn_config.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/thread_janitor.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/__init__.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/agent_status.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/candidate_reviewer.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/concepts.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/consolidate.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/core_memory.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/correlation.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/curator.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/dashboard.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/dialectic.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/dialectic_feed.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/dialog.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/distill.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/evolve_applier.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/extract.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/graph.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/invariants.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/lessons.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/memory_guard.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/missed_spawns.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/panel.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/peers.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/pickup.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/probes.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/process_health.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/session.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/shadow_review.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/skills.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/spawn.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/style.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/threads.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/tools/validate.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/verify_ingest.py +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper.egg-info/SOURCES.txt +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper.egg-info/dependency_links.txt +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper.egg-info/entry_points.txt +0 -0
- {threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: threadkeeper
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
4
4
|
Summary: Multi-agent shared brain across Claude Code/Desktop, Codex, Antigravity CLI, Gemini, Copilot, VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server.
|
|
5
5
|
Author: thread-keeper contributors
|
|
6
6
|
License: MIT
|
|
@@ -25,6 +25,7 @@ License-File: LICENSE
|
|
|
25
25
|
Requires-Dist: mcp>=1.0.0
|
|
26
26
|
Requires-Dist: pydantic>=2
|
|
27
27
|
Requires-Dist: pydantic-settings>=2
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
29
|
Provides-Extra: semantic
|
|
29
30
|
Requires-Dist: fastembed>=0.3; extra == "semantic"
|
|
30
31
|
Requires-Dist: numpy>=1.24.0; extra == "semantic"
|
|
@@ -251,9 +252,10 @@ while keeping `gemini` as legacy, and model selectors use dropdowns with exact
|
|
|
251
252
|
CLI model ids/labels instead of free-text fields. Probe backlog is due objective
|
|
252
253
|
probes only, not every registered probe, so a healthy cooldown shows `0 due
|
|
253
254
|
probes` instead of looking stuck. On macOS, `python -m threadkeeper.server`
|
|
254
|
-
automatically installs and launches it on MCP startup
|
|
255
|
-
|
|
256
|
-
running
|
|
255
|
+
automatically installs and launches it on MCP startup. The installed app records
|
|
256
|
+
a source fingerprint, so package upgrades rebuild the helper even when an older
|
|
257
|
+
bundle has a newer file timestamp, then restart any stale running menu-bar
|
|
258
|
+
process. Set
|
|
257
259
|
`THREADKEEPER_MENUBAR_AUTO_LAUNCH=0` to disable that behavior.
|
|
258
260
|
|
|
259
261
|
### Auto Update
|
|
@@ -210,9 +210,10 @@ while keeping `gemini` as legacy, and model selectors use dropdowns with exact
|
|
|
210
210
|
CLI model ids/labels instead of free-text fields. Probe backlog is due objective
|
|
211
211
|
probes only, not every registered probe, so a healthy cooldown shows `0 due
|
|
212
212
|
probes` instead of looking stuck. On macOS, `python -m threadkeeper.server`
|
|
213
|
-
automatically installs and launches it on MCP startup
|
|
214
|
-
|
|
215
|
-
running
|
|
213
|
+
automatically installs and launches it on MCP startup. The installed app records
|
|
214
|
+
a source fingerprint, so package upgrades rebuild the helper even when an older
|
|
215
|
+
bundle has a newer file timestamp, then restart any stale running menu-bar
|
|
216
|
+
process. Set
|
|
216
217
|
`THREADKEEPER_MENUBAR_AUTO_LAUNCH=0` to disable that behavior.
|
|
217
218
|
|
|
218
219
|
### Auto Update
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "threadkeeper"
|
|
7
|
-
version = "0.13.
|
|
7
|
+
version = "0.13.1"
|
|
8
8
|
description = "Multi-agent shared brain across Claude Code/Desktop, Codex, Antigravity CLI, Gemini, Copilot, VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server."
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
authors = [{ name = "thread-keeper contributors" }]
|
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
|
29
29
|
"mcp>=1.0.0",
|
|
30
30
|
"pydantic>=2",
|
|
31
31
|
"pydantic-settings>=2",
|
|
32
|
+
"pyyaml>=6.0",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
[project.optional-dependencies]
|
|
@@ -130,6 +130,35 @@ def test_menubar_source_falls_back_to_packaged_assets(fresh_mp, tmp_path, monkey
|
|
|
130
130
|
assert menubar_app._source_dir() == menubar_app._package_source_dir()
|
|
131
131
|
|
|
132
132
|
|
|
133
|
+
def test_app_current_requires_matching_source_fingerprint(tmp_path):
|
|
134
|
+
import threadkeeper.menubar_app as menubar_app
|
|
135
|
+
|
|
136
|
+
src = tmp_path / "source"
|
|
137
|
+
src.mkdir()
|
|
138
|
+
for name in menubar_app.SOURCE_FILES:
|
|
139
|
+
(src / name).write_text(f"{name}\n", encoding="utf-8")
|
|
140
|
+
|
|
141
|
+
app = tmp_path / menubar_app.APP_BUNDLE
|
|
142
|
+
binary = app / "Contents" / "MacOS" / menubar_app.APP_NAME
|
|
143
|
+
plist = app / "Contents" / "Info.plist"
|
|
144
|
+
binary.parent.mkdir(parents=True)
|
|
145
|
+
plist.parent.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
binary.write_text("old binary\n", encoding="utf-8")
|
|
147
|
+
plist.write_text("<plist></plist>\n", encoding="utf-8")
|
|
148
|
+
|
|
149
|
+
assert menubar_app._app_is_current(src, app) is False
|
|
150
|
+
|
|
151
|
+
marker = menubar_app._source_fingerprint_path(app)
|
|
152
|
+
marker.parent.mkdir(parents=True)
|
|
153
|
+
marker.write_text(menubar_app._source_fingerprint(src) + "\n", encoding="utf-8")
|
|
154
|
+
|
|
155
|
+
assert menubar_app._app_is_current(src, app) is True
|
|
156
|
+
|
|
157
|
+
(src / "ThreadKeeperAgentStatus.swift").write_text("// changed\n", encoding="utf-8")
|
|
158
|
+
|
|
159
|
+
assert menubar_app._app_is_current(src, app) is False
|
|
160
|
+
|
|
161
|
+
|
|
133
162
|
def test_install_app_builds_from_task_log_scratch_without_executable_bit(
|
|
134
163
|
fresh_mp,
|
|
135
164
|
tmp_path,
|
|
@@ -173,6 +202,10 @@ def test_install_app_builds_from_task_log_scratch_without_executable_bit(
|
|
|
173
202
|
assert calls[0][2] == task_logs / "menubar-build" / "source"
|
|
174
203
|
assert (installed / "Contents" / "Info.plist").exists()
|
|
175
204
|
assert (installed / "Contents" / "MacOS" / menubar_app.APP_NAME).exists()
|
|
205
|
+
marker = menubar_app._source_fingerprint_path(installed)
|
|
206
|
+
assert marker.read_text(encoding="utf-8").strip() == menubar_app._source_fingerprint(
|
|
207
|
+
src
|
|
208
|
+
)
|
|
176
209
|
assert not (src / "build").exists()
|
|
177
210
|
|
|
178
211
|
|
{threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/README.md
RENAMED
|
@@ -39,9 +39,10 @@ keeps the menu-bar helper from becoming the memory-pressure offender.
|
|
|
39
39
|
|
|
40
40
|
On macOS, `python -m threadkeeper.server` installs and launches this app
|
|
41
41
|
automatically when the MCP server starts. The startup hook is idempotent: it
|
|
42
|
-
rebuilds
|
|
43
|
-
|
|
44
|
-
process means the menu-bar
|
|
42
|
+
rebuilds when the installed app is missing or its recorded source fingerprint no
|
|
43
|
+
longer matches the bundled/source Swift files, registers the LaunchAgent, and
|
|
44
|
+
restarts the app when a rebuild or stale running process means the menu-bar
|
|
45
|
+
process is still using older code.
|
|
45
46
|
|
|
46
47
|
Disable automatic startup with:
|
|
47
48
|
|
|
@@ -6,6 +6,7 @@ failures are non-fatal.
|
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import hashlib
|
|
9
10
|
import os
|
|
10
11
|
import platform
|
|
11
12
|
import shutil
|
|
@@ -25,6 +26,8 @@ from .config import (
|
|
|
25
26
|
APP_NAME = "ThreadKeeperAgentStatus"
|
|
26
27
|
APP_BUNDLE = f"{APP_NAME}.app"
|
|
27
28
|
LAUNCH_LABEL = "local.threadkeeper.agent-status"
|
|
29
|
+
SOURCE_FILES = ("ThreadKeeperAgentStatus.swift", "Info.plist", "build.sh")
|
|
30
|
+
SOURCE_FINGERPRINT_FILE = "threadkeeper-source.sha256"
|
|
28
31
|
|
|
29
32
|
_attempted = False
|
|
30
33
|
|
|
@@ -53,7 +56,7 @@ def _prepare_build_source(src: Path) -> Path:
|
|
|
53
56
|
if build_src.exists():
|
|
54
57
|
shutil.rmtree(build_src)
|
|
55
58
|
build_src.mkdir(parents=True, exist_ok=True)
|
|
56
|
-
for name in
|
|
59
|
+
for name in SOURCE_FILES:
|
|
57
60
|
shutil.copy2(src / name, build_src / name)
|
|
58
61
|
return build_src
|
|
59
62
|
|
|
@@ -164,13 +167,25 @@ def _terminate_running_app() -> None:
|
|
|
164
167
|
_log("terminate_timeout")
|
|
165
168
|
|
|
166
169
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
for name in
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
def _source_fingerprint(src: Path) -> str:
|
|
171
|
+
digest = hashlib.sha256()
|
|
172
|
+
for name in SOURCE_FILES:
|
|
173
|
+
path = src / name
|
|
174
|
+
digest.update(name.encode("utf-8"))
|
|
175
|
+
digest.update(b"\0")
|
|
176
|
+
digest.update(path.read_bytes())
|
|
177
|
+
digest.update(b"\0")
|
|
178
|
+
return digest.hexdigest()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _source_fingerprint_path(app: Path) -> Path:
|
|
182
|
+
return app / "Contents" / "Resources" / SOURCE_FINGERPRINT_FILE
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _write_source_fingerprint(src: Path, app: Path) -> None:
|
|
186
|
+
marker = _source_fingerprint_path(app)
|
|
187
|
+
marker.parent.mkdir(parents=True, exist_ok=True)
|
|
188
|
+
marker.write_text(f"{_source_fingerprint(src)}\n", encoding="utf-8")
|
|
174
189
|
|
|
175
190
|
|
|
176
191
|
def _app_is_current(src: Path, app: Path) -> bool:
|
|
@@ -178,7 +193,14 @@ def _app_is_current(src: Path, app: Path) -> bool:
|
|
|
178
193
|
plist = app / "Contents" / "Info.plist"
|
|
179
194
|
if not binary.exists() or not plist.exists():
|
|
180
195
|
return False
|
|
181
|
-
|
|
196
|
+
marker = _source_fingerprint_path(app)
|
|
197
|
+
if not marker.exists():
|
|
198
|
+
return False
|
|
199
|
+
try:
|
|
200
|
+
return marker.read_text(encoding="utf-8").strip() == _source_fingerprint(src)
|
|
201
|
+
except OSError as e:
|
|
202
|
+
_log(f"source_fingerprint_check_failed err={e}")
|
|
203
|
+
return False
|
|
182
204
|
|
|
183
205
|
|
|
184
206
|
def _ensure_status_command() -> None:
|
|
@@ -236,6 +258,7 @@ def _install_app(src: Path, app: Path) -> bool:
|
|
|
236
258
|
if app.exists():
|
|
237
259
|
shutil.rmtree(app)
|
|
238
260
|
shutil.copytree(built, app)
|
|
261
|
+
_write_source_fingerprint(src, app)
|
|
239
262
|
except OSError as e:
|
|
240
263
|
_log(f"copy_app_failed err={e}")
|
|
241
264
|
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: threadkeeper
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
4
4
|
Summary: Multi-agent shared brain across Claude Code/Desktop, Codex, Antigravity CLI, Gemini, Copilot, VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server.
|
|
5
5
|
Author: thread-keeper contributors
|
|
6
6
|
License: MIT
|
|
@@ -25,6 +25,7 @@ License-File: LICENSE
|
|
|
25
25
|
Requires-Dist: mcp>=1.0.0
|
|
26
26
|
Requires-Dist: pydantic>=2
|
|
27
27
|
Requires-Dist: pydantic-settings>=2
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
29
|
Provides-Extra: semantic
|
|
29
30
|
Requires-Dist: fastembed>=0.3; extra == "semantic"
|
|
30
31
|
Requires-Dist: numpy>=1.24.0; extra == "semantic"
|
|
@@ -251,9 +252,10 @@ while keeping `gemini` as legacy, and model selectors use dropdowns with exact
|
|
|
251
252
|
CLI model ids/labels instead of free-text fields. Probe backlog is due objective
|
|
252
253
|
probes only, not every registered probe, so a healthy cooldown shows `0 due
|
|
253
254
|
probes` instead of looking stuck. On macOS, `python -m threadkeeper.server`
|
|
254
|
-
automatically installs and launches it on MCP startup
|
|
255
|
-
|
|
256
|
-
running
|
|
255
|
+
automatically installs and launches it on MCP startup. The installed app records
|
|
256
|
+
a source fingerprint, so package upgrades rebuild the helper even when an older
|
|
257
|
+
bundle has a newer file timestamp, then restart any stale running menu-bar
|
|
258
|
+
process. Set
|
|
257
259
|
`THREADKEEPER_MENUBAR_AUTO_LAUNCH=0` to disable that behavior.
|
|
258
260
|
|
|
259
261
|
### Auto Update
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/Info.plist
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{threadkeeper-0.13.0 → threadkeeper-0.13.1}/threadkeeper/assets/macos-agent-status/install.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|