kritya 0.8.2-beta
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/LICENSE +21 -0
- package/README.md +434 -0
- package/dist/agent/budget.js +25 -0
- package/dist/agent/compactor.js +78 -0
- package/dist/agent/contextWarning.js +6 -0
- package/dist/agent/killSwitch.js +111 -0
- package/dist/agent/loop.js +543 -0
- package/dist/agent/memory.js +145 -0
- package/dist/agent/plugins.js +408 -0
- package/dist/agent/skills.js +210 -0
- package/dist/agent/skillsCli.js +51 -0
- package/dist/agent/systemPrompt.js +122 -0
- package/dist/agent/tokens.js +55 -0
- package/dist/agent/toolExecutor.js +331 -0
- package/dist/agent/workflow.js +496 -0
- package/dist/agent/worktree.js +116 -0
- package/dist/atomicWrite.js +109 -0
- package/dist/audit/audit.js +230 -0
- package/dist/audit/cli.js +135 -0
- package/dist/commands/custom.js +69 -0
- package/dist/commands/mcpCommand.js +433 -0
- package/dist/commands/pluginsCommand.js +204 -0
- package/dist/commands/registry.js +739 -0
- package/dist/config/config.js +214 -0
- package/dist/config/debug.js +19 -0
- package/dist/config/models.js +82 -0
- package/dist/config/retention.js +24 -0
- package/dist/config/winAcl.js +38 -0
- package/dist/crash.js +102 -0
- package/dist/engine.js +113 -0
- package/dist/git/git.js +46 -0
- package/dist/headless.js +274 -0
- package/dist/hooks/hooks.js +122 -0
- package/dist/index.js +670 -0
- package/dist/lsp/client.js +413 -0
- package/dist/lsp/manager.js +0 -0
- package/dist/lsp/registry.js +62 -0
- package/dist/mcp/callback.js +141 -0
- package/dist/mcp/client.js +945 -0
- package/dist/mcp/login.js +117 -0
- package/dist/mcp/oauth.js +345 -0
- package/dist/mcp/servers.js +114 -0
- package/dist/mcp/spawnWin.js +86 -0
- package/dist/mcp/tokens.js +97 -0
- package/dist/mcp/transport.js +295 -0
- package/dist/net/urlSafety.js +158 -0
- package/dist/permissions/danger.js +96 -0
- package/dist/permissions/permissions.js +62 -0
- package/dist/permissions/rules.js +69 -0
- package/dist/plugins/discover.js +108 -0
- package/dist/plugins/mcp.js +80 -0
- package/dist/provider/client.js +460 -0
- package/dist/provider/switchyardClient.js +46 -0
- package/dist/provider/switchyardSidecar.js +245 -0
- package/dist/provider/textToolCalls.js +140 -0
- package/dist/repomap/repoMap.js +168 -0
- package/dist/repomap/symbols.js +190 -0
- package/dist/session/store.js +328 -0
- package/dist/shell/background.js +131 -0
- package/dist/shell/sandbox.js +396 -0
- package/dist/telemetry/metrics.js +172 -0
- package/dist/telemetry/otlp.js +128 -0
- package/dist/telemetry/tracer.js +240 -0
- package/dist/tools/askUser.js +64 -0
- package/dist/tools/bg.js +59 -0
- package/dist/tools/common.js +158 -0
- package/dist/tools/deepResearch.js +126 -0
- package/dist/tools/diff.js +70 -0
- package/dist/tools/document/docx.js +44 -0
- package/dist/tools/document/pdf.js +190 -0
- package/dist/tools/document/pptx.js +148 -0
- package/dist/tools/document/types.js +26 -0
- package/dist/tools/document/xlsx.js +87 -0
- package/dist/tools/document.js +389 -0
- package/dist/tools/edit.js +56 -0
- package/dist/tools/fetchUrl.js +218 -0
- package/dist/tools/fuzzyMatch.js +74 -0
- package/dist/tools/glob.js +34 -0
- package/dist/tools/grep.js +85 -0
- package/dist/tools/ignore.js +28 -0
- package/dist/tools/index.js +66 -0
- package/dist/tools/ls.js +27 -0
- package/dist/tools/lsp.js +239 -0
- package/dist/tools/notebook.js +215 -0
- package/dist/tools/read.js +32 -0
- package/dist/tools/repoMap.js +26 -0
- package/dist/tools/secretScan.js +143 -0
- package/dist/tools/shell.js +134 -0
- package/dist/tools/skills.js +69 -0
- package/dist/tools/subagent.js +57 -0
- package/dist/tools/tasks.js +43 -0
- package/dist/tools/webSearch.js +97 -0
- package/dist/tools/write.js +47 -0
- package/dist/tools/writeAgent.js +72 -0
- package/dist/trust/aiDisclosure.js +45 -0
- package/dist/trust/mcpTrust.js +111 -0
- package/dist/trust/trust.js +231 -0
- package/dist/types.js +1 -0
- package/dist/ui/AiDisclosurePrompt.js +23 -0
- package/dist/ui/App.js +449 -0
- package/dist/ui/Banner.js +71 -0
- package/dist/ui/ElicitationPrompt.js +46 -0
- package/dist/ui/Markdown.js +148 -0
- package/dist/ui/McpTrustPrompt.js +42 -0
- package/dist/ui/ModelPicker.js +19 -0
- package/dist/ui/PermissionPrompt.js +27 -0
- package/dist/ui/SelectList.js +24 -0
- package/dist/ui/Spinner.js +12 -0
- package/dist/ui/StatusLine.js +11 -0
- package/dist/ui/TranscriptItem.js +13 -0
- package/dist/ui/TrustPrompt.js +12 -0
- package/dist/ui/highlight.js +137 -0
- package/dist/ui/inline.js +241 -0
- package/dist/ui/mermaid.js +98 -0
- package/dist/ui/table.js +148 -0
- package/dist/ui/toolOutputPreview.js +34 -0
- package/dist/ui/useAgent.js +534 -0
- package/dist/ui/useKillSwitch.js +65 -0
- package/dist/ui/useSessionResume.js +39 -0
- package/dist/ui/useUsageBudget.js +149 -0
- package/dist/ui/viewport.js +71 -0
- package/dist/undo/undo.js +293 -0
- package/dist/version.js +4 -0
- package/package.json +122 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Venkata Ramanand
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
# kritya
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Beta** — APIs, flags, and behavior may still change between releases.
|
|
4
|
+
|
|
5
|
+
Open source, [MIT licensed](LICENSE).
|
|
6
|
+
|
|
7
|
+
A lean, interctive coding agent for your terminal. Provider-agnostic —
|
|
8
|
+
it works with any OpenAI-compatible endpoint: [build.nvidia.com](https://build.nvidia.com)
|
|
9
|
+
(Qwen3 Coder, Kimi K2, DeepSeek, GLM, Nemotron, ...) by default, plus OpenAI,
|
|
10
|
+
OpenRouter, Groq, DeepSeek, Mistral, Together, and local models via Ollama.
|
|
11
|
+
|
|
12
|
+
Works on Linux, macOS, and Windows.
|
|
13
|
+
|
|
14
|
+
Configured for OpenAI-compatible endpoint - used with build.nvidia.com; other
|
|
15
|
+
providers are wired the same way but not exercised.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
cd your-project
|
|
19
|
+
kritya .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The agent can read, write, and edit files, search code, and run shell commands —
|
|
23
|
+
autonomously looping until your request is done. Anything that mutates state
|
|
24
|
+
(writes, edits, shell commands) asks for your permission first.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Provider-agnostic** — OpenAI-compatible endpoint (NVIDIA, OpenAI,
|
|
29
|
+
OpenRouter, Groq, DeepSeek, Mistral, Together, Ollama, ...); tested with
|
|
30
|
+
NVIDIA only
|
|
31
|
+
- **Permission-gated actions** — every file write/edit/shell command asks
|
|
32
|
+
first, with configurable allow/deny rules
|
|
33
|
+
- **Sandboxed execution** — OS-enforced backstop (`bwrap`/`sandbox-exec`)
|
|
34
|
+
confining writes to the workspace; falls back to unsandboxed (with a
|
|
35
|
+
warning) if the sandbox binary isn't installed
|
|
36
|
+
- **Staged project workflow** — `/flow-brainstorm → spec → plan → build →
|
|
37
|
+
review → fix` for building something new end-to-end
|
|
38
|
+
- **MCP + Agent Plugins support** — extend with external tools/servers, each
|
|
39
|
+
individually trust-gated
|
|
40
|
+
- **Undo/redo/checkpoints** — revert or rewind file changes and conversation
|
|
41
|
+
state
|
|
42
|
+
- **Office docs & notebooks** — read/write Word, Excel, PowerPoint, PDF,
|
|
43
|
+
Jupyter
|
|
44
|
+
- **Headless/CI mode** — scriptable, no-TTY runs for automation
|
|
45
|
+
|
|
46
|
+
## Setup
|
|
47
|
+
|
|
48
|
+
Requires Node.js >=22.
|
|
49
|
+
|
|
50
|
+
1. Get an API key from your chosen provider (click any
|
|
51
|
+
model → "Get API Key").
|
|
52
|
+
2. Set it (any one of these):
|
|
53
|
+
- Put `NVIDIA_API_KEY=nvapi-...` in a `.env` file — checked in the workspace
|
|
54
|
+
you launch in, the directory you run from, and `~/.kritya/.env`
|
|
55
|
+
- Linux/macOS: `export NVIDIA_API_KEY=nvapi-...`
|
|
56
|
+
- Windows: `setx NVIDIA_API_KEY nvapi-...` (then open a new terminal)
|
|
57
|
+
|
|
58
|
+
Optional: add `TAVILY_API_KEY` the same way to enable the agent's
|
|
59
|
+
web-search tool (`/web-search`) — get one at [tavily.com](https://tavily.com).
|
|
60
|
+
|
|
61
|
+
Optional (Linux): install `bubblewrap` (`bwrap`) for OS-level command
|
|
62
|
+
sandboxing — macOS has its sandbox built in; without `bwrap`, Linux falls
|
|
63
|
+
back to running commands unsandboxed with a warning.
|
|
64
|
+
|
|
65
|
+
3. Install and run — pick one:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Option A: npm
|
|
69
|
+
npm install -g kritya@beta
|
|
70
|
+
cd ~/some-project
|
|
71
|
+
kritya # enter
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Option B: from source
|
|
76
|
+
git clone https://github.com/GenAICloudDevOps/Kritya.git
|
|
77
|
+
cd Kritya
|
|
78
|
+
npm install
|
|
79
|
+
npm run build
|
|
80
|
+
npm link # puts `kritya` on your PATH
|
|
81
|
+
cd ~/some-project
|
|
82
|
+
kritya # enter
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Screenshots
|
|
86
|
+
|
|
87
|
+
**One-time AI disclosure notice on first launch**
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
**Startup banner with model and workspace status**
|
|
92
|
+
|
|
93
|
+

|
|
94
|
+
|
|
95
|
+
**Permission prompt for a web search request**
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
99
|
+
**Command list shown after typing `/`**
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+
**Test suite and coverage output**
|
|
104
|
+
|
|
105
|
+

|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
kritya [directory] [options]
|
|
111
|
+
|
|
112
|
+
-c, --continue resume the most recent session for this directory
|
|
113
|
+
-r, --resume pick a past session from a list
|
|
114
|
+
-m, --model <id> use any model ID your provider offers
|
|
115
|
+
-p, --provider <name> nvidia (default), openai, openrouter, groq, deepseek,
|
|
116
|
+
mistral, together, ollama, switchyard (multi-model
|
|
117
|
+
routing via NVIDIA NeMo Switchyard), or a custom one
|
|
118
|
+
-h, --help help
|
|
119
|
+
-v, --version version
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
In-session commands (type `/` to see them with autocomplete; letters filter the list):
|
|
123
|
+
|
|
124
|
+
| Command | What it does |
|
|
125
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| `/model` | interactive model picker (`/model <id>` sets any model ID directly) |
|
|
127
|
+
| `/provider` | list providers, or `/provider <name>` to switch mid-session ([more](docs/CONFIGURATION.md#providers)) |
|
|
128
|
+
| `/flow-brainstorm <idea>` | start the staged new-project workflow (see below) |
|
|
129
|
+
| `/flow-spec` | project workflow: write the spec from the approved brainstorm |
|
|
130
|
+
| `/flow-plan` | project workflow: plan from the spec (read-only) |
|
|
131
|
+
| `/flow-build` | project workflow: implement the plan, with tests |
|
|
132
|
+
| `/flow-review` | project workflow: spec-compliance and security review of the build |
|
|
133
|
+
| `/flow-fix` | project workflow: fix the review's findings, each one re-verified |
|
|
134
|
+
| `/plan` | toggle plan mode: `/plan`, `/plan on`, `/plan off` (unrelated to `/flow-plan`) |
|
|
135
|
+
| `/project` | workflow status; `goto <phase>`, `rename <name>`, `clear` to end it |
|
|
136
|
+
| `/diff` | show the cumulative git diff of this session's changes |
|
|
137
|
+
| `/init` | scan the repo and generate a `KRITYA.md` project-memory file |
|
|
138
|
+
| `/commit` | have the agent review, stage, and commit the current git changes |
|
|
139
|
+
| `/web-search <query>` | search the web via Tavily; results are shown and added to context |
|
|
140
|
+
| `/mcp` | MCP server status; `/mcp add\|remove <name>`, `/mcp login\|logout <name>`, `/mcp trust` ([more](docs/CONFIGURATION.md#mcp-servers)) |
|
|
141
|
+
| `/skills` | list discovered skills (project + user-global) and why any were skipped |
|
|
142
|
+
| `/plugins` | list discovered Agent Plugins, what each contributes, and why any were skipped ([more](docs/CONFIGURATION.md#agent-plugins)) |
|
|
143
|
+
| `/undo` | revert all file changes from the agent's last turn |
|
|
144
|
+
| `/redo` | reapply the change most recently undone |
|
|
145
|
+
| `/checkpoint <name>` | save a named point in the session (`/checkpoint` alone lists saved ones) |
|
|
146
|
+
| `/rewind <name>` | rewind both the conversation and the files to a checkpoint |
|
|
147
|
+
| `/compact` | summarize older conversation to free context space |
|
|
148
|
+
| `/clear` | start a fresh conversation |
|
|
149
|
+
| `/cost` | token usage and estimated $ (see Pricing below) |
|
|
150
|
+
| `/audit` | show this session's permission decisions and verify the audit log's chain ([more](docs/CONFIGURATION.md#audit-log--telemetry)) |
|
|
151
|
+
| `/budget` | show session token budget; `/budget reset` or `/budget <number>` |
|
|
152
|
+
| `/kill` | emergency stop: `/kill [reason]` halts everything; `/kill off` releases |
|
|
153
|
+
| `/help` | command list |
|
|
154
|
+
| `/exit` | quit |
|
|
155
|
+
|
|
156
|
+
Custom `/commands` you define (see [docs/CONFIGURATION.md](docs/CONFIGURATION.md#custom-slash-commands)) also appear here.
|
|
157
|
+
|
|
158
|
+
`Esc` cancels a running request. `↑/↓` recalls input history. `Ctrl+O` toggles
|
|
159
|
+
full tool output. `Ctrl+K` is the kill switch (see below). `Ctrl+C` exits.
|
|
160
|
+
|
|
161
|
+
### More features
|
|
162
|
+
|
|
163
|
+
- **Staged new-project workflow** — ask kritya to build something new (a
|
|
164
|
+
FastAPI backend, a Next.js frontend, a CLI) and it doesn't dive straight into
|
|
165
|
+
code. It runs six phases — **brainstorm → spec → plan → build → review → fix**
|
|
166
|
+
— writing a durable artifact for each under `docs/<name>/` and stopping for
|
|
167
|
+
your approval between phases:
|
|
168
|
+
|
|
169
|
+
| Phase | Produces |
|
|
170
|
+
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
171
|
+
| `brainstorm` | `brainstorm.md` — problem, users, MVP features, stack |
|
|
172
|
+
| `spec` | `spec.md` — contracts, data schema, acceptance criteria (MUST/LATER), and non-functional requirements (security/reliability/performance/CI, asked about via `ask_user` — skipped if none apply) |
|
|
173
|
+
| `plan` | `plan.md` — architecture and ordered milestones, each tagged RISKY or ROUTINE, plus trust boundaries for any security/reliability requirement |
|
|
174
|
+
| `build` | the code, with tests written before the code they test, plus negative/failure-path tests for security/reliability-tagged milestones, and a live milestone checklist |
|
|
175
|
+
| `review` | `review.md` — a one-line scorecard, then spec-compliance, security, and reliability findings |
|
|
176
|
+
| `fix` | `fix.md` — the review's findings addressed and re-verified |
|
|
177
|
+
|
|
178
|
+
Spec comes before plan on purpose: the spec settles _what_ (and pins the
|
|
179
|
+
numbered acceptance criteria everything downstream is held to), the plan
|
|
180
|
+
settles _how_ and sequences milestones against those criteria. Each phase
|
|
181
|
+
reads only the artifact immediately before it, so nothing gets re-derived.
|
|
182
|
+
The current phase lives in `.kritya/project.json`, so the flow resumes across
|
|
183
|
+
sessions. While a workflow is active the statusline carries a `⚑ name:phase`
|
|
184
|
+
flag, and the spinner names the running phase; `/project` shows the full
|
|
185
|
+
picture — including a warning if an earlier artifact was edited after a
|
|
186
|
+
later one already depended on it — and `/project clear` ends it. A phase
|
|
187
|
+
refuses to run if the artifact it reads was never written (`--force`
|
|
188
|
+
overrides). The agent walks the phases on its own, or you drive them by hand
|
|
189
|
+
with `/flow-brainstorm <idea>`, `/flow-spec`, `/flow-plan`, `/flow-build`,
|
|
190
|
+
`/flow-review`, `/flow-fix` — and after each one kritya tells you which
|
|
191
|
+
command comes next, so the handoff doesn't depend on the model remembering
|
|
192
|
+
to say it. `/flow-fix` only fixes what review found; if anything is still
|
|
193
|
+
open afterward it tells you to run `/flow-review` again or `/flow-fix`
|
|
194
|
+
again once you've decided how to handle what's left.
|
|
195
|
+
|
|
196
|
+
The project is named from your idea unless you name it yourself with a short
|
|
197
|
+
prefix — `/flow-brainstorm reverser: a script that reverses a string` gives
|
|
198
|
+
you `docs/reverser/`. `/project rename <name>` moves an existing one.
|
|
199
|
+
|
|
200
|
+
Cost matters here — six phases in one session adds up — so kritya compacts
|
|
201
|
+
the conversation at each phase boundary (the artifact is on disk, so the
|
|
202
|
+
transcript that produced it is redundant), caps artifact length, and in the
|
|
203
|
+
build phase dispatches independent milestones to isolated write subagents
|
|
204
|
+
rather than pulling every file into the main context. The review phase runs
|
|
205
|
+
its two reviewers as read-only subagents, so only their findings come back;
|
|
206
|
+
the fix phase does the same to re-verify only what it changed.
|
|
207
|
+
|
|
208
|
+
In the plan phase, plan mode's read-only guard is relaxed just enough to let
|
|
209
|
+
the agent write Markdown under that project's own `docs/<name>/` folder —
|
|
210
|
+
other docs, application code, and shell stay blocked until you `/flow-build`.
|
|
211
|
+
Plan mode itself is still a separate, general-purpose toggle — `/plan`,
|
|
212
|
+
`/plan on`, `/plan off` — unrelated to the workflow's `/flow-plan` phase.
|
|
213
|
+
|
|
214
|
+
- **Trust levels** — `Shift+Tab` cycles **normal** (every write/edit asks
|
|
215
|
+
first) → **accept-edits** (file writes/edits auto-approve, no prompt) →
|
|
216
|
+
**plan** (read-only, nothing executes) → back to normal. The statusline
|
|
217
|
+
always shows which one you're in, with a running `(N auto-approved)` count
|
|
218
|
+
in accept-edits mode so you know how much slipped through before checking
|
|
219
|
+
`/diff`. Destructive shell commands (`rm -rf`, force-push, etc.) always
|
|
220
|
+
still prompt, in every mode — that guard never turns off. The first time you
|
|
221
|
+
switch into accept-edits mode each session, kritya asks you to confirm first
|
|
222
|
+
so it's a deliberate choice, not an accidental keypress. `/plan on` and
|
|
223
|
+
`/plan off` set the mode explicitly; a bare `/plan` toggles it. `/plan` never
|
|
224
|
+
touches the project workflow — that's `/flow-plan`.
|
|
225
|
+
- **Kill switch** — `Ctrl+K` (or `/kill [reason]`) is a hard stop for the whole
|
|
226
|
+
session. It aborts the in-flight model stream, any running tool, and every
|
|
227
|
+
subagent at once, then refuses everything afterwards: new messages, tool
|
|
228
|
+
calls, compaction, and any slash command that would drive the agent all come
|
|
229
|
+
back with `⛔ Kill switch ACTIVE` until you run `/kill off`. Unlike `Esc`
|
|
230
|
+
(which cancels one turn) it outranks every other mode — plan mode,
|
|
231
|
+
accept-edits, and allow rules cannot get a tool past it — and it works from
|
|
232
|
+
anywhere, including while a permission prompt is on screen. The statusline
|
|
233
|
+
shows `⛔ KILLED`, and both the stop and the release are written to the audit
|
|
234
|
+
log. It's session-only: restarting kritya comes up in the normal state.
|
|
235
|
+
- **Subagents** — the agent can dispatch one or more focused investigations to
|
|
236
|
+
fresh contexts at once (`spawn_agent`), each returning only its findings —
|
|
237
|
+
keeps the main conversation lean on big searches. It can also dispatch
|
|
238
|
+
**write-capable subagents** (`spawn_write_agent`) for independent chunks of
|
|
239
|
+
work that can proceed in parallel; each one is isolated on its own git
|
|
240
|
+
branch/worktree, so its edits and shell commands never touch your real
|
|
241
|
+
working tree — review the diff and merge the branch yourself when ready.
|
|
242
|
+
Destructive commands (`rm -rf`, force push, etc.) are always blocked inside
|
|
243
|
+
a write subagent, since there's no one there to confirm them; each subagent
|
|
244
|
+
also has a hard time limit and no more than 6 (read) / 4 (write) run in one
|
|
245
|
+
call.
|
|
246
|
+
- **Image attachments** — `@screenshot.png` sends the image to vision-capable
|
|
247
|
+
models alongside your message.
|
|
248
|
+
- **Undo / redo** — `/undo` reverts the last turn's file changes; `/redo`
|
|
249
|
+
reapplies them. Undo is multi-level. A file watcher also checkpoints edits
|
|
250
|
+
you make yourself (in your own editor) to any file kritya has touched this
|
|
251
|
+
session, as their own step in the right order — so hand-editing a file
|
|
252
|
+
between turns and later running `/undo` never silently discards that edit.
|
|
253
|
+
- **Checkpoint / rewind** — `/checkpoint <name>` saves a named point in the
|
|
254
|
+
session; `/rewind <name>` rolls _both_ the conversation and the files back to
|
|
255
|
+
it at once (e.g. "go back to before the auth refactor"), where `/undo` only
|
|
256
|
+
steps back file changes one turn at a time. Checkpoints are in-memory for the
|
|
257
|
+
current session.
|
|
258
|
+
- **Steer mid-run** — type while the agent is working and press Enter; your
|
|
259
|
+
message is queued and absorbed before its next step (no need to interrupt).
|
|
260
|
+
- **Auto-compaction → self-improving project memory** — when the conversation
|
|
261
|
+
nears the model's context window (80% of `contextWindow`, default 120k
|
|
262
|
+
tokens), older turns are summarized automatically; the statusline shows
|
|
263
|
+
current usage as `ctx N%`. Compaction (auto or manual `/compact`) also
|
|
264
|
+
distills durable, objective facts out of what's being summarized away —
|
|
265
|
+
build/test commands, package manager, conventions actually observed — and
|
|
266
|
+
merges any new ones into a `## Learned by kritya` section in `KRITYA.md`,
|
|
267
|
+
deduplicated and capped at 20 facts. Anything you or `/init` wrote above
|
|
268
|
+
that section is left untouched. It's scoped to describing the project, not
|
|
269
|
+
storing instructions, since this file is read back as background context on
|
|
270
|
+
every future run.
|
|
271
|
+
- **Token budget** — a session-wide cap on combined prompt + completion
|
|
272
|
+
tokens across every turn and model (default 1,000,000; set `tokenBudget` in
|
|
273
|
+
config, or `/budget <number>` mid-session). The statusline shows `budget N%`
|
|
274
|
+
once usage starts, turning yellow past 80% with a one-time warning, then
|
|
275
|
+
stops further turns entirely at 100% until you run `/budget reset` (clears
|
|
276
|
+
the count) or `/budget <number>` (raises the cap). `/cost` also reports it.
|
|
277
|
+
- **Background processes** — the agent can start dev servers/watchers with
|
|
278
|
+
`background: true`, read their output (`bg_output`), and stop them
|
|
279
|
+
(`bg_kill`); everything is killed when kritya exits. Foreground commands
|
|
280
|
+
accept a `timeout_seconds` (default 120), and long output keeps the tail,
|
|
281
|
+
where the errors are.
|
|
282
|
+
- **Git aware** — the statusline shows the current branch, the agent sees
|
|
283
|
+
`git status` each request, and `/undo` checkpoints are per turn.
|
|
284
|
+
- **@ file mentions** — type `@` in your message to autocomplete a file path
|
|
285
|
+
(↑↓ select, Tab/Enter attach); the file's content is sent along with your message.
|
|
286
|
+
- **Project memory** — create a `KRITYA.md` in your workspace root (or run
|
|
287
|
+
`/init` to generate one) with standing instructions; the agent reads it
|
|
288
|
+
every request.
|
|
289
|
+
- **Sub-task checklist → resumable plans** — for multi-step requests the agent
|
|
290
|
+
plans first and shows a live ☐/◐/☑ checklist as it works, with a compact
|
|
291
|
+
`tasks N/M` summary in the statusline. The checklist is saved alongside the
|
|
292
|
+
session, so `kritya -c` (and picking a session via `-r`) restores not just
|
|
293
|
+
the conversation but exactly which steps were done, in progress, or still
|
|
294
|
+
pending.
|
|
295
|
+
- **Diff preview** — write/edit permission prompts show a red/green line diff of
|
|
296
|
+
exactly what will change before you approve; code blocks in answers are
|
|
297
|
+
syntax-highlighted.
|
|
298
|
+
- **Session search** — `kritya -r` lists past sessions by title (first message);
|
|
299
|
+
type to filter.
|
|
300
|
+
- **Web search tool** — besides `/web-search`, the agent can search on its own when
|
|
301
|
+
it needs current information (needs `TAVILY_API_KEY` in `.env`, get one at
|
|
302
|
+
tavily.com). Web content is delimited as untrusted so pages can't inject
|
|
303
|
+
instructions into the agent.
|
|
304
|
+
- **Reading the web, not just searching it** — `fetch_url` pulls the full text
|
|
305
|
+
of one known URL (docs page, raw file, JSON endpoint), where search only
|
|
306
|
+
returns snippets; `deep_research` takes several focused sub-queries, reads
|
|
307
|
+
the top result pages for each, and returns one consolidated cited bundle for
|
|
308
|
+
broad comparison questions. Both refuse local and private-network addresses,
|
|
309
|
+
so neither can be steered at your internal network.
|
|
310
|
+
- **Office documents and notebooks** — `read_document`/`write_document` handle
|
|
311
|
+
Word (`.docx`), Excel (`.xlsx`), PowerPoint (`.pptx`), and PDF, with
|
|
312
|
+
`edit_spreadsheet` for targeted cell changes and `edit_pdf` for page
|
|
313
|
+
operations (delete, rotate, reorder, extract). Jupyter notebooks get
|
|
314
|
+
`read_notebook`/`edit_notebook`, which work cell-by-cell instead of forcing
|
|
315
|
+
the agent to rewrite the whole `.ipynb` JSON.
|
|
316
|
+
- **Agent Plugins** — bundle skills, slash commands, and MCP servers into one
|
|
317
|
+
versioned folder under `.kritya/plugins/` (project) or `~/.kritya/plugins/`
|
|
318
|
+
(global), so a capability you want everywhere is one folder to copy rather
|
|
319
|
+
than three things to wire up. `/plugins` shows what loaded, what each
|
|
320
|
+
contributes, and why anything was skipped. A workspace plugin only loads
|
|
321
|
+
once you've trusted that workspace, and any MCP server it declares still
|
|
322
|
+
needs its own approval ([more](docs/CONFIGURATION.md#agent-plugins)).
|
|
323
|
+
- **Prompt-caching awareness** — the system prompt is ordered stable-first
|
|
324
|
+
(identity and rules → project memory → volatile git status/listing last) so
|
|
325
|
+
providers can reuse their cached prompt prefix across turns instead of
|
|
326
|
+
re-reading everything. `/cost` and the statusline show how many prompt
|
|
327
|
+
tokens were served from the provider's cache; add an optional
|
|
328
|
+
`"cachedInput"` rate to your `pricing` config to see the dollar savings.
|
|
329
|
+
- **LSP integration** — the agent gets go-to-definition, find-references,
|
|
330
|
+
hover type info, live diagnostics, and project-wide rename from real language
|
|
331
|
+
servers (`lsp_definition`, `lsp_references`, `lsp_hover`, `lsp_diagnostics`,
|
|
332
|
+
`lsp_rename`), resolved semantically instead of by text search. `lsp_rename`
|
|
333
|
+
renames only the actual occurrences of that symbol — never a same-named but
|
|
334
|
+
unrelated variable, and never text in comments or strings — and asks for
|
|
335
|
+
permission like any other mutating tool.
|
|
336
|
+
Supports TypeScript/JavaScript, Python, Go, Rust, and C/C++ —
|
|
337
|
+
it uses whichever servers you have installed (`typescript-language-server`,
|
|
338
|
+
`pyright`, `gopls`, `rust-analyzer`, `clangd`) and tells the agent the
|
|
339
|
+
install command when one is missing. Servers spawn on first use, stay warm
|
|
340
|
+
for the session, and never require configuration.
|
|
341
|
+
|
|
342
|
+
## Headless / CI mode
|
|
343
|
+
|
|
344
|
+
Run one prompt to completion with no terminal UI and no TTY requirement —
|
|
345
|
+
for scripts, CI pipelines, and GitHub Actions:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
kritya --prompt "fix the failing tests" --output json
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Exits `0` on success, `1` on failure — check `$?` in a pipeline. `--output json`
|
|
352
|
+
prints a single JSON object on stdout: `{success, result, error, toolCalls,
|
|
353
|
+
usage, durationMs, model}`. Plain `--output text` (the default) just prints
|
|
354
|
+
the agent's final answer.
|
|
355
|
+
|
|
356
|
+
There's no terminal to show a permission prompt, so headless mode never
|
|
357
|
+
blocks waiting for one:
|
|
358
|
+
|
|
359
|
+
- Mutating tool calls are denied by default unless covered by an `allow` rule
|
|
360
|
+
in `.kritya/settings.json`, or `--allow-all` is passed to approve them all.
|
|
361
|
+
- Destructive commands (`rm -rf`, force-push, etc.) are **always** denied,
|
|
362
|
+
even with `--allow-all` — there's no one to confirm them, so that guard
|
|
363
|
+
never turns off.
|
|
364
|
+
- The workspace's own `.kritya/settings.json`, hooks, `.env`, and custom
|
|
365
|
+
commands only take effect with `--trust`, or if the workspace was already
|
|
366
|
+
trusted in a prior interactive session — never silently, since CI often
|
|
367
|
+
checks out untrusted branches/PRs.
|
|
368
|
+
- `--timeout <seconds>` caps the whole run (default 1800); a stuck turn is
|
|
369
|
+
aborted rather than hanging a CI job forever.
|
|
370
|
+
|
|
371
|
+
Example GitHub Actions step:
|
|
372
|
+
|
|
373
|
+
```yaml
|
|
374
|
+
- name: Fix failing tests with kritya
|
|
375
|
+
env:
|
|
376
|
+
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
|
|
377
|
+
run: |
|
|
378
|
+
npx kritya --prompt "run the test suite, fix any failing tests, and show a diff" \
|
|
379
|
+
--output json --allow-all > result.json
|
|
380
|
+
cat result.json
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Subagents (`spawn_agent`/`spawn_write_agent`) aren't available in headless
|
|
384
|
+
mode — a single prompt/response doesn't need the parallel-dispatch machinery
|
|
385
|
+
they're built for.
|
|
386
|
+
|
|
387
|
+
## Permissions & Configuration
|
|
388
|
+
|
|
389
|
+
Permission rules, sandboxing, the audit log and tracing, the config file,
|
|
390
|
+
provider fallback, custom slash commands, skills, hooks, MCP servers
|
|
391
|
+
(including OAuth login to hosted servers), and Agent Plugins are all covered in
|
|
392
|
+
**[docs/CONFIGURATION.md](docs/CONFIGURATION.md)**. The threat model and what
|
|
393
|
+
each safeguard does and doesn't guarantee are in
|
|
394
|
+
**[SECURITY.md](SECURITY.md)**.
|
|
395
|
+
|
|
396
|
+
The short version: mutating tools (`write_file`, `edit_file`, `shell`) prompt
|
|
397
|
+
for permission unless allowlisted in `.kritya/settings.json`; destructive
|
|
398
|
+
commands (`rm -rf`, force-push, etc.) always prompt regardless, and shell
|
|
399
|
+
commands are sandboxed by default on Linux/macOS. Provider and
|
|
400
|
+
model config lives in `~/.kritya/config.json`.
|
|
401
|
+
|
|
402
|
+
## Privacy
|
|
403
|
+
|
|
404
|
+
kritya collects **no telemetry** and phones home to nothing of its own by
|
|
405
|
+
default. Network requests go only to the model provider you configure (and to
|
|
406
|
+
Tavily if you use web search). Sessions and config stay on your machine under
|
|
407
|
+
`~/.kritya/`. The one opt-in exception is `KRITYA_OTEL_ENDPOINT` (see
|
|
408
|
+
[Audit log & telemetry](docs/CONFIGURATION.md#audit-log--telemetry)): if you explicitly set it, tracing/metrics spans are sent
|
|
409
|
+
to the OpenTelemetry Collector you point it at — nothing is exported anywhere
|
|
410
|
+
unless you configure that endpoint yourself.
|
|
411
|
+
|
|
412
|
+
## Development
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
npm run dev # run from source (tsx)
|
|
416
|
+
npm run build # compile to dist/ (strict TypeScript)
|
|
417
|
+
npm test # build + unit tests
|
|
418
|
+
npm run lint # eslint
|
|
419
|
+
npm run format # prettier --write
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Architecture: `src/provider` (OpenAI-compatible streaming client) → `src/agent`
|
|
423
|
+
(the tool-call loop, compaction, system prompt) → `src/tools` (plain-object
|
|
424
|
+
tools) → `src/ui` (Ink/React terminal UI), with `src/permissions`, `src/trust`,
|
|
425
|
+
`src/hooks`, `src/mcp`, `src/plugins`, `src/lsp`, `src/commands`, `src/session`,
|
|
426
|
+
`src/shell` (background processes + sandboxing), `src/audit`, `src/telemetry`,
|
|
427
|
+
and `src/git` supporting. `src/headless.ts` and `src/engine.ts` are the CI
|
|
428
|
+
entry points onto the same core. See
|
|
429
|
+
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for a full tour and
|
|
430
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) to get started.
|
|
431
|
+
|
|
432
|
+
## License
|
|
433
|
+
|
|
434
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Estimated cost in USD, pricing cached prompt tokens at cachedInput when configured. */
|
|
2
|
+
export function costFor(u, p) {
|
|
3
|
+
const cached = u.cachedPromptTokens ?? 0;
|
|
4
|
+
return (((u.promptTokens - cached) / 1e6) * p.input +
|
|
5
|
+
(cached / 1e6) * (p.cachedInput ?? p.input) +
|
|
6
|
+
(u.completionTokens / 1e6) * p.output);
|
|
7
|
+
}
|
|
8
|
+
/** What cached tokens would have cost at the full input rate, minus what they did cost. */
|
|
9
|
+
export function cacheSavingsFor(u, p) {
|
|
10
|
+
if (p.cachedInput === undefined)
|
|
11
|
+
return 0;
|
|
12
|
+
return ((u.cachedPromptTokens ?? 0) / 1e6) * (p.input - p.cachedInput);
|
|
13
|
+
}
|
|
14
|
+
/** Default session token budget (prompt + completion, combined across all models/turns). */
|
|
15
|
+
export const DEFAULT_TOKEN_BUDGET = 1_000_000;
|
|
16
|
+
/** Percent of the budget at which to warn the user, ahead of the hard stop. */
|
|
17
|
+
export const BUDGET_WARN_THRESHOLD_PCT = 80;
|
|
18
|
+
/** config.tokenBudget wins if set and positive; otherwise DEFAULT_TOKEN_BUDGET. */
|
|
19
|
+
export function tokenBudgetFor(config) {
|
|
20
|
+
return config.tokenBudget && config.tokenBudget > 0 ? config.tokenBudget : DEFAULT_TOKEN_BUDGET;
|
|
21
|
+
}
|
|
22
|
+
/** True only on the transition from below the threshold to at-or-above it, so the warning fires once per rise. */
|
|
23
|
+
export function crossedBudgetWarnThreshold(prevPct, nextPct, threshold = BUDGET_WARN_THRESHOLD_PCT) {
|
|
24
|
+
return prevPct < threshold && nextPct >= threshold;
|
|
25
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** How many recent messages survive compaction untouched. */
|
|
2
|
+
const KEEP_TAIL = 8;
|
|
3
|
+
/**
|
|
4
|
+
* Split history into an older part to summarize and a recent tail to keep.
|
|
5
|
+
* The tail never starts on a tool reply: the cut moves back to the assistant
|
|
6
|
+
* message that issued the tool calls, so call/reply pairs stay together.
|
|
7
|
+
*/
|
|
8
|
+
export function splitForCompaction(history) {
|
|
9
|
+
if (history.length <= KEEP_TAIL)
|
|
10
|
+
return { toSummarize: [], keep: history };
|
|
11
|
+
let cut = history.length - KEEP_TAIL;
|
|
12
|
+
while (cut > 0 && history[cut].role === "tool")
|
|
13
|
+
cut--;
|
|
14
|
+
return { toSummarize: history.slice(0, cut), keep: history.slice(cut) };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the replacement for the summarized-away messages when the model can't
|
|
18
|
+
* be asked to summarize them — because the summarization request itself
|
|
19
|
+
* failed, which is most likely precisely when it's needed (a full context, a
|
|
20
|
+
* rate-limited provider, a dropped connection).
|
|
21
|
+
*
|
|
22
|
+
* The alternative to this is losing the turn. Dropping the old messages
|
|
23
|
+
* outright would leave the model with no idea what it had already done, so
|
|
24
|
+
* this keeps the skeleton that survives cheaply without a model call: which
|
|
25
|
+
* files were touched and which commands ran. It is much worse than a real
|
|
26
|
+
* summary, and says so, so the model treats it as the partial record it is.
|
|
27
|
+
*/
|
|
28
|
+
export function fallbackSummary(toSummarize) {
|
|
29
|
+
const files = new Set();
|
|
30
|
+
const commands = [];
|
|
31
|
+
let userAsks = 0;
|
|
32
|
+
for (const m of toSummarize) {
|
|
33
|
+
if (m.role === "user" && typeof m.content === "string" && !m.content.startsWith("["))
|
|
34
|
+
userAsks++;
|
|
35
|
+
for (const call of m.tool_calls ?? []) {
|
|
36
|
+
let args;
|
|
37
|
+
try {
|
|
38
|
+
args = JSON.parse(call.function.arguments);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (typeof args.path === "string")
|
|
44
|
+
files.add(args.path);
|
|
45
|
+
if (typeof args.command === "string" && commands.length < 20)
|
|
46
|
+
commands.push(args.command);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const lines = [
|
|
50
|
+
`[Earlier work in this session — ${toSummarize.length} messages were dropped to stay within ` +
|
|
51
|
+
`the context window. The model that would have summarized them was unavailable, so this is ` +
|
|
52
|
+
`a mechanical record, not a summary: details, decisions, and outcomes are NOT captured here. ` +
|
|
53
|
+
`Re-read files or re-run commands before relying on their contents.]`,
|
|
54
|
+
`Requests from the user in that span: ${userAsks}`,
|
|
55
|
+
];
|
|
56
|
+
if (files.size) {
|
|
57
|
+
lines.push(`Files touched: ${[...files].slice(0, 40).join(", ")}`);
|
|
58
|
+
}
|
|
59
|
+
if (commands.length) {
|
|
60
|
+
lines.push(`Commands run: ${commands.map((c) => c.slice(0, 120)).join(" | ")}`);
|
|
61
|
+
}
|
|
62
|
+
return lines.join("\n");
|
|
63
|
+
}
|
|
64
|
+
/** Render messages as a plain-text transcript for the summarization request. */
|
|
65
|
+
export function renderTranscript(messages) {
|
|
66
|
+
return messages
|
|
67
|
+
.map((m) => {
|
|
68
|
+
if (m.role === "tool") {
|
|
69
|
+
return `[tool result]: ${String(m.content ?? "").slice(0, 400)}`;
|
|
70
|
+
}
|
|
71
|
+
const content = typeof m.content === "string" ? m.content : "";
|
|
72
|
+
const calls = m.tool_calls
|
|
73
|
+
?.map((c) => `${c.function.name}(${c.function.arguments.slice(0, 200)})`)
|
|
74
|
+
.join(", ");
|
|
75
|
+
return `${m.role}: ${content}${calls ? `\n[called: ${calls}]` : ""}`;
|
|
76
|
+
})
|
|
77
|
+
.join("\n\n");
|
|
78
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Percent of the context window at which to warn the user, ahead of auto-compaction. */
|
|
2
|
+
export const CONTEXT_WARN_THRESHOLD_PCT = 75;
|
|
3
|
+
/** True only on the transition from below the threshold to at-or-above it, so the warning fires once per rise. */
|
|
4
|
+
export function crossedContextWarnThreshold(prevPct, nextPct, threshold = CONTEXT_WARN_THRESHOLD_PCT) {
|
|
5
|
+
return prevPct < threshold && nextPct >= threshold;
|
|
6
|
+
}
|