@sightmap/sightkick 0.2.0 → 0.4.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @sightmap/sightkick
2
2
 
3
- The **sightkick** CLI: compile a `webmcp.tools.yaml` + a sightmap `.sightmap/`
4
- corpus into WebMCP tool IR, and install the sightkick agent skills.
3
+ The **sightkick** CLI: compile a `.sightkick/` tool layer + a sightmap
4
+ `.sightmap/` corpus into WebMCP tool IR, and install the sightkick agent skills.
5
5
 
6
6
  Ships as a native binary (no Go toolchain required). `npm install` pulls in only
7
7
  the `@sightmap/sightkick-<os>-<arch>` package matching your platform; the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sightmap/sightkick",
3
- "version": "0.2.0",
4
- "description": "sightkick — compile a webmcp.tools.yaml + sightmap corpus into WebMCP tool IR, and install the sightkick agent skills (native binary, no Go toolchain required)",
3
+ "version": "0.4.0",
4
+ "description": "sightkick — compile a .sightkick/ tool layer + sightmap corpus into WebMCP tool IR, and install the sightkick agent skills (native binary, no Go toolchain required)",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sightmap.org",
7
7
  "repository": {
@@ -34,11 +34,11 @@
34
34
  "access": "public"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@sightmap/sightkick-darwin-arm64": "0.2.0",
38
- "@sightmap/sightkick-darwin-x64": "0.2.0",
39
- "@sightmap/sightkick-linux-arm64": "0.2.0",
40
- "@sightmap/sightkick-linux-x64": "0.2.0",
41
- "@sightmap/sightkick-win32-arm64": "0.2.0",
42
- "@sightmap/sightkick-win32-x64": "0.2.0"
37
+ "@sightmap/sightkick-darwin-arm64": "0.4.0",
38
+ "@sightmap/sightkick-darwin-x64": "0.4.0",
39
+ "@sightmap/sightkick-linux-arm64": "0.4.0",
40
+ "@sightmap/sightkick-linux-x64": "0.4.0",
41
+ "@sightmap/sightkick-win32-arm64": "0.4.0",
42
+ "@sightmap/sightkick-win32-x64": "0.4.0"
43
43
  }
44
44
  }
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: sightkick-authoring
3
+ description: Author a .sightkick/ tool layer — the sightkick manifest that turns a sightmap corpus into named WebMCP tools (atomic view-scoped actions + guidance journeys), then compile it to IR with `sightkick build`. Use when you have (or are building) a `.sightmap/` corpus and want to define the tools an agent can call on that app. Pairs with sightmap-authoring (writes the corpus this reads) and sightkick-debug (runs the compiled tools on a live page).
4
+ activation:
5
+ - a `.sightmap/` corpus exists (or is being authored) and you want to define WebMCP tools over it
6
+ - writing or editing a `.sightkick/` tool layer
7
+ - a `sightkick build` reports unresolved component/property/view references
8
+ ---
9
+
10
+ # sightkick-authoring: write a .sightkick/ tool layer
11
+
12
+ sightkick compiles two inputs into a self-contained **IR**: a `.sightmap/` corpus
13
+ (the app's component map — authored with the **`sightmap-authoring`** skill) and a
14
+ **`.sightkick/`** tool layer (authored here). `sightkick build` resolves every
15
+ reference against the corpus and reports unresolved ones with candidate lists.
16
+ Once it compiles, drive the tools on a live page with the **`sightkick-debug`**
17
+ skill.
18
+
19
+ The two sit side by side in the app directory:
20
+
21
+ ```
22
+ <app>/
23
+ .sightmap/ # the corpus (components + views), read by sightkick
24
+ .sightkick/ # the tool layer, authored here
25
+ *.yaml # any number of files — all merged into one manifest
26
+ ```
27
+
28
+ `.sightkick/` mirrors `.sightmap/`: every `*.yaml` file inside is **merged** into
29
+ one manifest (tools and journeys concatenated), with **no dependencies** between
30
+ files — the whole directory is the manifest. Split a large tool layer however
31
+ helps (e.g. one file per view, plus a `journeys.yaml`); keep a tiny one in a
32
+ single `tools.yaml`.
33
+
34
+ **Prerequisite:** the corpus must exist first — tools reference corpus
35
+ **component names** and **declared properties**, so if a name/property isn't in
36
+ the corpus, author it there (sightmap-authoring) before referencing it here.
37
+
38
+ ## Mental model
39
+
40
+ - A **tool** is one atomic action at a single point in time — no navigation
41
+ crossing mid-tool. It bundles ordered `steps` (fill/click/…) and/or a
42
+ `returns` read, and yields a structured result.
43
+ - Multi-step flows are **not executed** by a runtime. A **journey** is a
44
+ compile-time ordering over tools that compiles into **guidance breadcrumbs**
45
+ ("after `add_task`, consider `list_tasks`") attached to each tool's result. The
46
+ agent sequences; you hand it the map.
47
+ - Tools address elements by **component query** (component identity + extracted
48
+ properties + descendant scope), never raw CSS.
49
+
50
+ ## File shape
51
+
52
+ Each `.sightkick/*.yaml` file may set any of these top-level keys; they're merged
53
+ across the directory:
54
+
55
+ ```yaml
56
+ version: 1 # optional (defaults to 1); set it once
57
+ name: myapp # optional — the IR name; defaults to the app dir's name
58
+ corpus: ../.sightmap # optional — path to the corpus, relative to .sightkick/;
59
+ # defaults to the sibling ../.sightmap
60
+ tools: [ ... ] # the tools (at least one across the whole directory)
61
+ journeys: [ ... ] # optional
62
+ ```
63
+
64
+ The singular fields (`version`/`name`/`corpus`) are taken from whichever file
65
+ sets them (a conflict warns); `tools` and `journeys` accumulate. Most apps set
66
+ `version`/`name` in the first file and never touch `corpus`.
67
+
68
+ ## Tools
69
+
70
+ ```yaml
71
+ - name: add_task # required, unique
72
+ description: Add a task. # shown in getTools(); a result-shape hint is appended automatically
73
+ mode: live # live (default) drives the DOM; api is opt-in reads-only
74
+ ensure_view: Home # a corpus VIEW name — scopes component resolution to that
75
+ # view (+ globals) AND view-scopes the tool at runtime
76
+ # (it only registers on pages whose route matches)
77
+ params: # become the tool's input schema; referenced as {{name}}
78
+ - name: title
79
+ type: string # string | number | boolean | enum
80
+ required: true
81
+ description: The task title.
82
+ # values: [A, B, C] # required when type: enum
83
+ guard: # optional idempotency guard — exactly one of present/absent
84
+ present: { query: 'TaskItem[title="{{title}}"]' } # SKIP the steps when this exists
85
+ # absent: { query: ... } # SKIP when it does NOT exist
86
+ steps: [ ... ] # ordered actions (below)
87
+ returns: { ... } # the structured result (below)
88
+ ```
89
+
90
+ A `live` tool needs **at least one `step` or a `returns`**. `ensure_view` is
91
+ optional but recommended — it both disambiguates component names and controls
92
+ which page the tool appears on.
93
+
94
+ ### Steps (each is a single-key mapping: the op)
95
+
96
+ | Step | Body | Does |
97
+ |------|------|------|
98
+ | `fill` | `query`, `value` | Type `value` (supports `{{param}}`) into the matched input. |
99
+ | `click` | `query` | Click the matched element. |
100
+ | `wait_for` | `query`, `timeout_ms` (default 5000) | Wait until the query matches — use after a mutating action to confirm the visible result. |
101
+ | `navigate` | `view` | Client-navigate to a corpus **view** by name. |
102
+ | `goto` | `url` | Navigate to a URL template (`{{param}}` interpolated). |
103
+
104
+ Reads are **not** steps — declare them with `returns`. A tool that ends with a
105
+ mutation should `wait_for` its own visible feedback before returning.
106
+
107
+ ### Returns (exactly one of `value` or `list`, or description-only)
108
+
109
+ ```yaml
110
+ returns:
111
+ description: One task title. # optional; also folded into the tool description
112
+ value: # a single scalar
113
+ query: 'TaskItem[title="{{title}}"]'
114
+ property: title # a DECLARED corpus property of the matched component
115
+ ```
116
+
117
+ ```yaml
118
+ returns:
119
+ description: The current task rows.
120
+ list: # an array of objects, one per match
121
+ rows: TaskItem # a compquery; every match is a row
122
+ fields: # outputName: <declared property of the row>
123
+ title: title # scalar shorthand, or: title: { property: title }
124
+ done: done
125
+ ```
126
+
127
+ ## Component queries (CSS-shaped, over corpus components)
128
+
129
+ - `Component` — by corpus component name.
130
+ - `Component[prop="v"]` — filter on an **extracted property** (not a raw DOM
131
+ attribute). Operators: `=` exact, `^=` prefix, `*=` substring; append ` i` for
132
+ case-insensitive (e.g. `[label="done" i]`).
133
+ - `A B` — descendant; the **last** component is the target, an ancestor predicate
134
+ scopes it (`TaskItem[title="{{title}}"] TaskToggle`). There is **no `>`** child
135
+ combinator — use whitespace.
136
+ - `Component#N` — 0-based occurrence when several match (weak fallback; prefer a
137
+ distinguishing property).
138
+ - `{{param}}` interpolates a tool param into any query/value/url.
139
+
140
+ Every property you filter on or read must be **declared in the corpus**. If a
141
+ label is CSS-uppercased on screen but lowercase in the DOM text, match
142
+ case-insensitively with ` i`.
143
+
144
+ ## Journeys → guidance (not execution)
145
+
146
+ ```yaml
147
+ journeys:
148
+ - name: add_and_review
149
+ description: Add a task, then review the list.
150
+ steps:
151
+ - add_task # bare tool name
152
+ - tool: list_tasks # or a mapping with a reason
153
+ reason: see the task you just added
154
+ ```
155
+
156
+ Each journey needs **≥2 steps** to produce guidance edges. A tool shared across
157
+ journeys accumulates the union of its successors. Journeys never navigate or run
158
+ anything — they only shape the breadcrumbs in results.
159
+
160
+ ## Worked example (a task-list app)
161
+
162
+ One file, `.sightkick/tools.yaml` (no `corpus:` — it defaults to the sibling
163
+ `../.sightmap`). Split it across several `.sightkick/*.yaml` files once it grows.
164
+
165
+ ```yaml
166
+ version: 1
167
+ name: tasks
168
+ tools:
169
+ - name: list_tasks
170
+ description: List the current tasks and whether each is done.
171
+ ensure_view: Home
172
+ returns:
173
+ description: The task rows (title + done-state).
174
+ list:
175
+ rows: TaskItem
176
+ fields:
177
+ title: title
178
+ done: done
179
+
180
+ - name: add_task
181
+ description: Add a task to the list.
182
+ ensure_view: Home
183
+ params:
184
+ - name: title
185
+ type: string
186
+ required: true
187
+ description: The task title.
188
+ steps:
189
+ - fill: { query: NewTaskInput, value: "{{title}}" }
190
+ - click: { query: AddTaskButton }
191
+ - wait_for: { query: 'TaskItem[title="{{title}}"]' }
192
+ returns:
193
+ description: The title of the new task.
194
+ value: { query: 'TaskItem[title="{{title}}"]', property: title }
195
+
196
+ - name: complete_task
197
+ description: Mark a task done by clicking its toggle.
198
+ ensure_view: Home
199
+ params:
200
+ - name: title
201
+ type: string
202
+ required: true
203
+ description: The task to complete.
204
+ steps:
205
+ - click: { query: 'TaskItem[title="{{title}}"] TaskToggle' }
206
+ - wait_for: { query: 'TaskItem[title="{{title}}"] TaskToggle[label="Undo"]' }
207
+ returns:
208
+ value: { query: 'TaskItem[title="{{title}}"]', property: done }
209
+
210
+ journeys:
211
+ - name: add_and_review
212
+ description: Add a task, then review the list.
213
+ steps:
214
+ - add_task
215
+ - tool: list_tasks
216
+ reason: confirm the task you just added
217
+ ```
218
+
219
+ ## Build & fix
220
+
221
+ ```sh
222
+ sightkick build <APP_DIR> -o /tmp/x.ir.json # <APP_DIR> holds .sightkick/ + .sightmap/
223
+ sightkick build <APP_DIR> --verify # also checks returns extractors against captured
224
+ # view snapshots; warns on fields empty on every row
225
+ ```
226
+
227
+ The compiler is your validator. Common diagnostics and fixes:
228
+
229
+ - **unresolved component / property / view** — the name isn't in the corpus (for
230
+ that view). `build` prints candidates; fix the query, or declare the
231
+ component/property in the corpus (sightmap-authoring). Remember property refs
232
+ resolve against the **row/target** component.
233
+ - **`returns has both value and list`** — pick one.
234
+ - **`live tool needs at least one step or a returns`** — add a step or a read.
235
+ - **`unrecognized step op`** / **`not a single-key mapping`** — each step is one
236
+ op key (`fill`/`click`/`wait_for`/`navigate`/`goto`) with its body.
237
+ - **`--verify` says a field resolves empty on every row** — the declared property
238
+ extracts nothing on the live DOM; fix the property's extractor in the corpus.
239
+
240
+ For `--verify` you need a captured snapshot of the view (`sightmap capture` /
241
+ `snapshot` in the sightmap-browser skill). Once `build` is clean, run the tools
242
+ on a live page with the **sightkick-debug** skill.
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: sightkick-debug
3
- description: Drive and debug a generated sightkick WebMCP tool script on a live page via `sightmap browser`, with no bespoke sightkick extension. Use when you have a sightkick corpus + `webmcp.tools.yaml` (in the sightkick repo's `examples/` or a `sites/<name>/` dir) and want to inject its compiled tools into a running site and exercise them — either agent-driven (getTools/executeTool over `sightmap browser eval`) or Gemini-driven via the vendored WebMCP inspector.
3
+ description: Drive and debug a generated sightkick WebMCP tool script on a live page via `sightmap browser`, with no bespoke sightkick extension. Use when you have a sightkick `.sightkick/` tool layer + `.sightmap/` corpus (in the sightkick repo's `examples/` or a `sites/<name>/` dir) and want to inject its compiled tools into a running site and exercise them — either agent-driven (getTools/executeTool over `sightmap browser eval`) or Gemini-driven via the vendored WebMCP inspector.
4
4
  activation:
5
- - a sightkick `webmcp.tools.yaml` + `.sightmap/` corpus is present and you want to run its tools on a live page
5
+ - a sightkick `.sightkick/` tool layer + `.sightmap/` corpus is present and you want to run its tools on a live page
6
6
  - debugging why a generated WebMCP tool does/doesn't register or fire on a real site
7
7
  ---
8
8
 
9
9
  # sightkick-debug: run generated WebMCP tools on a live page
10
10
 
11
- sightkick compiles a `.sightmap/` corpus + a `webmcp.tools.yaml` into an **IR**,
11
+ sightkick compiles a `.sightmap/` corpus + a `.sightkick/` tool layer into an **IR**,
12
12
  and a ~19 KB runtime bundle turns that IR into WebMCP tools on
13
13
  `document.modelContext`. You do **not** need a bespoke extension to get them onto
14
14
  a page: `sightmap browser eval` injects the runtime bundle, then
@@ -29,10 +29,39 @@ sightmap browser install # Chrome-for-Testing; needs >=152 for n
29
29
 
30
30
  That installs the **`sightmap-browser`** skill (driving a live session) and
31
31
  **`sightmap-authoring`** skill (building the `.sightmap/` corpus) alongside this
32
- one — everything needed to build and test a `webmcp.tools.yaml`. No repo checkout
33
- is required; `<CORPUS_DIR>` below is any directory holding a `webmcp.tools.yaml`
32
+ one — everything needed to build and test a `.sightkick/` tool layer. No repo checkout
33
+ is required; `<CORPUS_DIR>` below is any directory holding a `.sightkick/` tool layer
34
34
  + `.sightmap/` corpus.
35
35
 
36
+ To **write** the `.sightkick/` tool layer (its tool/step/`returns`/`journeys` grammar),
37
+ see the **`sightkick-authoring`** skill — this skill assumes it already exists and
38
+ compiles.
39
+
40
+ ## Quick start: `sightkick browser`
41
+
42
+ For the common case, one command does the whole setup — build the IR, start a
43
+ sightmap session, and persist-inject the runtime + tools:
44
+
45
+ ```sh
46
+ sightkick browser <CORPUS_DIR>
47
+ # auto-opens the corpus's home-view URL (or pass --url URL)
48
+ # --webmcp expose the native document.modelContext (adds the blink flags)
49
+ # --extensions P load an unpacked extension (e.g. a WebMCP inspector)
50
+ # --profile/--cdp-port/--chrome-flag passed through to sightmap
51
+ # --no-start inject into an already-running session (e.g. after editing the corpus)
52
+ ```
53
+
54
+ It runs sightmap from `<CORPUS_DIR>`, so the session lives in that dir's
55
+ `.sightmap/` — **drive it from there** (`cd <CORPUS_DIR> && sightmap browser
56
+ eval …`, see §4). The inject is **persisted**, so on SPA route changes (and, once
57
+ sightmap's persist re-injection lands, full navigations too) the tools
58
+ re-register automatically. Re-run `sightkick browser <CORPUS_DIR> --no-start`
59
+ after editing the corpus to refresh the injected IR.
60
+
61
+ The manual steps below (§1–§3) are the same thing unbundled — reach for them for
62
+ finer control, to understand what's happening, or when driving a site the wrapper
63
+ didn't start.
64
+
36
65
  ## 1. Build the two artifacts
37
66
 
38
67
  Both come straight from the installed `sightkick` CLI:
@@ -41,7 +70,7 @@ Both come straight from the installed `sightkick` CLI:
41
70
  # The payload: the runtime bundle (exposes window.__sightkick.load).
42
71
  sightkick runtime -o /tmp/sightkick-runtime.js
43
72
 
44
- # The IR for the corpus you're testing (any dir with webmcp.tools.yaml + .sightmap/).
73
+ # The IR for the corpus you're testing (any dir with .sightkick/ + .sightmap/).
45
74
  sightkick build <CORPUS_DIR> -o /tmp/x.ir.json
46
75
  ```
47
76
 
@@ -109,18 +138,45 @@ sleep 1; sightmap browser eval "window.__t" # -> ["tool_a","t
109
138
 
110
139
  ## 4. Drive the tools
111
140
 
112
- ### Mode A (polyfill): drive over eval
113
- The polyfill's `executeTool(tool, args)` accepts a bare `{name}`:
141
+ ### Mode A (agent/scripted): drive via `sightkick call`
142
+ The scripted entry point is `sightkick call <app-dir> <tool> --param k=v ...`. It
143
+ prints the resolved `ToolResult` as JSON and exits non-zero on `ok:false`.
144
+ `--via` picks how the tool runs:
114
145
 
115
146
  ```sh
116
- sightmap browser eval "window.__r='RUN';document.modelContext.executeTool({name:'search'},{query:'ATL to LHR'}).then(function(r){window.__r=r.content[0].text}).catch(function(e){window.__r='ERR '+e});'go'"
117
- sleep 2; sightmap browser eval "window.__r" # the ToolResult JSON (ok/value/items/guidance)
147
+ sightkick call . add_task --param title="Water the plants" # --via webmcp (default)
148
+ sightkick call . add_task --param title="Water the plants" --via cli # translate steps to CLI commands
118
149
  ```
119
150
 
120
- The result envelope is `{content:[{type:'text',text:<ToolResult JSON>}]}`; the
121
- `ToolResult` carries `ok`, `value`/`items`, `skipped` (idempotency guard hit),
122
- and `guidance` (next-step breadcrumbs). The sightkick repo's
123
- `packages/runtime/eval/run.mjs` is a full scripted example of this loop.
151
+ **`--via webmcp`** asks the page's own registered tool to run itself, so it
152
+ exercises the contract a real WebMCP client uses. It requires the runtime on the
153
+ page that's what step 3 above sets up — and a tool is only registered on its
154
+ own view, so an out-of-view tool simply isn't there to call.
155
+
156
+ **`--via cli`** translates the tool's steps into `sightmap browser <verb>`
157
+ commands and needs no runtime at all: a plain `sightmap browser start` is
158
+ enough. Two reasons to reach for it:
159
+
160
+ - **Portal-rendered targets.** The runtime clicks by dispatching synthetic
161
+ events, which do not reach elements rendered outside the app's own DOM
162
+ subtree — dropdown menu items and modal buttons. The same element takes a
163
+ real click fine, so a tool that silently no-ops under `--via webmcp` often
164
+ works under `--via cli`.
165
+ - **Any page you don't control.** A third-party app has no sightkick runtime on
166
+ it, so `--via webmcp` has nothing to call.
167
+
168
+ The `ToolResult` carries `ok`, `value`/`items`, `skipped` (idempotency guard
169
+ hit), and `guidance` (next-step breadcrumbs). The sightkick repo's
170
+ `packages/runtime/eval/run.mjs` is a full scripted example driving the WebMCP
171
+ surface directly.
172
+
173
+ > **Don't script `document.modelContext.executeTool` yourself.** Its call shape
174
+ > differs by surface: the polyfill takes a bare `{name}`, but a **native**
175
+ > `document.modelContext` (present on Chrome ≥150 even with no blink flags) is
176
+ > stricter and rejects it with `Failed to parse input arguments`, and wraps the
177
+ > result in an envelope (`{content:[{type:'text',text:<ToolResult JSON>}]}`).
178
+ > `sightkick call` handles both — use it for Mode A, and leave `executeTool` to
179
+ > the inspector in Mode B.
124
180
 
125
181
  ### Mode B (native): drive via the inspector
126
182
  Open the inspector's sidebar and prompt Gemini — it enumerates