@kurrent/kcap 1.3.0-beta.6 → 1.3.0-beta.8
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 +26 -144
- package/bin/resolve.js +2 -7
- package/kcap/.claude-plugin/plugin.json +1 -1
- package/kcap/.codex-mcp.json +2 -2
- package/kcap/.mcp.json +2 -2
- package/kcap/README.md +11 -9
- package/kcap/hooks/hooks.json +11 -10
- package/kcap/skills/agent-flows/SKILL.md +36 -25
- package/kcap/skills/eval-watch/SKILL.md +112 -140
- package/kcap/skills/guided-tour/SKILL.md +258 -470
- package/kcap/skills/recap/SKILL.md +5 -2
- package/kcap/skills/review-flows/SKILL.md +9 -4
- package/kcap/skills/start-agents/SKILL.md +27 -7
- package/kcap/skills/work-items/SKILL.md +54 -22
- package/package.json +9 -9
- package/kcap/hooks/persist-session-id.sh +0 -11
- package/kcap/hooks/set-title-prompt.sh +0 -36
- package/kcap/skills/suggest-review-flow/SKILL.md +0 -94
|
@@ -4,38 +4,31 @@ description: >-
|
|
|
4
4
|
Follow a kcap import that setup just started and the evals landing on it —
|
|
5
5
|
"Follow my kcap import" (the prompt `kcap setup` hands to your agent), "watch my
|
|
6
6
|
import", "how is my import going", "are my evals done yet". Reads the handoff file
|
|
7
|
-
setup wrote, polls
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
skill.
|
|
7
|
+
setup wrote, polls the eval state of exactly the sessions that import created,
|
|
8
|
+
waits for the sessions that imported while the user watched to be evaluated,
|
|
9
|
+
summarizes their evals, links to the results and offers the guided tour. Works on
|
|
10
|
+
every plan. Not for browsing evals in general — that is the guided-tour skill.
|
|
12
11
|
---
|
|
13
12
|
|
|
14
13
|
# Eval watch
|
|
15
14
|
|
|
16
|
-
You follow ONE import: the sessions listed in a handoff file `kcap setup` wrote. Every
|
|
17
|
-
|
|
18
|
-
an
|
|
19
|
-
|
|
15
|
+
You follow ONE import: the sessions listed in a handoff file `kcap setup` wrote. Every lookup you
|
|
16
|
+
make names only those sessions — never a repo-wide or tenant-wide read. If a response ever carries
|
|
17
|
+
an entry for a session id outside the cohort, discard it: never count it, link it, or mention it.
|
|
18
|
+
|
|
19
|
+
Eval state comes from `get_session_evals` in the `kcap-sessions` MCP — never from the
|
|
20
|
+
`kcap-analytics` MCP, which is a paid feature the workspace may not have.
|
|
20
21
|
|
|
21
22
|
## The safety rule — read this first
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
Two grammars gate everything below:
|
|
24
25
|
- **Run id** (a `(run: <id>)` token, and the file's own `run_id`): `^[0-9a-f]{32}$`.
|
|
25
|
-
- **Session id** (`session_ids`, `foreground_succeeded_ids`, and any id you
|
|
26
|
-
`^[A-Za-z0-9_-]{1,128}$`.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
The `kcap-analytics` MCP takes raw SQL text with no parameter binding, so the session-id grammar is
|
|
32
|
-
the SQL-safety boundary, not just a format check. The class excludes quote, backslash, whitespace
|
|
33
|
-
and `;`, so a value that matches it can never close a string literal or open a new statement — a
|
|
34
|
-
matching id is emitted as a single-quoted literal (`'<id>'`) with **no escaping**, because none is
|
|
35
|
-
needed and none should be attempted. A run of hyphens such as `--` matches the grammar and is safe
|
|
36
|
-
to emit this way: inside a quoted literal it is data, not a SQL comment. An id that fails either
|
|
37
|
-
grammar is **never** placed in SQL, a path, or a link — reject it and move on (section 2 below says
|
|
38
|
-
what each layer's rejection does); never sanitize or escape an id into something that would pass.
|
|
26
|
+
- **Session id** (`session_ids`, `foreground_succeeded_ids`, and any id you pass to a tool or put
|
|
27
|
+
in a link): `^[A-Za-z0-9_-]{1,128}$`.
|
|
28
|
+
|
|
29
|
+
An id that fails its grammar is **never** placed in a tool call, a path, or a link — reject it and
|
|
30
|
+
move on (section 2 below says what each layer's rejection does); never sanitize or escape an id
|
|
31
|
+
into something that would pass.
|
|
39
32
|
|
|
40
33
|
## 1. Find the run
|
|
41
34
|
|
|
@@ -44,12 +37,12 @@ what each layer's rejection does); never sanitize or escape an id into something
|
|
|
44
37
|
it does match, the run is bound: read ONLY `<config>/import-handoff-<id>.json`, where `<config>`
|
|
45
38
|
is `$KCAP_CONFIG_DIR` when set, else `~/.config/kcap`, and check it against every Layer A rule in
|
|
46
39
|
section 2, plus one more: its `run_id` field must equal `<id>` exactly. Missing, unreadable, or
|
|
47
|
-
failing any of those checks → say so and CLOSE (section 7) with no
|
|
40
|
+
failing any of those checks → say so and CLOSE (section 7) with no lookup. **Never fall through to
|
|
48
41
|
another run's file** — a different run's cohort does not belong to the run the user asked for.
|
|
49
42
|
- Otherwise list `<config>/import-handoff-*.json`, keep the locator-valid ones (section 2) that are
|
|
50
|
-
watchable (`handoff_offered` true, or `handoff_suppressed` is `
|
|
51
|
-
`no_agent_detected`), pick the newest `written_at`, and say which others you passed over.
|
|
52
|
-
- Nothing qualifies → say you found no import to follow and CLOSE with no
|
|
43
|
+
watchable (`handoff_offered` true, or `handoff_suppressed` is `analytics_not_in_plan`,
|
|
44
|
+
`skill_not_installed` or `no_agent_detected`), pick the newest `written_at`, and say which others you passed over.
|
|
45
|
+
- Nothing qualifies → say you found no import to follow and CLOSE with no lookup.
|
|
53
46
|
|
|
54
47
|
## 2. Validate the file — three layers
|
|
55
48
|
|
|
@@ -57,24 +50,25 @@ A failure at a lower layer never re-opens the file for a higher one.
|
|
|
57
50
|
|
|
58
51
|
**Layer A** (may this file be selected at all): JSON parses; `schema_version` is 1; `run_id` matches
|
|
59
52
|
`^[0-9a-f]{32}$`; `written_at` parses as ISO-8601 and is within the last 24 hours; `handoff_offered`
|
|
60
|
-
is a boolean. Fail → not selectable; try the next candidate file, or close with no
|
|
53
|
+
is a boolean. Fail → not selectable; try the next candidate file, or close with no lookup if none
|
|
61
54
|
remain.
|
|
62
55
|
|
|
63
|
-
**Layer B** (may its ids drive data): `cohort` ∈ {exact, partial_exact, unknown}; `
|
|
64
|
-
{not_needed, running, exited_zero, failed}; `foreground_certainty` ∈ {complete, incomplete};
|
|
56
|
+
**Layer B** (may its ids drive data): `cohort` ∈ {exact, partial_exact, unknown}; `scope` ∈ {all,
|
|
57
|
+
repos} (absent reads as `all`); `background` ∈ {not_needed, running, exited_zero, failed}; `foreground_certainty` ∈ {complete, incomplete};
|
|
65
58
|
`handoff_suppressed` is null or one of import_failed, no_new_sessions, nothing_landed,
|
|
66
|
-
analytics_not_in_plan, skill_not_installed, no_agent_detected
|
|
59
|
+
analytics_not_in_plan, skill_not_installed, no_agent_detected (older setups write
|
|
60
|
+
`analytics_not_in_plan`); every entry of `session_ids` and
|
|
67
61
|
`foreground_succeeded_ids` matches `^[A-Za-z0-9_-]{1,128}$`; `unattributed_on_disk` is a
|
|
68
62
|
non-negative integer. Fail → **no data**: the file stays selected, but say the record is unreadable
|
|
69
|
-
and CLOSE with links only (section 7), no
|
|
63
|
+
and CLOSE with links only (section 7), no lookup.
|
|
70
64
|
|
|
71
65
|
**Layer C** (may it supply links): `server_url` starts with `http://` or `https://`. Fail → no
|
|
72
|
-
file-sourced links (fall back to `kcap whoami` per section 7) and no
|
|
66
|
+
file-sourced links (fall back to `kcap whoami` per section 7) and no lookup — an unverifiable
|
|
73
67
|
`server_url` also means the server-binding check in section 4 cannot run. `background_log` and
|
|
74
68
|
`profile` are shown as plain text — never as a link or a command to run — only when they contain no
|
|
75
69
|
control characters and are under 512 characters; otherwise omit them.
|
|
76
70
|
|
|
77
|
-
Never splice an unvalidated value into
|
|
71
|
+
Never splice an unvalidated value into a tool call, a path, or a link.
|
|
78
72
|
|
|
79
73
|
## 3. No-handoff files
|
|
80
74
|
|
|
@@ -82,144 +76,122 @@ If `handoff_offered` is false, branch on `handoff_suppressed` and CLOSE:
|
|
|
82
76
|
- `no_new_sessions` → "nothing to watch — that import found no new sessions".
|
|
83
77
|
- `nothing_landed` → "the sessions that import selected were skipped at import time"; point at
|
|
84
78
|
`kcap import --all` for per-session reasons; no retry.
|
|
85
|
-
- `
|
|
86
|
-
|
|
87
|
-
`
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
- `import_failed` → "that import did not get running"; name `kcap import --all --yes` when the
|
|
80
|
+
file's `scope` is `all`; when it is `repos`, say to run `kcap setup` again and choose the same
|
|
81
|
+
repositories at the same Only me / Shared levels (plain `kcap import` would not keep those levels); and the `background_log`
|
|
82
|
+
when displayable.
|
|
83
|
+
- `analytics_not_in_plan` / `skill_not_installed` / `no_agent_detected` → the import ran and you
|
|
84
|
+
can follow it now: continue as if offered, watching the file's cohort as usual (sections 4–5).
|
|
90
85
|
|
|
91
86
|
## 4. Bind to the server — fail closed
|
|
92
87
|
|
|
93
88
|
If the selected file's `cohort` is `unknown`, skip this check and go straight to section 5 — there
|
|
94
|
-
is no cohort to
|
|
89
|
+
is no cohort to look up either way, and the mismatch remediation below would be misleading when
|
|
95
90
|
watching was never possible.
|
|
96
91
|
|
|
97
|
-
Otherwise,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
Otherwise, in this order — any CLOSE here makes **NO** eval lookup:
|
|
93
|
+
|
|
94
|
+
1. If `get_connection` is not among your `kcap-sessions` tools, this kcap is older than the skill:
|
|
95
|
+
say to update kcap and restart the agent, then CLOSE. `kcap whoami` is no substitute — it runs
|
|
96
|
+
in another process and cannot say which server the agent's MCP server reads.
|
|
97
|
+
2. Run `kcap whoami`. If it fails because kcap is not logged in, say to run `kcap login`, then CLOSE.
|
|
98
|
+
3. Call `get_connection` and compare its `server_url` to the file's — lowercase scheme and host,
|
|
99
|
+
drop a default port (80 for http, 443 for https only), trim a trailing slash, compare the path
|
|
100
|
+
as-is — and its `profile` to the file's `profile`, exactly. Both match → continue to section 5.
|
|
101
|
+
Either differs → CLOSE with the file's links and this remediation: start your agent from a
|
|
102
|
+
shell with `KCAP_PROFILE=<profile>`, `KCAP_URL` unset, and `KCAP_CONFIG_DIR` set if kcap uses a
|
|
103
|
+
custom config directory — restarting the agent, since its kcap-sessions server only reads those
|
|
104
|
+
when it starts — then prompt again.
|
|
103
105
|
|
|
104
106
|
## 5. Watch the cohort
|
|
105
107
|
|
|
106
|
-
Cohort = `session_ids` (`cohort: unknown` or empty → CLOSE with links, no
|
|
107
|
-
the entries of `foreground_succeeded_ids` that are also in `session_ids
|
|
108
|
-
imported while the user watched setup, and the ones this watch waits on.
|
|
109
|
-
count. A foreground entry missing from `session_ids` is never
|
|
110
|
-
"M of the sessions that imported first are outside the 500 this
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
most
|
|
108
|
+
Cohort = `session_ids` (`cohort: unknown` or empty → CLOSE with links, no lookup); N is its size.
|
|
109
|
+
Foreground = the entries of `foreground_succeeded_ids` that are also in `session_ids`, in file
|
|
110
|
+
order: the sessions that imported while the user watched setup, and the ones this watch waits on.
|
|
111
|
+
K is the foreground count. A foreground entry missing from `session_ids` is never looked up; when
|
|
112
|
+
any are missing, say "M of the sessions that imported first are outside the 500 this import
|
|
113
|
+
recorded".
|
|
114
|
+
|
|
115
|
+
**Watched set** W, at most 25 sessions: the first 25 foreground sessions, then — only while fewer
|
|
116
|
+
than 25 — the other cohort sessions in file order. Open with: "Watching W of the N sessions from
|
|
117
|
+
this import; waiting for the K that imported first to be evaluated". When K > 25 add "the first 25
|
|
118
|
+
of them"; for `partial_exact` add "the sessions that imported first and the most recent others,
|
|
119
|
+
500 at most; older ones may land and evaluate unobserved"; if `unattributed_on_disk` > 0 add the
|
|
114
120
|
sentence from section 7.
|
|
115
121
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
poll — nothing from it commits — and counts as one failure toward the two-failure stop rule.
|
|
140
|
-
|
|
141
|
-
A 429 fails the poll. If its text matches `retry after (\d+)s`, wait that many seconds before the
|
|
142
|
-
next poll; otherwise wait 60 seconds. Never poll sooner than the 30-second cadence either way. HTTP
|
|
143
|
-
403 with `analytics_not_in_plan` → CLOSE immediately with the plan sentence (section 6); no
|
|
144
|
-
polling, no retry, no wait.
|
|
145
|
-
|
|
146
|
-
Progress = cohort ids that arrived / cohort size, and foreground ids evaluated / K, both computed
|
|
147
|
-
from the batch rows only — never a separate count, never repo- or tenant-wide. Cadence: first
|
|
148
|
-
snapshot immediately, then every 30 seconds. Say the progress after each poll.
|
|
149
|
-
|
|
150
|
-
**Baseline**: the first snapshot's rows. An eval present there is *pre-existing*; one whose
|
|
151
|
-
`eval_run_id` was null or absent there and non-null in a later poll *completed during this watch*.
|
|
152
|
-
Every summary labels each session with one of those two phrases.
|
|
122
|
+
**One poll** = ONE `get_session_evals` call with `session_ids` set to W — never two in flight. Each
|
|
123
|
+
entry's `state` is one of:
|
|
124
|
+
- `not_found` — not ingested yet (or not visible to you);
|
|
125
|
+
- `not_evaluated` — arrived, no eval yet;
|
|
126
|
+
- `queued` — arrived, eval waiting for a worker or a retry (`queue_position`, `next_attempt_at`);
|
|
127
|
+
- `running` — arrived, eval in progress (`questions_done` of `total_questions`);
|
|
128
|
+
- `completed` — arrived and evaluated: `eval_run_id`, `evaluated_at`, `overall_score` (out of 5),
|
|
129
|
+
`judge_model`, `summary`, `categories`, `weakest_questions`;
|
|
130
|
+
- `failed` — arrived, the eval run failed (`failure_reason`);
|
|
131
|
+
- `error` — this lookup failed.
|
|
132
|
+
|
|
133
|
+
Arrived = any state but `not_found` and `error`. A tool error (including a login prompt), or any
|
|
134
|
+
`error` entry, fails the poll: nothing from it commits, and it counts toward the two-failure stop
|
|
135
|
+
rule. If the tool is missing — no `kcap-sessions` MCP, or one without `get_session_evals` — this
|
|
136
|
+
kcap is older than the skill: say to update kcap and restart the agent, then CLOSE.
|
|
137
|
+
|
|
138
|
+
Progress = watched sessions arrived / W and foreground sessions evaluated / min(K, 25), from the
|
|
139
|
+
poll's entries only. Cadence: first snapshot immediately, then every 30 seconds. Say the progress
|
|
140
|
+
after each poll.
|
|
141
|
+
|
|
142
|
+
**Baseline**: the first snapshot. A session `completed` there is *pre-existing*; one that was not
|
|
143
|
+
`completed` there, or completed with a different `eval_run_id`, and is `completed` in a later poll
|
|
144
|
+
*completed during this watch*. Every summary labels each session with one of those two phrases.
|
|
153
145
|
|
|
154
146
|
**Stop**, evaluated in this order after each poll whose results have already committed:
|
|
155
|
-
1. K > 0 and every foreground session
|
|
147
|
+
1. K > 0 and every watched foreground session is `completed` → summarize all of them, ordered by
|
|
156
148
|
`evaluated_at` then `session_id`. A pre-existing eval counts: the wait is for these sessions to
|
|
157
149
|
be evaluated, whenever that happened.
|
|
158
|
-
2. K = 0 and three
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
3. `cohort: exact`,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Never fires for `partial_exact` or an empty cohort.
|
|
150
|
+
2. K = 0 and three watched sessions completed during this watch → summarize those three, ordered by
|
|
151
|
+
`evaluated_at` then `session_id`. Pre-existing evals never satisfy this rule: on a re-import they
|
|
152
|
+
belong to an earlier run, and stopping on them would end the watch before this import produced
|
|
153
|
+
anything.
|
|
154
|
+
3. `cohort: exact`, N ≤ 25, and every cohort session is `completed` → summarize the foreground
|
|
155
|
+
sessions, or with K = 0 the three with the newest `evaluated_at` (ties by `session_id`). This
|
|
156
|
+
rule may fire on the first snapshot with nothing but pre-existing evals: a cohort that is
|
|
157
|
+
already fully evaluated has nothing left to wait for, and the labels say so.
|
|
167
158
|
4. Two consecutive failed polls → stop, reporting the failures.
|
|
168
159
|
5. 10 minutes since the first snapshot → stop. No new poll starts after the deadline, but a poll
|
|
169
160
|
already dispatched before it finishes normally, and its outcome is still checked against rules
|
|
170
161
|
1–4 first. On a deadline stop, summarize the foreground sessions that did complete and name the
|
|
171
|
-
ones still pending, with
|
|
172
|
-
|
|
162
|
+
ones still pending, with their state; if none of them completed, summarize instead up to three
|
|
163
|
+
watched sessions with the newest `evaluated_at` (ties by `session_id`).
|
|
173
164
|
|
|
174
165
|
No idle early-stop — quiet polls are normal and are not a reason to stop early. Evals take minutes
|
|
175
166
|
per session, and the foreground sessions are the newest, so they are usually the last to complete.
|
|
176
167
|
|
|
177
|
-
**
|
|
178
|
-
the
|
|
179
|
-
|
|
180
|
-
`
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
Strongest = highest mean, weakest = lowest, ties alphabetical by category. A truncated or failed
|
|
191
|
-
detail query, or a session with no assessed rows, → summarize that session by `overall_score` alone
|
|
192
|
-
and say so; this never affects poll success or the stop rules above.
|
|
193
|
-
|
|
194
|
-
**What a detailed session summary says**, one short paragraph each: the link (section 7), the
|
|
195
|
-
baseline label, then vendor and model, length (`duration_min`, rounded, and `event_count`),
|
|
196
|
-
`overall_score` out of 5 and the judge model, strongest and weakest category with their means, and
|
|
197
|
-
the two weakest questions with their scores. When the `kcap-sessions` MCP is available, call `get_session_summary` once per
|
|
198
|
-
summarized session and lead with its title or first sentence when it returns one; an empty summary
|
|
199
|
-
is left out silently, never called "no summary".
|
|
200
|
-
|
|
201
|
-
**Cohort facts**, one line after the summaries, from the last committed poll's rows only: sessions
|
|
202
|
-
arrived out of N, sessions evaluated, and the mean `overall_score` across the evaluated ones.
|
|
168
|
+
**What a session summary says.** The first five sessions in summary order get one short paragraph
|
|
169
|
+
each: the link (section 7), the baseline label, `overall_score` out of 5 and the judge model, the
|
|
170
|
+
strongest and weakest of `categories` by `score` (ties alphabetical by name), the two
|
|
171
|
+
`weakest_questions` with their scores, and the gist of the judge's `summary`. When
|
|
172
|
+
`get_session_summary` returns a title or a first sentence for the session, lead with it; an empty
|
|
173
|
+
summary is left out silently, never called "no summary". Every later session gets a one-line entry
|
|
174
|
+
— link, label and `overall_score`. A completed entry with no categories is summarized by
|
|
175
|
+
`overall_score` alone; say so.
|
|
176
|
+
|
|
177
|
+
**Cohort facts**, one line after the summaries, from the last committed poll only: watched
|
|
178
|
+
sessions arrived out of W, sessions evaluated, and the mean `overall_score` across the evaluated
|
|
179
|
+
ones.
|
|
203
180
|
|
|
204
181
|
## 6. When little or nothing completes
|
|
205
182
|
|
|
206
183
|
Explain the real gates plainly: auto-eval may be off for a repository; the server needs an eval
|
|
207
184
|
agent configured; sessions under the minimum event count are skipped; already-evaluated sessions
|
|
208
|
-
are not re-run; a recovery sweep runs hourly.
|
|
209
|
-
immediately with the block below.
|
|
210
|
-
|
|
211
|
-
Plan sentence (used here and from sections 3 and 5): "Insights isn't in this workspace's plan, so
|
|
212
|
-
evals can't be followed from here; your import continues and evals appear in the Capacitor UI."
|
|
185
|
+
are not re-run; a recovery sweep runs hourly. A `failed` session names its `failure_reason`.
|
|
213
186
|
|
|
214
187
|
## 7. CLOSE — always
|
|
215
188
|
|
|
216
189
|
1. How to keep watching: prompt `Follow my kcap import` again (with the same `(run: <id>)` line
|
|
217
190
|
when you were bound to one).
|
|
218
|
-
2. Links:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
once logged in.
|
|
191
|
+
2. Links: `<server_url>/sessions/<session_id>?tab=evaluation` per session; all results
|
|
192
|
+
`<server_url>/sessions`. No valid file → `server_url` from `kcap whoami`; if that fails too, say
|
|
193
|
+
the results live in the Capacitor server UI and that `kcap whoami` prints the URL once logged
|
|
194
|
+
in.
|
|
223
195
|
3. When `unattributed_on_disk` > 0: "N sessions on disk had no repository match; any of them that
|
|
224
196
|
imported show without one — `kcap remap` places them."
|
|
225
197
|
4. Offer the guided tour with the exact prompt `Start kcap guided tour`.
|