Skip to content
Merged
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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,23 @@ jobs:
cache: npm
- run: npm ci
- run: npm test
- run: npm run build:example

plans:
# Replays the example's Gherkin plans through agent-browser against native WebMCP.
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx agent-browser install --with-deps || npx agent-browser install
- run: npm run build:example
- name: Start the example
run: |
npm run start:example &
for i in $(seq 1 30); do curl -sf http://localhost:3000/ >/dev/null && break; sleep 1; done
- run: npm run test:plans
Binary file added docs/board.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/task-detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions examples/with-sightmap-webmcp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Local sightmap browser session state
.sightmap/.session
.sightmap/snapshots/
28 changes: 28 additions & 0 deletions examples/with-sightmap-webmcp/.sightkick/journeys.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: 1

# Journeys are compile-time orderings over tools. They never execute; they
# become `guidance` breadcrumbs on each tool's result so a turn-by-turn agent
# knows what tends to come next. Which journeys matter is the second human
# decision in this layer: it comes from how people actually use the app —
# analytics, session replay, support tickets — not from the corpus.

journeys:
- name: add_and_review
description: Add a task, then confirm it on the board.
steps:
- add_task
- tool: list_tasks
reason: confirm the task you just added is listed

- name: triage
description: Open a task, finish it from its page, come back, check the board.
steps:
- open_task
- tool: read_task
reason: confirm you opened the right task
- tool: mark_done
reason: finish it
- tool: back_to_board
reason: return to the board
- tool: set_filter
reason: switch to Done to see it moved
124 changes: 124 additions & 0 deletions examples/with-sightmap-webmcp/.sightkick/tools.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
version: 1
name: tasks

# The tool layer: named, atomic actions over the corpus in ../.sightmap/.
# Tools never carry selectors — they address the app by component name and
# extracted property, and `sightkick build` refuses any name the corpus
# doesn't have. Human review happens here: which actions deserve to be tools
# is a product decision, not something to derive mechanically.

tools:
- name: list_tasks
description: List the tasks currently shown on the board, with their done state.
ensure_view: TaskBoard
returns:
description: One row per visible task.
list:
rows: TaskRow
fields:
title: title
done: done

- name: add_task
description: Add a task to the board by title. Idempotent — an existing title is left alone.
ensure_view: TaskBoard
params:
- name: title
type: string
required: true
description: The task title.
guard:
present: { query: 'TaskRow[title="{{title}}"]' }
steps:
- fill: { query: NewTaskInput, value: "{{title}}" }
- click: { query: AddTaskButton }
- wait_for: { query: 'TaskRow[title="{{title}}"]' }
returns:
description: The done state of the row just added ("false").
value: { query: 'TaskRow[title="{{title}}"]', property: done }

- name: complete_task
description: Mark a task done from the board by clicking its toggle. Idempotent.
ensure_view: TaskBoard
params:
- name: title
type: string
required: true
guard:
present: { query: 'TaskRow[title="{{title}}"][done="true"]' }
steps:
- click: { query: 'TaskRow[title="{{title}}"] TaskToggle' }
- wait_for: { query: 'TaskRow[title="{{title}}"][done="true"]' }
returns:
value: { query: 'TaskRow[title="{{title}}"]', property: done }

- name: delete_task
description: Delete a task from the board by title, then return the remaining rows.
ensure_view: TaskBoard
params:
- name: title
type: string
required: true
steps:
- click: { query: 'TaskRow[title="{{title}}"] DeleteTaskButton' }
returns:
description: The rows left on the board.
list:
rows: TaskRow
fields:
title: title
done: done

- name: set_filter
description: Show All, Active, or Done tasks, and return the rows now visible.
ensure_view: TaskBoard
params:
- name: filter
type: enum
required: true
values: [All, Active, Done]
steps:
- click: { query: 'FilterButton[label="{{filter}}"]' }
- wait_for: { query: 'FilterButton[label="{{filter}}"][active="true"]' }
returns:
list:
rows: TaskRow
fields:
title: title
done: done

- name: open_task
description: Open a task's detail page from the board. Navigates to /tasks/:id.
ensure_view: TaskBoard
params:
- name: title
type: string
required: true
steps:
- click: { query: 'TaskRow[title="{{title}}"] TaskLink' }
- wait_for: { view: TaskDetail }

- name: read_task
description: Read the open task's title and status from its detail page.
ensure_view: TaskDetail
returns:
description: The status shown on the page ("Active" or "Done").
value: { query: TaskDetail, property: status }

- name: mark_done
description: Mark the open task done from its detail page. Idempotent.
ensure_view: TaskDetail
guard:
present: { query: 'TaskDetail[status="Done"]' }
steps:
- click: { query: MarkDoneButton }
- wait_for: { query: 'TaskDetail[status="Done"]' }
returns:
value: { query: TaskDetail, property: status }

- name: back_to_board
description: Return from a task's detail page to the board.
ensure_view: TaskDetail
steps:
- click: { query: TaskDetail BackToBoardLink }
- wait_for: { view: TaskBoard }
13 changes: 13 additions & 0 deletions examples/with-sightmap-webmcp/.sightmap/about.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 1

views:
- name: About
route: /about
url: http://localhost:3000/about
source: app/about/page.tsx
dependencies:
- app/layout.tsx
description: Static explainer. No tools are registered here; that is correct, not a failure.
components:
- name: AboutText
selector: '[data-component="AboutText"]'
1 change: 1 addition & 0 deletions examples/with-sightmap-webmcp/.sightmap/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version: 1
69 changes: 69 additions & 0 deletions examples/with-sightmap-webmcp/.sightmap/home.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
version: 1

# Seeded by `sightmap-next seed`, then curated against `next dev` with the
# sightmap-authoring skill (snapshot --coverage until 0 orphaned nodes).

views:
- name: TaskBoard
route: /
url: http://localhost:3000/
source: app/page.tsx
dependencies:
- app/layout.tsx
- components/TaskBoard.tsx
- lib/store.ts
description: The board — add a task, filter, toggle, delete, or open one.
memory:
- State is in-memory per tab and resets on a full page load; every fresh open shows the same three seed tasks
- Adding submits a form, so Enter in the input works as well as the button
- Filter labels are All / Active / Done (not "Completed")
components:
- name: TaskBoard
selector: '[data-component="TaskBoard"]'
source: components/TaskBoard.tsx
children:
- name: NewTaskInput
selector: '[data-component="NewTaskInput"]'
description: Controlled React input; the runtime fills it through the native value setter
- name: AddTaskButton
selector: '[data-component="AddTaskButton"]'
- name: FilterBar
selector: '[data-component="FilterBar"]'
children:
- name: FilterButton
selector: '[data-component="FilterButton"]'
properties:
- name: label
extract: text
- name: active
extract: attr=aria-pressed
- name: TaskList
selector: '[data-component="TaskList"]'
- name: TaskRow
selector: '[data-component="TaskRow"]'
description: One task. Title lives in the link; done state is mirrored onto data-done so it is filterable
properties:
- name: title
extract: TaskLink.text
- name: done
extract: attr=data-done
- name: id
extract: attr=data-id
children:
- name: TaskToggle
selector: '[data-component="TaskToggle"]'
description: Reads "Done" on an active task and "Undo" on a done one
properties:
- name: label
extract: text
- name: TaskLink
selector: '[data-component="TaskLink"]'
description: Client-side navigation to /tasks/:id
properties:
- name: text
extract: text
- name: DeleteTaskButton
selector: '[data-component="DeleteTaskButton"]'
- name: EmptyState
selector: '[data-component="EmptyState"]'
description: Rendered only when the current filter shows no rows
16 changes: 16 additions & 0 deletions examples/with-sightmap-webmcp/.sightmap/shared.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: 1

# Content reachable from every route (the spec's shared.yaml convention).
components:
- name: SiteNav
selector: '[data-component="SiteNav"]'
source: app/layout.tsx
children:
- name: NavLink
selector: '[data-component="NavLink"]'
properties:
- name: label
extract: text
- name: RouteAnnouncer
selector: '#__next-route-announcer__'
description: Next.js client-navigation announcer for screen readers; empty until a navigation, never a target
44 changes: 44 additions & 0 deletions examples/with-sightmap-webmcp/.sightmap/tasks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
version: 1

views:
- name: TaskDetail
route: /tasks/:id
source: app/tasks/[id]/page.tsx
dependencies:
- app/layout.tsx
- components/TaskDetail.tsx
- lib/store.ts
description: One task's page — title, status badge, mark-done, back link.
memory:
- Reached by client-side navigation from the board; a full load of /tasks/:id shows the seed state, so ids above 3 render TaskMissing
- The Mark done button is disabled once the task is done, so a second click is a no-op rather than a toggle
components:
- name: TaskDetail
selector: '[data-component="TaskDetail"]'
source: components/TaskDetail.tsx
properties:
- name: title
extract: TaskTitle.text
- name: status
extract: attr=data-status
children:
- name: BackToBoardLink
selector: '[data-component="BackToBoardLink"]'
- name: TaskTitle
selector: '[data-component="TaskTitle"]'
properties:
- name: text
extract: text
- name: StatusBadge
selector: '[data-component="StatusBadge"]'
properties:
- name: label
extract: text
- name: MarkDoneButton
selector: '[data-component="MarkDoneButton"]'
- name: TaskMissing
selector: '[data-component="TaskMissing"]'
description: Shown for an id the store does not have
children:
- name: BackToBoardLink
selector: '[data-component="BackToBoardLink"]'
37 changes: 37 additions & 0 deletions examples/with-sightmap-webmcp/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# AGENTS.md

How a coding agent should work in this app. Install the skills first:

```bash
npx @sightmap/sightkick skills install # sightkick-authoring, sightkick-debug, sightmap-authoring, sightmap-browser
npx agent-browser skills get core # agent-browser's own workflow (also: skills get webmcp-gen)
```

## Before touching UI code

Read `.sightmap/` for the view you are changing — component names, properties, and the
`memory:` notes are the contract every tool and plan depends on. Keep
`data-component="Name"` attributes stable; they are the selectors.

## After changing UI code

1. `npm run build && npm run start`
2. `npx sightmap browser start --detach --url http://localhost:3000/`
3. `npx sightmap snapshot --coverage --url <the view>` — fix the corpus until `0 orphaned T3 ✓`
4. `npm run sightmap:validate` — the tool layer must still compile
5. `npm run test:plans` — every plan must pass; if a plan's expectation is now wrong,
update the plan and re-stamp it, never loosen the expectation to get green
6. Commit the `.sightmap/` and `.sightkick/` changes with the code change

## Adding a tool (human-in-the-loop)

Draft one tool per real user action on the view, following `sightkick-authoring`. Then
stop and ask which of the drafted tools matter — do not add every possible action. Journeys
are ranked by the product owner from real usage, not guessed.

## Do not

- Regenerate `.sightmap/` from source. `sightmap-next seed` is a one-time scaffold.
- Put CSS selectors in `.sightkick/`. Tools reference corpus component names only.
- Edit `public/.well-known/*`, `public/sightkick-runtime.js`, or `webmcp.init.js` by hand;
`sightmap-next build` writes them.
Loading
Loading