tandem-mode 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,204 +1,249 @@
1
- # Tandem Mode
2
-
3
- [![npm version](https://img.shields.io/npm/v/tandem-mode.svg)](https://www.npmjs.com/package/tandem-mode)
4
- [![license](https://img.shields.io/npm/l/tandem-mode.svg)](./LICENSE)
5
- [![node](https://img.shields.io/node/v/tandem-mode.svg)](./package.json)
6
- [![good first issues](https://img.shields.io/github/issues/zdrave08/Tandem-Mode/good%20first%20issue)](https://github.com/zdrave08/Tandem-Mode/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
7
-
8
- **English** | [Srpski](./README.sr.md)
9
-
10
- A terminal coding agent built specifically for the DeepSeek API — not a generic
11
- LLM harness with DeepSeek bolted on. Interactive REPL, planner/worker/reviewer
12
- orchestration, cache-aware prompt architecture, and first-class support for
13
- Windows, macOS, and Linux.
14
-
15
- > **Independent open-source project. Not affiliated with or endorsed by
16
- > DeepSeek.**
17
-
18
- ## Status
19
-
20
- M0–M4 of the [development plan](./deepseek-cli-development-plan.md) are done.
21
- Published on npm, usable today for real coding tasks against a local repo.
22
-
23
- First benchmark run is in — see [full results](./docs/benchmark-results.md).
24
- Headline: all three configurations (Pro-only, Flash-only, orchestration)
25
- fixed 4/4 injected bugs correctly in a real WooCommerce plugin, with a clean
26
- `php -l` and zero unnecessary file changes. But for this class of task
27
- (single-file, well-scoped fixes), orchestration cost ~9.5× more and took
28
- ~19× longer than Pro-only for the same correctness — the opposite of what
29
- you'd want orchestration for. Read as: orchestration's overhead needs a
30
- bigger, more decomposable task to pay for itself, which this first run didn't
31
- test. Reported as-is, not adjusted to fit expectations.
32
-
33
- ## Features
34
-
35
- - **Agentic loop** — `deepseek-v4-pro`/`deepseek-v4-flash` with tool calling:
36
- `read_file`, `list_dir`, `search`, `edit`, `git_diff`, `shell`, `view_image`,
37
- `web_search`.
38
- - **Two entry points** — interactive REPL for normal work, single-shot mode
39
- (`tandem "task" --yes`) for scripting and CI.
40
- - **Orchestration** (`/plan`) — a planner produces a JSON task list, workers
41
- execute each task with a fresh minimal context, a reviewer checks the git
42
- diff against the original task and either approves or requests corrections
43
- (capped by `max_review_loops`).
44
- - **Cache-aware prompts** — the worker's system prompt + repo map + plan stay
45
- byte-identical across calls in one orchestration run, so DeepSeek's context
46
- cache actually hits. Measured live: cache-hit tokens grew call over call
47
- within a single `/plan` run.
48
- - **Vision** — `view_image` lets the agent look at a screenshot you point it
49
- to; `/paste` grabs an image straight from the clipboard (Windows, macOS,
50
- and Linux via X11/`xclip` or Wayland/`wl-clipboard`).
51
- - **Web search** — isolated calls to DeepSeek's Responses API when the agent
52
- needs something outside the repo (an error message, a library's docs).
53
- - **Session persistence** — every session is a JSONL file; `/resume` continues
54
- the latest (or a named) one, `/fork` branches off without touching the
55
- original.
56
- - **Safety** — destructive actions (file edits, shell commands not on a small
57
- read-only allowlist) always ask for confirmation; `y`/`n`/`a` where `a`
58
- remembers the choice for that tool for the rest of the session. `--yes`
59
- bypasses this for CI but always prints what it auto-approved.
60
- - **Cost control** — usage is read only from the API's own `usage` field,
61
- never estimated. A budget (`/budget`, `--budget`) hard-stops the loop before
62
- the next API call if exceeded, without losing the session. `/usage` also
63
- shows what the session would have cost off-peak, and the REPL warns you at
64
- startup if you're currently in a peak-pricing window.
65
- - **ESC to interrupt** — cancels the in-flight call (and any running shell
66
- command) cleanly, keeping whatever was generated so far.
67
- - **Cross-platform** — the shell tool runs PowerShell natively on Windows and
68
- `sh` on macOS/Linux, file edits preserve the original CRLF/LF line ending
69
- style, paths are handled with `path.resolve` throughout.
70
-
71
- ## Quick start
72
-
73
- ```bash
74
- npm install -g tandem-mode
75
- tandem # interactive REPL
76
- tandem "explain what this repo does" --yes # one-shot
77
- ```
78
-
79
- Or from source (useful for contributing, or to track `main` instead of the
80
- latest npm release):
81
-
82
- ```bash
83
- git clone https://github.com/zdrave08/Tandem-Mode.git
84
- cd Tandem-Mode
85
- pnpm install
86
- pnpm dev # interactive REPL
87
- pnpm dev "explain what this repo does" --yes # one-shot
88
- ```
89
-
90
- On first run you'll be asked for a DeepSeek API key (get one at
91
- [platform.deepseek.com](https://platform.deepseek.com)) and a default
92
- model/reasoning effort. The key is stored in your OS's own secure credential
93
- store — never in a config file or log:
94
-
95
- - **Windows** — DPAPI-encrypted (tied to your OS user account) in
96
- `%LOCALAPPDATA%\TandemMode\credentials.dat`.
97
- - **macOS** — the login Keychain, via the built-in `security` CLI.
98
- - **Linux** — the Secret Service (gnome-keyring, KWallet, or similar), via
99
- `secret-tool` (install `libsecret-tools` if it's missing).
100
-
101
- `DEEPSEEK_API_KEY` works everywhere and always takes priority, for CI use.
102
-
103
- ## Usage
104
-
105
- ```bash
106
- tandem # interactive REPL
107
- tandem "fix the off-by-one in foo.ts"
108
- tandem --plan "add dark mode to the settings page"
109
- tandem "task" --model deepseek-v4-flash --effort low --budget 0.50 --yes
110
- ```
111
-
112
- | Flag | Effect |
113
- |---|---|
114
- | `--resume` / `-r` | Continue the latest session in this directory |
115
- | `--yes` / `-y` | Auto-approve destructive actions (prints what it approved) |
116
- | `--plan` | Run planner/worker/reviewer orchestration instead of a single turn |
117
- | `--model` | `deepseek-v4-pro` or `deepseek-v4-flash` |
118
- | `--effort` | `low`, `high`, or `max` reasoning effort |
119
- | `--budget` | Hard USD cap for the run |
120
- | `--max-review-loops` | Cap on orchestration correction cycles (default 3) |
121
-
122
- ### REPL commands
123
-
124
- | Command | Effect |
125
- |---|---|
126
- | `/plan <task>` | Planner/worker/reviewer orchestration |
127
- | `/model [name]` | Show or switch model |
128
- | `/thinking [on\|off]` | Toggle thinking mode |
129
- | `/effort [low\|high\|max]` | Show or set reasoning effort |
130
- | `/status` | Current session/settings |
131
- | `/usage` | Tokens, cost, off-peak projection |
132
- | `/budget [amount]` | Show or set the session budget |
133
- | `/new` | Start a fresh session |
134
- | `/fork` | New session, carrying over the current history |
135
- | `/resume [id]` | Continue the latest (or a named) session |
136
- | `/paste [message]` | Send a clipboard image to the agent |
137
- | `/clear` | Clear the screen |
138
- | `/help` | This list |
139
- | `/exit` | Quit |
140
-
141
- Press **ESC** at any time during a turn to interrupt it.
142
-
143
- ## Architecture
144
-
145
- ```
146
- tandem (no args) ─┬─> interactive REPL ─┬─> single turn: agent loop (tools)
147
- │ └─> /plan: planner → worker(s) → reviewer
148
- tandem "task" ──┘ (single-shot, same agent loop, for scripts/CI)
149
- ```
150
-
151
- - `src/deepseek/` — thin client for DeepSeek's OpenAI-compatible API
152
- (streaming + non-streaming chat completions, vision, web search via
153
- Responses API). See [`docs/api-notes.en.md`](./docs/api-notes.en.md)
154
- (Serbian original: [`docs/api-notes.md`](./docs/api-notes.md)) for the
155
- API behavior this is built against, verified live rather than assumed.
156
- - `src/agent/` — the tool-calling loop, tool implementations, session
157
- persistence, usage/cost tracking.
158
- - `src/orchestrator/` — planner/worker/reviewer, repo-map generation, the
159
- cache-aware stable-prefix construction.
160
- - `src/repl/` — slash commands, runtime config, the first-run wizard.
161
- - `src/cli.tsx` — Ink-based terminal UI and the REPL/single-shot entry points.
162
-
163
- ## Configuration
164
-
165
- Layered: **session** (in-REPL commands) > **project** (`.tandem/config.json`
166
- in the current directory) > **global** (`~/.tandem/config.json`).
167
-
168
- ```json
169
- {
170
- "defaultModel": "deepseek-v4-pro",
171
- "defaultReasoningEffort": "high",
172
- "budgetUsd": 5,
173
- "maxReviewLoops": 3,
174
- "verifyCommand": "pnpm typecheck && pnpm test",
175
- "language": "en"
176
- }
177
- ```
178
-
179
- `verifyCommand` runs before each `/plan` review round and its result goes to
180
- the reviewer. `language` is the UI language (`en` or `sr`, default `en`); the
181
- `--lang` flag and the `TANDEM_LANG` environment variable override it, in that
182
- order. The first-run wizard asks for it.
183
-
184
- The API key is never stored in these files — it lives in the OS credential
185
- store (or `DEEPSEEK_API_KEY` env var, which takes priority, for CI use).
186
-
187
- ## Development
188
-
189
- See [CONTRIBUTING.md](./CONTRIBUTING.md).
190
-
191
- ```bash
192
- pnpm install
193
- pnpm typecheck
194
- pnpm dev "some task"
195
- ```
196
-
197
- ## Disclaimer
198
-
199
- This is an independent, community-run project. It is not affiliated with,
200
- endorsed by, or sponsored by DeepSeek.
201
-
202
- ## License
203
-
204
- [MIT](./LICENSE)
1
+ # Tandem Mode
2
+
3
+ [![npm version](https://img.shields.io/npm/v/tandem-mode.svg)](https://www.npmjs.com/package/tandem-mode)
4
+ [![license](https://img.shields.io/npm/l/tandem-mode.svg)](./LICENSE)
5
+ [![node](https://img.shields.io/node/v/tandem-mode.svg)](./package.json)
6
+ [![good first issues](https://img.shields.io/github/issues/zdrave08/Tandem-Mode/good%20first%20issue)](https://github.com/zdrave08/Tandem-Mode/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
7
+
8
+ **English** | [Srpski](./README.sr.md)
9
+
10
+ A terminal coding agent built specifically for the DeepSeek API — not a generic
11
+ LLM harness with DeepSeek bolted on. Interactive REPL, planner/worker/reviewer
12
+ orchestration, cache-aware prompt architecture, and first-class support for
13
+ Windows, macOS, and Linux.
14
+
15
+ > **Independent open-source project. Not affiliated with or endorsed by
16
+ > DeepSeek.**
17
+
18
+ ## Status
19
+
20
+ M0–M4 of the [development plan](./deepseek-cli-development-plan.md) are done.
21
+ Published on npm, usable today for real coding tasks against a local repo.
22
+
23
+ First benchmark run is in — see [full results](./docs/benchmark-results.md).
24
+ Headline: all three configurations (Pro-only, Flash-only, orchestration)
25
+ fixed 4/4 injected bugs correctly in a real WooCommerce plugin, with a clean
26
+ `php -l` and zero unnecessary file changes. But for this class of task
27
+ (single-file, well-scoped fixes), orchestration cost ~9.5× more and took
28
+ ~19× longer than Pro-only for the same correctness — the opposite of what
29
+ you'd want orchestration for. Read as: orchestration's overhead needs a
30
+ bigger, more decomposable task to pay for itself, which this first run didn't
31
+ test. Reported as-is, not adjusted to fit expectations.
32
+
33
+ ## Features
34
+
35
+ - **Agentic loop** — `deepseek-v4-pro`/`deepseek-v4-flash` with tool calling:
36
+ `read_file`, `list_dir`, `search`, `edit`, `git_diff`, `shell`, `view_image`,
37
+ `web_search`.
38
+ - **Two entry points** — interactive REPL for normal work, single-shot mode
39
+ (`tandem "task" --yes`) for scripting and CI.
40
+ - **Orchestration** (`/plan`) — a planner produces a JSON task list, workers
41
+ execute each task with a fresh minimal context, a reviewer checks the git
42
+ diff against the original task and either approves or requests corrections
43
+ (capped by `max_review_loops`).
44
+ - **Cache-aware prompts** — the worker's system prompt + repo map + plan stay
45
+ byte-identical across calls in one orchestration run, so DeepSeek's context
46
+ cache actually hits. Measured live: cache-hit tokens grew call over call
47
+ within a single `/plan` run.
48
+ - **Vision** — `view_image` lets the agent look at a screenshot you point it
49
+ to; `/paste` grabs an image straight from the clipboard (Windows, macOS,
50
+ and Linux via X11/`xclip` or Wayland/`wl-clipboard`).
51
+ - **Web search** — isolated calls to DeepSeek's Responses API when the agent
52
+ needs something outside the repo (an error message, a library's docs).
53
+ - **Session persistence** — every session is a JSONL file; `/resume` continues
54
+ the latest (or a named) one, `/fork` branches off without touching the
55
+ original.
56
+ - **Safety** — destructive actions (file edits, shell commands not on a small
57
+ read-only allowlist) always ask for confirmation; `y`/`n`/`a` where `a`
58
+ remembers the choice for that tool for the rest of the session. `--yes`
59
+ bypasses this for CI but always prints what it auto-approved.
60
+ - **Cost control** — usage is read only from the API's own `usage` field,
61
+ never estimated. A budget (`/budget`, `--budget`) hard-stops the loop before
62
+ the next API call if exceeded, without losing the session. `/usage` also
63
+ shows what the session would have cost off-peak, and the REPL warns you at
64
+ startup if you're currently in a peak-pricing window.
65
+ - **Status line** — the last line of every turn shows model, mode (chat/plan),
66
+ cost (and budget), and the current call out of the iteration limit; it turns
67
+ yellow, then red, as you approach the budget or the limit.
68
+ - **Readable tool output** — `edit` shows a red/green diff, `read_file`, `search`
69
+ and `list_dir` collapse to a one-line summary (`120 lines read`, `no matches`).
70
+ - **ESC to interrupt** — cancels the in-flight call (and any running shell
71
+ command) cleanly, keeping whatever was generated so far.
72
+ - **Cross-platform** — the shell tool runs PowerShell natively on Windows and
73
+ `sh` on macOS/Linux, file edits preserve the original CRLF/LF line ending
74
+ style, paths are handled with `path.resolve` throughout.
75
+
76
+ ## Quick start
77
+
78
+ ```bash
79
+ npm install -g tandem-mode
80
+ tandem # interactive REPL
81
+ tandem "explain what this repo does" --yes # one-shot
82
+ ```
83
+
84
+ Or from source (useful for contributing, or to track `main` instead of the
85
+ latest npm release):
86
+
87
+ ```bash
88
+ git clone https://github.com/zdrave08/Tandem-Mode.git
89
+ cd Tandem-Mode
90
+ pnpm install
91
+ pnpm dev # interactive REPL
92
+ pnpm dev "explain what this repo does" --yes # one-shot
93
+ ```
94
+
95
+ On first run you'll be asked for a DeepSeek API key (get one at
96
+ [platform.deepseek.com](https://platform.deepseek.com)) and a default
97
+ model/reasoning effort. The key is stored in your OS's own secure credential
98
+ store — never in a config file or log:
99
+
100
+ - **Windows** — DPAPI-encrypted (tied to your OS user account) in
101
+ `%LOCALAPPDATA%\TandemMode\credentials.dat`.
102
+ - **macOS** — the login Keychain, via the built-in `security` CLI.
103
+ - **Linux** — the Secret Service (gnome-keyring, KWallet, or similar), via
104
+ `secret-tool` (install `libsecret-tools` if it's missing).
105
+
106
+ `DEEPSEEK_API_KEY` works everywhere and always takes priority, for CI use.
107
+
108
+ ## Usage
109
+
110
+ ```bash
111
+ tandem # interactive REPL
112
+ tandem "fix the off-by-one in foo.ts"
113
+ tandem --plan "add dark mode to the settings page"
114
+ tandem "task" --model deepseek-v4-flash --effort low --budget 0.50 --yes
115
+ ```
116
+
117
+ | Flag | Effect |
118
+ |---|---|
119
+ | `--resume` / `-r` | Continue the latest session in this directory |
120
+ | `--yes` / `-y` | Auto-approve destructive actions (prints what it approved) |
121
+ | `--plan` | Run planner/worker/reviewer orchestration instead of a single turn |
122
+ | `--model` | `deepseek-v4-pro` or `deepseek-v4-flash` |
123
+ | `--effort` | `low`, `high`, or `max` reasoning effort |
124
+ | `--budget` | Hard USD cap for the run |
125
+ | `--max-review-loops` | Cap on orchestration correction cycles (default 3) |
126
+ | `--max-iterations` | Model calls per chunk of a turn (default 25); at the limit the agent asks whether to continue |
127
+ | `--auto-continue` | Continue at the iteration limit without asking — only together with a budget (`--budget`) |
128
+
129
+ ### REPL commands
130
+
131
+ | Command | Effect |
132
+ |---|---|
133
+ | `/plan <task>` | Planner/worker/reviewer orchestration |
134
+ | `/model [name]` | Show or switch model |
135
+ | `/thinking [on\|off]` | Toggle thinking mode |
136
+ | `/effort [low\|high\|max]` | Show or set reasoning effort |
137
+ | `/status` | Current session/settings |
138
+ | `/diff [--staged] [path]` | Working-tree diff, no API call |
139
+ | `/usage` | Tokens, cost, off-peak projection |
140
+ | `/budget [amount]` | Show or set the session budget |
141
+ | `/new` | Start a fresh session |
142
+ | `/save-profile <name>` | Save model, effort, budget and review loops as a named profile |
143
+ | `/profile [name]` | Load a saved profile, or list them |
144
+ | `/compact` | Summarize the history into a new session to free up context; the old session stays on disk |
145
+ | `/fork` | New session, carrying over the current history |
146
+ | `/resume [id]` | Continue the latest (or a named) session |
147
+ | `/paste [message]` | Send a clipboard image to the agent |
148
+ | `/clear` | Clear the screen |
149
+ | `/help` | This list |
150
+ | `/exit` | Quit |
151
+
152
+ Press **ESC** at any time during a turn to interrupt it.
153
+
154
+ ## Architecture
155
+
156
+ ```
157
+ tandem (no args) ─┬─> interactive REPL ─┬─> single turn: agent loop (tools)
158
+ │ └─> /plan: planner → worker(s) → reviewer
159
+ tandem "task" ──┘ (single-shot, same agent loop, for scripts/CI)
160
+ ```
161
+
162
+ - `src/deepseek/` — thin client for DeepSeek's OpenAI-compatible API
163
+ (streaming + non-streaming chat completions, vision, web search via
164
+ Responses API). See [`docs/api-notes.en.md`](./docs/api-notes.en.md)
165
+ (Serbian original: [`docs/api-notes.md`](./docs/api-notes.md)) for the
166
+ API behavior this is built against, verified live rather than assumed.
167
+ - `src/agent/` — the tool-calling loop, tool implementations, session
168
+ persistence, usage/cost tracking.
169
+ - `src/orchestrator/` — planner/worker/reviewer, repo-map generation, the
170
+ cache-aware stable-prefix construction.
171
+ - `src/repl/` — slash commands, runtime config, the first-run wizard.
172
+ - `src/cli.tsx` — Ink-based terminal UI and the REPL/single-shot entry points.
173
+
174
+ ## Configuration
175
+
176
+ Layered: **session** (in-REPL commands) > **project** (`.tandem/config.json`
177
+ in the current directory) > **global** (`~/.tandem/config.json`).
178
+
179
+ ```json
180
+ {
181
+ "defaultModel": "deepseek-v4-pro",
182
+ "defaultReasoningEffort": "high",
183
+ "budgetUsd": 5,
184
+ "maxReviewLoops": 3,
185
+ "maxIterations": 25,
186
+ "autoContinue": false,
187
+ "verifyCommand": "pnpm typecheck && pnpm test",
188
+ "notifyCommand": "curl -s -d \"Tandem: $TANDEM_OUTCOME\" https://ntfy.sh/my-topic",
189
+ "language": "en"
190
+ }
191
+ ```
192
+
193
+ `maxIterations` is how many model calls one turn may make before Tandem stops and
194
+ asks "continue for N more calls? (y/n/a)"; `a` remembers the answer for the rest of
195
+ the session. With `autoContinue` (or `--auto-continue`) it continues on its own, but
196
+ only when a budget is set, because the budget is the hard stop before the next API
197
+ call. In non-interactive runs (`--yes`) without `--auto-continue` it stops instead of
198
+ waiting for an answer, and the session is saved so you can send "continue".
199
+
200
+ `verifyCommand` runs before each `/plan` review round and its result goes to
201
+ the reviewer. `language` is the UI language (`en` or `sr`, default `en`); the
202
+ `--lang` flag and the `TANDEM_LANG` environment variable override it, in that
203
+ order. The first-run wizard asks for it.
204
+
205
+ `notifyCommand` runs when a `/plan` run ends: reviewer approved, `max_review_loops`
206
+ reached, or an error (not when you interrupt it with ESC). The details come in as
207
+ environment variables, never spliced into the command text: `TANDEM_OUTCOME`
208
+ (`approved`, `max_review_loops` or `error`), `TANDEM_TASK`, `TANDEM_NOTES` (the
209
+ reviewer's notes, or the error message), `TANDEM_REVIEW_LOOPS` and `TANDEM_COST_USD`.
210
+ The command runs in your shell (PowerShell on Windows, `sh` elsewhere) with a 15 s
211
+ limit, and a failure only prints a warning. Any shell command works, so Slack,
212
+ ntfy, a desktop notification or a webhook are all just examples:
213
+
214
+ ```json
215
+ { "notifyCommand": "curl -s -d \"Tandem: $TANDEM_OUTCOME\" https://ntfy.sh/my-topic" }
216
+ ```
217
+
218
+ On Windows (PowerShell) the variables are written `$env:TANDEM_OUTCOME`.
219
+
220
+ Like `verifyCommand`, `notifyCommand` runs without asking, so only put it in a
221
+ project's `.tandem/config.json` if you trust that project.
222
+
223
+ **Profiles:** `/save-profile <name>` stores the current model, reasoning effort,
224
+ budget and review loops in `~/.tandem/profiles/<name>.json`, and `/profile <name>`
225
+ loads them back into the running session (only the fields present in the file are
226
+ applied). `/profile` alone lists the saved ones. Names use `a-z`, `0-9`, `-` and `_`.
227
+ Profiles never include `verifyCommand` or the API key.
228
+
229
+ The API key is never stored in these files — it lives in the OS credential
230
+ store (or `DEEPSEEK_API_KEY` env var, which takes priority, for CI use).
231
+
232
+ ## Development
233
+
234
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
235
+
236
+ ```bash
237
+ pnpm install
238
+ pnpm typecheck
239
+ pnpm dev "some task"
240
+ ```
241
+
242
+ ## Disclaimer
243
+
244
+ This is an independent, community-run project. It is not affiliated with,
245
+ endorsed by, or sponsored by DeepSeek.
246
+
247
+ ## License
248
+
249
+ [MIT](./LICENSE)
package/README.sr.md CHANGED
@@ -130,9 +130,13 @@ tandem "task" --model deepseek-v4-flash --effort low --budget 0.50 --yes
130
130
  | `/thinking [on\|off]` | Uključi/isključi thinking mode |
131
131
  | `/effort [low\|high\|max]` | Prikaži ili postavi reasoning effort |
132
132
  | `/status` | Trenutna sesija/podešavanja |
133
+ | `/diff [--staged] [putanja]` | Diff radnog stabla, bez API poziva |
133
134
  | `/usage` | Tokeni, cena, off-peak projekcija |
134
135
  | `/budget [iznos]` | Prikaži ili postavi budžet sesije |
135
136
  | `/new` | Nova sesija |
137
+ | `/save-profile <ime>` | Sačuvaj model, effort, budžet i review krugove kao imenovan profil |
138
+ | `/profile [ime]` | Učitaj sačuvan profil, ili prikaži listu |
139
+ | `/compact` | Sažmi istoriju u novu sesiju da se oslobodi kontekst; stara sesija ostaje na disku |
136
140
  | `/fork` | Nova sesija sa kopijom trenutne istorije |
137
141
  | `/resume [id]` | Nastavi poslednju (ili navedenu) sesiju |
138
142
  | `/paste [poruka]` | Pošalji sliku iz clipboard-a agentu |
@@ -175,6 +179,7 @@ Slojevito: **sesija** (komande u REPL-u) > **projekat**
175
179
  "budgetUsd": 5,
176
180
  "maxReviewLoops": 3,
177
181
  "verifyCommand": "pnpm typecheck && pnpm test",
182
+ "notifyCommand": "curl -s -d \"Tandem: $TANDEM_OUTCOME\" https://ntfy.sh/my-topic",
178
183
  "language": "sr"
179
184
  }
180
185
  ```
@@ -184,6 +189,30 @@ ide reviewer-u. `language` je jezik interfejsa (`en` ili `sr`, podrazumevano
184
189
  `en`); `--lang` flag i `TANDEM_LANG` env promenljiva ga nadjačavaju, tim
185
190
  redom. First-run wizard ga pita.
186
191
 
192
+ `notifyCommand` se pokreće kad se `/plan` završi: reviewer odobrio, dostignut
193
+ `max_review_loops`, ili greška (ne i kad ga prekineš sa ESC). Detalji dolaze kao
194
+ promenljive okruženja, nikad ubačeni u tekst komande: `TANDEM_OUTCOME`
195
+ (`approved`, `max_review_loops` ili `error`), `TANDEM_TASK`, `TANDEM_NOTES`
196
+ (beleške reviewer-a, ili poruka greške), `TANDEM_REVIEW_LOOPS` i `TANDEM_COST_USD`.
197
+ Komanda radi u tvojoj školjki (PowerShell na Windows-u, `sh` drugde) sa limitom
198
+ od 15 s, a neuspeh samo ispiše upozorenje. Može bilo koja shell komanda, pa su
199
+ Slack, ntfy, desktop obaveštenje ili webhook samo primeri:
200
+
201
+ ```json
202
+ { "notifyCommand": "curl -s -d \"Tandem: $TANDEM_OUTCOME\" https://ntfy.sh/my-topic" }
203
+ ```
204
+
205
+ Na Windows-u (PowerShell) promenljive se pišu `$env:TANDEM_OUTCOME`.
206
+
207
+ Kao i `verifyCommand`, `notifyCommand` se pokreće bez pitanja, pa ga stavljaj u
208
+ `.tandem/config.json` projekta samo ako tom projektu veruješ.
209
+
210
+ **Profili:** `/save-profile <ime>` čuva trenutni model, reasoning effort, budžet i
211
+ review krugove u `~/.tandem/profiles/<ime>.json`, a `/profile <ime>` ih vraća u
212
+ sesiju koja radi (primenjuju se samo polja koja postoje u fajlu). `/profile` bez
213
+ argumenta prikazuje sačuvane. Imena koriste `a-z`, `0-9`, `-` i `_`.
214
+ Profili nikad ne sadrže `verifyCommand` ni API ključ.
215
+
187
216
  API ključ se nikad ne čuva u ovim fajlovima — živi u OS-ovom skladištu
188
217
  kredencijala (ili u `DEEPSEEK_API_KEY` env promenljivoj, koja ima
189
218
  prioritet, za CI upotrebu).
@@ -0,0 +1,80 @@
1
+ import { chatCompletionOnce } from "../deepseek/client.js";
2
+ // Tool output is where long sessions grow (file dumps, shell output), so it is
3
+ // cut hard in the transcript the summarizer sees. The summary only needs the gist.
4
+ const MAX_TOOL_OUTPUT_CHARS = 1_500;
5
+ const MAX_TOOL_ARGS_CHARS = 300;
6
+ const MAX_TRANSCRIPT_CHARS = 300_000;
7
+ export const SUMMARY_MODEL = "deepseek-v4-flash";
8
+ const SUMMARIZER_SYSTEM_PROMPT = `You compact the history of a coding-agent session so work can continue from your
9
+ summary alone. Write a dense summary with these parts, skipping any that are empty:
10
+ - Goal: what the user is trying to achieve, plus constraints and preferences they stated.
11
+ - Done: files read or changed (exact paths), commands run and their results.
12
+ - Decisions: what was decided and why.
13
+ - State: where things stand right now, including failing tests or errors still open.
14
+ - Next: what was about to happen or is still to do.
15
+ Keep exact file paths, identifiers and error messages that matter. Do not invent anything
16
+ that is not in the conversation. No preamble. Reply in the language the user wrote in.`;
17
+ export const SUMMARY_PREFIX = "Summary of our conversation so far (the earlier messages were compacted to save context):";
18
+ const SUMMARY_ACK = "Understood. I'll continue from this summary.";
19
+ function truncate(text, max) {
20
+ return text.length > max ? `${text.slice(0, max)} … (truncated, ${text.length} chars)` : text;
21
+ }
22
+ /** Plain-text rendering of the history. Text avoids tool_call/tool pairing rules in the summarizer request. */
23
+ export function buildTranscript(messages) {
24
+ const toolNames = new Map();
25
+ for (const m of messages) {
26
+ for (const call of m.toolCalls ?? [])
27
+ toolNames.set(call.id, call.function.name);
28
+ }
29
+ const parts = [];
30
+ for (const m of messages) {
31
+ if (m.role === "system")
32
+ continue;
33
+ if (m.role === "user") {
34
+ parts.push(`USER:\n${m.content ?? ""}`);
35
+ }
36
+ else if (m.role === "assistant") {
37
+ const calls = (m.toolCalls ?? [])
38
+ .map((c) => `[called ${c.function.name}(${truncate(c.function.arguments, MAX_TOOL_ARGS_CHARS)})]`)
39
+ .join("\n");
40
+ const text = [m.content ?? "", calls].filter(Boolean).join("\n");
41
+ if (text)
42
+ parts.push(`ASSISTANT:\n${text}`);
43
+ }
44
+ else {
45
+ const name = (m.toolCallId && toolNames.get(m.toolCallId)) || "tool";
46
+ parts.push(`TOOL RESULT (${name}):\n${truncate(m.content ?? "", MAX_TOOL_OUTPUT_CHARS)}`);
47
+ }
48
+ }
49
+ const transcript = parts.join("\n\n");
50
+ if (transcript.length <= MAX_TRANSCRIPT_CHARS)
51
+ return transcript;
52
+ // Too long even after trimming tool output: keep the most recent part.
53
+ return `[earlier part of the conversation omitted]\n\n${transcript.slice(-MAX_TRANSCRIPT_CHARS)}`;
54
+ }
55
+ /** One model call. Throws on API errors or an empty summary; the caller's history is never touched here. */
56
+ export async function compactConversation(env, messages, systemPrompt, signal) {
57
+ const response = await chatCompletionOnce(env, {
58
+ model: SUMMARY_MODEL,
59
+ messages: [
60
+ { role: "system", content: SUMMARIZER_SYSTEM_PROMPT },
61
+ { role: "user", content: `Conversation to summarize:\n\n${buildTranscript(messages)}` },
62
+ ],
63
+ ...(signal ? { signal } : {}),
64
+ });
65
+ const summary = response.content.trim();
66
+ if (!summary)
67
+ throw new Error("The model returned an empty summary.");
68
+ // The acknowledgement keeps user/assistant alternation, so the next user turn
69
+ // never follows another user message.
70
+ return {
71
+ messages: [
72
+ { role: "system", content: systemPrompt },
73
+ { role: "user", content: `${SUMMARY_PREFIX}\n\n${summary}` },
74
+ { role: "assistant", content: SUMMARY_ACK },
75
+ ],
76
+ summary,
77
+ usage: response.usage,
78
+ };
79
+ }
80
+ //# sourceMappingURL=compact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compact.js","sourceRoot":"","sources":["../../src/agent/compact.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,17 @@ const MAX_REASONING_CHARS = 200_000;
17
17
  export async function* runAgentLoop(messages, opts) {
18
18
  const ctx = { cwd: opts.cwd, env: opts.env, usage: opts.usage, ...(opts.signal ? { signal: opts.signal } : {}) };
19
19
  const maxIterations = opts.maxIterations ?? 25;
20
- for (let iteration = 0; iteration < maxIterations; iteration++) {
20
+ // Granica se pomera za još jedan "komad" svaki put kad korisnik (ili autoContinue) dozvoli nastavak.
21
+ let limit = maxIterations;
22
+ for (let iteration = 0;; iteration++) {
23
+ if (iteration >= limit) {
24
+ const proceed = opts.onIterationLimit ? await opts.onIterationLimit({ iterations: iteration }) : false;
25
+ if (!proceed) {
26
+ yield { type: "max_iterations_reached" };
27
+ return;
28
+ }
29
+ limit += maxIterations;
30
+ }
21
31
  if (opts.signal?.aborted) {
22
32
  yield { type: "interrupted" };
23
33
  return;
@@ -29,6 +39,7 @@ export async function* runAgentLoop(messages, opts) {
29
39
  return;
30
40
  }
31
41
  }
42
+ yield { type: "iteration", n: iteration + 1, max: limit };
32
43
  let reasoning = "";
33
44
  let content = "";
34
45
  const toolCallBuf = new Map();
@@ -159,6 +170,5 @@ export async function* runAgentLoop(messages, opts) {
159
170
  await appendSessionMessage(opts.session, toolMsg);
160
171
  }
161
172
  }
162
- yield { type: "max_iterations_reached" };
163
173
  }
164
174
  //# sourceMappingURL=loop.js.map