margin 0.2.1__py3-none-any.whl
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.
- margin/__init__.py +0 -0
- margin/batch.py +41 -0
- margin/claude.py +89 -0
- margin/cli.py +294 -0
- margin/config.py +188 -0
- margin/fold.py +750 -0
- margin/images.py +66 -0
- margin/inbox.py +335 -0
- margin/outline.py +282 -0
- margin/patches.py +243 -0
- margin/pinch_check.py +363 -0
- margin/skills/margin/SKILL.md +95 -0
- margin/store.py +186 -0
- margin/sync.py +666 -0
- margin-0.2.1.dist-info/METADATA +113 -0
- margin-0.2.1.dist-info/RECORD +20 -0
- margin-0.2.1.dist-info/WHEEL +5 -0
- margin-0.2.1.dist-info/entry_points.txt +2 -0
- margin-0.2.1.dist-info/licenses/LICENSE +21 -0
- margin-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: margin
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Publish a repo's markdown to a self-hosted Outline, keep comments anchored, and bring each new comment back to an AI agent in batches
|
|
5
|
+
Author: Konstantinos Georgiou
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/drkostas/margin
|
|
8
|
+
Keywords: outline,markdown,comments,claude,claude-code,agents,documentation
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Topic :: Documentation
|
|
11
|
+
Classifier: Topic :: Utilities
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# margin
|
|
20
|
+
|
|
21
|
+
margin publishes the markdown files of your repositories to a self-hosted [Outline](https://github.com/outline/outline) wiki, and brings every comment you leave there back to an AI agent such as Claude Code. You read your docs in a browser or on your phone, select a sentence and comment on it, and the agent answers in the thread and changes the file.
|
|
22
|
+
|
|
23
|
+
Each repository becomes one Outline collection. Folders become parent documents, and a folder's `README.md` is that folder's page.
|
|
24
|
+
|
|
25
|
+
## What it does
|
|
26
|
+
|
|
27
|
+
- **Publishes changes as small patches.** A comment in Outline is attached to a piece of text. Replacing a whole document through Outline's API removes every one of those attachments, so margin never does that. It works out the smallest changes, applies them one at a time, and checks each result against what it expected.
|
|
28
|
+
- **Publishes the pictures a file shows.** A local image referenced by a relative path (``) is uploaded once as an attachment of its document, so everyone who can read the document sees it. The file keeps its relative path. A changed picture counts as a changed file.
|
|
29
|
+
- **Never settles a disagreement by "newest wins".** margin keeps both sides' text from the last sync. If only the file changed, it patches Outline. If someone edited the document in Outline, or both changed, it leaves both alone and reports the document.
|
|
30
|
+
- **Collects comments two ways.** Outline sends a signed webhook for each new comment. A catch-up poll every few minutes finds anything the webhook missed, for example while the machine was asleep. A comment that arrives both ways is recorded once.
|
|
31
|
+
- **Sends comments in batches.** A burst of comments becomes one message. A batch is delivered after a quiet period (3 minutes by default), never later than a cap after its first comment (15 minutes), and at once when a comment mentions the agent's user.
|
|
32
|
+
- **Keeps the agent out of its own inbox.** Comments written by the agent's user are never recorded, so its replies cannot wake it.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install margin
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
margin needs Python 3.11 or newer and has no dependencies outside the standard library. You also need an Outline server that you can reach, with two users, yourself and the agent. [`examples/outline`](examples/outline) has a Docker Compose setup for Outline, Redis and a small sign-in provider.
|
|
41
|
+
|
|
42
|
+
## Configure
|
|
43
|
+
|
|
44
|
+
margin reads `~/.config/margin/config.toml`. Secrets are never written in it. Each secret is the output of a command, for example a password manager or the macOS keychain.
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
[outline]
|
|
48
|
+
url = "https://docs.example.com"
|
|
49
|
+
api_key_command = ["pass", "outline/agent-api-key"] # the agent's key, a normal member
|
|
50
|
+
admin_key_command = ["pass", "outline/admin-api-key"] # used only to create the webhook
|
|
51
|
+
|
|
52
|
+
[intake]
|
|
53
|
+
listen = "127.0.0.1:8771"
|
|
54
|
+
webhook_url = "http://host.docker.internal:8771/outline" # the address Outline posts to
|
|
55
|
+
secret_command = ["pass", "outline/webhook-secret"]
|
|
56
|
+
poll_seconds = 600
|
|
57
|
+
|
|
58
|
+
[batch]
|
|
59
|
+
quiet_seconds = 180
|
|
60
|
+
cap_seconds = 900
|
|
61
|
+
|
|
62
|
+
[notify]
|
|
63
|
+
command = ["my-notifier", "{route}"] # receives each batch as JSON on stdin; {text}, {repo} and {repo_path} are filled in
|
|
64
|
+
|
|
65
|
+
[[repo]]
|
|
66
|
+
name = "my-project" # the Outline collection
|
|
67
|
+
path = "~/code/my-project"
|
|
68
|
+
exclude = ["vendor/**"]
|
|
69
|
+
route = "my-project" # passed to the notifier as {route}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
When `[notify]` has no command, margin prints each batch.
|
|
73
|
+
|
|
74
|
+
Outline refuses to send webhooks to private addresses. If margin runs on the same machine as an Outline container, add the container's host gateway address to Outline's `ALLOWED_PRIVATE_IP_ADDRESSES` setting.
|
|
75
|
+
|
|
76
|
+
## Use
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
margin sync # publish what changed, in every configured repo
|
|
80
|
+
margin sync my-project --full
|
|
81
|
+
margin serve # webhook receiver, catch-up poll, batching and a sync every minute
|
|
82
|
+
margin status # documents that need attention, comments waiting
|
|
83
|
+
margin reply <comment-id> "Changed to five minutes, see the Lifetime section."
|
|
84
|
+
margin resolve <comment-id>
|
|
85
|
+
margin thread <comment-id>
|
|
86
|
+
margin link docs/plan.md # publish these files now and print a Markdown link to each
|
|
87
|
+
margin setup-claude # link the skill, register Outline's MCP, deny its destructive tools
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
A batch looks like this.
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
2 new comment(s) in Outline.
|
|
94
|
+
|
|
95
|
+
### Cache design
|
|
96
|
+
my-project/docs/cache.md https://docs.example.com/doc/cache-design-pbS4AcgGK0
|
|
97
|
+
- [393bdced-...] Alex on "least recently used first":
|
|
98
|
+
Please also say what happens when memory is full.
|
|
99
|
+
- [819aea0e-...] Alex on "The cache holds entries":
|
|
100
|
+
Is ten minutes measured from write or from last read?
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The notifier also receives every comment as structured JSON, with the document's title, link and repository path.
|
|
104
|
+
|
|
105
|
+
## Limits
|
|
106
|
+
|
|
107
|
+
- A comment attached to text that the agent then rewrites loses its attachment. The thread stays, with no highlighted text. margin keeps its patches small so that this happens only to text that really changed.
|
|
108
|
+
- Outline re-parses a whole list or quote when a patch has to cross into the next block. Comments inside that block lose their attachment the same way. New content at the end of a document is appended and touches nothing.
|
|
109
|
+
- Edits made in Outline are reported, not copied back to the file. That direction is planned.
|
|
110
|
+
|
|
111
|
+
## Licence
|
|
112
|
+
|
|
113
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
margin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
margin/batch.py,sha256=el8bHtPY0TPz1xllKcaqy7_SgXKijpvtEx2jMSDXnoE,1748
|
|
3
|
+
margin/claude.py,sha256=gZhRE07r9wxKO7LQZQwrvAaEFZfZBlJBcbT5e_0HxcU,3577
|
|
4
|
+
margin/cli.py,sha256=6I-9jZ1ZqhmaX34-TNsmzRrCkoCcGAUGEz-Y1gMqtVQ,13788
|
|
5
|
+
margin/config.py,sha256=ijQ98rl4KycYYIHGxKmRpQ77oUmxt3AKrnSSswl1_R4,8389
|
|
6
|
+
margin/fold.py,sha256=QWwMOzw6T0EFSi8z8Pf78fUjt32DoJhMa2UGepgv-BI,38426
|
|
7
|
+
margin/images.py,sha256=A9c8dqQlAOA4ycV8PU_t6S2rfkb3BZvYkBpJrw-uq4E,3156
|
|
8
|
+
margin/inbox.py,sha256=WeIakbR5wvj9BYmCFt3r2oleuVFVV_AmBgwboYvDa4Y,15312
|
|
9
|
+
margin/outline.py,sha256=85sUXQXBSBwp_hTs_Pkh0w0r-HI3xI7pSwfyKlp4dro,12232
|
|
10
|
+
margin/patches.py,sha256=4GSqKevQkJM6nf-Xi2_rffmqGp-cqPsEOyQSIWeMebk,11837
|
|
11
|
+
margin/pinch_check.py,sha256=zij6y4TpAvalMkbfG5nK8KkadfYPBszXMHiiJ_9vRJ4,18177
|
|
12
|
+
margin/store.py,sha256=B6Ozo5oJZXsI6r7fw5NcdwH7UYPiXLPul13lLhApJa0,8757
|
|
13
|
+
margin/sync.py,sha256=ocPWRPkU8oPSQy_njxsRFxnLhJpqLMWyYsWPgtgRe2o,35710
|
|
14
|
+
margin/skills/margin/SKILL.md,sha256=eXvX5l-SBVsbG7im5AZEX_GiYRlJMZlrv3-V-dv03gE,6279
|
|
15
|
+
margin-0.2.1.dist-info/licenses/LICENSE,sha256=y2yT4XOJ5hjMzENqKJShtqLiwI-IE1PnWqMxbhhL5r8,1078
|
|
16
|
+
margin-0.2.1.dist-info/METADATA,sha256=8rMHTJfTNu0PdY8h1OxbyjiNh0FNqK2epp-wGF6dpbM,5995
|
|
17
|
+
margin-0.2.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
18
|
+
margin-0.2.1.dist-info/entry_points.txt,sha256=-5w3KEPp-au9A_bOPxtQGR67B9egA10Me2jOSRbXoDs,43
|
|
19
|
+
margin-0.2.1.dist-info/top_level.txt,sha256=u21cuPWaJENEWAYmCFNkRU86jsBUCoVDmgkRuy2U1SY,7
|
|
20
|
+
margin-0.2.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Konstantinos Georgiou
|
|
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 @@
|
|
|
1
|
+
margin
|