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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +434 -0
  3. package/dist/agent/budget.js +25 -0
  4. package/dist/agent/compactor.js +78 -0
  5. package/dist/agent/contextWarning.js +6 -0
  6. package/dist/agent/killSwitch.js +111 -0
  7. package/dist/agent/loop.js +543 -0
  8. package/dist/agent/memory.js +145 -0
  9. package/dist/agent/plugins.js +408 -0
  10. package/dist/agent/skills.js +210 -0
  11. package/dist/agent/skillsCli.js +51 -0
  12. package/dist/agent/systemPrompt.js +122 -0
  13. package/dist/agent/tokens.js +55 -0
  14. package/dist/agent/toolExecutor.js +331 -0
  15. package/dist/agent/workflow.js +496 -0
  16. package/dist/agent/worktree.js +116 -0
  17. package/dist/atomicWrite.js +109 -0
  18. package/dist/audit/audit.js +230 -0
  19. package/dist/audit/cli.js +135 -0
  20. package/dist/commands/custom.js +69 -0
  21. package/dist/commands/mcpCommand.js +433 -0
  22. package/dist/commands/pluginsCommand.js +204 -0
  23. package/dist/commands/registry.js +739 -0
  24. package/dist/config/config.js +214 -0
  25. package/dist/config/debug.js +19 -0
  26. package/dist/config/models.js +82 -0
  27. package/dist/config/retention.js +24 -0
  28. package/dist/config/winAcl.js +38 -0
  29. package/dist/crash.js +102 -0
  30. package/dist/engine.js +113 -0
  31. package/dist/git/git.js +46 -0
  32. package/dist/headless.js +274 -0
  33. package/dist/hooks/hooks.js +122 -0
  34. package/dist/index.js +670 -0
  35. package/dist/lsp/client.js +413 -0
  36. package/dist/lsp/manager.js +0 -0
  37. package/dist/lsp/registry.js +62 -0
  38. package/dist/mcp/callback.js +141 -0
  39. package/dist/mcp/client.js +945 -0
  40. package/dist/mcp/login.js +117 -0
  41. package/dist/mcp/oauth.js +345 -0
  42. package/dist/mcp/servers.js +114 -0
  43. package/dist/mcp/spawnWin.js +86 -0
  44. package/dist/mcp/tokens.js +97 -0
  45. package/dist/mcp/transport.js +295 -0
  46. package/dist/net/urlSafety.js +158 -0
  47. package/dist/permissions/danger.js +96 -0
  48. package/dist/permissions/permissions.js +62 -0
  49. package/dist/permissions/rules.js +69 -0
  50. package/dist/plugins/discover.js +108 -0
  51. package/dist/plugins/mcp.js +80 -0
  52. package/dist/provider/client.js +460 -0
  53. package/dist/provider/switchyardClient.js +46 -0
  54. package/dist/provider/switchyardSidecar.js +245 -0
  55. package/dist/provider/textToolCalls.js +140 -0
  56. package/dist/repomap/repoMap.js +168 -0
  57. package/dist/repomap/symbols.js +190 -0
  58. package/dist/session/store.js +328 -0
  59. package/dist/shell/background.js +131 -0
  60. package/dist/shell/sandbox.js +396 -0
  61. package/dist/telemetry/metrics.js +172 -0
  62. package/dist/telemetry/otlp.js +128 -0
  63. package/dist/telemetry/tracer.js +240 -0
  64. package/dist/tools/askUser.js +64 -0
  65. package/dist/tools/bg.js +59 -0
  66. package/dist/tools/common.js +158 -0
  67. package/dist/tools/deepResearch.js +126 -0
  68. package/dist/tools/diff.js +70 -0
  69. package/dist/tools/document/docx.js +44 -0
  70. package/dist/tools/document/pdf.js +190 -0
  71. package/dist/tools/document/pptx.js +148 -0
  72. package/dist/tools/document/types.js +26 -0
  73. package/dist/tools/document/xlsx.js +87 -0
  74. package/dist/tools/document.js +389 -0
  75. package/dist/tools/edit.js +56 -0
  76. package/dist/tools/fetchUrl.js +218 -0
  77. package/dist/tools/fuzzyMatch.js +74 -0
  78. package/dist/tools/glob.js +34 -0
  79. package/dist/tools/grep.js +85 -0
  80. package/dist/tools/ignore.js +28 -0
  81. package/dist/tools/index.js +66 -0
  82. package/dist/tools/ls.js +27 -0
  83. package/dist/tools/lsp.js +239 -0
  84. package/dist/tools/notebook.js +215 -0
  85. package/dist/tools/read.js +32 -0
  86. package/dist/tools/repoMap.js +26 -0
  87. package/dist/tools/secretScan.js +143 -0
  88. package/dist/tools/shell.js +134 -0
  89. package/dist/tools/skills.js +69 -0
  90. package/dist/tools/subagent.js +57 -0
  91. package/dist/tools/tasks.js +43 -0
  92. package/dist/tools/webSearch.js +97 -0
  93. package/dist/tools/write.js +47 -0
  94. package/dist/tools/writeAgent.js +72 -0
  95. package/dist/trust/aiDisclosure.js +45 -0
  96. package/dist/trust/mcpTrust.js +111 -0
  97. package/dist/trust/trust.js +231 -0
  98. package/dist/types.js +1 -0
  99. package/dist/ui/AiDisclosurePrompt.js +23 -0
  100. package/dist/ui/App.js +449 -0
  101. package/dist/ui/Banner.js +71 -0
  102. package/dist/ui/ElicitationPrompt.js +46 -0
  103. package/dist/ui/Markdown.js +148 -0
  104. package/dist/ui/McpTrustPrompt.js +42 -0
  105. package/dist/ui/ModelPicker.js +19 -0
  106. package/dist/ui/PermissionPrompt.js +27 -0
  107. package/dist/ui/SelectList.js +24 -0
  108. package/dist/ui/Spinner.js +12 -0
  109. package/dist/ui/StatusLine.js +11 -0
  110. package/dist/ui/TranscriptItem.js +13 -0
  111. package/dist/ui/TrustPrompt.js +12 -0
  112. package/dist/ui/highlight.js +137 -0
  113. package/dist/ui/inline.js +241 -0
  114. package/dist/ui/mermaid.js +98 -0
  115. package/dist/ui/table.js +148 -0
  116. package/dist/ui/toolOutputPreview.js +34 -0
  117. package/dist/ui/useAgent.js +534 -0
  118. package/dist/ui/useKillSwitch.js +65 -0
  119. package/dist/ui/useSessionResume.js +39 -0
  120. package/dist/ui/useUsageBudget.js +149 -0
  121. package/dist/ui/viewport.js +71 -0
  122. package/dist/undo/undo.js +293 -0
  123. package/dist/version.js +4 -0
  124. 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
+ ![One-time AI disclosure notice on first launch](screenshots/1.png)
90
+
91
+ **Startup banner with model and workspace status**
92
+
93
+ ![Startup banner with model and workspace status](screenshots/2.png)
94
+
95
+ **Permission prompt for a web search request**
96
+
97
+ ![Permission prompt for a web search request](screenshots/3.png)
98
+
99
+ **Command list shown after typing `/`**
100
+
101
+ ![Command list shown after typing `/`](screenshots/4.png)
102
+
103
+ **Test suite and coverage output**
104
+
105
+ ![Test suite and coverage output](screenshots/5.png)
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
+ }