Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ User says any of:
```
Monitor on path: <dir>/feedback/inbox.jsonl
```
Do NOT poll — let the Monitor notification arrive.
Do NOT poll — let the Monitor notification arrive. The Monitor is a
convenience, not the source of truth: it only catches comments that arrive
while it is running. To see every open comment at any time (including ones
sent while no session was watching), ask the server:
```
curl -s http://localhost:<port>/pending
```
It returns the inbox comments that no history change has answered yet.

## Responding to a feedback batch

Expand Down Expand Up @@ -71,10 +78,9 @@ When a new batch arrives in `inbox.jsonl`:
## On startup in a directory that already has feedback

If you find `<dir>/feedback/inbox.jsonl` and `<dir>/feedback/history.json` and the skill has been invoked in this session:
1. Scan inbox for comment ids.
2. Scan history's `changes[*].in_response_to` union — those are already processed.
3. If unprocessed comments exist, tell the user the count and ask whether to process now.
4. Either way, set up the Monitor on the inbox.
1. Ask the server for the open comments: `curl -s http://localhost:<port>/pending`. It lists every inbox comment that no history change has answered yet. (Equivalent by hand: inbox comment ids minus the union of history's `changes[*].in_response_to`.)
2. If there are open comments, tell the user the count and ask whether to process them now.
3. Either way, set up the Monitor on the inbox for new ones.

## Stop flow (user wants to kill the server)

Expand Down
42 changes: 42 additions & 0 deletions lib/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,53 @@ def do_GET(self):
}
self._json(200, info)
return
if parsed.path == "/pending":
# Comments in the inbox that no history change has answered yet.
# Lets an agent pull the open feedback at any time, even when no
# live Monitor saw it arrive (new session, server restarted, etc.).
self._json(200, {"pending": self._pending_comments()})
return
if parsed.path.startswith("/lib/"):
self._serve_from_lib(parsed.path[len("/lib/"):])
return
super().do_GET()

def _pending_comments(self):
"""Inbox comments whose id is not in any history in_response_to."""
inbox = self.feedback_dir / "inbox.jsonl"
history = self.feedback_dir / "history.json"

answered = set()
if history.exists():
try:
for batch in json.loads(history.read_text() or "[]"):
for ch in batch.get("changes", []):
for cid in ch.get("in_response_to", []):
answered.add(cid)
except (json.JSONDecodeError, AttributeError):
pass

pending = []
if inbox.exists():
for line in inbox.read_text().splitlines():
line = line.strip()
if not line:
continue
try:
batch = json.loads(line)
except json.JSONDecodeError:
continue
for c in batch.get("comments", []):
if c.get("id") and c["id"] not in answered:
pending.append({
"id": c["id"],
"comment": c.get("comment", ""),
"type": c.get("type"),
"page_url": batch.get("page_url"),
"submitted_at": batch.get("submitted_at"),
})
return pending

def _serve_from_lib(self, rel: str):
# Path-traversal-safe lookup inside LIB_DIR
try:
Expand Down