Update README for locations, Novel rename, and scene removal
Docs had drifted from the code across several prior renames; refreshes the data model, API surface table, and tool counts to match, and documents the new Locations feature and chapter character summary alongside the actual current commit.
This commit is contained in:
@@ -21,7 +21,7 @@ same edit.
|
||||
| `Novelly.Web` | React 19, TypeScript, Vite, TanStack Query, Tailwind v4 |
|
||||
|
||||
The back end is one project organised by feature, not by layer. Each feature folder —
|
||||
`Projects/`, `Characters/`, `Chapters/`, `Beats/`, `Scenes/`, `Tags/`, `Agent/` — holds its
|
||||
`Novels/`, `Characters/`, `Chapters/`, `Beats/`, `Tags/`, `Locations/`, `Agent/` — holds its
|
||||
entity, DTOs, service and endpoints together, so adding a capability means touching one
|
||||
folder rather than four. `Common/` holds what genuinely crosses features and `Data/` holds
|
||||
the `DbContext` and migrations.
|
||||
@@ -65,7 +65,7 @@ and everything else keeps working.
|
||||
### Tests
|
||||
|
||||
```bash
|
||||
dotnet test # 73 tests
|
||||
dotnet test # 174 tests
|
||||
cd src/Novelly.Web && npm run build # typecheck + bundle
|
||||
```
|
||||
|
||||
@@ -98,13 +98,13 @@ out of `appsettings.json` and use user-secrets or the environment.
|
||||
## The data model
|
||||
|
||||
```
|
||||
Project ──┬── Character ──┬── CharacterRelationship
|
||||
│ └── CharacterArcStage (the arc: flat, ordered)
|
||||
├── Chapter ──┬── Beat (the outline: flat, ordered)
|
||||
│ └── Scene (the prose)
|
||||
├── Tag (applied to characters, chapters and beats)
|
||||
├── OpenQuestion (attached to a chapter and/or a character)
|
||||
└── AgentConversation ── AgentMessage
|
||||
Novel ──┬── Character ──┬── CharacterRelationship
|
||||
│ └── CharacterArcStage (the arc: flat, ordered)
|
||||
├── Chapter ──── Beat (the outline: flat, ordered)
|
||||
├── Tag (applied to characters, chapters and beats)
|
||||
├── Location (applied to chapters)
|
||||
├── OpenQuestion (attached to a chapter and/or a character)
|
||||
└── AgentConversation ── AgentMessage
|
||||
```
|
||||
|
||||
**A chapter outline is a paragraph plus a table.** The paragraph is the chapter's
|
||||
@@ -113,18 +113,20 @@ Project ──┬── Character ──┬── CharacterRelationship
|
||||
| Column | What goes in it |
|
||||
|---|---|
|
||||
| Beat | A three-to-five word handle — "she burns the atlas", not a sentence |
|
||||
| Character | Whose beat it is. Optional; not every beat belongs to one person |
|
||||
| Characters | Who's in the beat. Optional; not every beat belongs to anyone, and a beat can name more than one |
|
||||
| What happened | The event itself |
|
||||
| What's next | What it sets in motion — the hook into the following beat |
|
||||
| Scene | Optional grouping: which scene will carry this beat's prose |
|
||||
|
||||
Beats are flat and ordered by `SortOrder` within their chapter. There is no nesting and
|
||||
no tree — reordering is one call that takes the beat ids in the order wanted.
|
||||
no tree — reordering is one call that takes the beat ids in the order wanted. The outline
|
||||
tab also rolls up the distinct set of characters across a chapter's beats under the beat
|
||||
and word counts, each linking to that character's page — a quick cast list without
|
||||
opening every beat.
|
||||
|
||||
**Beats plan; scenes carry prose.** The two layers are deliberately separate: an outline
|
||||
is for working out what happens, and a scene is where you write it. A beat's `SceneId` is
|
||||
the optional link between them, and it is nullable in both directions — deleting a scene
|
||||
ungroups its beats rather than deleting the plan.
|
||||
**Beats plan; the chapter's `Prose` carries the draft.** A chapter has one prose field,
|
||||
written and redrafted in place; `WordCount` is recomputed from it whenever it changes.
|
||||
There is no separate scene entity — the outline (beats) and the draft (prose) are the
|
||||
two views of the same chapter.
|
||||
|
||||
**Main characters carry the book; supporting characters hold it up.** A character's
|
||||
`Importance` (`Main` or `Supporting`) is separate from their `Role` — role is the part they
|
||||
@@ -149,19 +151,26 @@ append the decision to the notes of whatever it was attached to — so a settled
|
||||
up where you re-read it rather than in a list you have stopped looking at. Resolved
|
||||
questions drop off the list unless you ask for them.
|
||||
|
||||
**Tags cross-reference the book.** A tag is scoped to one project, unique by name
|
||||
**Tags cross-reference the book.** A tag is scoped to one novel, unique by name
|
||||
(case-insensitively), and can be attached to any character, chapter or beat. Applying an
|
||||
unknown tag by name creates it, so tagging is one action rather than two. `GET
|
||||
/api/tags/{id}/references` returns everything carrying a tag, which is how you trace a
|
||||
motif or a thread across all three kinds at once.
|
||||
|
||||
**A chapter can have several locations.** Locations replaced the old single free-text
|
||||
`Setting` field: a chapter now carries a multi-select list of locations (where and when
|
||||
it takes place), each a novel-scoped, name-deduped entity — applying an unknown location
|
||||
by name creates it, same as tags. A "Locations" tab on the novel lists every location
|
||||
with its chapter count, and `GET /api/locations/{id}/references` lists every chapter set
|
||||
there.
|
||||
|
||||
## The embedded agent
|
||||
|
||||
`NovelAgentService` runs the tool-use loop: it calls the Messages API, executes any tools
|
||||
Claude asks for, feeds every result back in a single user turn, and repeats until Claude
|
||||
stops asking. It has 29 tools covering the brief, characters and their arcs, chapter outlines
|
||||
(beats), scenes, tags and open questions — all of them going through the same application
|
||||
services the REST API uses.
|
||||
stops asking. It has 33 tools covering the brief, characters and their arcs, chapter
|
||||
outlines (beats), tags, locations and open questions — all of them going through the same
|
||||
application services the REST API uses.
|
||||
|
||||
A few deliberate choices worth knowing about:
|
||||
|
||||
@@ -179,7 +188,7 @@ A few deliberate choices worth knowing about:
|
||||
|
||||
## The MCP server
|
||||
|
||||
A stdio MCP server exposing 38 tools over the same REST API. It holds no domain logic of
|
||||
A stdio MCP server exposing 45 tools over the same REST API. It holds no domain logic of
|
||||
its own — it is a second front end, not a second implementation.
|
||||
|
||||
Build it, then point your MCP client at the produced binary:
|
||||
@@ -230,19 +239,20 @@ running and `.mcp.json` is set up.
|
||||
|
||||
| Resource | Routes |
|
||||
|---|---|
|
||||
| Projects | `GET\|POST /api/projects`, `GET\|PATCH\|DELETE /api/projects/{id}` |
|
||||
| Characters | `GET\|POST /api/projects/{id}/characters`, `GET\|PATCH\|DELETE /api/characters/{id}`, `POST /api/characters/{id}/relationships` |
|
||||
| Chapters | `GET\|POST /api/projects/{id}/chapters`, `GET\|PATCH\|DELETE /api/chapters/{id}` |
|
||||
| Novels | `GET\|POST /api/novels`, `GET\|PATCH\|DELETE /api/novels/{id}` |
|
||||
| Characters | `GET\|POST /api/novels/{id}/characters`, `GET\|PATCH\|DELETE /api/characters/{id}`, `POST /api/characters/{id}/relationships` |
|
||||
| Chapters | `GET\|POST /api/novels/{id}/chapters`, `GET\|PATCH\|DELETE /api/chapters/{id}` |
|
||||
| Beats | `GET\|POST /api/chapters/{id}/beats`, `POST /api/chapters/{id}/beats/reorder`, `GET\|PATCH\|DELETE /api/beats/{id}` |
|
||||
| Scenes | `GET\|POST /api/chapters/{id}/scenes`, `GET\|PATCH\|DELETE /api/scenes/{id}` |
|
||||
| Tags | `GET\|POST /api/projects/{id}/tags`, `GET /api/tags/{id}/references`, `PATCH\|DELETE /api/tags/{id}` |
|
||||
| Tags | `GET\|POST /api/novels/{id}/tags`, `GET /api/tags/{id}/references`, `PATCH\|DELETE /api/tags/{id}` |
|
||||
| Locations | `GET\|POST /api/novels/{id}/locations`, `GET /api/locations/{id}/references`, `PATCH\|DELETE /api/locations/{id}` |
|
||||
| Arcs | `GET\|POST /api/characters/{id}/arc`, `POST /api/characters/{id}/arc/reorder`, `GET\|PATCH\|DELETE /api/arc-stages/{id}` |
|
||||
| Questions | `GET\|POST /api/projects/{id}/questions`, `GET\|PATCH\|DELETE /api/questions/{id}`, `POST /api/questions/{id}/resolve`, `POST /api/questions/{id}/reopen` |
|
||||
| Agent | `GET /api/projects/{id}/agent/conversations`, `POST /api/projects/{id}/agent/messages`, `GET\|DELETE /api/conversations/{id}` |
|
||||
| Questions | `GET\|POST /api/novels/{id}/questions`, `GET\|PATCH\|DELETE /api/questions/{id}`, `POST /api/questions/{id}/resolve`, `POST /api/questions/{id}/reopen` |
|
||||
| Agent | `GET /api/novels/{id}/agent/conversations`, `POST /api/novels/{id}/agent/messages`, `GET\|DELETE /api/conversations/{id}` |
|
||||
|
||||
`PATCH` bodies are partial: an omitted field is left alone, an empty string clears it. A
|
||||
`tags` array replaces that item's tags outright and creates any names the project has not
|
||||
seen; omitting it leaves tags untouched.
|
||||
`tags` array replaces that item's tags outright and creates any names the novel has not
|
||||
seen; the same is true of a chapter's `locations` array. Omitting either leaves it
|
||||
untouched.
|
||||
Enums travel as names (`"Protagonist"`, `"Drafted"`), never ordinals. In development the
|
||||
OpenAPI document is at `/openapi/v1.json`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user