agentic-comments 0.1.0 → 0.2.0

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.
package/README.md CHANGED
@@ -6,12 +6,13 @@
6
6
 
7
7
  Comment threads and suggestions anchored to any content, from people and agents.
8
8
 
9
- > Status: first release, 0.1.0. Threads, suggestions, and anchor survival work. The CLI, the MCP
10
- > server, and notifications come next.
9
+ > Status: see [npm](https://www.npmjs.com/package/agentic-comments) for the latest released version. The
10
+ > `main` branch can have changes that npm does not have yet.
11
11
 
12
12
  ## Install
13
13
 
14
- Requires Node.js 20 or later. The quick start imports `yjs`, so install it with this package:
14
+ Requires Node.js 20 or later. `yjs` is a peer dependency. Your app and this package must use the same
15
+ copy of `yjs`, because the package checks the Yjs types of your document. Install both:
15
16
 
16
17
  ```sh
17
18
  npm install agentic-comments yjs
@@ -126,6 +127,92 @@ suggestions.accept(s.id, ada);
126
127
  record is, and it can win settle, so every replica undoes the real edit. The sync layer must decide who
127
128
  may write.
128
129
 
130
+ ## CLI and MCP server
131
+
132
+ Both run on a local store: one file that holds a Yjs document. The text is in the `text` root, and
133
+ non-text values are in the `values` map. A file starts with a magic mark and a hash, so a damaged or zero-filled file is refused and not read as empty. A write
134
+ takes a lock file, saves with a rename, and runs again if the file changed meanwhile, so parallel processes
135
+ on one machine lose nothing. A full disk or a file size limit fails the write and leaves the old file. A lock left by a dead process, or by this process under another mark, is cleared. So is an empty lock older
136
+ than two seconds and any lock older than a minute. The store is for one
137
+ machine. It cannot check a lock held in another container or on another machine. Two containers that
138
+ share a volume can have the same process id, and each then clears the other's lock, so they can lose writes. A save that
139
+ would pass 50 MB is refused.
140
+
141
+ If the version you install has no `agentic-comments` command, build from source (see [Develop](#develop)).
142
+ In the cloned repo, run `npm run build` and use `node dist/cli.js` where these examples say
143
+ `npx agentic-comments`.
144
+
145
+ ```sh
146
+ export AGENTIC_COMMENTS_FILE=./comments.ydoc
147
+ export AGENTIC_COMMENTS_AS=human:ada # kind:name. The operator sets this.
148
+ npx agentic-comments init --text "Hello brave world"
149
+ npx agentic-comments comment --text-quote brave --body "Word choice? @scribe"
150
+ npx agentic-comments list --status open
151
+ AGENTIC_COMMENTS_AS=agent_of_human:scribe npx agentic-comments suggest --text-quote brave --replace bold
152
+ npx agentic-comments accept <id>
153
+ ```
154
+
155
+ - Commands: `init`, `list`, `show`, `comment`, `reply`, `resolve`, `reopen`, `relocate`, `suggestions`,
156
+ `suggest`, `accept`, `reject`, `mcp`. Run `agentic-comments --help` for flags.
157
+ - Every result is JSON. An error is JSON on stderr with a `code`, and the exit code is 1. A usage error
158
+ exits 2.
159
+ - The principal comes from `--as` or `AGENTIC_COMMENTS_AS`. Scopes come only from
160
+ `AGENTIC_COMMENTS_SCOPES`, a comma list such as `comments:accept`. No flag takes a scope or an author,
161
+ and a flag that does not apply to a command is refused. `AGENTIC_COMMENTS_LOCK_TIMEOUT_MS` sets the wait
162
+ for a lock, in whole milliseconds.
163
+ - `npx agentic-comments mcp` (or the `agentic-comments-mcp` binary, which reads the same environment) starts
164
+ the MCP server on stdio. The operator fixes the principal and the
165
+ scopes when the server starts. No tool takes an author or a scope. A call with any key a tool does not list is refused, so an agent
166
+ cannot pick its own kind or rights. Tools: `list_threads`, `get_thread`, `add_comment`, `reply_to_thread`, `resolve_thread`,
167
+ `reopen_thread`, `relocate_thread`, `list_suggestions`, `suggest_change`, `accept_suggestion`,
168
+ `reject_suggestion`.
169
+ - A text change is `{"replace": "new text"}`. A change for a cell, slide, or canvas anchor is
170
+ `{"value": <JSON>}`.
171
+ - From code: `import { FileStore, CommentsService, operatorActor } from "agentic-comments/local"`.
172
+
173
+ ## Notifications
174
+
175
+ `Notifications` turns changes into events that your app can send to a notifier. It watches the data, so it
176
+ sees changes from other replicas too. It replays nothing from before you created it. A first load into an
177
+ empty document looks like new changes, so create the stream with `muted: true` and call `unmute()` after
178
+ that load.
179
+
180
+ ```ts
181
+ import { Notifications } from "agentic-comments";
182
+
183
+ const notifications = new Notifications(comments, suggestions, { muted: true, onError: (error, source) => report(error, source) });
184
+ // ...load the document or connect the provider, then:
185
+ notifications.unmute();
186
+ const stop = notifications.subscribe(async (event) => {
187
+ if (event.type === "mention") await notify(event.mention, event.threadId); // fetch the text with the ids
188
+ });
189
+ ```
190
+
191
+ - Events: `thread.created`, `message.added`, `mention` (one per handle), `thread.resolved`,
192
+ `thread.reopened`, `suggestion.created`, `suggestion.accepted`, `suggestion.rejected`.
193
+ - An event holds ids, the actor, `machineMade`, the anchor type, `source` (`local` or `remote`), and a time.
194
+ It never holds the text of a comment, a suggestion, or a change. Fetch that with the ids.
195
+ - The same change has the same `id` on every replica. So a notifier that must
196
+ send once keeps the ids it has sent. Within one replica, an id is delivered at most once, and a listener
197
+ that throws is not retried. A replica remembers its last 10,000 ids (`seenLimit`) and drops a
198
+ repeat within that window. Two accepts at once give one id, and the actor can differ between replicas. A `suggestion.rejected` event
199
+ can be overturned: if another replica accepted at the same time, the merge ends as accepted, and a
200
+ `suggestion.accepted` event follows.
201
+ An id for a status change includes the clock time, so a clock coarser than the changes can merge two.
202
+ - Use `source` to decide where to send. If every replica runs a notifier, filter on `local`, or each
203
+ replica sends its own copy of every remote change.
204
+ - Events are delivered after the Yjs transaction that caused them has finished, so a listener can write to
205
+ the document. A listener that throws, or returns a rejected promise, cannot break a write or another
206
+ listener. The
207
+ error goes to `onError`, or to `errors()`.
208
+ - Write a thread in one Yjs transaction, as `add` does. A thread whose map arrives empty and gets its fields
209
+ and messages in one later transaction is treated as created then. A thread built in more steps than that
210
+ can make no event.
211
+ - A peer that deletes a thread or a suggestion and adds it back under the same id makes a `created` event
212
+ again once the id has left the `seenLimit` window.
213
+ - A change that arrives as a whole thread, with its replies and status inside, makes an event for each
214
+ message and for a resolved status. A reopen inside such a thread makes none. A message past the thread's read limit makes no event.
215
+
129
216
  ## Author kinds
130
217
 
131
218
  | Kind | Meaning | Label |
@@ -138,7 +225,7 @@ suggestions.accept(s.id, ada);
138
225
 
139
226
  ## Limits
140
227
 
141
- - No file store, CLI, MCP server, or HTTP API yet.
228
+ - No HTTP API yet. The file store is for one machine. It is not a sync server.
142
229
  - `add` checks that a text anchor points into this document and matches its quote.
143
230
  - The library trusts the host to call `stamp()` only for a principal it authenticated.
144
231
  - A reply to a resolved thread is refused on the replica that sees the resolve. A reply and a
@@ -149,6 +236,14 @@ suggestions.accept(s.id, ada);
149
236
  on every read, so keep it fast.
150
237
  - A local reply stops at 1,000 messages. Replicas that reply at once can pass that. A read shows the first
151
238
  2,000 valid messages. `skipped` and `problems()` report the rest.
239
+ - If several processes clear one stale lock at the same moment, two writes can very rarely overlap. The
240
+ save check catches almost every such case. A symlink as the store path is followed. A filesystem with no
241
+ hard links makes `init` use a check and a rename, which can race.
242
+ - The CLI trusts its caller: a process that can set `AGENTIC_COMMENTS_AS` and `AGENTIC_COMMENTS_SCOPES` can
243
+ act as anyone. Give an agent the MCP server, where the operator fixes both. Do not give it a shell with
244
+ the store file and the CLI.
245
+ - Events come from the data and not from the calls, so a status change that two replicas make at once gives
246
+ each replica its own event, and a thread that arrives whole gives events for its current state only.
152
247
  - Every replica of a document must register the same custom anchor kinds.
153
248
  - A repeated message id hides every copy. A peer that appends a copy of a thread's only message makes
154
249
  the thread unreadable.
package/SKILL.md CHANGED
@@ -25,7 +25,8 @@ Check `thread.anchorState`. An `orphaned` thread has lost its content. Read it,
25
25
  Suggest a change with `suggestions.add({ anchor, change, summary }, actor)`. Accept needs a person or
26
26
  the `comments:accept` scope. You never accept or reject your own suggestion.
27
27
 
28
- The CLI and the MCP server are not built yet.
28
+ Over MCP, call `list_threads`, `add_comment`, `reply_to_thread`, `resolve_thread`, `suggest_change`,
29
+ `accept_suggestion`, and `reject_suggestion`. You never pass an author or a scope.
29
30
 
30
31
  ## Rules
31
32