@sinoia/hubdoc-tools 1.11.0 → 1.13.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/cli.js +1257 -880
- package/docs/llm-usage.md +104 -0
- package/package.json +1 -1
package/docs/llm-usage.md
CHANGED
|
@@ -408,6 +408,110 @@ without going through a workspace-scoped ACL. Example:
|
|
|
408
408
|
`hubdoc permissions grant --to <user-id> --to-type user --on <project-id>
|
|
409
409
|
--on-type project --level admin`.
|
|
410
410
|
|
|
411
|
+
### Concurrent markdown editing (`hubdoc md`, corex#594)
|
|
412
|
+
|
|
413
|
+
`hubdoc md` exposes the corex `Tools::Documents::MarkdownEditor` (the
|
|
414
|
+
same tool internal Ruby agents use) through a single HTTP endpoint
|
|
415
|
+
(`POST /api/v1/documents/files/:id/markdown_editor`). Eleven surgical
|
|
416
|
+
actions, one HTTP round-trip each:
|
|
417
|
+
|
|
418
|
+
- **Discovery** : `structure` (outline + line numbers), `stats` (word
|
|
419
|
+
counts, section sizes, TODOs), `search --pattern <regex> [--context N]`.
|
|
420
|
+
- **Read** : `read [--from-line N --to-line M | --offset N --limit M]`,
|
|
421
|
+
`read-section --heading "…" [--level N] [--index N]`.
|
|
422
|
+
- **Write** : `write-section --heading "…" (--content <text> |
|
|
423
|
+
--content-file <path>)`, `append`, `insert (--at-line N | --at-offset
|
|
424
|
+
N)`, `delete (--from-line/--to-line | --from-offset/--to-offset)`,
|
|
425
|
+
`replace --find "…" (--replace-with "…" | --replace-with-file <path>)
|
|
426
|
+
[--all]`.
|
|
427
|
+
- **Save** : `save [--create-version] [--version-comment "…"]` — the
|
|
428
|
+
content is auto-saved by every write; `save` is just for the explicit
|
|
429
|
+
version snapshot.
|
|
430
|
+
|
|
431
|
+
**Concurrency & merge — handled server-side, no client state** :
|
|
432
|
+
|
|
433
|
+
Each write action executes on the server as a single load-blob → mutate
|
|
434
|
+
→ save-blob → `publish Documents::FileContentUpdated` cycle. Two agents
|
|
435
|
+
running `hubdoc md insert` in parallel serialise at the Rails +
|
|
436
|
+
ActiveStorage level; the second one loads the first one's result before
|
|
437
|
+
applying its own. No client-side coordination or CRDT to maintain.
|
|
438
|
+
|
|
439
|
+
Cross-modal coordination (agent + human on the web UI): every write
|
|
440
|
+
broadcasts `content_refresh` over `DocumentEditingChannel`, which the
|
|
441
|
+
frontend's Y.js session reloads from server content. Cf. the corex
|
|
442
|
+
design `.claude/context/designs/y-rb-implementation.md`.
|
|
443
|
+
|
|
444
|
+
**Inter-agent coordination via events** :
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
# Agent B watches for edits on the file, reacts.
|
|
448
|
+
hubdoc events watch \
|
|
449
|
+
--type Documents::FileContentUpdated \
|
|
450
|
+
--timeout 900 --json \
|
|
451
|
+
| jq -r '.items[-1].data.file_id'
|
|
452
|
+
# → returns as soon as another agent (or the human) touches the file.
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**Typical agent workflow — build a section incrementally without racing** :
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# 1. Discover current structure (10 KB, no side effects).
|
|
459
|
+
hubdoc md structure "$FID" --json
|
|
460
|
+
|
|
461
|
+
# 2. Read just the section we care about.
|
|
462
|
+
hubdoc md read-section "$FID" --heading "Analyse" --json > current.md
|
|
463
|
+
|
|
464
|
+
# 3. Regenerate its body locally, push atomically.
|
|
465
|
+
llm-generate-analysis > new-analysis.md
|
|
466
|
+
hubdoc md write-section "$FID" --heading "Analyse" \
|
|
467
|
+
--content-file new-analysis.md --json
|
|
468
|
+
|
|
469
|
+
# 4. Snapshot when done.
|
|
470
|
+
hubdoc md save "$FID" --create-version --version-comment "Analyse v2" --json
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Events — react to what's happening (corex#593)
|
|
474
|
+
|
|
475
|
+
`hubdoc events ls` and `hubdoc events watch` sit on top of the Rails
|
|
476
|
+
Event Store stream. Every mutation done by `projects/sprints/tickets/
|
|
477
|
+
tasks` (create, update, status_changed, moved, deliverable_attached,
|
|
478
|
+
channel_message_posted, …) is published as a typed event; the CLI lets
|
|
479
|
+
an agent either **poll** the stream once (`ls`) or **block** until
|
|
480
|
+
something happens (`watch`).
|
|
481
|
+
|
|
482
|
+
- **Type aliases** (agent-friendly): `ticket.created | ticket.updated |
|
|
483
|
+
ticket.status_changed | ticket.assigned | ticket.blocked |
|
|
484
|
+
ticket.escalated | ticket.failed | ticket.moved | ticket.comment |
|
|
485
|
+
ticket.deliverable | ticket.deleted | sprint.{created,started,updated,
|
|
486
|
+
completed,deleted} | project.{created,updated,started,completed,
|
|
487
|
+
archived,deleted} | task.{created,updated,completed,deleted}`. Full RES
|
|
488
|
+
class names (`Projects::Tickets::TicketStatusChanged`) are also
|
|
489
|
+
accepted verbatim.
|
|
490
|
+
- **Cursor**: each `ls` response includes `next_cursor` — pass it as
|
|
491
|
+
`--since` on the next call to resume. `watch` re-uses the same `since`
|
|
492
|
+
across empty polls (never skips an event that arrives mid-poll).
|
|
493
|
+
- **Timeout**: `watch --timeout <s>` (default 60) — exit code 2 with a
|
|
494
|
+
`{ ok:false, reason:"timeout", waited_ms, since }` envelope on stderr
|
|
495
|
+
when the window expires. This is distinct from network errors (exit 1).
|
|
496
|
+
- **Poll cadence**: `watch --poll-interval-ms <n>` (default 1000, min
|
|
497
|
+
100). Kept short so the perceived latency stays low; harmless because
|
|
498
|
+
each poll is a lightweight bare-array read.
|
|
499
|
+
|
|
500
|
+
Two typical patterns:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
# 1) Wait for the dispatched agent to finish before moving on.
|
|
504
|
+
hubdoc events watch --project "$PID" --ticket "$TID" \
|
|
505
|
+
--type ticket.status_changed --timeout 900 --json \
|
|
506
|
+
| jq -r '.items[-1].data.new_status' # → done / failed / blocked
|
|
507
|
+
# (returns as soon as the first matching event arrives)
|
|
508
|
+
|
|
509
|
+
# 2) Loop over the day's activity from a saved checkpoint.
|
|
510
|
+
CURSOR=$(cat .last-cursor 2>/dev/null || echo "")
|
|
511
|
+
hubdoc events ls --project "$PID" --since "$CURSOR" --limit 200 --json \
|
|
512
|
+
| tee day-batch.json | jq -r '.next_cursor' > .last-cursor
|
|
513
|
+
```
|
|
514
|
+
|
|
411
515
|
`hubdoc tasks --project=<pid> --ticket=<tid>` — `ls | show | create |
|
|
412
516
|
update | delete | complete | reorder`. Sub-tasks live under a ticket;
|
|
413
517
|
both parents are required on every call.
|