grask 0.1.0rc1__tar.gz

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.
grask-0.1.0rc1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kumar Prateek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: grask
3
+ Version: 0.1.0rc1
4
+ Summary: One question about your own code, at the end of a Claude Code session.
5
+ Keywords: claude-code,learning,code-comprehension,developer-tools
6
+ Author: Kumar Prateek
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.12
17
+ Project-URL: Homepage, https://github.com/imkp1/grask
18
+ Project-URL: Repository, https://github.com/imkp1/grask
19
+ Project-URL: Issues, https://github.com/imkp1/grask/issues
20
+ Description-Content-Type: text/markdown
21
+
22
+ # grask
23
+
24
+ [![CI](https://github.com/imkp1/grask/actions/workflows/ci.yml/badge.svg)](https://github.com/imkp1/grask/actions/workflows/ci.yml)
25
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue)](https://www.python.org/downloads/)
26
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
27
+ [![Status: alpha](https://img.shields.io/badge/status-alpha-orange)](#status)
28
+
29
+ One question about your own code, when you finish coding.
30
+
31
+ You can't tell the difference between understanding something and having watched it happen.
32
+ Shipping code with an agent used to force the issue — you couldn't ship what you didn't
33
+ understand, because it wouldn't run. That forcing function is gone.
34
+
35
+ grask watches your Claude Code sessions end, decides whether anything in one was worth
36
+ asking about, and — usually not — stays silent. When something was, it mints a single
37
+ multiple-choice question about the mechanism you shipped, and asks it the next time you
38
+ run `/grask`.
39
+
40
+ Most sessions produce nothing. That is the system working, not a failure to find something.
41
+
42
+ ## Install
43
+
44
+ Requires Python 3.12+ and the [Claude Code CLI](https://claude.com/claude-code) already
45
+ installed and authenticated. grask has no runtime dependencies and needs no second API
46
+ key — it shells out to the `claude` binary you already use.
47
+
48
+ ```bash
49
+ git clone https://github.com/imkp1/grask && cd grask
50
+ uv tool install .
51
+ ```
52
+
53
+ This puts two commands on your PATH: `grask` (ask a question) and `grask-hook` (the
54
+ capture trigger). `pipx install .` works the same way. Not on PyPI yet — install from a
55
+ checkout.
56
+
57
+ Installing as a tool rather than `uv sync` is deliberate: both surfaces below invoke
58
+ `grask` by name, so it has to resolve without a path. Use `uv sync` only for working on
59
+ grask itself.
60
+
61
+ ### Wire up the hook
62
+
63
+ Capture runs when a session ends. Add to your Claude Code `settings.json`:
64
+
65
+ ```json
66
+ {
67
+ "hooks": {
68
+ "SessionEnd": [
69
+ {
70
+ "hooks": [
71
+ { "type": "command", "command": "grask-hook" }
72
+ ]
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ ```
78
+
79
+ If you installed with `uv sync` instead, this has to be the absolute path to
80
+ `.venv/bin/grask-hook` — and it breaks the moment you move the checkout.
81
+
82
+ The hook reads the payload, spawns a detached worker, and returns immediately — it never
83
+ blocks the end of your session, and it never speaks. Failures go to
84
+ `~/.claude/grask/grask.log`, never to your terminal.
85
+
86
+ ### Install the skill (optional)
87
+
88
+ ```bash
89
+ grask skill --install # writes ~/.claude/skills/grask/SKILL.md
90
+ ```
91
+
92
+ This gets you `/grask` inside Claude Code, using the native question UI. `--dir` targets a
93
+ different skills directory — `.claude/skills` next to a repo, for a project-level install.
94
+ Without the skill, `grask` on the command line does the same job in the terminal.
95
+
96
+ `grask skill` with no flags prints the file instead of writing it, if you want to read it
97
+ before it lands.
98
+
99
+ ## Use
100
+
101
+ ```bash
102
+ grask # ask the next pending question, or say there's nothing
103
+ ```
104
+
105
+ You get one question, three or four options, and one line of orientation about which
106
+ session it came from. Pick a letter. Grading is mechanical — the answer key was minted
107
+ when the question was, so there is no model call, no latency, and no judge to argue with.
108
+ `enter` skips. `/wrong` rejects the premise if the question misreads what happened.
109
+
110
+ Questions expire after 7 days. A probe about work you did last week is a quiz.
111
+
112
+ ## Privacy — read this before installing
113
+
114
+ grask reads your Claude Code transcripts, and it is not scoped to one project:
115
+
116
+ - **It reads every transcript under `~/.claude/projects/`**, across all your repositories.
117
+ - **It sends transcript content to a model** — your prompts, the agent's replies, and the
118
+ before/after text of edits — by shelling out to `claude -p`. That call runs under your
119
+ existing Claude Code authentication and is subject to whatever data policy your account
120
+ already has. No data goes anywhere else, and grask adds no telemetry.
121
+ - **It stores what it extracts locally**, in a SQLite database at `~/.claude/grask/`,
122
+ including verbatim quotes of things you typed.
123
+
124
+ Controls:
125
+
126
+ - `GRASK_HOME` relocates the database and log.
127
+ - The corpus tools take `--exclude` to skip projects by name substring, and `--root` to
128
+ point at a different transcript directory.
129
+
130
+ If any of your repositories are covered by an agreement that prohibits sending source to a
131
+ model, do not install the hook.
132
+
133
+ **Nothing derived from a real transcript belongs in this repository.** The tools write
134
+ under `GRASK_HOME` by default for exactly that reason, and `.gitignore` is a second line of
135
+ defence. This project has made that mistake once already.
136
+
137
+ ## How it works
138
+
139
+ Four stages, cheapest first. Each one filters, so only what survives pays for the next.
140
+
141
+ | Stage | Module | Cost | Job |
142
+ |---|---|---|---|
143
+ | 0 — extract | `transcript.py` | free | Pull the developer's own turns out of a session log. Tool results, file snapshots, and injected skill text are not the developer thinking. Sessions with no human turns stop here. |
144
+ | 1 — triage | `triage.py` | one call | List every moment worth asking about, each anchored to a verbatim quote and the turn it came from. Sees turns and file *paths*, never file contents. Most sessions yield nothing. |
145
+ | — select | `select.py` | free | Rank the moments and pick one. Deliberately code, not prompt: a model asked to both find and choose picks arbitrarily, and the topic changed run to run on an unchanged session. |
146
+ | 2 — seed | `seed.py` | one call | State, as a falsifiable claim, what the developer may have accepted without understanding. Stored, so a better stage-3 prompt can re-ask the whole corpus later. |
147
+ | 3 — probe | `probe.py` | one call | Write one multiple-choice question about the mechanism, with the answer key and an explanation. |
148
+
149
+ Two rules are enforced in code rather than prompted for, because instruction is not a
150
+ control:
151
+
152
+ - **The evidence rule.** A triaged moment whose quote does not appear in the turn it names
153
+ is demoted to silence. Same for a seed quote that appears nowhere the developer typed.
154
+ - **The one-question rule.** A stem with two questions in it cannot have one correct
155
+ option, so it is rejected and regenerated, up to three attempts.
156
+
157
+ The question must also teach something portable. A question whose answer is "because this
158
+ file says so" is answerable only by whoever sat through the session and is worth nothing
159
+ once they close the file.
160
+
161
+ ### Model selection
162
+
163
+ grask names no model. It calls `claude -p` with no `--model` flag, so every stage runs on
164
+ whatever you currently have selected, and there is no second credential to manage. Tools
165
+ are disallowed and slash commands disabled — grask sends one self-contained prompt and
166
+ wants one JSON object back, so letting it wander the repo would just cost money.
167
+
168
+ ## Cost
169
+
170
+ grask's own prompts are small. The real cost is the Claude Code context inherited by
171
+ shelling out to `claude -p` at all, which scales with *your* configuration rather than with
172
+ grask — `--disable-slash-commands` exists in `llm.py` for precisely this reason and cuts a
173
+ call substantially on a large setup.
174
+
175
+ Stages 2 and 3 only run on sessions triage kept, which is a minority. The corpus tools
176
+ print an estimated range and refuse to spend without `--go`.
177
+
178
+ ## Development
179
+
180
+ ```bash
181
+ uv sync # working on grask itself, rather than installing it
182
+ uv run pytest # 221 tests, no network, no model calls, sub-second
183
+ uv run ruff check .
184
+ uv run mypy
185
+ ```
186
+
187
+ The suite never calls a model. Every stage is injected as a plain callable, so the whole
188
+ pipeline is drivable by test doubles — that seam is why the tests are free and fast.
189
+ Tests marked `calibration` do call the real model and cost money; they are deselected by
190
+ default and run deliberately with `pytest -m calibration`.
191
+
192
+ ### Corpus tools
193
+
194
+ Diagnostics for working on grask itself, not part of normal use. All of them write under
195
+ `GRASK_HOME`, never the working directory.
196
+
197
+ ```bash
198
+ uv run python -m grask.survey # what's in the local transcript corpus (free)
199
+ uv run python -m grask.triage_run # run stage 1 over the corpus; costs money
200
+ uv run python -m grask.capture_run # run the full pipeline over past sessions; --go to spend
201
+ ```
202
+
203
+ `capture_run` skips grask's own project by default. The sessions that end from now on are
204
+ overwhelmingly grask's own, and waiting for the queue to fill measures grask on grask.
205
+
206
+ ## Status
207
+
208
+ Alpha, and honest about it. The capture pipeline, storage, both delivery surfaces, and
209
+ mechanical grading all work end to end.
210
+
211
+ Not built: resurfacing a missed question, and cross-session dedup — two sessions can
212
+ currently produce near-identical probes.
213
+
214
+ Design notes and the reasoning behind each decision are in
215
+ [`docs/design/grask-design.md`](docs/design/grask-design.md); [`IDEA.md`](IDEA.md) covers
216
+ what this is and why it might not work. [`docs/README.md`](docs/README.md) says which of
217
+ those documents are current and which are records.
218
+
219
+ ## Contributing
220
+
221
+ Issues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for the
222
+ three rules that are easy to break by accident, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
223
+
224
+ A question grask asked that was *bad* — wrong premise, wrong key, or testing nothing
225
+ portable — is the single most useful thing you can report. There is an issue template
226
+ for exactly that.
227
+
228
+ For anything where grask leaked, over-collected, or wrote outside `GRASK_HOME`, see
229
+ [SECURITY.md](SECURITY.md) and report it privately rather than in a public issue.
230
+
231
+ ## License
232
+
233
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,212 @@
1
+ # grask
2
+
3
+ [![CI](https://github.com/imkp1/grask/actions/workflows/ci.yml/badge.svg)](https://github.com/imkp1/grask/actions/workflows/ci.yml)
4
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6
+ [![Status: alpha](https://img.shields.io/badge/status-alpha-orange)](#status)
7
+
8
+ One question about your own code, when you finish coding.
9
+
10
+ You can't tell the difference between understanding something and having watched it happen.
11
+ Shipping code with an agent used to force the issue — you couldn't ship what you didn't
12
+ understand, because it wouldn't run. That forcing function is gone.
13
+
14
+ grask watches your Claude Code sessions end, decides whether anything in one was worth
15
+ asking about, and — usually not — stays silent. When something was, it mints a single
16
+ multiple-choice question about the mechanism you shipped, and asks it the next time you
17
+ run `/grask`.
18
+
19
+ Most sessions produce nothing. That is the system working, not a failure to find something.
20
+
21
+ ## Install
22
+
23
+ Requires Python 3.12+ and the [Claude Code CLI](https://claude.com/claude-code) already
24
+ installed and authenticated. grask has no runtime dependencies and needs no second API
25
+ key — it shells out to the `claude` binary you already use.
26
+
27
+ ```bash
28
+ git clone https://github.com/imkp1/grask && cd grask
29
+ uv tool install .
30
+ ```
31
+
32
+ This puts two commands on your PATH: `grask` (ask a question) and `grask-hook` (the
33
+ capture trigger). `pipx install .` works the same way. Not on PyPI yet — install from a
34
+ checkout.
35
+
36
+ Installing as a tool rather than `uv sync` is deliberate: both surfaces below invoke
37
+ `grask` by name, so it has to resolve without a path. Use `uv sync` only for working on
38
+ grask itself.
39
+
40
+ ### Wire up the hook
41
+
42
+ Capture runs when a session ends. Add to your Claude Code `settings.json`:
43
+
44
+ ```json
45
+ {
46
+ "hooks": {
47
+ "SessionEnd": [
48
+ {
49
+ "hooks": [
50
+ { "type": "command", "command": "grask-hook" }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ ```
57
+
58
+ If you installed with `uv sync` instead, this has to be the absolute path to
59
+ `.venv/bin/grask-hook` — and it breaks the moment you move the checkout.
60
+
61
+ The hook reads the payload, spawns a detached worker, and returns immediately — it never
62
+ blocks the end of your session, and it never speaks. Failures go to
63
+ `~/.claude/grask/grask.log`, never to your terminal.
64
+
65
+ ### Install the skill (optional)
66
+
67
+ ```bash
68
+ grask skill --install # writes ~/.claude/skills/grask/SKILL.md
69
+ ```
70
+
71
+ This gets you `/grask` inside Claude Code, using the native question UI. `--dir` targets a
72
+ different skills directory — `.claude/skills` next to a repo, for a project-level install.
73
+ Without the skill, `grask` on the command line does the same job in the terminal.
74
+
75
+ `grask skill` with no flags prints the file instead of writing it, if you want to read it
76
+ before it lands.
77
+
78
+ ## Use
79
+
80
+ ```bash
81
+ grask # ask the next pending question, or say there's nothing
82
+ ```
83
+
84
+ You get one question, three or four options, and one line of orientation about which
85
+ session it came from. Pick a letter. Grading is mechanical — the answer key was minted
86
+ when the question was, so there is no model call, no latency, and no judge to argue with.
87
+ `enter` skips. `/wrong` rejects the premise if the question misreads what happened.
88
+
89
+ Questions expire after 7 days. A probe about work you did last week is a quiz.
90
+
91
+ ## Privacy — read this before installing
92
+
93
+ grask reads your Claude Code transcripts, and it is not scoped to one project:
94
+
95
+ - **It reads every transcript under `~/.claude/projects/`**, across all your repositories.
96
+ - **It sends transcript content to a model** — your prompts, the agent's replies, and the
97
+ before/after text of edits — by shelling out to `claude -p`. That call runs under your
98
+ existing Claude Code authentication and is subject to whatever data policy your account
99
+ already has. No data goes anywhere else, and grask adds no telemetry.
100
+ - **It stores what it extracts locally**, in a SQLite database at `~/.claude/grask/`,
101
+ including verbatim quotes of things you typed.
102
+
103
+ Controls:
104
+
105
+ - `GRASK_HOME` relocates the database and log.
106
+ - The corpus tools take `--exclude` to skip projects by name substring, and `--root` to
107
+ point at a different transcript directory.
108
+
109
+ If any of your repositories are covered by an agreement that prohibits sending source to a
110
+ model, do not install the hook.
111
+
112
+ **Nothing derived from a real transcript belongs in this repository.** The tools write
113
+ under `GRASK_HOME` by default for exactly that reason, and `.gitignore` is a second line of
114
+ defence. This project has made that mistake once already.
115
+
116
+ ## How it works
117
+
118
+ Four stages, cheapest first. Each one filters, so only what survives pays for the next.
119
+
120
+ | Stage | Module | Cost | Job |
121
+ |---|---|---|---|
122
+ | 0 — extract | `transcript.py` | free | Pull the developer's own turns out of a session log. Tool results, file snapshots, and injected skill text are not the developer thinking. Sessions with no human turns stop here. |
123
+ | 1 — triage | `triage.py` | one call | List every moment worth asking about, each anchored to a verbatim quote and the turn it came from. Sees turns and file *paths*, never file contents. Most sessions yield nothing. |
124
+ | — select | `select.py` | free | Rank the moments and pick one. Deliberately code, not prompt: a model asked to both find and choose picks arbitrarily, and the topic changed run to run on an unchanged session. |
125
+ | 2 — seed | `seed.py` | one call | State, as a falsifiable claim, what the developer may have accepted without understanding. Stored, so a better stage-3 prompt can re-ask the whole corpus later. |
126
+ | 3 — probe | `probe.py` | one call | Write one multiple-choice question about the mechanism, with the answer key and an explanation. |
127
+
128
+ Two rules are enforced in code rather than prompted for, because instruction is not a
129
+ control:
130
+
131
+ - **The evidence rule.** A triaged moment whose quote does not appear in the turn it names
132
+ is demoted to silence. Same for a seed quote that appears nowhere the developer typed.
133
+ - **The one-question rule.** A stem with two questions in it cannot have one correct
134
+ option, so it is rejected and regenerated, up to three attempts.
135
+
136
+ The question must also teach something portable. A question whose answer is "because this
137
+ file says so" is answerable only by whoever sat through the session and is worth nothing
138
+ once they close the file.
139
+
140
+ ### Model selection
141
+
142
+ grask names no model. It calls `claude -p` with no `--model` flag, so every stage runs on
143
+ whatever you currently have selected, and there is no second credential to manage. Tools
144
+ are disallowed and slash commands disabled — grask sends one self-contained prompt and
145
+ wants one JSON object back, so letting it wander the repo would just cost money.
146
+
147
+ ## Cost
148
+
149
+ grask's own prompts are small. The real cost is the Claude Code context inherited by
150
+ shelling out to `claude -p` at all, which scales with *your* configuration rather than with
151
+ grask — `--disable-slash-commands` exists in `llm.py` for precisely this reason and cuts a
152
+ call substantially on a large setup.
153
+
154
+ Stages 2 and 3 only run on sessions triage kept, which is a minority. The corpus tools
155
+ print an estimated range and refuse to spend without `--go`.
156
+
157
+ ## Development
158
+
159
+ ```bash
160
+ uv sync # working on grask itself, rather than installing it
161
+ uv run pytest # 221 tests, no network, no model calls, sub-second
162
+ uv run ruff check .
163
+ uv run mypy
164
+ ```
165
+
166
+ The suite never calls a model. Every stage is injected as a plain callable, so the whole
167
+ pipeline is drivable by test doubles — that seam is why the tests are free and fast.
168
+ Tests marked `calibration` do call the real model and cost money; they are deselected by
169
+ default and run deliberately with `pytest -m calibration`.
170
+
171
+ ### Corpus tools
172
+
173
+ Diagnostics for working on grask itself, not part of normal use. All of them write under
174
+ `GRASK_HOME`, never the working directory.
175
+
176
+ ```bash
177
+ uv run python -m grask.survey # what's in the local transcript corpus (free)
178
+ uv run python -m grask.triage_run # run stage 1 over the corpus; costs money
179
+ uv run python -m grask.capture_run # run the full pipeline over past sessions; --go to spend
180
+ ```
181
+
182
+ `capture_run` skips grask's own project by default. The sessions that end from now on are
183
+ overwhelmingly grask's own, and waiting for the queue to fill measures grask on grask.
184
+
185
+ ## Status
186
+
187
+ Alpha, and honest about it. The capture pipeline, storage, both delivery surfaces, and
188
+ mechanical grading all work end to end.
189
+
190
+ Not built: resurfacing a missed question, and cross-session dedup — two sessions can
191
+ currently produce near-identical probes.
192
+
193
+ Design notes and the reasoning behind each decision are in
194
+ [`docs/design/grask-design.md`](docs/design/grask-design.md); [`IDEA.md`](IDEA.md) covers
195
+ what this is and why it might not work. [`docs/README.md`](docs/README.md) says which of
196
+ those documents are current and which are records.
197
+
198
+ ## Contributing
199
+
200
+ Issues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for the
201
+ three rules that are easy to break by accident, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
202
+
203
+ A question grask asked that was *bad* — wrong premise, wrong key, or testing nothing
204
+ portable — is the single most useful thing you can report. There is an issue template
205
+ for exactly that.
206
+
207
+ For anything where grask leaked, over-collected, or wrote outside `GRASK_HOME`, see
208
+ [SECURITY.md](SECURITY.md) and report it privately rather than in a public issue.
209
+
210
+ ## License
211
+
212
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,78 @@
1
+ [project]
2
+ name = "grask"
3
+ version = "0.1.0-rc1"
4
+ description = "One question about your own code, at the end of a Claude Code session."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Kumar Prateek" }]
10
+ keywords = ["claude-code", "learning", "code-comprehension", "developer-tools"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Environment :: Console",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Topic :: Software Development :: Quality Assurance",
18
+ "Typing :: Typed",
19
+ ]
20
+ # Nothing at runtime. grask shells out to the `claude` CLI the developer has
21
+ # already installed and authenticated, which is the whole of its model access.
22
+ dependencies = []
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/imkp1/grask"
26
+ Repository = "https://github.com/imkp1/grask"
27
+ Issues = "https://github.com/imkp1/grask/issues"
28
+
29
+ [project.scripts]
30
+ grask = "grask.cli:main"
31
+ grask-hook = "grask.hook:main"
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.11.31,<0.12.0"]
35
+ build-backend = "uv_build"
36
+
37
+ # `SKILL.md` lives inside the package rather than at the repo root, and
38
+ # `grask skill --install` reads it with importlib.resources. Anything outside
39
+ # `src/grask/` can only reach an sdist — and nobody installs an sdist's files,
40
+ # because pip and uv build a wheel from it first. A skill the user is told to
41
+ # copy has to be in the wheel or it does not exist for anyone who installed
42
+ # rather than cloned. CI asserts it is there.
43
+
44
+ [tool.pytest.ini_options]
45
+ # Calibration tests call the real model: they cost money and are not
46
+ # deterministic. Deselected by default so the ordinary suite stays free and
47
+ # offline; run them deliberately with `pytest -m calibration`.
48
+ addopts = "-m 'not calibration'"
49
+ markers = [
50
+ "calibration: measures real model behaviour, not our code. Costs money.",
51
+ ]
52
+
53
+ [tool.ruff]
54
+ line-length = 100
55
+ target-version = "py312"
56
+
57
+ [tool.ruff.lint]
58
+ select = ["E", "F", "I", "UP", "B", "SIM"]
59
+
60
+ [tool.mypy]
61
+ python_version = "3.12"
62
+ files = ["src/grask"]
63
+ # The package ships py.typed, which is a promise to anyone importing it. These
64
+ # settings are what make that promise checkable rather than decorative.
65
+ strict = true
66
+ # The pipeline stages are injected as plain callables (`triage=`, `complete=`,
67
+ # `store_factory=`) so control flow is testable without spending money on a
68
+ # model. Annotating those parameters would pin test doubles to the production
69
+ # signature, which is the seam's whole point. Everything else is checked.
70
+ disallow_untyped_defs = false
71
+ disallow_incomplete_defs = false
72
+
73
+ [dependency-groups]
74
+ dev = [
75
+ "pytest>=9.1.1",
76
+ "ruff>=0.14",
77
+ "mypy>=1.14",
78
+ ]
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: grask
3
+ description: Serve the next pending grask probe — a multiple-choice question about code the developer recently shipped — using the native question UI. Use when the user types /grask.
4
+ ---
5
+
6
+ # Serving a grask probe
7
+
8
+ Grask quizzes the developer on mechanisms they shipped without fully
9
+ understanding. You are the delivery surface only: you serve the question and
10
+ relay the answer. You never grade, never guess, and never see the answer key.
11
+
12
+ ## Hard rules
13
+
14
+ - Never open or query grask's database directly. The two subcommands below are
15
+ the entire interface.
16
+ - Never speculate about which option is correct — not in text, not in labels,
17
+ not in previews, not before or after the pick. Grading happens in
18
+ `grask record`.
19
+ - One native question, one round. No confidence round, no follow-ups.
20
+
21
+ ## Flow
22
+
23
+ 1. Run:
24
+
25
+ ```
26
+ grask serve --json
27
+ ```
28
+
29
+ If the output is `{"pending": null}`, say there is nothing pending and stop.
30
+ If the command is not found, grask is not installed on this PATH — say so and
31
+ stop rather than hunting for a checkout to `cd` into.
32
+
33
+ 2. Ask ONE native question, preview-style (like plan-mode option picks), built
34
+ entirely from the served JSON:
35
+
36
+ - `question`: a context line from `topic` and `created_at`, then the full
37
+ question text — e.g.
38
+ `from 2026-07-21 · idempotency of the retry path — What would happen if …?`
39
+ The question must be readable inside the picker itself; do not rely on
40
+ markdown printed before it.
41
+ - One option per stored option, in stored order:
42
+ - `label`: the letter plus the first few distinguishing words of the
43
+ option (e.g. `a) dedup to a no-op`). Keep labels short; they are not the
44
+ full text.
45
+ - `preview`: the full stored option text, verbatim and unabridged. The
46
+ side-by-side preview pane is where the developer reads the option.
47
+ - In exactly one option's preview (or the question text), append a footer
48
+ note: "Other" accepts `skip`, or `wrong: <what's off>` if the question
49
+ misreads what happened.
50
+
51
+ 3. Record the result:
52
+ - Picked letter L: `grask record <probe_id> --pick L`
53
+ - Skipped: `grask record <probe_id> --skip`
54
+ - Premise rejected: `grask record <probe_id> --wrong --objection
55
+ "<their words>"` (omit `--objection` if they gave no reason).
56
+
57
+ Show the result: ✓ or ✗ from `outcome`, then the `explanation` verbatim. If
58
+ the command prints `{"error": ...}`, show the error and stop — do not retry
59
+ with different flags.
60
+
61
+ 4. Run `serve` again. If another probe is pending, say so and offer to
62
+ continue — do not auto-serve it.
@@ -0,0 +1,3 @@
1
+ """grask — one question about your own code, at the end of a session."""
2
+
3
+ __version__ = "0.1.0"