openwriter 0.5.3 → 0.5.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/client/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
11
11
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
12
12
|
<link href="https://fonts.googleapis.com/css2?family=Charter:ital,wght@0,400;0,700;1,400&family=Crimson+Pro:ital,wght@0,300;0,400;0,600;0,700;1,400&family=DM+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=DM+Serif+Display&family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&family=Inter:wght@400;500;600;700&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Literata:ital,opsz,wght@0,7..72,400;0,7..72,600;0,7..72,700;1,7..72,400&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,600;1,6..72,400&family=Playfair+Display:wght@400;600;700;900&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,400&family=Space+Grotesk:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet" />
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-97-SDxVw.js"></script>
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/index-BR_sMmFf.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
package/dist/server/mcp.js
CHANGED
|
@@ -13,8 +13,9 @@ import { DATA_DIR, ensureDataDir, resolveDocPath } from './helpers.js';
|
|
|
13
13
|
import { getDocument, getWordCount, getPendingChangeCount, getTitle, getStatus, getNodesByIds, getMetadata, setMetadata, applyChanges, applyTextEdits, updateDocument, save, markAllNodesAsPending, setAgentLock, updatePendingCacheForActiveDoc, populateDocumentFile, applyChangesToFile, applyTextEditsToFile, getDocId, getFilePath, } from './state.js';
|
|
14
14
|
import { listDocuments, switchDocument, createDocument, createDocumentFile, deleteDocument, openFile, getActiveFilename, updateDocumentTitle, promoteTempFile, archiveDocument, unarchiveDocument, resolveDocId } from './documents.js';
|
|
15
15
|
import { broadcastDocumentSwitched, broadcastDocumentsChanged, broadcastWorkspacesChanged, broadcastTitleChanged, broadcastMetadataChanged, broadcastPendingDocsChanged, broadcastWritingStarted, broadcastWritingFinished } from './ws.js';
|
|
16
|
-
import { listWorkspaces, getWorkspace, getDocTitle, getItemContext, addDoc, updateWorkspaceContext, createWorkspace, deleteWorkspace, addContainerToWorkspace, findOrCreateWorkspace, findOrCreateContainer, moveDoc, renameWorkspace, renameContainer } from './workspaces.js';
|
|
16
|
+
import { listWorkspaces, getWorkspace, getDocTitle, getItemContext, addDoc, updateWorkspaceContext, createWorkspace, deleteWorkspace, addContainerToWorkspace, findOrCreateWorkspace, findOrCreateContainer, moveDoc, removeContainer, renameWorkspace, renameContainer } from './workspaces.js';
|
|
17
17
|
import { addDocTag, removeDocTag, getDocTagsByFilename, getCachedDocument } from './state.js';
|
|
18
|
+
import { findDocNode } from './workspace-tree.js';
|
|
18
19
|
import { importGoogleDoc } from './gdoc-import.js';
|
|
19
20
|
import { toCompactFormat, compactNodes, parseMarkdownContent, mergeParagraphsToHardBreaks } from './compact.js';
|
|
20
21
|
import matter from 'gray-matter';
|
|
@@ -507,27 +508,11 @@ export const TOOL_REGISTRY = [
|
|
|
507
508
|
description: 'Get progressive disclosure context for a document in a workspace: workspace-level context (characters, settings, rules) and tags. Use before writing to understand context.',
|
|
508
509
|
schema: {
|
|
509
510
|
workspaceFile: z.string().describe('Workspace manifest filename'),
|
|
510
|
-
|
|
511
|
+
docId: z.string().describe('Document docId (8-char hex from list_documents)'),
|
|
511
512
|
},
|
|
512
|
-
handler: async ({ workspaceFile,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
},
|
|
516
|
-
{
|
|
517
|
-
name: 'add_doc',
|
|
518
|
-
description: 'Add a document to a workspace. Optionally place it inside a container.',
|
|
519
|
-
schema: {
|
|
520
|
-
workspaceFile: z.string().describe('Workspace manifest filename'),
|
|
521
|
-
docFile: z.string().describe('Document filename to add (e.g. "Chapter 1.md")'),
|
|
522
|
-
containerId: z.string().optional().describe('Container ID to add into (null = root level)'),
|
|
523
|
-
title: z.string().optional().describe('Display title for the doc'),
|
|
524
|
-
},
|
|
525
|
-
handler: async ({ workspaceFile, docFile, containerId, title }) => {
|
|
526
|
-
addDoc(workspaceFile, containerId ?? null, docFile, title || docFile.replace(/\.md$/, ''));
|
|
527
|
-
broadcastWorkspacesChanged();
|
|
528
|
-
return {
|
|
529
|
-
content: [{ type: 'text', text: `Added "${docFile}" to workspace${containerId ? ` in container ${containerId}` : ''}` }],
|
|
530
|
-
};
|
|
513
|
+
handler: async ({ workspaceFile, docId }) => {
|
|
514
|
+
const filename = resolveDocId(docId);
|
|
515
|
+
return { content: [{ type: 'text', text: JSON.stringify(getItemContext(workspaceFile, filename), null, 2) }] };
|
|
531
516
|
},
|
|
532
517
|
},
|
|
533
518
|
{
|
|
@@ -561,45 +546,70 @@ export const TOOL_REGISTRY = [
|
|
|
561
546
|
return { content: [{ type: 'text', text: `Created container "${name}" (id:${result.containerId})` }] };
|
|
562
547
|
},
|
|
563
548
|
},
|
|
549
|
+
{
|
|
550
|
+
name: 'delete_container',
|
|
551
|
+
description: 'Delete a container from a workspace. Any docs inside are removed from the workspace (files are NOT deleted from disk).',
|
|
552
|
+
schema: {
|
|
553
|
+
workspaceFile: z.string().describe('Workspace manifest filename'),
|
|
554
|
+
containerId: z.string().describe('Container ID to delete'),
|
|
555
|
+
},
|
|
556
|
+
handler: async ({ workspaceFile, containerId }) => {
|
|
557
|
+
removeContainer(workspaceFile, containerId);
|
|
558
|
+
broadcastWorkspacesChanged();
|
|
559
|
+
return { content: [{ type: 'text', text: `Deleted container ${containerId}` }] };
|
|
560
|
+
},
|
|
561
|
+
},
|
|
564
562
|
{
|
|
565
563
|
name: 'tag_doc',
|
|
566
564
|
description: 'Add a tag to a document. Tags are stored in the document\'s frontmatter — they travel with the file. A doc can have multiple tags.',
|
|
567
565
|
schema: {
|
|
568
|
-
|
|
566
|
+
docId: z.string().describe('Document docId (8-char hex from list_documents)'),
|
|
569
567
|
tag: z.string().describe('Tag name to add'),
|
|
570
568
|
},
|
|
571
|
-
handler: async ({
|
|
572
|
-
|
|
569
|
+
handler: async ({ docId, tag }) => {
|
|
570
|
+
const filename = resolveDocId(docId);
|
|
571
|
+
addDocTag(filename, tag);
|
|
573
572
|
broadcastDocumentsChanged();
|
|
574
|
-
return { content: [{ type: 'text', text: `Tagged "${
|
|
573
|
+
return { content: [{ type: 'text', text: `Tagged "${filename}" with [${tag}]` }] };
|
|
575
574
|
},
|
|
576
575
|
},
|
|
577
576
|
{
|
|
578
577
|
name: 'untag_doc',
|
|
579
578
|
description: 'Remove a tag from a document.',
|
|
580
579
|
schema: {
|
|
581
|
-
|
|
580
|
+
docId: z.string().describe('Document docId (8-char hex from list_documents)'),
|
|
582
581
|
tag: z.string().describe('Tag name to remove'),
|
|
583
582
|
},
|
|
584
|
-
handler: async ({
|
|
585
|
-
|
|
583
|
+
handler: async ({ docId, tag }) => {
|
|
584
|
+
const filename = resolveDocId(docId);
|
|
585
|
+
removeDocTag(filename, tag);
|
|
586
586
|
broadcastDocumentsChanged();
|
|
587
|
-
return { content: [{ type: 'text', text: `Removed tag [${tag}] from "${
|
|
587
|
+
return { content: [{ type: 'text', text: `Removed tag [${tag}] from "${filename}"` }] };
|
|
588
588
|
},
|
|
589
589
|
},
|
|
590
590
|
{
|
|
591
591
|
name: 'move_doc',
|
|
592
|
-
description: '
|
|
592
|
+
description: 'Add a document to a workspace, or move it within the workspace. If the doc is not yet in the workspace it will be added; if it is already present it will be moved to the target container.',
|
|
593
593
|
schema: {
|
|
594
594
|
workspaceFile: z.string().describe('Workspace manifest filename'),
|
|
595
|
-
|
|
595
|
+
docId: z.string().describe('Document docId (8-char hex from list_documents)'),
|
|
596
596
|
targetContainerId: z.string().optional().describe('Target container ID (omit for root level)'),
|
|
597
597
|
afterFile: z.string().optional().describe('Place after this file (omit for beginning)'),
|
|
598
598
|
},
|
|
599
|
-
handler: async ({ workspaceFile,
|
|
600
|
-
|
|
599
|
+
handler: async ({ workspaceFile, docId, targetContainerId, afterFile }) => {
|
|
600
|
+
const filename = resolveDocId(docId);
|
|
601
|
+
const ws = getWorkspace(workspaceFile);
|
|
602
|
+
const existing = findDocNode(ws.root, filename);
|
|
603
|
+
if (existing) {
|
|
604
|
+
moveDoc(workspaceFile, filename, targetContainerId ?? null, afterFile ?? null);
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
const title = getDocTitle(filename);
|
|
608
|
+
addDoc(workspaceFile, targetContainerId ?? null, filename, title, afterFile ?? null);
|
|
609
|
+
}
|
|
601
610
|
broadcastWorkspacesChanged();
|
|
602
|
-
|
|
611
|
+
const action = existing ? 'Moved' : 'Added';
|
|
612
|
+
return { content: [{ type: 'text', text: `${action} "${filename}"${targetContainerId ? ` to container ${targetContainerId}` : ' to root'}` }] };
|
|
603
613
|
},
|
|
604
614
|
},
|
|
605
615
|
{
|
|
@@ -22,6 +22,20 @@ export function createWorkspaceRouter(b) {
|
|
|
22
22
|
res.status(400).json({ error: err.message });
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
+
// Static paths before parameterized — otherwise :filename captures "reorder"
|
|
26
|
+
router.put('/api/workspaces/reorder', (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const { order } = req.body;
|
|
29
|
+
if (!Array.isArray(order))
|
|
30
|
+
return res.status(400).json({ error: 'order must be an array' });
|
|
31
|
+
reorderWorkspaces(order);
|
|
32
|
+
b.broadcastWorkspacesChanged();
|
|
33
|
+
res.json({ success: true });
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
res.status(400).json({ error: err.message });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
25
39
|
router.get('/api/workspaces/:filename', (req, res) => {
|
|
26
40
|
try {
|
|
27
41
|
res.json(getWorkspace(req.params.filename));
|
|
@@ -50,19 +64,6 @@ export function createWorkspaceRouter(b) {
|
|
|
50
64
|
res.status(400).json({ error: err.message });
|
|
51
65
|
}
|
|
52
66
|
});
|
|
53
|
-
router.put('/api/workspaces/reorder', (req, res) => {
|
|
54
|
-
try {
|
|
55
|
-
const { order } = req.body;
|
|
56
|
-
if (!Array.isArray(order))
|
|
57
|
-
return res.status(400).json({ error: 'order must be an array' });
|
|
58
|
-
reorderWorkspaces(order);
|
|
59
|
-
b.broadcastWorkspacesChanged();
|
|
60
|
-
res.json({ success: true });
|
|
61
|
-
}
|
|
62
|
-
catch (err) {
|
|
63
|
-
res.status(400).json({ error: err.message });
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
67
|
// Doc operations
|
|
67
68
|
router.post('/api/workspaces/:filename/docs', (req, res) => {
|
|
68
69
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openwriter",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "The open-source writing surface for AI agents. Markdown-native editor with pending change review — your agent writes, you accept or reject.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/skill/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ description: |
|
|
|
14
14
|
Requires: OpenWriter MCP server configured. Browser UI at localhost:5050.
|
|
15
15
|
metadata:
|
|
16
16
|
author: travsteward
|
|
17
|
-
version: "0.
|
|
17
|
+
version: "0.1.1"
|
|
18
18
|
repository: https://github.com/travsteward/openwriter
|
|
19
19
|
license: MIT
|
|
20
20
|
---
|
|
@@ -27,6 +27,7 @@ You are a writing collaborator. You read documents and make edits **exclusively
|
|
|
27
27
|
|
|
28
28
|
1. **ALWAYS write content in the editor, never in the terminal.** OpenWriter is a collaborative writing surface. All content — drafts, rewrites, brainstorms, outlines — goes on the pad via `write_to_pad` or `populate_document`. Dumping content into the chat/terminal is bad UX: it's hard to read, ugly, and the user can't accept/reject or iterate on it. If you're generating text the user will read, it goes in the editor.
|
|
29
29
|
2. **The terminal is for discussion only.** Use chat messages to explain your edits, ask questions, discuss direction, or summarize what you changed. Never use it as the writing surface.
|
|
30
|
+
3. **Name every document.** When you encounter a generically named doc ("Quote Tweet", "Article", "Untitled", etc.), rename it based on its content before proceeding. Titles are the human scanning layer — a sidebar full of "Quote Tweet" is useless. Use `rename_item` with the docId. Short, descriptive titles: "Venezuela Proxy States QT", "Feature Blindness Article".
|
|
30
31
|
|
|
31
32
|
## Setup — Which Path?
|
|
32
33
|
|
|
@@ -78,29 +79,37 @@ After editing, tell the user:
|
|
|
78
79
|
|
|
79
80
|
**Note:** You cannot run `claude mcp add` from inside a session (nested session error). That's why we edit the JSON directly when configuring from within Claude Code. Also, `claude mcp add` appends to the end — always verify the entry is first after adding.
|
|
80
81
|
|
|
82
|
+
## Document Identity: Titles vs DocIds
|
|
83
|
+
|
|
84
|
+
Every document has an immutable **docId** (8-char hex, e.g. `a1b2c3d4`) in its YAML frontmatter. Titles are for human communication and agent reasoning. DocIds are for agent action.
|
|
85
|
+
|
|
86
|
+
- `list_documents` and `read_pad` always show both title and docId
|
|
87
|
+
- All doc-targeting tools take `docId` as their parameter (not filename)
|
|
88
|
+
- Two documents can have the same title — the docId disambiguates
|
|
89
|
+
|
|
81
90
|
## MCP Tools Reference (32 tools)
|
|
82
91
|
|
|
83
92
|
### Document Operations
|
|
84
93
|
|
|
85
|
-
| Tool | Description |
|
|
86
|
-
|
|
87
|
-
| `read_pad` | Read the current document (compact tagged-line format) |
|
|
88
|
-
| `write_to_pad` | Apply edits as pending decorations (rewrite, insert, delete) |
|
|
89
|
-
| `populate_document` | Populate an empty doc with content (two-step creation flow) |
|
|
90
|
-
| `get_pad_status` | Lightweight poll: word count, pending changes, userSignaledReview |
|
|
91
|
-
| `get_nodes` | Fetch specific nodes by ID |
|
|
92
|
-
| `get_metadata` | Get frontmatter metadata for the active document |
|
|
93
|
-
| `set_metadata` | Update frontmatter metadata (merge, set key to null to remove) |
|
|
94
|
+
| Tool | Key Params | Description |
|
|
95
|
+
|------|-----------|-------------|
|
|
96
|
+
| `read_pad` | — | Read the current document (compact tagged-line format with `id:` in header) |
|
|
97
|
+
| `write_to_pad` | `docId`, `changes` | Apply edits as pending decorations (rewrite, insert, delete) |
|
|
98
|
+
| `populate_document` | `docId?`, `content` | Populate an empty doc with content (two-step creation flow) |
|
|
99
|
+
| `get_pad_status` | — | Lightweight poll: word count, pending changes, userSignaledReview |
|
|
100
|
+
| `get_nodes` | `nodeIds` | Fetch specific nodes by ID |
|
|
101
|
+
| `get_metadata` | — | Get frontmatter metadata for the active document |
|
|
102
|
+
| `set_metadata` | `metadata` | Update frontmatter metadata (merge, set key to null to remove) |
|
|
94
103
|
|
|
95
104
|
### Document Lifecycle
|
|
96
105
|
|
|
97
|
-
| Tool | Description |
|
|
98
|
-
|
|
99
|
-
| `list_documents` | List all documents with
|
|
100
|
-
| `switch_document` | Switch to a different document by
|
|
101
|
-
| `create_document` | Create a new empty document
|
|
102
|
-
| `open_file` | Open an existing .md file from any location on disk |
|
|
103
|
-
| `delete_document` | Delete a document file (moves to OS trash, recoverable) |
|
|
106
|
+
| Tool | Key Params | Description |
|
|
107
|
+
|------|-----------|-------------|
|
|
108
|
+
| `list_documents` | — | List all documents with title, docId, word count, active status |
|
|
109
|
+
| `switch_document` | `docId` | Switch to a different document by docId |
|
|
110
|
+
| `create_document` | `title?`, ... | Create a new empty document — response includes docId |
|
|
111
|
+
| `open_file` | `path` | Open an existing .md file from any location on disk |
|
|
112
|
+
| `delete_document` | `docId` | Delete a document file (moves to OS trash, recoverable) |
|
|
104
113
|
|
|
105
114
|
### Import
|
|
106
115
|
|
|
@@ -132,16 +141,16 @@ After editing, tell the user:
|
|
|
132
141
|
|
|
133
142
|
### Agent Marks
|
|
134
143
|
|
|
135
|
-
| Tool | Description |
|
|
136
|
-
|
|
137
|
-
| `get_agent_marks` | Get inline feedback marks left by the user (optional
|
|
138
|
-
| `resolve_agent_marks` | Remove marks after addressing feedback (pass mark IDs) |
|
|
144
|
+
| Tool | Key Params | Description |
|
|
145
|
+
|------|-----------|-------------|
|
|
146
|
+
| `get_agent_marks` | `docId?` | Get inline feedback marks left by the user (optional docId — omit for all docs) |
|
|
147
|
+
| `resolve_agent_marks` | `mark_ids` | Remove marks after addressing feedback (pass mark IDs) |
|
|
139
148
|
|
|
140
149
|
### Text Operations
|
|
141
150
|
|
|
142
|
-
| Tool | Description |
|
|
143
|
-
|
|
144
|
-
| `edit_text` | Fine-grained text edits within a node (find/replace, add/remove marks) |
|
|
151
|
+
| Tool | Key Params | Description |
|
|
152
|
+
|------|-----------|-------------|
|
|
153
|
+
| `edit_text` | `docId`, `nodeId`, `edits` | Fine-grained text edits within a node (find/replace, add/remove marks) |
|
|
145
154
|
|
|
146
155
|
### Image Generation
|
|
147
156
|
|
|
@@ -166,7 +175,7 @@ OpenWriter has two distinct modes: **editing** existing documents and **creating
|
|
|
166
175
|
|
|
167
176
|
For making changes to existing documents — rewrites, insertions, deletions:
|
|
168
177
|
|
|
169
|
-
- Use `write_to_pad` for all edits — **`
|
|
178
|
+
- Use `write_to_pad` for all edits — **`docId` is required** (8-char hex from `list_documents` or `read_pad`)
|
|
170
179
|
- Send **3-8 changes per call** for a responsive, streaming feel
|
|
171
180
|
- Always `read_pad` before editing to get fresh node IDs
|
|
172
181
|
- Respect `pendingChanges > 0` — wait for the user to accept/reject before sending more
|
|
@@ -214,39 +223,42 @@ This eliminates the need for separate `create_workspace`, `create_container`, an
|
|
|
214
223
|
|
|
215
224
|
```
|
|
216
225
|
1. get_pad_status → check pendingChanges and userSignaledReview
|
|
217
|
-
2. read_pad → get full document with node IDs
|
|
218
|
-
3. write_to_pad({
|
|
226
|
+
2. read_pad → get full document with node IDs + docId
|
|
227
|
+
3. write_to_pad({ docId: "a1b2c3d4", changes: [...] })
|
|
219
228
|
4. Wait → user accepts/rejects in browser
|
|
220
229
|
```
|
|
221
230
|
|
|
222
231
|
### Multi-document
|
|
223
232
|
|
|
224
233
|
```
|
|
225
|
-
1. list_documents → see all docs
|
|
226
|
-
2. read_pad → read active doc (or switch_document first)
|
|
227
|
-
3. write_to_pad({
|
|
228
|
-
→ edits go to the
|
|
234
|
+
1. list_documents → see all docs with title + [docId]
|
|
235
|
+
2. read_pad → read active doc (or switch_document({ docId }) first)
|
|
236
|
+
3. write_to_pad({ docId: "e5f6a7b8", changes: [...] })
|
|
237
|
+
→ edits go to the identified doc, no view switch needed
|
|
229
238
|
```
|
|
230
239
|
|
|
231
240
|
### Creating new content (two-step)
|
|
232
241
|
|
|
233
242
|
```
|
|
234
243
|
1. create_document({ title: "My Doc", workspace: "Project", container: "Chapters" })
|
|
235
|
-
→
|
|
236
|
-
2. populate_document({ content: "# ..." })
|
|
237
|
-
|
|
238
|
-
|
|
244
|
+
→ returns docId "a1b2c3d4", spinner appears
|
|
245
|
+
2. populate_document({ docId: "a1b2c3d4", content: "# ..." })
|
|
246
|
+
→ content delivered, spinner clears
|
|
247
|
+
3. read_pad → get node IDs + docId if further edits needed
|
|
248
|
+
4. write_to_pad({ docId: "a1b2c3d4", ... }) → refine with edits
|
|
239
249
|
```
|
|
240
250
|
|
|
241
251
|
### Building a workspace (multiple docs)
|
|
242
252
|
|
|
243
253
|
```
|
|
244
254
|
1. create_document({ title: "Ch 1", workspace: "My Book", container: "Chapters" })
|
|
245
|
-
|
|
255
|
+
→ returns docId "ch1docid"
|
|
256
|
+
2. populate_document({ docId: "ch1docid", content: "..." })
|
|
246
257
|
3. create_document({ title: "Ch 2", workspace: "My Book", container: "Chapters" })
|
|
247
|
-
|
|
258
|
+
→ returns docId "ch2docid"
|
|
259
|
+
4. populate_document({ docId: "ch2docid", content: "..." })
|
|
248
260
|
5. create_document({ title: "Character Bible", workspace: "My Book", container: "References" })
|
|
249
|
-
6. populate_document({ content: "..." })
|
|
261
|
+
6. populate_document({ docId: "<from step 5>", content: "..." })
|
|
250
262
|
7. tag_doc + update_workspace_context → organize and add context
|
|
251
263
|
```
|
|
252
264
|
|
|
@@ -258,8 +270,8 @@ Users can select text in the browser, right-click, and leave an "Agent Mark" —
|
|
|
258
270
|
|
|
259
271
|
```
|
|
260
272
|
1. User says "check my marks" (or you see the hint in read_pad output)
|
|
261
|
-
2. get_agent_marks → all marks across all docs, grouped by
|
|
262
|
-
3. Address each mark → rewrite, insert, delete via write_to_pad
|
|
273
|
+
2. get_agent_marks → all marks across all docs, grouped by document
|
|
274
|
+
3. Address each mark → rewrite, insert, delete via write_to_pad (use docId)
|
|
263
275
|
4. resolve_agent_marks([ids]) → clears decorations in browser
|
|
264
276
|
```
|
|
265
277
|
|