@kurrent/kcap 1.1.0 → 1.2.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/bin/refresh.js +1 -0
- package/kcap/.claude-plugin/plugin.json +1 -1
- package/kcap/.codex-mcp.json +4 -0
- package/kcap/.mcp.json +6 -0
- package/kcap/README.md +17 -0
- package/kcap/hooks/hooks.json +26 -0
- package/kcap/skills/eval-watch/SKILL.md +190 -0
- package/package.json +9 -9
package/bin/refresh.js
CHANGED
package/kcap/.codex-mcp.json
CHANGED
package/kcap/.mcp.json
CHANGED
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
"cwd": "${CLAUDE_PROJECT_DIR}",
|
|
36
36
|
"description": "Plan ledger — declare the plan, spec or design document this session works from (declare_plan_document), declare its task list (set_plan_tasks), record each task's status change (update_plan_task) and read the plan back after compaction (get_plan)."
|
|
37
37
|
},
|
|
38
|
+
"kcap-artefacts": {
|
|
39
|
+
"command": "kcap",
|
|
40
|
+
"args": ["mcp", "artefacts"],
|
|
41
|
+
"cwd": "${CLAUDE_PROJECT_DIR}",
|
|
42
|
+
"description": "Publish a self-contained HTML page — a plan, a report, a comparison — and get back a link to share. The page is sandboxed with no network access, so inline everything as data URIs. Private until you set visibility."
|
|
43
|
+
},
|
|
38
44
|
"kcap-analytics": {
|
|
39
45
|
"command": "kcap",
|
|
40
46
|
"args": ["mcp", "analytics"],
|
package/kcap/README.md
CHANGED
|
@@ -57,6 +57,23 @@ Governed read-only SQL over the org's curated coding-agent analytics views (sess
|
|
|
57
57
|
|
|
58
58
|
Repo-aware: defaults to the cwd's repo; pass `scope: "global"` for org-wide questions. Requires `kcap login` and a kcap-server new enough to expose the `/api/analytics` endpoints.
|
|
59
59
|
|
|
60
|
+
### `kcap-artefacts`
|
|
61
|
+
|
|
62
|
+
Publish a self-contained HTML page — a plan, a report, a comparison — and get back a link to share. The page is served under a sandbox with no network access, so every style, script and image has to be inlined as a data URI; an external URL renders as nothing.
|
|
63
|
+
|
|
64
|
+
| Tool | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `publish_artefact` | Publish a page (`title`, and either `html` or a local `path`); optional `description`, `visibility`, `grants`, `session_ids`, `response_schema` to make it answerable, and `update_id` to revise an existing artefact without changing its URL |
|
|
67
|
+
| `await_artefact_responses` | Block until people have answered — the human checkpoint. Returns on a respondent count, on a close, or on a timeout (which is not an error) |
|
|
68
|
+
| `get_artefact_results` | Tallies and each person's current answer, without waiting |
|
|
69
|
+
| `close_artefact_responses` | Freeze a version's answers; `closed: false` reopens and clears any deadline |
|
|
70
|
+
| `list_my_artefacts` | The artefacts you can see — id, title, audience, latest version, URL |
|
|
71
|
+
| `set_artefact_visibility` | Replace an artefact's audience (`none` / `org` / `scoped` + grants) |
|
|
72
|
+
|
|
73
|
+
An artefact is private to its owner until visibility says otherwise. The current session is cited automatically from `KCAP_SESSION_ID`, so a publish is attributed to the work that produced it.
|
|
74
|
+
|
|
75
|
+
Declaring a `response_schema` turns the page into a form the server validates and tallies: fields of type `choice`, `multi`, `score` or `text`, plus a `results_mode` deciding what other viewers see — `owner` (default, only you), `aggregate` (tallies, no names or free text) or `named` (who said what). Who answered is always the authenticated viewer; there is no field for it to forge. Reading, version history and takedown stay in the web UI.
|
|
76
|
+
|
|
60
77
|
`kcap mcp judge` is intentionally not auto-registered. Add it with `claude mcp add kcap-judge -- kcap mcp judge` if you want it.
|
|
61
78
|
|
|
62
79
|
**Hooks** — Automatically captures session activity and forwards it to the Kurrent Capacitor server:
|
package/kcap/hooks/hooks.json
CHANGED
|
@@ -68,6 +68,32 @@
|
|
|
68
68
|
]
|
|
69
69
|
}
|
|
70
70
|
],
|
|
71
|
+
"PostToolUse": [
|
|
72
|
+
{
|
|
73
|
+
"matcher": "*",
|
|
74
|
+
"hooks": [
|
|
75
|
+
{
|
|
76
|
+
"type": "command",
|
|
77
|
+
"command": "kcap hook --claude --no-update-check",
|
|
78
|
+
"timeout": 5,
|
|
79
|
+
"async": true
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"PostToolUseFailure": [
|
|
85
|
+
{
|
|
86
|
+
"matcher": "*",
|
|
87
|
+
"hooks": [
|
|
88
|
+
{
|
|
89
|
+
"type": "command",
|
|
90
|
+
"command": "kcap hook --claude --no-update-check",
|
|
91
|
+
"timeout": 5,
|
|
92
|
+
"async": true
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
],
|
|
71
97
|
"Notification": [
|
|
72
98
|
{
|
|
73
99
|
"hooks": [
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eval-watch
|
|
3
|
+
description: >-
|
|
4
|
+
Follow a kcap import that setup just started and the evals landing on it —
|
|
5
|
+
"Follow my kcap import" (the prompt `kcap setup` hands to your agent), "watch my
|
|
6
|
+
import", "how is my import going", "are my evals done yet". Reads the handoff file
|
|
7
|
+
setup wrote, polls Capacitor analytics for exactly the sessions that import
|
|
8
|
+
created, summarizes the first three completed evals, links to the results and
|
|
9
|
+
offers the guided tour. Not for browsing evals in general — that is the analytics
|
|
10
|
+
or guided-tour skill.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Eval watch
|
|
14
|
+
|
|
15
|
+
You follow ONE import: the sessions listed in a handoff file `kcap setup` wrote. Every query you
|
|
16
|
+
issue names only those sessions — never a repo-wide or tenant-wide read, and never a query without
|
|
17
|
+
an `IN (...)` or `session_id = '...'` bound to this cohort. If a response ever includes a row for a
|
|
18
|
+
session id outside the cohort, discard that row: never count it, link it, or mention it.
|
|
19
|
+
|
|
20
|
+
## The safety rule — read this first
|
|
21
|
+
|
|
22
|
+
Three grammars gate everything below:
|
|
23
|
+
- **Run id** (a `(run: <id>)` token, and the file's own `run_id`): `^[0-9a-f]{32}$`.
|
|
24
|
+
- **Session id** (`session_ids`, `foreground_succeeded_ids`, and any id you place in a query):
|
|
25
|
+
`^[A-Za-z0-9_-]{1,128}$`.
|
|
26
|
+
- **Repo hash** (the `repo_hash` field an analytics response returns, used only in the repo-scoped
|
|
27
|
+
link form): `^[0-9a-f]{16}$`. A `repo_hash` that fails this never reaches a link — fall back to
|
|
28
|
+
the no-repo-hash link form (section 7).
|
|
29
|
+
|
|
30
|
+
The `kcap-analytics` MCP takes raw SQL text with no parameter binding, so the session-id grammar is
|
|
31
|
+
the SQL-safety boundary, not just a format check. The class excludes quote, backslash, whitespace
|
|
32
|
+
and `;`, so a value that matches it can never close a string literal or open a new statement — a
|
|
33
|
+
matching id is emitted as a single-quoted literal (`'<id>'`) with **no escaping**, because none is
|
|
34
|
+
needed and none should be attempted. A run of hyphens such as `--` matches the grammar and is safe
|
|
35
|
+
to emit this way: inside a quoted literal it is data, not a SQL comment. An id that fails either
|
|
36
|
+
grammar is **never** placed in SQL, a path, or a link — reject it and move on (section 2 below says
|
|
37
|
+
what each layer's rejection does); never sanitize or escape an id into something that would pass.
|
|
38
|
+
|
|
39
|
+
## 1. Find the run
|
|
40
|
+
|
|
41
|
+
- If the prompt carries a line `(run: <id>)`, `<id>` must match `^[0-9a-f]{32}$` before you use it
|
|
42
|
+
as a filename component. If it does not match, treat the prompt as carrying no run id at all. If
|
|
43
|
+
it does match, the run is bound: read ONLY `<config>/import-handoff-<id>.json`, where `<config>`
|
|
44
|
+
is `$KCAP_CONFIG_DIR` when set, else `~/.config/kcap`, and check it against every Layer A rule in
|
|
45
|
+
section 2, plus one more: its `run_id` field must equal `<id>` exactly. Missing, unreadable, or
|
|
46
|
+
failing any of those checks → say so and CLOSE (section 7) with no query. **Never fall through to
|
|
47
|
+
another run's file** — a different run's cohort does not belong to the run the user asked for.
|
|
48
|
+
- Otherwise list `<config>/import-handoff-*.json`, keep the locator-valid ones (section 2) that are
|
|
49
|
+
watchable (`handoff_offered` true, or `handoff_suppressed` is `skill_not_installed` or
|
|
50
|
+
`no_agent_detected`), pick the newest `written_at`, and say which others you passed over.
|
|
51
|
+
- Nothing qualifies → say you found no import to follow and CLOSE with no query.
|
|
52
|
+
|
|
53
|
+
## 2. Validate the file — three layers
|
|
54
|
+
|
|
55
|
+
A failure at a lower layer never re-opens the file for a higher one.
|
|
56
|
+
|
|
57
|
+
**Layer A** (may this file be selected at all): JSON parses; `schema_version` is 1; `run_id` matches
|
|
58
|
+
`^[0-9a-f]{32}$`; `written_at` parses as ISO-8601 and is within the last 24 hours; `handoff_offered`
|
|
59
|
+
is a boolean. Fail → not selectable; try the next candidate file, or close with no query if none
|
|
60
|
+
remain.
|
|
61
|
+
|
|
62
|
+
**Layer B** (may its ids drive data): `cohort` ∈ {exact, partial_exact, unknown}; `background` ∈
|
|
63
|
+
{not_needed, running, exited_zero, failed}; `foreground_certainty` ∈ {complete, incomplete};
|
|
64
|
+
`handoff_suppressed` is null or one of import_failed, no_new_sessions, nothing_landed,
|
|
65
|
+
analytics_not_in_plan, skill_not_installed, no_agent_detected; every entry of `session_ids` and
|
|
66
|
+
`foreground_succeeded_ids` matches `^[A-Za-z0-9_-]{1,128}$`; `unattributed_on_disk` is a
|
|
67
|
+
non-negative integer. Fail → **no data**: the file stays selected, but say the record is unreadable
|
|
68
|
+
and CLOSE with links only (section 7), no query.
|
|
69
|
+
|
|
70
|
+
**Layer C** (may it supply links): `server_url` starts with `http://` or `https://`. Fail → no
|
|
71
|
+
file-sourced links (fall back to `kcap whoami` per section 7) and no query — an unverifiable
|
|
72
|
+
`server_url` also means the server-binding check in section 4 cannot run. `background_log` and
|
|
73
|
+
`profile` are shown as plain text — never as a link or a command to run — only when they contain no
|
|
74
|
+
control characters and are under 512 characters; otherwise omit them.
|
|
75
|
+
|
|
76
|
+
Never splice an unvalidated value into SQL, a path, or a link.
|
|
77
|
+
|
|
78
|
+
## 3. No-handoff files
|
|
79
|
+
|
|
80
|
+
If `handoff_offered` is false, branch on `handoff_suppressed` and CLOSE:
|
|
81
|
+
- `no_new_sessions` → "nothing to watch — that import found no new sessions".
|
|
82
|
+
- `nothing_landed` → "the sessions that import selected were skipped at import time"; point at
|
|
83
|
+
`kcap import --all` for per-session reasons; no retry.
|
|
84
|
+
- `analytics_not_in_plan` → the plan sentence (section 6); the import ran; no retry.
|
|
85
|
+
- `import_failed` → "that import did not get running"; name `kcap import --all --yes` and the
|
|
86
|
+
`background_log` when displayable.
|
|
87
|
+
- `skill_not_installed` / `no_agent_detected` → the import ran and you evidently have the skill
|
|
88
|
+
now: continue as if offered, watching the file's cohort as usual (sections 4–5).
|
|
89
|
+
|
|
90
|
+
## 4. Bind to the server — fail closed
|
|
91
|
+
|
|
92
|
+
If the selected file's `cohort` is `unknown`, skip this check and go straight to section 5 — there
|
|
93
|
+
is no cohort to query either way, and the mismatch remediation below would be misleading when
|
|
94
|
+
watching was never possible.
|
|
95
|
+
|
|
96
|
+
Otherwise, run `kcap whoami` and compare its server URL to `server_url`: lowercase scheme and host,
|
|
97
|
+
drop a default port (80 for http, 443 for https only), trim a trailing slash, compare the path
|
|
98
|
+
as-is. Match → continue to section 5. Mismatch or `whoami` failure → issue **NO** analytics query.
|
|
99
|
+
CLOSE with the file's links and this remediation: start your agent from a shell where `kcap whoami`
|
|
100
|
+
reports `<server_url>` — set `KCAP_PROFILE=<profile>`, unset `KCAP_URL`, and set `KCAP_CONFIG_DIR`
|
|
101
|
+
if kcap uses a custom config directory — then prompt again.
|
|
102
|
+
|
|
103
|
+
## 5. Watch the cohort
|
|
104
|
+
|
|
105
|
+
Cohort = `session_ids` (`cohort: unknown` or empty → CLOSE with links, no query). Open with:
|
|
106
|
+
"Watching N sessions from this import (K landed before the handoff)" — K is
|
|
107
|
+
`foreground_succeeded_ids.length`; for `partial_exact` add "the 500 most recent; older ones may
|
|
108
|
+
land and evaluate unobserved"; if `unattributed_on_disk` > 0 add the sentence from section 7.
|
|
109
|
+
|
|
110
|
+
Every query uses `query_analytics` with `scope: 'global'` (the MCP defaults to the caller's cwd
|
|
111
|
+
repo and will not widen on its own), **ONE call at a time — never two in flight** — and names only
|
|
112
|
+
cohort ids, each already validated against the grammar above, as single-quoted literals in an
|
|
113
|
+
`IN (...)` list or `session_id = '...'`. If a response ever includes a row whose `session_id` is
|
|
114
|
+
not in `session_ids`, drop that row before using the response for anything.
|
|
115
|
+
|
|
116
|
+
**Request budget:** at most 20 cohort queries per poll. `floor = ceil(N / 20)`. The FIRST poll uses
|
|
117
|
+
batch size `floor` exactly — no probing at a smaller size first. Every batch passes `max_rows`
|
|
118
|
+
equal to its own size:
|
|
119
|
+
|
|
120
|
+
SELECT s.session_id, s.repo_hash, e.eval_run_id, e.evaluated_at, e.overall_score, e.judge_model
|
|
121
|
+
FROM v_an_sessions s LEFT JOIN v_an_eval_summaries e ON e.session_id = s.session_id
|
|
122
|
+
WHERE s.session_id IN ('<id>', '<id>', ...)
|
|
123
|
+
|
|
124
|
+
A row = arrived; a non-null `eval_run_id` = completed. Each response body carries `max_rows`, the
|
|
125
|
+
server's effective cap. After the first poll: if any body reported `max_rows < floor`, fail closed
|
|
126
|
+
at once — say the server's row cap is below what watching this many sessions needs, and CLOSE,
|
|
127
|
+
having spent no more than the 20-query budget. Otherwise raise the batch to `min(100, max_rows)`
|
|
128
|
+
for later polls. Any batch that comes back `truncated: true`, in the first poll or any later one,
|
|
129
|
+
fails that poll — the cap decides, not the truncation flag alone.
|
|
130
|
+
|
|
131
|
+
One poll = every batch it needs, each non-truncated. A failed or truncated batch discards the whole
|
|
132
|
+
poll — nothing from it commits — and counts as one failure toward the two-failure stop rule.
|
|
133
|
+
|
|
134
|
+
A 429 fails the poll. If its text matches `retry after (\d+)s`, wait that many seconds before the
|
|
135
|
+
next poll; otherwise wait 60 seconds. Never poll sooner than the 30-second cadence either way. HTTP
|
|
136
|
+
403 with `analytics_not_in_plan` → CLOSE immediately with the plan sentence (section 6); no
|
|
137
|
+
polling, no retry, no wait.
|
|
138
|
+
|
|
139
|
+
Progress = cohort ids that arrived / cohort size, computed from the batch rows only — never a
|
|
140
|
+
separate count, never repo- or tenant-wide. Cadence: first snapshot immediately, then every 30
|
|
141
|
+
seconds.
|
|
142
|
+
|
|
143
|
+
**Stop**, evaluated in this order after each poll whose results have already committed:
|
|
144
|
+
1. Three distinct cohort sessions have a completed eval → summarize the first three, ordered by
|
|
145
|
+
`evaluated_at` then `session_id` (the same tie-break applies to rows already present at the very
|
|
146
|
+
first snapshot, so more than three arriving at once always picks the same three).
|
|
147
|
+
2. `cohort: exact`, non-empty, and every id in `session_ids` has a completed eval → summarize what
|
|
148
|
+
completed. Never fires for `partial_exact` or an empty cohort.
|
|
149
|
+
3. Two consecutive failed polls → stop, reporting the failures.
|
|
150
|
+
4. 10 minutes since the first snapshot → stop. No new poll starts after the deadline, but a poll
|
|
151
|
+
already dispatched before it finishes normally, and its outcome is still checked against rules
|
|
152
|
+
1–3 first: a third distinct completion in that final poll wins over the deadline summary, and if
|
|
153
|
+
that final poll is itself the second consecutive failure, report it as the deadline stop while
|
|
154
|
+
still naming the failures.
|
|
155
|
+
|
|
156
|
+
No idle early-stop — quiet polls are normal and are not a reason to stop early.
|
|
157
|
+
|
|
158
|
+
**Per-session detail** (one session per query, at most 6 queries across the whole run — enough for
|
|
159
|
+
the three sessions rule 1 summarizes):
|
|
160
|
+
|
|
161
|
+
SELECT category, AVG(score) AS mean FROM v_an_eval_scores WHERE session_id = '<id>' GROUP BY category
|
|
162
|
+
SELECT question_id, score FROM v_an_eval_scores WHERE session_id = '<id>' ORDER BY score ASC, question_id ASC LIMIT 2
|
|
163
|
+
|
|
164
|
+
Strongest = highest mean, weakest = lowest, ties alphabetical by category. A truncated or failed
|
|
165
|
+
detail query, or a session with no score rows, → summarize that session by `overall_score` alone
|
|
166
|
+
and say so; this never affects poll success or the stop rules above. When the `kcap-sessions` MCP is
|
|
167
|
+
available, use it to enrich a session's title; otherwise the session id is enough.
|
|
168
|
+
|
|
169
|
+
## 6. When little or nothing completes
|
|
170
|
+
|
|
171
|
+
Explain the real gates plainly: auto-eval may be off for a repository; the server needs an eval
|
|
172
|
+
agent configured; sessions under the minimum event count are skipped; already-evaluated sessions
|
|
173
|
+
are not re-run; a recovery sweep runs hourly. If the analytics MCP is absent entirely, close
|
|
174
|
+
immediately with the block below.
|
|
175
|
+
|
|
176
|
+
Plan sentence (used here and from sections 3 and 5): "Insights isn't in this workspace's plan, so
|
|
177
|
+
evals can't be followed from here; your import continues and evals appear in the Capacitor UI."
|
|
178
|
+
|
|
179
|
+
## 7. CLOSE — always
|
|
180
|
+
|
|
181
|
+
1. How to keep watching: prompt `Follow my kcap import` again (with the same `(run: <id>)` line
|
|
182
|
+
when you were bound to one).
|
|
183
|
+
2. Links: with a `repo_hash` matching `^[0-9a-f]{16}$`, `<server_url>/repo/<repo_hash>/sessions/<session_id>?tab=evaluation`;
|
|
184
|
+
otherwise (no `repo_hash`, or one that fails the grammar) `<server_url>/sessions/<session_id>?tab=evaluation`;
|
|
185
|
+
all results `<server_url>/sessions`. No valid file → `server_url` from `kcap whoami`; if that
|
|
186
|
+
fails too, say the results live in the Capacitor server UI and that `kcap whoami` prints the URL
|
|
187
|
+
once logged in.
|
|
188
|
+
3. When `unattributed_on_disk` > 0: "N sessions on disk had no repository match; any of them that
|
|
189
|
+
imported show without one — `kcap remap` places them."
|
|
190
|
+
4. Offer the guided tour with the exact prompt `Start kcap guided tour`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kurrent/kcap",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI companion for Kurrent Capacitor — records and visualizes Claude Code sessions",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"repository": {
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
"postinstall": "node bin/postinstall.js"
|
|
15
15
|
},
|
|
16
16
|
"optionalDependencies": {
|
|
17
|
-
"@kurrent/kcap-darwin-arm64": "1.
|
|
18
|
-
"@kurrent/kcap-linux-arm64": "1.
|
|
19
|
-
"@kurrent/kcap-linux-musl-arm64": "1.
|
|
20
|
-
"@kurrent/kcap-linux-musl-x64": "1.
|
|
21
|
-
"@kurrent/kcap-linux-x64": "1.
|
|
22
|
-
"@kurrent/kcap-win-x64": "1.
|
|
17
|
+
"@kurrent/kcap-darwin-arm64": "1.2.0",
|
|
18
|
+
"@kurrent/kcap-linux-arm64": "1.2.0",
|
|
19
|
+
"@kurrent/kcap-linux-musl-arm64": "1.2.0",
|
|
20
|
+
"@kurrent/kcap-linux-musl-x64": "1.2.0",
|
|
21
|
+
"@kurrent/kcap-linux-x64": "1.2.0",
|
|
22
|
+
"@kurrent/kcap-win-x64": "1.2.0"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"bin/",
|
|
26
26
|
"kcap/"
|
|
27
27
|
],
|
|
28
|
-
"kcapReleaseCommit": "
|
|
29
|
-
"kcapReleaseTag": "v1.
|
|
28
|
+
"kcapReleaseCommit": "d7a1d7225e72573766636da70325ba136bb37566",
|
|
29
|
+
"kcapReleaseTag": "v1.2.0"
|
|
30
30
|
}
|