cabildo 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.
- cabildo-0.1.0/.gitignore +9 -0
- cabildo-0.1.0/.gitlab-ci.yml +38 -0
- cabildo-0.1.0/LICENSE +21 -0
- cabildo-0.1.0/PKG-INFO +121 -0
- cabildo-0.1.0/README.md +96 -0
- cabildo-0.1.0/publishing-page.yml +314 -0
- cabildo-0.1.0/pyproject.toml +50 -0
- cabildo-0.1.0/scripts/install.py +136 -0
- cabildo-0.1.0/scripts/pypi-publish +23 -0
- cabildo-0.1.0/scripts/release +31 -0
- cabildo-0.1.0/src/cabildo/__init__.py +3 -0
- cabildo-0.1.0/src/cabildo/actions.py +710 -0
- cabildo-0.1.0/src/cabildo/cli.py +353 -0
- cabildo-0.1.0/src/cabildo/context.py +84 -0
- cabildo-0.1.0/src/cabildo/daemon.py +188 -0
- cabildo-0.1.0/src/cabildo/desktop.py +123 -0
- cabildo-0.1.0/src/cabildo/guard.py +108 -0
- cabildo-0.1.0/src/cabildo/hooks/__init__.py +0 -0
- cabildo-0.1.0/src/cabildo/hooks/_common.py +32 -0
- cabildo-0.1.0/src/cabildo/hooks/cli.py +40 -0
- cabildo-0.1.0/src/cabildo/hooks/post_tool_use.py +34 -0
- cabildo-0.1.0/src/cabildo/hooks/pre_tool_use.py +44 -0
- cabildo-0.1.0/src/cabildo/hooks/prompt_submit.py +38 -0
- cabildo-0.1.0/src/cabildo/hooks/session_end.py +24 -0
- cabildo-0.1.0/src/cabildo/hooks/session_start.py +36 -0
- cabildo-0.1.0/src/cabildo/hooks/stop.py +42 -0
- cabildo-0.1.0/src/cabildo/mcp_server.py +448 -0
- cabildo-0.1.0/src/cabildo/notify.py +113 -0
- cabildo-0.1.0/src/cabildo/store.py +840 -0
- cabildo-0.1.0/src/cabildo/tui.py +311 -0
- cabildo-0.1.0/src/cabildo/usage.py +202 -0
- cabildo-0.1.0/tests/__init__.py +0 -0
- cabildo-0.1.0/tests/conftest.py +32 -0
- cabildo-0.1.0/tests/test_claims.py +101 -0
- cabildo-0.1.0/tests/test_context.py +24 -0
- cabildo-0.1.0/tests/test_hooks_midturn.py +139 -0
- cabildo-0.1.0/tests/test_land_verification.py +152 -0
- cabildo-0.1.0/tests/test_mcp_tools.py +93 -0
- cabildo-0.1.0/tests/test_spawn.py +201 -0
- cabildo-0.1.0/tests/test_store.py +129 -0
- cabildo-0.1.0/tests/test_tui.py +59 -0
- cabildo-0.1.0/uv.lock +794 -0
cabildo-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# cabildo CI/CD -- test, then on the default branch publish to PyPI via OIDC
|
|
2
|
+
# Trusted Publishing when pyproject's version isn't on PyPI yet (no stored API
|
|
3
|
+
# token). Same publishing mechanism as pytest-parallex and musil; plain uv
|
|
4
|
+
# instead of moon/proto because this repo has no task graph (yet).
|
|
5
|
+
|
|
6
|
+
default:
|
|
7
|
+
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
|
|
8
|
+
|
|
9
|
+
variables:
|
|
10
|
+
UV_LINK_MODE: copy
|
|
11
|
+
|
|
12
|
+
# Avoid duplicate (branch + MR) pipelines.
|
|
13
|
+
workflow:
|
|
14
|
+
rules:
|
|
15
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
16
|
+
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
|
|
17
|
+
when: never
|
|
18
|
+
- if: $CI_COMMIT_BRANCH
|
|
19
|
+
|
|
20
|
+
ci:
|
|
21
|
+
# OIDC Trusted Publishing: GitLab mints a short-lived id_token; scripts/pypi-publish
|
|
22
|
+
# exchanges it for a one-shot PyPI token. The `pypi` environment matches the PyPI
|
|
23
|
+
# publisher config (it is also recorded on non-publishing runs -- cosmetic only).
|
|
24
|
+
id_tokens:
|
|
25
|
+
PYPI_ID_TOKEN:
|
|
26
|
+
aud: pypi
|
|
27
|
+
environment:
|
|
28
|
+
name: pypi
|
|
29
|
+
url: https://pypi.org/project/cabildo/
|
|
30
|
+
before_script:
|
|
31
|
+
# slim image is bare; the land() tests drive real git against a bare remote.
|
|
32
|
+
- apt-get update && apt-get install -y --no-install-recommends git ca-certificates
|
|
33
|
+
script:
|
|
34
|
+
- uv run --extra dev pytest -q
|
|
35
|
+
- |
|
|
36
|
+
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
|
|
37
|
+
sh scripts/release
|
|
38
|
+
fi
|
cabildo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jorge Eduardo Cardona Gaviria
|
|
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.
|
cabildo-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cabildo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: IPC bus + chat room for coordinating parallel Claude Code agents: messaging, presence, and advisory locks with hook-level enforcement
|
|
5
|
+
Author-email: Jorge Eduardo Cardona Gaviria <jorgeecardona@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agents,claude-code,coordination,ipc,mcp,multi-agent
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Communications :: Chat
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: mcp>=1.2
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# cabildo
|
|
27
|
+
|
|
28
|
+
A shared **IPC bus / chat room** for coordinating multiple Claude Code agents
|
|
29
|
+
running at the same time. Agents can message each other (direct or by topic),
|
|
30
|
+
discover who else is working, and — crucially — take **advisory locks** to
|
|
31
|
+
coordinate shared actions like committing and pushing.
|
|
32
|
+
|
|
33
|
+
It's a normal Python package: one **stdio MCP server** (the tools agents call)
|
|
34
|
+
plus a set of **hook scripts** (delivery, liveness, and lock enforcement). All state lives in
|
|
35
|
+
a single SQLite DB shared by every session.
|
|
36
|
+
|
|
37
|
+
## How identity works (the tricky bit)
|
|
38
|
+
|
|
39
|
+
Claude Code does **not** give an MCP server the `session_id`. But hooks *do* get
|
|
40
|
+
it, and both the hooks and the MCP subprocess are descendants of the same
|
|
41
|
+
`claude` process. So:
|
|
42
|
+
|
|
43
|
+
- The `SessionStart` hook writes `session_id ⇄ claude_pid` into the DB.
|
|
44
|
+
- The MCP server resolves *its own* owning `claude_pid` by walking `/proc` and
|
|
45
|
+
looks up the session_id bound to it.
|
|
46
|
+
|
|
47
|
+
1 session ⇄ 1 MCP process, so this is unambiguous and fully automatic — the
|
|
48
|
+
model never has to carry an ID around.
|
|
49
|
+
|
|
50
|
+
## Data model (`~/.cabildo/chat.db`, WAL)
|
|
51
|
+
|
|
52
|
+
| table | role |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `sessions` | one row per agent: `handle`, `cwd`, `project`, `claude_pid`, `last_seen_at` |
|
|
55
|
+
| `topics` | channels; auto per-project channel `#proj:<name>` + global `#general` |
|
|
56
|
+
| `subscriptions` | who follows which topic |
|
|
57
|
+
| `messages` | `from` → `topic` **or** `to_session` (DM), `body`, `urgent` |
|
|
58
|
+
| `reads` | explicit read receipts — messages are **never** auto-read |
|
|
59
|
+
| `claims` | advisory leases (`resource`, holder, TTL) for coordination |
|
|
60
|
+
|
|
61
|
+
## MCP tools
|
|
62
|
+
|
|
63
|
+
**Presence:** `whoami`, `set_handle`, `list_agents`
|
|
64
|
+
**Channels:** `list_topics`, `join_topic`, `leave_topic`
|
|
65
|
+
**Messaging:** `send(body, to=?, topic=?, urgent=?)`, `inbox(unread_only=?)`,
|
|
66
|
+
`read(message_ids|topic|all)`, `history(topic|with_agent)`
|
|
67
|
+
**Coordination:** `claim(resource, ttl_seconds, note)`, `release(resource)`,
|
|
68
|
+
`claims()`
|
|
69
|
+
|
|
70
|
+
### Coordinating a push
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
claims() # anyone holding it?
|
|
74
|
+
claim("push:myrepo", note="rebase+push") # ok:false tells you who holds it
|
|
75
|
+
# ... do the commit & push ...
|
|
76
|
+
release("push:myrepo")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Locks are **advisory** (agents cooperate by convention) and auto-expire via TTL,
|
|
80
|
+
so a crashed agent never deadlocks the bus. `SessionEnd` also drops a session's
|
|
81
|
+
locks.
|
|
82
|
+
|
|
83
|
+
## Hook surface
|
|
84
|
+
|
|
85
|
+
Hooks are the only way into a running session's context, and the tool-call
|
|
86
|
+
loop is the only clock: an agent that is thinking or writing text cannot be
|
|
87
|
+
reached until its next tool call or turn boundary. cabildo uses six events:
|
|
88
|
+
|
|
89
|
+
| event | job |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `SessionStart` | register on the bus, bind `session_id ⇄ claude_pid`, inject a banner (who you are, peers, locks, unread) |
|
|
92
|
+
| `UserPromptSubmit` | refresh liveness, inject a one-line `📬 N unread …` notice (silent when nothing) |
|
|
93
|
+
| `PostToolUse` (`*`) | **mid-turn delivery**: inject unread URGENT mail at the next tool-call boundary. Each message injects once (tracked in `deliveries`; delivery ≠ read). Ordinary mail never interrupts a turn. Also keeps `last_seen` fresh during long autonomous turns. |
|
|
94
|
+
| `PreToolUse` (`Bash\|Edit\|Write\|NotebookEdit`) | **claims enforcement**: deny `git push` while another session holds `push:<project>`, and deny edits to paths another session holds `file:<path>` on (globs work: `file:src/*`). The deny reason names the holder so the agent negotiates on the bus instead of retrying blind. Decision logic in `cabildo/guard.py`. |
|
|
95
|
+
| `Stop` | refuse to go idle with unread URGENT mail — delivery is pull-based, so an idle agent is unreachable; the hook stops the stopping instead |
|
|
96
|
+
| `SessionEnd` | deregister, drop the session's locks |
|
|
97
|
+
|
|
98
|
+
Urgency is the escalation axis: normal mail waits for the next user turn;
|
|
99
|
+
urgent mail interrupts at the next tool call and holds the turn open at Stop.
|
|
100
|
+
Locks stay advisory between cooperating agents but the PreToolUse guard gives
|
|
101
|
+
them teeth at the two places where clobbering actually happens (push, file
|
|
102
|
+
writes).
|
|
103
|
+
|
|
104
|
+
Deliberately unused: `PreCompact`/`PostCompact`, `SubagentStart/Stop`,
|
|
105
|
+
`Notification`, `TeammateIdle` — nothing on the bus needs them yet, and every
|
|
106
|
+
extra per-event hook is latency on somebody's session.
|
|
107
|
+
|
|
108
|
+
## Install
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
python scripts/install.py
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This creates a venv, installs the package, registers the MCP server at
|
|
115
|
+
**user scope** (all projects), and appends the hooks to
|
|
116
|
+
`~/.claude/settings.json` (existing hooks are preserved). See the script for the
|
|
117
|
+
exact commands if you'd rather do it by hand.
|
|
118
|
+
|
|
119
|
+
## Config
|
|
120
|
+
|
|
121
|
+
- `CABILDO_HOME` — where the DB lives (default `~/.cabildo`).
|
cabildo-0.1.0/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# cabildo
|
|
2
|
+
|
|
3
|
+
A shared **IPC bus / chat room** for coordinating multiple Claude Code agents
|
|
4
|
+
running at the same time. Agents can message each other (direct or by topic),
|
|
5
|
+
discover who else is working, and — crucially — take **advisory locks** to
|
|
6
|
+
coordinate shared actions like committing and pushing.
|
|
7
|
+
|
|
8
|
+
It's a normal Python package: one **stdio MCP server** (the tools agents call)
|
|
9
|
+
plus a set of **hook scripts** (delivery, liveness, and lock enforcement). All state lives in
|
|
10
|
+
a single SQLite DB shared by every session.
|
|
11
|
+
|
|
12
|
+
## How identity works (the tricky bit)
|
|
13
|
+
|
|
14
|
+
Claude Code does **not** give an MCP server the `session_id`. But hooks *do* get
|
|
15
|
+
it, and both the hooks and the MCP subprocess are descendants of the same
|
|
16
|
+
`claude` process. So:
|
|
17
|
+
|
|
18
|
+
- The `SessionStart` hook writes `session_id ⇄ claude_pid` into the DB.
|
|
19
|
+
- The MCP server resolves *its own* owning `claude_pid` by walking `/proc` and
|
|
20
|
+
looks up the session_id bound to it.
|
|
21
|
+
|
|
22
|
+
1 session ⇄ 1 MCP process, so this is unambiguous and fully automatic — the
|
|
23
|
+
model never has to carry an ID around.
|
|
24
|
+
|
|
25
|
+
## Data model (`~/.cabildo/chat.db`, WAL)
|
|
26
|
+
|
|
27
|
+
| table | role |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `sessions` | one row per agent: `handle`, `cwd`, `project`, `claude_pid`, `last_seen_at` |
|
|
30
|
+
| `topics` | channels; auto per-project channel `#proj:<name>` + global `#general` |
|
|
31
|
+
| `subscriptions` | who follows which topic |
|
|
32
|
+
| `messages` | `from` → `topic` **or** `to_session` (DM), `body`, `urgent` |
|
|
33
|
+
| `reads` | explicit read receipts — messages are **never** auto-read |
|
|
34
|
+
| `claims` | advisory leases (`resource`, holder, TTL) for coordination |
|
|
35
|
+
|
|
36
|
+
## MCP tools
|
|
37
|
+
|
|
38
|
+
**Presence:** `whoami`, `set_handle`, `list_agents`
|
|
39
|
+
**Channels:** `list_topics`, `join_topic`, `leave_topic`
|
|
40
|
+
**Messaging:** `send(body, to=?, topic=?, urgent=?)`, `inbox(unread_only=?)`,
|
|
41
|
+
`read(message_ids|topic|all)`, `history(topic|with_agent)`
|
|
42
|
+
**Coordination:** `claim(resource, ttl_seconds, note)`, `release(resource)`,
|
|
43
|
+
`claims()`
|
|
44
|
+
|
|
45
|
+
### Coordinating a push
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
claims() # anyone holding it?
|
|
49
|
+
claim("push:myrepo", note="rebase+push") # ok:false tells you who holds it
|
|
50
|
+
# ... do the commit & push ...
|
|
51
|
+
release("push:myrepo")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Locks are **advisory** (agents cooperate by convention) and auto-expire via TTL,
|
|
55
|
+
so a crashed agent never deadlocks the bus. `SessionEnd` also drops a session's
|
|
56
|
+
locks.
|
|
57
|
+
|
|
58
|
+
## Hook surface
|
|
59
|
+
|
|
60
|
+
Hooks are the only way into a running session's context, and the tool-call
|
|
61
|
+
loop is the only clock: an agent that is thinking or writing text cannot be
|
|
62
|
+
reached until its next tool call or turn boundary. cabildo uses six events:
|
|
63
|
+
|
|
64
|
+
| event | job |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `SessionStart` | register on the bus, bind `session_id ⇄ claude_pid`, inject a banner (who you are, peers, locks, unread) |
|
|
67
|
+
| `UserPromptSubmit` | refresh liveness, inject a one-line `📬 N unread …` notice (silent when nothing) |
|
|
68
|
+
| `PostToolUse` (`*`) | **mid-turn delivery**: inject unread URGENT mail at the next tool-call boundary. Each message injects once (tracked in `deliveries`; delivery ≠ read). Ordinary mail never interrupts a turn. Also keeps `last_seen` fresh during long autonomous turns. |
|
|
69
|
+
| `PreToolUse` (`Bash\|Edit\|Write\|NotebookEdit`) | **claims enforcement**: deny `git push` while another session holds `push:<project>`, and deny edits to paths another session holds `file:<path>` on (globs work: `file:src/*`). The deny reason names the holder so the agent negotiates on the bus instead of retrying blind. Decision logic in `cabildo/guard.py`. |
|
|
70
|
+
| `Stop` | refuse to go idle with unread URGENT mail — delivery is pull-based, so an idle agent is unreachable; the hook stops the stopping instead |
|
|
71
|
+
| `SessionEnd` | deregister, drop the session's locks |
|
|
72
|
+
|
|
73
|
+
Urgency is the escalation axis: normal mail waits for the next user turn;
|
|
74
|
+
urgent mail interrupts at the next tool call and holds the turn open at Stop.
|
|
75
|
+
Locks stay advisory between cooperating agents but the PreToolUse guard gives
|
|
76
|
+
them teeth at the two places where clobbering actually happens (push, file
|
|
77
|
+
writes).
|
|
78
|
+
|
|
79
|
+
Deliberately unused: `PreCompact`/`PostCompact`, `SubagentStart/Stop`,
|
|
80
|
+
`Notification`, `TeammateIdle` — nothing on the bus needs them yet, and every
|
|
81
|
+
extra per-event hook is latency on somebody's session.
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
python scripts/install.py
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This creates a venv, installs the package, registers the MCP server at
|
|
90
|
+
**user scope** (all projects), and appends the hooks to
|
|
91
|
+
`~/.claude/settings.json` (existing hooks are preserved). See the script for the
|
|
92
|
+
exact commands if you'd rather do it by hand.
|
|
93
|
+
|
|
94
|
+
## Config
|
|
95
|
+
|
|
96
|
+
- `CABILDO_HOME` — where the DB lives (default `~/.cabildo`).
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
- generic [ref=f7e1]:
|
|
2
|
+
- link "Skip to main content" [ref=f7e2] [cursor=pointer]:
|
|
3
|
+
- /url: "#content"
|
|
4
|
+
- alert [ref=f7e258]:
|
|
5
|
+
- generic [ref=f7e259]: Error
|
|
6
|
+
- text: The trusted publisher could not be registered
|
|
7
|
+
- button "Close" [ref=f7e262] [cursor=pointer]
|
|
8
|
+
- banner [ref=f7e3]:
|
|
9
|
+
- generic [ref=f7e5]:
|
|
10
|
+
- generic [ref=f7e6]:
|
|
11
|
+
- link [ref=f7e8] [cursor=pointer]:
|
|
12
|
+
- /url: /
|
|
13
|
+
- img "PyPI" [ref=f7e9]
|
|
14
|
+
- search [ref=f7e10]:
|
|
15
|
+
- generic [ref=f7e11]: Search PyPI
|
|
16
|
+
- textbox "Search PyPI" [ref=f7e12]:
|
|
17
|
+
- /placeholder: Type '/' to search projects
|
|
18
|
+
- button "Search" [ref=f7e13]
|
|
19
|
+
- navigation "Main navigation" [ref=f7e265]:
|
|
20
|
+
- button "View menu" [ref=f7e266] [cursor=pointer]:
|
|
21
|
+
- img "User Image" [ref=f7e267]
|
|
22
|
+
- text: jorgeecardona
|
|
23
|
+
- main [ref=f7e30]:
|
|
24
|
+
- generic [ref=f7e31]:
|
|
25
|
+
- navigation "Breadcrumbs" [ref=f7e32]:
|
|
26
|
+
- list [ref=f7e33]:
|
|
27
|
+
- listitem [ref=f7e34]: Your account
|
|
28
|
+
- generic [ref=f7e35]:
|
|
29
|
+
- navigation "Account navigation" [ref=f7e38]:
|
|
30
|
+
- list [ref=f7e39]:
|
|
31
|
+
- listitem [ref=f7e40]:
|
|
32
|
+
- link "Your projects" [ref=f7e41] [cursor=pointer]:
|
|
33
|
+
- /url: /manage/projects/
|
|
34
|
+
- listitem [ref=f7e43]:
|
|
35
|
+
- link "Your organizations" [ref=f7e44] [cursor=pointer]:
|
|
36
|
+
- /url: /manage/organizations/
|
|
37
|
+
- listitem [ref=f7e46]:
|
|
38
|
+
- link "Account settings" [ref=f7e47] [cursor=pointer]:
|
|
39
|
+
- /url: /manage/account/
|
|
40
|
+
- listitem [ref=f7e49]:
|
|
41
|
+
- link "Publishing" [ref=f7e50] [cursor=pointer]:
|
|
42
|
+
- /url: /manage/account/publishing/
|
|
43
|
+
- generic [ref=f7e52]:
|
|
44
|
+
- navigation "Account navigation":
|
|
45
|
+
- list:
|
|
46
|
+
- listitem
|
|
47
|
+
- listitem
|
|
48
|
+
- listitem
|
|
49
|
+
- listitem
|
|
50
|
+
- generic [ref=f7e53]:
|
|
51
|
+
- generic [ref=f7e54]:
|
|
52
|
+
- heading "Trusted Publisher Management" [level=1] [ref=f7e55]
|
|
53
|
+
- paragraph [ref=f7e56]: OpenID Connect (OIDC) provides a flexible, credential-free mechanism for delegating publishing authority for a PyPI package to a trusted third party service, like GitHub Actions.
|
|
54
|
+
- paragraph [ref=f7e57]: PyPI users and projects can use trusted publishers to automate their release processes, without needing to use API tokens or passwords.
|
|
55
|
+
- paragraph [ref=f7e58]:
|
|
56
|
+
- text: You can read more about trusted publishers and how to use them
|
|
57
|
+
- link "here" [ref=f7e59] [cursor=pointer]:
|
|
58
|
+
- /url: https://docs.pypi.org/trusted-publishers/
|
|
59
|
+
- text: .
|
|
60
|
+
- heading "Manage publishers" [level=2] [ref=f7e60]
|
|
61
|
+
- heading "Projects with active publishers" [level=3] [ref=f7e61]
|
|
62
|
+
- table [ref=f7e62]:
|
|
63
|
+
- rowgroup [ref=f7e63]:
|
|
64
|
+
- row [ref=f7e64]:
|
|
65
|
+
- columnheader "Project" [ref=f7e65]
|
|
66
|
+
- columnheader [ref=f7e66]
|
|
67
|
+
- rowgroup [ref=f7e67]:
|
|
68
|
+
- row [ref=f7e68]:
|
|
69
|
+
- cell "folderbot" [ref=f7e69]
|
|
70
|
+
- cell [ref=f7e70]:
|
|
71
|
+
- link "Manage" [ref=f7e71] [cursor=pointer]:
|
|
72
|
+
- /url: /manage/project/folderbot/settings/publishing/
|
|
73
|
+
- row [ref=f7e72]:
|
|
74
|
+
- cell "musil" [ref=f7e73]
|
|
75
|
+
- cell [ref=f7e74]:
|
|
76
|
+
- link "Manage" [ref=f7e75] [cursor=pointer]:
|
|
77
|
+
- /url: /manage/project/musil/settings/publishing/
|
|
78
|
+
- row [ref=f7e76]:
|
|
79
|
+
- cell "pytest-parallex" [ref=f7e77]
|
|
80
|
+
- cell [ref=f7e78]:
|
|
81
|
+
- link "Manage" [ref=f7e79] [cursor=pointer]:
|
|
82
|
+
- /url: /manage/project/pytest-parallex/settings/publishing/
|
|
83
|
+
- heading "Pending publishers" [level=3] [ref=f7e80]
|
|
84
|
+
- paragraph [ref=f7e81]: No pending publishers are currently configured. Publishers for projects that don't exist yet can be added below.
|
|
85
|
+
- generic [ref=f7e82]:
|
|
86
|
+
- heading "Add a new pending publisher" [level=2] [ref=f7e83]
|
|
87
|
+
- paragraph [ref=f7e84]: You can use this page to register "pending" trusted publishers.
|
|
88
|
+
- paragraph [ref=f7e85]:
|
|
89
|
+
- text: These publishers behave similarly to trusted publishers registered against specific projects, except that they allow users to
|
|
90
|
+
- strong [ref=f7e86]: create
|
|
91
|
+
- text: the project if it doesn't already exist. Once the project is created, the "pending" publisher becomes an ordinary trusted publisher. You can read more about "pending" and ordinary trusted publishers
|
|
92
|
+
- link "here" [ref=f7e87] [cursor=pointer]:
|
|
93
|
+
- /url: https://docs.pypi.org/trusted-publishers/
|
|
94
|
+
- text: .
|
|
95
|
+
- paragraph [ref=f7e88]:
|
|
96
|
+
- text: Configuring a "pending" publisher for a project name does
|
|
97
|
+
- strong [ref=f7e89]: not
|
|
98
|
+
- text: reserve that name. Until the project is created, any other user may create it, including via their own "pending" publisher.
|
|
99
|
+
- generic [ref=f7e90]:
|
|
100
|
+
- generic [ref=f7e91]:
|
|
101
|
+
- button "GitHub" [ref=f7e92] [cursor=pointer]
|
|
102
|
+
- button "GitLab" [active] [ref=f7e93] [cursor=pointer]
|
|
103
|
+
- button "Google" [ref=f7e94] [cursor=pointer]
|
|
104
|
+
- button "ActiveState" [ref=f7e95] [cursor=pointer]
|
|
105
|
+
- generic [ref=f7e271]:
|
|
106
|
+
- paragraph [ref=f7e272]:
|
|
107
|
+
- text: Read more about GitLab CI/CD OpenID Connect support
|
|
108
|
+
- link "here" [ref=f7e273] [cursor=pointer]:
|
|
109
|
+
- /url: https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html
|
|
110
|
+
- text: .
|
|
111
|
+
- generic [ref=f7e274]:
|
|
112
|
+
- generic [ref=f7e275]:
|
|
113
|
+
- generic [ref=f7e276]: PyPI Project Name (required)
|
|
114
|
+
- textbox "project name" [ref=f7e277]
|
|
115
|
+
- paragraph [ref=f7e278]: The project (on PyPI) that will be created when this publisher is used
|
|
116
|
+
- generic [ref=f7e279]:
|
|
117
|
+
- generic [ref=f7e280]: Namespace (required)
|
|
118
|
+
- textbox "Namespace (required)" [ref=f7e281]:
|
|
119
|
+
- /placeholder: namespace
|
|
120
|
+
- paragraph [ref=f7e282]: The GitLab username or GitLab group/subgroup namespace that the project is under
|
|
121
|
+
- generic [ref=f7e283]:
|
|
122
|
+
- generic [ref=f7e284]: Project name (required)
|
|
123
|
+
- textbox "Project name (required) ActiveState Project name (required)" [ref=f7e285]:
|
|
124
|
+
- /placeholder: project
|
|
125
|
+
- paragraph [ref=f7e286]: The name of the GitLab project that contains the publishing workflow
|
|
126
|
+
- generic [ref=f7e287]:
|
|
127
|
+
- generic [ref=f7e288]: Top-level pipeline file path (required)
|
|
128
|
+
- textbox "Top-level pipeline file path (required)" [ref=f7e289]:
|
|
129
|
+
- /placeholder: .gitlab-ci.yml
|
|
130
|
+
- paragraph [ref=f7e290]: The file path of the top-level pipeline, relative to the project's root. This file should exist in the project configured above (external pipelines are not supported).
|
|
131
|
+
- generic [ref=f7e291]:
|
|
132
|
+
- generic [ref=f7e292]: Environment name (optional)
|
|
133
|
+
- textbox "release" [ref=f7e293]
|
|
134
|
+
- paragraph [ref=f7e294]:
|
|
135
|
+
- text: The name of the
|
|
136
|
+
- link "GitLab CI/CD environment" [ref=f7e295] [cursor=pointer]:
|
|
137
|
+
- /url: https://docs.gitlab.com/ee/ci/environments/
|
|
138
|
+
- text: that the above workflow uses for publishing. This should be configured under the project's settings. While not required, a dedicated publishing environment is
|
|
139
|
+
- strong [ref=f7e296]: strongly
|
|
140
|
+
- text: encouraged,
|
|
141
|
+
- strong [ref=f7e297]: especially
|
|
142
|
+
- text: if your project has maintainers with commit access who shouldn't have PyPI publishing access.
|
|
143
|
+
- button "Add" [ref=f7e299] [cursor=pointer]
|
|
144
|
+
- contentinfo [ref=f7e126]:
|
|
145
|
+
- generic [ref=f7e128]:
|
|
146
|
+
- generic [ref=f7e129]:
|
|
147
|
+
- heading "Help" [level=2] [ref=f7e130]
|
|
148
|
+
- navigation "Help navigation" [ref=f7e131]:
|
|
149
|
+
- list [ref=f7e132]:
|
|
150
|
+
- listitem [ref=f7e133]:
|
|
151
|
+
- link "Installing packages" [ref=f7e134] [cursor=pointer]:
|
|
152
|
+
- /url: https://packaging.python.org/tutorials/installing-packages/
|
|
153
|
+
- listitem [ref=f7e135]:
|
|
154
|
+
- link "Uploading packages" [ref=f7e136] [cursor=pointer]:
|
|
155
|
+
- /url: https://packaging.python.org/tutorials/packaging-projects/
|
|
156
|
+
- listitem [ref=f7e137]:
|
|
157
|
+
- link "User guide" [ref=f7e138] [cursor=pointer]:
|
|
158
|
+
- /url: https://packaging.python.org/
|
|
159
|
+
- listitem [ref=f7e139]:
|
|
160
|
+
- link "Project name retention" [ref=f7e140] [cursor=pointer]:
|
|
161
|
+
- /url: https://www.python.org/dev/peps/pep-0541/
|
|
162
|
+
- listitem [ref=f7e141]:
|
|
163
|
+
- link "FAQs" [ref=f7e142] [cursor=pointer]:
|
|
164
|
+
- /url: /help/
|
|
165
|
+
- generic [ref=f7e143]:
|
|
166
|
+
- heading "About PyPI" [level=2] [ref=f7e144]
|
|
167
|
+
- navigation "About PyPI navigation" [ref=f7e145]:
|
|
168
|
+
- list [ref=f7e146]:
|
|
169
|
+
- listitem [ref=f7e147]:
|
|
170
|
+
- link "PyPI Blog" [ref=f7e148] [cursor=pointer]:
|
|
171
|
+
- /url: https://blog.pypi.org
|
|
172
|
+
- listitem [ref=f7e149]:
|
|
173
|
+
- link "Infrastructure dashboard" [ref=f7e150] [cursor=pointer]:
|
|
174
|
+
- /url: https://dtdg.co/pypi
|
|
175
|
+
- listitem [ref=f7e151]:
|
|
176
|
+
- link "Statistics" [ref=f7e152] [cursor=pointer]:
|
|
177
|
+
- /url: /stats/
|
|
178
|
+
- listitem [ref=f7e153]:
|
|
179
|
+
- link "Logos & trademarks" [ref=f7e154] [cursor=pointer]:
|
|
180
|
+
- /url: /trademarks/
|
|
181
|
+
- listitem [ref=f7e155]:
|
|
182
|
+
- link "Our sponsors" [ref=f7e156] [cursor=pointer]:
|
|
183
|
+
- /url: /sponsors/
|
|
184
|
+
- generic [ref=f7e157]:
|
|
185
|
+
- heading "Contributing to PyPI" [level=2] [ref=f7e158]
|
|
186
|
+
- navigation "How to contribute navigation" [ref=f7e159]:
|
|
187
|
+
- list [ref=f7e160]:
|
|
188
|
+
- listitem [ref=f7e161]:
|
|
189
|
+
- link "Bugs and feedback" [ref=f7e162] [cursor=pointer]:
|
|
190
|
+
- /url: /help/#feedback
|
|
191
|
+
- listitem [ref=f7e163]:
|
|
192
|
+
- link "Contribute on GitHub" [ref=f7e164] [cursor=pointer]:
|
|
193
|
+
- /url: https://github.com/pypi/warehouse
|
|
194
|
+
- listitem [ref=f7e165]:
|
|
195
|
+
- link "Translate PyPI" [ref=f7e166] [cursor=pointer]:
|
|
196
|
+
- /url: https://hosted.weblate.org/projects/pypa/warehouse/
|
|
197
|
+
- listitem [ref=f7e167]:
|
|
198
|
+
- link "Sponsor PyPI" [ref=f7e168] [cursor=pointer]:
|
|
199
|
+
- /url: /sponsors/
|
|
200
|
+
- listitem [ref=f7e169]:
|
|
201
|
+
- link "Development credits" [ref=f7e170] [cursor=pointer]:
|
|
202
|
+
- /url: https://github.com/pypi/warehouse/graphs/contributors
|
|
203
|
+
- generic [ref=f7e171]:
|
|
204
|
+
- heading "Using PyPI" [level=2] [ref=f7e172]
|
|
205
|
+
- navigation "Using PyPI navigation" [ref=f7e173]:
|
|
206
|
+
- list [ref=f7e174]:
|
|
207
|
+
- listitem [ref=f7e175]:
|
|
208
|
+
- link "Terms of Service" [ref=f7e176] [cursor=pointer]:
|
|
209
|
+
- /url: https://policies.python.org/pypi.org/Terms-of-Service/
|
|
210
|
+
- listitem [ref=f7e177]:
|
|
211
|
+
- link "Report security issue" [ref=f7e178] [cursor=pointer]:
|
|
212
|
+
- /url: /security/
|
|
213
|
+
- listitem [ref=f7e179]:
|
|
214
|
+
- link "Code of conduct" [ref=f7e180] [cursor=pointer]:
|
|
215
|
+
- /url: https://policies.python.org/python.org/code-of-conduct/
|
|
216
|
+
- listitem [ref=f7e181]:
|
|
217
|
+
- link "Privacy Notice" [ref=f7e182] [cursor=pointer]:
|
|
218
|
+
- /url: https://policies.python.org/pypi.org/Privacy-Notice/
|
|
219
|
+
- listitem [ref=f7e183]:
|
|
220
|
+
- link "Acceptable Use Policy" [ref=f7e184] [cursor=pointer]:
|
|
221
|
+
- /url: https://policies.python.org/pypi.org/Acceptable-Use-Policy/
|
|
222
|
+
- separator [ref=f7e185]
|
|
223
|
+
- generic [ref=f7e186]:
|
|
224
|
+
- paragraph [ref=f7e187]:
|
|
225
|
+
- text: "Status:"
|
|
226
|
+
- link "All Systems Operational" [ref=f7e270] [cursor=pointer]:
|
|
227
|
+
- /url: https://status.python.org/
|
|
228
|
+
- paragraph [ref=f7e189]:
|
|
229
|
+
- text: Developed and maintained by the Python community, for the Python community.
|
|
230
|
+
- link "Donate today!" [ref=f7e190] [cursor=pointer]:
|
|
231
|
+
- /url: https://donate.pypi.org
|
|
232
|
+
- paragraph [ref=f7e191]:
|
|
233
|
+
- text: "\"PyPI\", \"Python Package Index\", and the blocks logos are registered"
|
|
234
|
+
- link "trademarks" [ref=f7e192] [cursor=pointer]:
|
|
235
|
+
- /url: /trademarks/
|
|
236
|
+
- text: of the
|
|
237
|
+
- link "Python Software Foundation" [ref=f7e193] [cursor=pointer]:
|
|
238
|
+
- /url: https://www.python.org/psf-landing
|
|
239
|
+
- text: .
|
|
240
|
+
- paragraph [ref=f7e194]:
|
|
241
|
+
- text: © 2026
|
|
242
|
+
- link "Python Software Foundation" [ref=f7e195] [cursor=pointer]:
|
|
243
|
+
- /url: https://www.python.org/psf-landing/
|
|
244
|
+
- link "Site map" [ref=f7e196] [cursor=pointer]:
|
|
245
|
+
- /url: /sitemap/
|
|
246
|
+
- paragraph [ref=f7e197]:
|
|
247
|
+
- text: Deployed from
|
|
248
|
+
- link [ref=f7e198] [cursor=pointer]:
|
|
249
|
+
- /url: https://github.com/pypi/warehouse/commit/ce84faeb10ffa2742a6c777275ae32fdece657ef
|
|
250
|
+
- code [ref=f7e199]: ce84fae
|
|
251
|
+
- text:
|
|
252
|
+
- button "Switch to desktop version" [ref=f7e201] [cursor=pointer]
|
|
253
|
+
- list [ref=f7e204]:
|
|
254
|
+
- listitem [ref=f7e205]:
|
|
255
|
+
- button "English"
|
|
256
|
+
- listitem [ref=f7e206]:
|
|
257
|
+
- button "español" [ref=f7e207]
|
|
258
|
+
- listitem [ref=f7e208]:
|
|
259
|
+
- button "français" [ref=f7e209]
|
|
260
|
+
- listitem [ref=f7e210]:
|
|
261
|
+
- button "日本語" [ref=f7e211]
|
|
262
|
+
- listitem [ref=f7e212]:
|
|
263
|
+
- button "português (Brasil)" [ref=f7e213]
|
|
264
|
+
- listitem [ref=f7e214]:
|
|
265
|
+
- button "українська" [ref=f7e215]
|
|
266
|
+
- listitem [ref=f7e216]:
|
|
267
|
+
- button "Ελληνικά" [ref=f7e217]
|
|
268
|
+
- listitem [ref=f7e218]:
|
|
269
|
+
- button "Deutsch" [ref=f7e219]
|
|
270
|
+
- listitem [ref=f7e220]:
|
|
271
|
+
- button "中文 (简体)" [ref=f7e221]
|
|
272
|
+
- listitem [ref=f7e222]:
|
|
273
|
+
- button "中文 (繁體)" [ref=f7e223]
|
|
274
|
+
- listitem [ref=f7e224]:
|
|
275
|
+
- button "русский" [ref=f7e225]
|
|
276
|
+
- listitem [ref=f7e226]:
|
|
277
|
+
- button "עברית" [ref=f7e227]
|
|
278
|
+
- listitem [ref=f7e228]:
|
|
279
|
+
- button "Esperanto" [ref=f7e229]
|
|
280
|
+
- listitem [ref=f7e230]:
|
|
281
|
+
- button "한국어" [ref=f7e231]
|
|
282
|
+
- generic [ref=f7e232]:
|
|
283
|
+
- link "AWS Cloud computing and Security Sponsor" [ref=f7e233] [cursor=pointer]:
|
|
284
|
+
- /url: https://aws.amazon.com/
|
|
285
|
+
- generic [ref=f7e234]: AWS
|
|
286
|
+
- generic [ref=f7e235]: Cloud computing and Security Sponsor
|
|
287
|
+
- link "Datadog Monitoring" [ref=f7e236] [cursor=pointer]:
|
|
288
|
+
- /url: https://www.datadoghq.com/
|
|
289
|
+
- generic [ref=f7e237]: Datadog
|
|
290
|
+
- generic [ref=f7e238]: Monitoring
|
|
291
|
+
- link "Depot Continuous Integration" [ref=f7e239] [cursor=pointer]:
|
|
292
|
+
- /url: https://depot.dev
|
|
293
|
+
- generic [ref=f7e240]: Depot
|
|
294
|
+
- generic [ref=f7e241]: Continuous Integration
|
|
295
|
+
- link "Fastly CDN" [ref=f7e242] [cursor=pointer]:
|
|
296
|
+
- /url: https://www.fastly.com/
|
|
297
|
+
- generic [ref=f7e243]: Fastly
|
|
298
|
+
- generic [ref=f7e244]: CDN
|
|
299
|
+
- link "Google Download Analytics" [ref=f7e245] [cursor=pointer]:
|
|
300
|
+
- /url: https://careers.google.com/
|
|
301
|
+
- generic [ref=f7e246]: Google
|
|
302
|
+
- generic [ref=f7e247]: Download Analytics
|
|
303
|
+
- link "Pingdom Monitoring" [ref=f7e248] [cursor=pointer]:
|
|
304
|
+
- /url: https://www.pingdom.com/
|
|
305
|
+
- generic [ref=f7e249]: Pingdom
|
|
306
|
+
- generic [ref=f7e250]: Monitoring
|
|
307
|
+
- link "Sentry Error logging" [ref=f7e251] [cursor=pointer]:
|
|
308
|
+
- /url: https://sentry.io/for/python/?utm_source=pypi&utm_medium=paid-community&utm_campaign=python-na-evergreen&utm_content=static-ad-pypi-sponsor-learnmore
|
|
309
|
+
- generic [ref=f7e252]: Sentry
|
|
310
|
+
- generic [ref=f7e253]: Error logging
|
|
311
|
+
- link "StatusPage Status page" [ref=f7e254] [cursor=pointer]:
|
|
312
|
+
- /url: https://statuspage.io
|
|
313
|
+
- generic [ref=f7e255]: StatusPage
|
|
314
|
+
- generic [ref=f7e256]: Status page
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cabildo"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "IPC bus + chat room for coordinating parallel Claude Code agents: messaging, presence, and advisory locks with hook-level enforcement"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Jorge Eduardo Cardona Gaviria", email = "jorgeecardona@gmail.com" },
|
|
11
|
+
]
|
|
12
|
+
keywords = ["claude-code", "agents", "mcp", "ipc", "coordination", "multi-agent"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Operating System :: POSIX :: Linux",
|
|
18
|
+
"Operating System :: MacOS",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Software Development :: Libraries",
|
|
24
|
+
"Topic :: Communications :: Chat",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"mcp>=1.2",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = ["pytest>=8.0"]
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
cabildo = "cabildo.cli:main"
|
|
35
|
+
cabildo-mcp = "cabildo.mcp_server:main"
|
|
36
|
+
cabildo-hook = "cabildo.hooks.cli:main"
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
testpaths = ["tests"]
|
|
40
|
+
addopts = "-q"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["hatchling"]
|
|
44
|
+
build-backend = "hatchling.build"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/cabildo"]
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.sdist]
|
|
50
|
+
exclude = [".claude", ".gitignore"]
|