@composer-app/mcp 0.0.1-beta.3 → 0.0.1-beta.5
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/dist/{chunk-VVYEIOFH.js → chunk-A5KBJAJW.js} +636 -136
- package/dist/cli.js +1 -1
- package/dist/mcp.js +11 -3
- package/package.json +1 -1
- package/skill/SKILL.md +284 -28
package/dist/cli.js
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
__test_clearRooms,
|
|
3
|
+
__test_dispatch,
|
|
4
|
+
__test_setRoom,
|
|
2
5
|
startMcpHttpServer,
|
|
3
|
-
startMcpServer
|
|
4
|
-
|
|
6
|
+
startMcpServer,
|
|
7
|
+
teardownAllRooms
|
|
8
|
+
} from "./chunk-A5KBJAJW.js";
|
|
5
9
|
export {
|
|
10
|
+
__test_clearRooms,
|
|
11
|
+
__test_dispatch,
|
|
12
|
+
__test_setRoom,
|
|
6
13
|
startMcpHttpServer,
|
|
7
|
-
startMcpServer
|
|
14
|
+
startMcpServer,
|
|
15
|
+
teardownAllRooms
|
|
8
16
|
};
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -12,8 +12,38 @@ to create, join, monitor, or act in a Composer doc.
|
|
|
12
12
|
|
|
13
13
|
### 1. Create
|
|
14
14
|
Triggers: "send this markdown to Composer", "make a Composer doc with this".
|
|
15
|
-
Action: call `composer_create_room({
|
|
16
|
-
|
|
15
|
+
Action: call `composer_create_room({ ... })`.
|
|
16
|
+
|
|
17
|
+
**First run only — ask the user what to call you.** If you have no saved
|
|
18
|
+
name on this machine, the MCP returns an error instructing you to stop
|
|
19
|
+
and ask. Offer one suggested default they can accept with a tap:
|
|
20
|
+
|
|
21
|
+
- If you know the user's first name, suggest `"<FirstName>'s Agent"`
|
|
22
|
+
(e.g. `"Josh's Agent"`).
|
|
23
|
+
- Otherwise suggest something playful that isn't a model family — `Monty`,
|
|
24
|
+
`Gerty`, `Rosie`, `Otto`, `Pip`. Do **not** suggest Claude, Gemini,
|
|
25
|
+
Sonnet, Opus, Haiku, GPT, or any other model name.
|
|
26
|
+
|
|
27
|
+
Phrase it like: *"I'll go by Monty in Composer docs — sound good, or pick
|
|
28
|
+
your own?"* Retry the tool call with their answer as `actingAs`. It
|
|
29
|
+
persists to `~/.composer/user.json` and is reused forever.
|
|
30
|
+
|
|
31
|
+
**On success** (first run or any subsequent run), the return gives you
|
|
32
|
+
two ordered steps — the field names encode the order:
|
|
33
|
+
|
|
34
|
+
1. `step1_sayToUser` — output this FIRST. It always starts with the
|
|
35
|
+
`browserUrl` because the user needs the link to open the doc; it
|
|
36
|
+
also carries the `@<your-name>` tagging hint. Relay it; you can
|
|
37
|
+
paraphrase lightly but do not drop the URL or the mention syntax.
|
|
38
|
+
2. `step2_callTool` — a structured `{ tool, args, why }` directive for
|
|
39
|
+
the `composer_next_event` loop. **Do not run it inline.** Hand the
|
|
40
|
+
loop to a background subagent (see "Monitor — runs in a subagent"
|
|
41
|
+
below). End your turn once the subagent is spawned.
|
|
42
|
+
|
|
43
|
+
Skipping step 1 leaves the user without the URL — they have no way into
|
|
44
|
+
the doc. Skipping the subagent spawn leaves the room attached but
|
|
45
|
+
silent; saying "I'm monitoring" without spawning the loop is a lie,
|
|
46
|
+
every mention gets missed.
|
|
17
47
|
|
|
18
48
|
**Seeding — prefer a file path when one exists.** Pick exactly one:
|
|
19
49
|
|
|
@@ -32,15 +62,76 @@ to sync changes back.
|
|
|
32
62
|
|
|
33
63
|
### 2. Join
|
|
34
64
|
Triggers: a share prompt with a Composer URL, "/composer join <url>".
|
|
35
|
-
Action: extract the URL
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
Action: extract the URL from the prompt and call `composer_join_room({ url })`.
|
|
66
|
+
Same first-run rule as Create. On success, the return carries the same
|
|
67
|
+
ordered pair: output `step1_sayToUser` first (confirms the URL the user
|
|
68
|
+
just joined), then spawn the monitor subagent — same flow as Create
|
|
69
|
+
(see "Monitor — runs in a subagent" below).
|
|
70
|
+
|
|
71
|
+
### 3. Monitor — runs in a subagent
|
|
72
|
+
|
|
73
|
+
Triggers: "watch this doc", or automatically after create/join.
|
|
74
|
+
|
|
75
|
+
The monitor loop is delegated to a background subagent. Polling
|
|
76
|
+
`composer_next_event` from the main thread fills the conversation
|
|
77
|
+
context with idle-tick chatter and mention-handling that belongs in
|
|
78
|
+
the doc, not the terminal. Spawn the subagent, end your turn, let the
|
|
79
|
+
doc be the conversation.
|
|
80
|
+
|
|
81
|
+
**How to spawn (Claude Code).** Use the `Agent` tool with:
|
|
82
|
+
|
|
83
|
+
- `subagent_type: "general-purpose"`
|
|
84
|
+
- `run_in_background: true`
|
|
85
|
+
- `description: "Composer monitor: <roomId>"` — short, identifies the room
|
|
86
|
+
- `prompt`: the template below, with `{roomId}` and `{actingAs}` filled in
|
|
87
|
+
|
|
88
|
+
Prompt template:
|
|
89
|
+
|
|
90
|
+
> Invoke the composer skill, then run the monitor loop for room
|
|
91
|
+
> `{roomId}` as `{actingAs}`. Your only job is the in-doc conversation:
|
|
92
|
+
> call `composer_next_event({ roomId: "{roomId}" })` and follow each
|
|
93
|
+
> return's `requiredNextToolCall` directive verbatim, looping until the
|
|
94
|
+
> goodbye branch fires.
|
|
95
|
+
>
|
|
96
|
+
> All write tools (`composer_add_comment`, `composer_add_suggestion`,
|
|
97
|
+
> `composer_reply_comment`, `composer_reply_suggestion`,
|
|
98
|
+
> `composer_resolve_thread`) are yours to use as the skill describes.
|
|
99
|
+
> The doc IS your conversation — do not narrate to the parent between
|
|
100
|
+
> ticks.
|
|
101
|
+
>
|
|
102
|
+
> Exit and return when ANY of these happen:
|
|
103
|
+
> 1. `composer_next_event` returns `kind: "timeout"` with
|
|
104
|
+
> `recentActivity: false` — say `userMessage` EXACTLY in the doc
|
|
105
|
+
> (it's the goodbye line), then exit.
|
|
106
|
+
> 2. A request inside the doc clearly needs the parent terminal
|
|
107
|
+
> (a code change, a shell command, an external action the parent
|
|
108
|
+
> would do). Post a short reply: "I'll get on this in the terminal,"
|
|
109
|
+
> then exit with a one-sentence summary of the ask.
|
|
110
|
+
> 3. An unrecoverable error (auth lost, room destroyed). Exit with
|
|
111
|
+
> the error.
|
|
112
|
+
|
|
113
|
+
**Main thread after spawning.** Turn ends. Do **not** also call
|
|
114
|
+
`composer_next_event` — two listeners on the same room means duplicated
|
|
115
|
+
replies. If the user asks "what's happening in Composer?", check the
|
|
116
|
+
subagent's status (`TaskList` / `TaskOutput`) rather than re-entering
|
|
117
|
+
the loop yourself.
|
|
118
|
+
|
|
119
|
+
**Inside the loop (what the subagent does).** Default timeout is 30
|
|
120
|
+
seconds. Every return carries a structured directive — follow it
|
|
121
|
+
without waiting for user input.
|
|
122
|
+
|
|
123
|
+
On `mention`: handle the event (reply / suggestion / resolve as needed),
|
|
124
|
+
output any in-doc action, then execute `requiredNextToolCall` — which
|
|
125
|
+
is another `composer_next_event` call. Do not pause for the user to
|
|
126
|
+
acknowledge. The doc is the conversation.
|
|
127
|
+
|
|
128
|
+
On `timeout`: check `recentActivity`.
|
|
129
|
+
- `recentActivity: true` → the return includes `requiredNextToolCall`.
|
|
130
|
+
Execute it — the user is still working, just not tagging you.
|
|
131
|
+
- `recentActivity: false` → the return includes `userMessage` and
|
|
132
|
+
`instruction` but NO `requiredNextToolCall`. Say `userMessage`
|
|
133
|
+
EXACTLY ("I've left the document…") and exit (per exit rule 1
|
|
134
|
+
above). Do not paraphrase — users recognize the line across sessions.
|
|
44
135
|
|
|
45
136
|
On `mention`, the event contains everything you need to act in one turn:
|
|
46
137
|
|
|
@@ -64,22 +155,58 @@ or `composer_add_comment` — no extra `composer_get_section` call is needed in
|
|
|
64
155
|
the common case. Reach for `sectionMarkdown` to understand surrounding context
|
|
65
156
|
before replying or suggesting.
|
|
66
157
|
|
|
67
|
-
**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
need
|
|
72
|
-
|
|
158
|
+
**The event only carries the triggering message.** If the thread already has
|
|
159
|
+
replies (from the user, or from another agent), call `composer_get_thread({
|
|
160
|
+
roomId, threadId })` before replying. The return has every reply with author
|
|
161
|
+
and timestamp — essential when the user tagged you mid-conversation and you
|
|
162
|
+
need to catch up on what's already been said.
|
|
163
|
+
|
|
164
|
+
**`reason` is your main filter:**
|
|
165
|
+
|
|
166
|
+
- `"direct_mention"` — sidecar or text explicitly tagged you. Always
|
|
167
|
+
reply (unless the content is purely a thank-you that doesn't need an
|
|
168
|
+
answer — never emit empty acknowledgements).
|
|
169
|
+
- `"active_thread"` — a plain reply on a thread you're already in. Reply
|
|
170
|
+
if the content invites one; skip if it's plainly addressed to another
|
|
171
|
+
person, is a thank-you, or is otherwise a conversational dead-end.
|
|
172
|
+
- `"solo_room"` — you're alone with one human who didn't tag anyone.
|
|
173
|
+
**Default to a helpful reply** — they almost certainly want your
|
|
174
|
+
input. Skip only when the text reads like:
|
|
175
|
+
- a **note-to-self** ("TODO: fix this later", "remember to check
|
|
176
|
+
the date"),
|
|
177
|
+
- a bare **acknowledgement** ("k", "got it", "done"),
|
|
178
|
+
- a stage direction / aside ("ugh", "hmm"),
|
|
179
|
+
- or anything that visibly isn't pointed at you (quoted text,
|
|
180
|
+
drafts they're jotting down).
|
|
181
|
+
When in doubt, reply — the user can always ignore you.
|
|
73
182
|
|
|
74
183
|
### 4. Act
|
|
75
|
-
Triggers: direct requests like "add a summary to section 2".
|
|
76
|
-
Action:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
184
|
+
Triggers: direct requests in the terminal like "add a summary to section 2".
|
|
185
|
+
Action: the main thread is also attached to the room — call the write
|
|
186
|
+
tools from here and report back concisely. Don't hand terminal directives
|
|
187
|
+
to the monitor subagent; the subagent handles in-doc mentions, the main
|
|
188
|
+
thread handles in-terminal asks. They share the same MCP, so writes from
|
|
189
|
+
either show up in the doc.
|
|
190
|
+
|
|
191
|
+
## Tools
|
|
192
|
+
|
|
193
|
+
Read tools:
|
|
194
|
+
- `composer_get_full_doc` — entire doc as markdown.
|
|
195
|
+
- `composer_get_section` — one section by `headingId`.
|
|
196
|
+
- `composer_get_thread` — full state of a thread (all replies, anchor,
|
|
197
|
+
containing section). Call this when `composer_next_event` surfaces a
|
|
198
|
+
mention on a thread that already has history — the event gives you
|
|
199
|
+
only the triggering message.
|
|
200
|
+
|
|
201
|
+
Write tools:
|
|
202
|
+
- `composer_add_comment` — NEW comment on any span in the doc. Use when
|
|
203
|
+
raising something outside the current thread's anchor.
|
|
204
|
+
- `composer_add_suggestion` — propose a text replacement (lands as
|
|
205
|
+
pending). Can target any span — `fromThreadId` inherits the source
|
|
206
|
+
thread's anchor; `anchor` specifies a span elsewhere. Call it multiple
|
|
207
|
+
times in a turn to suggest in several spots.
|
|
208
|
+
- `composer_reply_comment` / `composer_reply_suggestion` — reply on an
|
|
209
|
+
existing thread.
|
|
83
210
|
- `composer_resolve_thread` — mark resolved.
|
|
84
211
|
|
|
85
212
|
There is no "just edit" tool in v1. All text changes go through suggestions
|
|
@@ -135,6 +262,104 @@ Picking a broader `textToFind` than the user asked for (the whole sentence
|
|
|
135
262
|
when they highlighted a phrase, the whole paragraph when they asked about
|
|
136
263
|
one clause) is the main failure mode. When in doubt, default to path 1.
|
|
137
264
|
|
|
265
|
+
### Cross-span: reply and suggest anywhere in the doc
|
|
266
|
+
|
|
267
|
+
A comment/reply thread is anchored to *one* span, but your response is
|
|
268
|
+
not confined to that span. When the user's question (or your own
|
|
269
|
+
judgment) points elsewhere:
|
|
270
|
+
|
|
271
|
+
- **Suggest a change to different text.** Call `composer_add_suggestion`
|
|
272
|
+
with `anchor: { headingId, textToFind }` pointing at the target. You
|
|
273
|
+
can post multiple suggestions in one turn — e.g., the user says "the
|
|
274
|
+
flour amount is off and so is the bake time" → two suggestions, each
|
|
275
|
+
anchored to its own span.
|
|
276
|
+
- **Open a new thread elsewhere.** Call `composer_add_comment` with
|
|
277
|
+
its own anchor. Useful for cross-references ("see also the
|
|
278
|
+
conclusion") or raising something the user didn't ask about but
|
|
279
|
+
should see.
|
|
280
|
+
- **Still reply on the original thread too** if the user's question
|
|
281
|
+
deserves a direct answer — but only when the reply says something
|
|
282
|
+
the suggestion/new-comment doesn't already convey. Don't post
|
|
283
|
+
"see my suggestion"; the card IS the answer.
|
|
284
|
+
|
|
285
|
+
Order of operations for a multi-span response: post the suggestion(s)
|
|
286
|
+
/ new comment(s) first, then (optionally) a reply on the originating
|
|
287
|
+
thread pointing out the bigger picture. That way the originating
|
|
288
|
+
thread's reply can reference what you just did.
|
|
289
|
+
|
|
290
|
+
### Suggest completely — accepting must leave the doc correct
|
|
291
|
+
|
|
292
|
+
Goal: the user clicks Accept and is done. They should never have to
|
|
293
|
+
hunt down downstream edits you forgot.
|
|
294
|
+
|
|
295
|
+
**Load enough context before you suggest.** The event gives you
|
|
296
|
+
`sectionMarkdown` for the containing section — usually enough for
|
|
297
|
+
wording changes. For anything that might appear elsewhere in the doc
|
|
298
|
+
(numbers, names, product/feature references, versions, dates,
|
|
299
|
+
terminology, heading text), call `composer_get_full_doc` first.
|
|
300
|
+
One extra read is much cheaper than shipping a broken doc.
|
|
301
|
+
|
|
302
|
+
**Scan for ripples before posting.** Common ones:
|
|
303
|
+
|
|
304
|
+
- **Counts and enumerations.** "The three examples below" / "three
|
|
305
|
+
things to remember" — if you add or remove an item, update the
|
|
306
|
+
count and any ordinal words ("first", "finally").
|
|
307
|
+
- **Cross-references.** "As in section 2", "see the conclusion",
|
|
308
|
+
"per step 3 above". If your edit moves or renames the target,
|
|
309
|
+
update the reference too.
|
|
310
|
+
- **Restated facts.** Recipes reference an ingredient twice; release
|
|
311
|
+
notes cite a version in both intro and body; specs quote a number
|
|
312
|
+
in a heading and a paragraph. One fact, multiple spans — cover
|
|
313
|
+
all of them.
|
|
314
|
+
- **Subject/verb and pronoun agreement.** "X and Y are" → trim to
|
|
315
|
+
just X → "X is". Changing from plural to singular ripples.
|
|
316
|
+
- **Neighboring flow.** Rewriting sentence 2 can break sentence 3
|
|
317
|
+
("This is why..."). Fix the continuation.
|
|
318
|
+
- **Heading changes.** If you change heading text, any prose that
|
|
319
|
+
says "see the Intro section" may need updating.
|
|
320
|
+
|
|
321
|
+
**Post every ripple as its own suggestion, in the same turn.** Don't
|
|
322
|
+
leave the user to hunt for companion edits. The tool accepts one
|
|
323
|
+
anchor per call — call it multiple times. Each suggestion stays
|
|
324
|
+
tight to its own span (this is NOT oversuggesting — it's covering
|
|
325
|
+
the actual surface of the change).
|
|
326
|
+
|
|
327
|
+
If a ripple is too structural for a clean suggestion (reorder a list,
|
|
328
|
+
split a paragraph), post the ones you can AND a short reply flagging
|
|
329
|
+
what's still open. The user shouldn't be surprised.
|
|
330
|
+
|
|
331
|
+
**When in doubt about the scope of a ripple, fetch the full doc.**
|
|
332
|
+
Don't guess.
|
|
333
|
+
|
|
334
|
+
### Auto-suggest when the user confirms a concrete proposal
|
|
335
|
+
|
|
336
|
+
When a user flags something qualitative ("this is too much flour", "this
|
|
337
|
+
sentence is clunky", "this number feels off"), lead with a **concrete
|
|
338
|
+
counter-proposal framed as a question** — then, if they confirm, post
|
|
339
|
+
the suggestion immediately without waiting for a second "yes, go ahead".
|
|
340
|
+
|
|
341
|
+
Two turns, not three:
|
|
342
|
+
|
|
343
|
+
1. **Turn 1 (propose).** Reply on the thread with one specific
|
|
344
|
+
alternative phrased as a check: "Does 200g seem right?", "How about
|
|
345
|
+
'gently fold' instead of 'stir'?", "Would 45 minutes read better than
|
|
346
|
+
90?". Pick a real number / phrase — not "would you like me to
|
|
347
|
+
suggest a different amount?" (that's a question about your behavior,
|
|
348
|
+
not a proposal).
|
|
349
|
+
2. **Turn 2 (commit on confirmation).** When the user replies with any
|
|
350
|
+
variant of yes ("yes", "sure", "go for it", "perfect", a thumbs-up
|
|
351
|
+
emoji), call `composer_add_suggestion` with `fromThreadId: event.threadId`
|
|
352
|
+
and the concrete replacement. Do NOT also post a comment reply — the
|
|
353
|
+
suggestion card IS your reply (see "Keep comment text terse" above).
|
|
354
|
+
|
|
355
|
+
If the user says no / picks a different value / redirects, follow their
|
|
356
|
+
lead — do not post the original proposal anyway.
|
|
357
|
+
|
|
358
|
+
If you can't name a concrete alternative (e.g. the thread is too
|
|
359
|
+
abstract to guess a number), ask a clarifying question instead. Don't
|
|
360
|
+
propose something generic just to fill the slot — "Would you like me
|
|
361
|
+
to shorten this?" is worthless without a target length.
|
|
362
|
+
|
|
138
363
|
## Anchors
|
|
139
364
|
|
|
140
365
|
Write tools take:
|
|
@@ -143,9 +368,40 @@ Write tools take:
|
|
|
143
368
|
{ headingId: "intro-0", textToFind: "the exact words to anchor on", occurrence?: 1 }
|
|
144
369
|
```
|
|
145
370
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
371
|
+
### Pick the right span — anchor = what gets deleted
|
|
372
|
+
|
|
373
|
+
Your `textToFind` is literally cut out when the user accepts; your
|
|
374
|
+
`replacementText` is inserted in its place. So:
|
|
375
|
+
|
|
376
|
+
- **Anchor the whole unit you're changing.** Replacing a sentence →
|
|
377
|
+
include the terminal punctuation (`.`, `?`, `!`). Replacing a bullet
|
|
378
|
+
item → anchor the item's text (not the `- ` marker; that's block
|
|
379
|
+
structure). Replacing a paragraph → anchor the whole paragraph.
|
|
380
|
+
- **Include any trailing punctuation you're changing.** Converting a
|
|
381
|
+
statement to a question? End the anchor at the `.` and end the
|
|
382
|
+
replacement with `?`. Don't anchor "the statement" alone and
|
|
383
|
+
replace with "the question?" — you'll end up with `the question?.`.
|
|
384
|
+
- **Match your `replacementText`'s shape to the anchor's shape.** Inline
|
|
385
|
+
replacement inside a paragraph → replacement is inline (no leading
|
|
386
|
+
`- `, `#`, or blank line). Replacing a full list → replacement is a
|
|
387
|
+
full markdown list. Single-paragraph markdown is unwrapped to inline
|
|
388
|
+
on accept; multi-block markdown is inserted as blocks.
|
|
389
|
+
- **Formatting is part of your replacement, not the anchor.** If the
|
|
390
|
+
original had `**bold**` or a link, the anchor's formatting is gone
|
|
391
|
+
on accept — your replacement must include the markdown syntax for
|
|
392
|
+
any formatting you want preserved.
|
|
393
|
+
- **Anchor at token boundaries, not mid-word.** `textToFind: "istrat"`
|
|
394
|
+
to hit the middle of "administration" is fragile. Use whole words
|
|
395
|
+
or sentence boundaries. Use `occurrence` when the same phrase
|
|
396
|
+
appears multiple times.
|
|
397
|
+
- **Mind the whitespace.** By default, do not include leading or
|
|
398
|
+
trailing whitespace in the anchor, and end `replacementText` at the
|
|
399
|
+
same boundary. If you include a trailing space in the anchor,
|
|
400
|
+
include one in the replacement too; otherwise words smash together.
|
|
401
|
+
|
|
402
|
+
If you get `text_not_found`, the error message includes the current
|
|
403
|
+
section text. Re-plan against the fresh text and retry. Never retry
|
|
404
|
+
with stale content.
|
|
149
405
|
|
|
150
406
|
## Discoverability
|
|
151
407
|
|