@keepsnappy/agent 0.1.0-alpha.20 → 0.1.0-alpha.22
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 +37 -0
- package/dist/appium-driver.js +1 -1
- package/dist/keepsnappy.js +5699 -115
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -206,6 +206,43 @@ newest measured commit, its word and runs), so an empty list on an unmeasured br
|
|
|
206
206
|
lists each measured commit once, with how many sessions measured it. `places` lists the places of one session (a run set, or the newest closed one of a journey on a phone and branch) with each measure's median, spread and the components with the most own time, ranked by `--measure` and `--sort`; a session without stored statistics says `not computed`, never zero. Words come from the one word table (Worse / Warning / Better …), "Worse (not blocking)" when the
|
|
207
207
|
branch was measured differently from main.
|
|
208
208
|
|
|
209
|
+
## `keepsnappy investigate` (a local agent looks for the cause)
|
|
210
|
+
|
|
211
|
+
`keepsnappy investigate <finding-id>` is runner B of plan 82: it starts your own `claude` or `codex` as a background process on your machine, on your own login, and hands it the finding. It never starts by itself, and it never decides anything: the agent answers with hypotheses (predictions with a measurable expected change), and only the next measured session confirms or rejects one.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
claude login # once; or: codex login. The login is yours: this CLI never reads or sends a token
|
|
215
|
+
export KEEPSNAPPY_KEY=pl_rk_…
|
|
216
|
+
keepsnappy investigate f_0123456789abcdef --repo ~/apps/my-app # stores the hypotheses on the finding
|
|
217
|
+
keepsnappy investigate f_0123456789abcdef --dry-run # everything except the last step; prints what would be stored
|
|
218
|
+
keepsnappy investigate f_0123456789abcdef --agent codex --max-minutes 10 --json
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
What it does, in order:
|
|
222
|
+
1. Reads the investigation package with the same client as the MCP tool `get_investigation` (stored results only).
|
|
223
|
+
2. Checks that the commit ids in the package are 7–40 lowercase hex characters (anything else is refused before git is called), then creates a separate worktree of your repo (`git worktree add --detach`, in the system temp directory) at the commit the finding was measured at: the PR's commit, or main's commit for a main finding. Every git call the CLI makes on your repo runs with `-c core.hooksPath=/dev/null`, so the repo's hooks (a `post-checkout` that installs or generates things) never run for a PR's commit. Your working copy, branch and uncommitted files are never touched. The worktree is removed at the end, also after a failure, a timeout or Ctrl-C. The commit must be in your clone (`git fetch` first when it says it is not).
|
|
224
|
+
3. Computes the range itself, because the agent has no shell: `git log` and `git diff` (fixed arguments, `--no-ext-diff --no-textconv`, output capped: log 20 KB, stat 20 KB, diff 100 KB, with a note when cut) from main's commit to the measured commit, put in the prompt after the package. Without a main commit in the package, or with one your clone does not have, the prompt says so.
|
|
225
|
+
3b. **Checks for a false alarm first.** The area is the files the package names for the finding (the components' files, and the files in the place ids' screen, parent and target), kept only if they are plain repo-relative paths that exist at the measured commit. The CLI computes, with fixed arguments, hooks off, literal pathspecs after `--`, from the two measured trees (`git diff <main commit> <measured commit>`, not the PR's range since the fork: main may have changed the area after it, and then the PR builds the old code): a symmetric log (`<` a commit only main has, `>` one only the PR has), the stat and a capped diff (40 KB) of those files, and says `AREA DIFF EMPTY: yes | no | unknown`. It also reads the stored facts through the client the tools use (`main_sessions`, `main_vs_previous`, `place_trend`, and the package's noisy marks, threshold, measured-differently and setup notes, at most 12 KB) and puts them in the prompt as the false-alarm facts, so claude and codex see the same ones. If the area diff is empty the first hypothesis is `kind: no-area-change` ("likely false alarm") with `checked`; the CLI leaves out a false alarm its own check does not support (the area changed, could not be checked, another range or file) with its reason, and still submits the other hypotheses; only when nothing is left does it submit nothing. The server cannot check this: a `no-area-change` from the skill path (or any key holder) is stored on the agent's word. The last line of the log is `KEEPSNAPPY-STATUS <word> [n]`, which `keepsnappy watch` reads for its second notification.
|
|
226
|
+
4. Runs the agent in that worktree, read-only, with the prompt every runner shares (`packages/mcp/src/investigate-prompt.ts`, version `investigate-v4`, the same rules as the skill) and the package as the answer layout prints it. The prompt goes on stdin, so a large package does not hit the argument limit. Nothing the user or the repo configures can widen what the agent may do:
|
|
227
|
+
- `claude -p --output-format json --max-turns N --restricted --permission-mode dontAsk --permission-prompts none --tools Read,Grep,Glob --allowedTools <Read,Grep,Glob + the KeepSnappy read tools by name> --disallowedTools Bash,PowerShell,Edit,Write,… --disable-slash-commands --mcp-config <private file> --strict-mcp-config`. Which flag closes which hole: `--restricted` ignores the user's, the project's (a PR's committed `.claude/settings.json`, with its hooks and allow rules) and the local settings files and removes Bash; `--permission-mode dontAsk` and `--permission-prompts none` deny whatever is not allowed and ask nobody; `--tools` and `--allowedTools` give it Read, Grep, Glob and the read-only KeepSnappy tools only (`get_investigation`, `get_finding`, `list_findings`, `list_problems`, `compare_runs`, `window_history`, `commits_between`, `session_places`, `place_trend`, `main_sessions`, `main_changes`, `main_vs_previous`, `pr_status`, `explain_layers`; never `submit_hypotheses`, so a dry run cannot store, and never `recompute_with_new_rules`; no wildcard); `--strict-mcp-config` keeps your other MCP servers out; `--disable-slash-commands` keeps the repo's and your skills and commands out. The KeepSnappy key is in a mode 0600 file that is deleted afterwards, not in the arguments or the agent's environment. It relies on the final JSON document's `result`, `is_error` and `subtype` fields. All these flags are in `claude --help` of 2.1.308 (`--max-turns` is not listed there; the prompt's own tool-call budget does not depend on it). The repo's CLAUDE.md can still be read as text by the agent; it can run and write nothing.
|
|
228
|
+
- `codex exec --ignore-user-config --ignore-rules --ephemeral -c default_permissions="ks" -c 'permissions.ks.filesystem={":minimal"="read","<worktree>"="read","/private/tmp"="none","/tmp"="none"}' -c approval_policy="never" -c project_doc_max_bytes=0 --color never -o <file> -`. **Codex's read scope is the worktree and the system minimum, with `/tmp` and `/private/tmp` denied by name (the minimum includes `/tmp`, which holds other tools' output); its shell can write nothing and reach no network, and git cannot run in it, so the prompt carries the log, stat and diff.** The permission profile replaces `--sandbox read-only`, which reads every file you can (`~/.zshenv`, `~/.ssh`): with it, a PR's text could make codex quote a secret into a hypothesis that is then stored on the server. Checked with `codex sandbox` and the same `-c` values (no model; the test `investigate.test.ts` runs it when `codex` is installed): the worktree reads (also under `/tmp`); a sibling file, `/tmp` and the home directory do not. **Not checked: that `codex exec` applies the profile the same way in a live run** (that needs a model). `project_doc_max_bytes=0` keeps the repo's `AGENTS.md` out of the prompt (checked with `codex debug prompt-input`). Whether `codex exec --ignore-user-config` still loads a repo's `.codex/config.toml` or hooks is not checked against a live run either; trust for a project lives in the user config it ignores. `--ignore-user-config` keeps your configured MCP servers and approval reviewer out.
|
|
229
|
+
5. Budget: `--max-turns` (default 26: 25 tool calls and the answer) and `--max-minutes` (default 15). At the time limit the agent and every process it started are killed, and it says `no result within N min`. More than 5 MB of output stops it too.
|
|
230
|
+
6. Validates the answer with the one hypothesis schema (`@keepsnappy/dashboard-contract/hypotheses`), after filling `runner: cli`, the agent (the model name claude reports, else `claude` / `codex`), `prompt_version` and the package's `package_digest`. An invalid answer is shown with the schema's paths and is not submitted. An answer of no hypothesis stores nothing.
|
|
231
|
+
7. Submits with the same client as the MCP tool `submit_hypotheses` and prints `N hypotheses, open`, then each one: component, files, expected change. `--json` prints one document `{ finding_id, submission, stored }` (`stored` is null on a dry run). Progress and errors go to stderr.
|
|
232
|
+
|
|
233
|
+
Setup, once. The repo comes from `--repo`, else from the app → repo map in `keepsnappy.yml` (the file `keepsnappy run` reads: the current directory, or `--config`). The app is the one the key reads. Nothing else in the file changes:
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
investigate:
|
|
237
|
+
agent: claude # claude | codex (default claude); --agent wins
|
|
238
|
+
max_turns: 26 # --max-turns wins
|
|
239
|
+
max_minutes: 15 # --max-minutes wins
|
|
240
|
+
repos: # app → local clone; ~ is your home, a relative path is relative to where you run the command
|
|
241
|
+
tapin: ~/apps/gigaverse-mobile
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Exit codes: 0 stored (or a dry run, or no hypothesis), 1 no usable answer (timeout, agent error, invalid output), 2 wrong flags or settings, 130 Ctrl-C. Needs a key with read scope, like the MCP tools.
|
|
245
|
+
|
|
209
246
|
## Output layout
|
|
210
247
|
|
|
211
248
|
```
|
package/dist/appium-driver.js
CHANGED
|
@@ -1030,7 +1030,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
1030
1030
|
import { fileURLToPath } from "node:url";
|
|
1031
1031
|
var DRIVER_NAME = "appium";
|
|
1032
1032
|
function driverVersion() {
|
|
1033
|
-
const built = "0.1.0-alpha.
|
|
1033
|
+
const built = "0.1.0-alpha.22";
|
|
1034
1034
|
if (built)
|
|
1035
1035
|
return built;
|
|
1036
1036
|
let dir = dirname(fileURLToPath(import.meta.url));
|