tandem-mode 0.0.1
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 +191 -0
- package/README.sr.md +194 -0
- package/dist/agent/clipboard.js +26 -0
- package/dist/agent/clipboard.js.map +1 -0
- package/dist/agent/loop.js +157 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/pricing.js +21 -0
- package/dist/agent/pricing.js.map +1 -0
- package/dist/agent/session.js +78 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/agent/tools/edit.js +78 -0
- package/dist/agent/tools/edit.js.map +1 -0
- package/dist/agent/tools/gitDiff.js +42 -0
- package/dist/agent/tools/gitDiff.js.map +1 -0
- package/dist/agent/tools/index.js +24 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/tools/listDir.js +29 -0
- package/dist/agent/tools/listDir.js.map +1 -0
- package/dist/agent/tools/readFile.js +40 -0
- package/dist/agent/tools/readFile.js.map +1 -0
- package/dist/agent/tools/search.js +51 -0
- package/dist/agent/tools/search.js.map +1 -0
- package/dist/agent/tools/shell.js +95 -0
- package/dist/agent/tools/shell.js.map +1 -0
- package/dist/agent/tools/types.js +11 -0
- package/dist/agent/tools/types.js.map +1 -0
- package/dist/agent/tools/viewImage.js +34 -0
- package/dist/agent/tools/viewImage.js.map +1 -0
- package/dist/agent/tools/webSearch.js +27 -0
- package/dist/agent/tools/webSearch.js.map +1 -0
- package/dist/agent/usage.js +36 -0
- package/dist/agent/usage.js.map +1 -0
- package/dist/benchmark/runner.js +176 -0
- package/dist/benchmark/runner.js.map +1 -0
- package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js +50 -0
- package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js.map +1 -0
- package/dist/benchmark/types.js +2 -0
- package/dist/benchmark/types.js.map +1 -0
- package/dist/cli.js +442 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/credentials.js +80 -0
- package/dist/config/credentials.js.map +1 -0
- package/dist/config/env.js +12 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/schema.js +6 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/store.js +35 -0
- package/dist/config/store.js.map +1 -0
- package/dist/deepseek/client.js +145 -0
- package/dist/deepseek/client.js.map +1 -0
- package/dist/deepseek/types.js +2 -0
- package/dist/deepseek/types.js.map +1 -0
- package/dist/deepseek/vision.js +59 -0
- package/dist/deepseek/vision.js.map +1 -0
- package/dist/deepseek/webSearch.js +43 -0
- package/dist/deepseek/webSearch.js.map +1 -0
- package/dist/orchestrator/orchestrate.js +63 -0
- package/dist/orchestrator/orchestrate.js.map +1 -0
- package/dist/orchestrator/planner.js +48 -0
- package/dist/orchestrator/planner.js.map +1 -0
- package/dist/orchestrator/repoMap.js +52 -0
- package/dist/orchestrator/repoMap.js.map +1 -0
- package/dist/orchestrator/reviewer.js +53 -0
- package/dist/orchestrator/reviewer.js.map +1 -0
- package/dist/orchestrator/types.js +18 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/repl/commands.js +139 -0
- package/dist/repl/commands.js.map +1 -0
- package/dist/repl/state.js +2 -0
- package/dist/repl/state.js.map +1 -0
- package/dist/repl/system-prompt.js +5 -0
- package/dist/repl/system-prompt.js.map +1 -0
- package/dist/repl/wizard.js +34 -0
- package/dist/repl/wizard.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zdravko Lukic
|
|
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,191 @@
|
|
|
1
|
+
# Tandem Mode
|
|
2
|
+
|
|
3
|
+
**English** | [Srpski](./README.sr.md)
|
|
4
|
+
|
|
5
|
+
A terminal coding agent built specifically for the DeepSeek API — not a generic
|
|
6
|
+
LLM harness with DeepSeek bolted on. Interactive REPL, planner/worker/reviewer
|
|
7
|
+
orchestration, cache-aware prompt architecture, and Windows-first from the
|
|
8
|
+
start.
|
|
9
|
+
|
|
10
|
+
> **Independent open-source project. Not affiliated with or endorsed by
|
|
11
|
+
> DeepSeek.**
|
|
12
|
+
|
|
13
|
+
## Status
|
|
14
|
+
|
|
15
|
+
Early stage (M0–M3 of the [development plan](./deepseek-cli-development-plan.md)
|
|
16
|
+
are done, M4 in progress). Usable today for real coding tasks against a local
|
|
17
|
+
repo.
|
|
18
|
+
|
|
19
|
+
First benchmark run is in — see [full results](./docs/benchmark-results.md).
|
|
20
|
+
Headline: all three configurations (Pro-only, Flash-only, orchestration)
|
|
21
|
+
fixed 4/4 injected bugs correctly in a real WooCommerce plugin, with a clean
|
|
22
|
+
`php -l` and zero unnecessary file changes. But for this class of task
|
|
23
|
+
(single-file, well-scoped fixes), orchestration cost ~9.5× more and took
|
|
24
|
+
~19× longer than Pro-only for the same correctness — the opposite of what
|
|
25
|
+
you'd want orchestration for. Read as: orchestration's overhead needs a
|
|
26
|
+
bigger, more decomposable task to pay for itself, which this first run didn't
|
|
27
|
+
test. Reported as-is, not adjusted to fit expectations.
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- **Agentic loop** — `deepseek-v4-pro`/`deepseek-v4-flash` with tool calling:
|
|
32
|
+
`read_file`, `list_dir`, `search`, `edit`, `git_diff`, `shell`, `view_image`,
|
|
33
|
+
`web_search`.
|
|
34
|
+
- **Two entry points** — interactive REPL for normal work, single-shot mode
|
|
35
|
+
(`tandem "task" --yes`) for scripting and CI.
|
|
36
|
+
- **Orchestration** (`/plan`) — a planner produces a JSON task list, workers
|
|
37
|
+
execute each task with a fresh minimal context, a reviewer checks the git
|
|
38
|
+
diff against the original task and either approves or requests corrections
|
|
39
|
+
(capped by `max_review_loops`).
|
|
40
|
+
- **Cache-aware prompts** — the worker's system prompt + repo map + plan stay
|
|
41
|
+
byte-identical across calls in one orchestration run, so DeepSeek's context
|
|
42
|
+
cache actually hits. Measured live: cache-hit tokens grew call over call
|
|
43
|
+
within a single `/plan` run.
|
|
44
|
+
- **Vision** — `view_image` lets the agent look at a screenshot you point it
|
|
45
|
+
to; `/paste` grabs an image straight from the Windows clipboard.
|
|
46
|
+
- **Web search** — isolated calls to DeepSeek's Responses API when the agent
|
|
47
|
+
needs something outside the repo (an error message, a library's docs).
|
|
48
|
+
- **Session persistence** — every session is a JSONL file; `/resume` continues
|
|
49
|
+
the latest (or a named) one, `/fork` branches off without touching the
|
|
50
|
+
original.
|
|
51
|
+
- **Safety** — destructive actions (file edits, shell commands not on a small
|
|
52
|
+
read-only allowlist) always ask for confirmation; `y`/`n`/`a` where `a`
|
|
53
|
+
remembers the choice for that tool for the rest of the session. `--yes`
|
|
54
|
+
bypasses this for CI but always prints what it auto-approved.
|
|
55
|
+
- **Cost control** — usage is read only from the API's own `usage` field,
|
|
56
|
+
never estimated. A budget (`/budget`, `--budget`) hard-stops the loop before
|
|
57
|
+
the next API call if exceeded, without losing the session. `/usage` also
|
|
58
|
+
shows what the session would have cost off-peak, and the REPL warns you at
|
|
59
|
+
startup if you're currently in a peak-pricing window.
|
|
60
|
+
- **ESC to interrupt** — cancels the in-flight call (and any running shell
|
|
61
|
+
command) cleanly, keeping whatever was generated so far.
|
|
62
|
+
- **Windows-first** — the shell tool runs PowerShell natively, file edits
|
|
63
|
+
preserve the original CRLF/LF line ending style, paths are handled with
|
|
64
|
+
`path.resolve` throughout.
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/zdrave08/Tandem-Mode.git
|
|
70
|
+
cd Tandem-Mode
|
|
71
|
+
pnpm install
|
|
72
|
+
pnpm dev # interactive REPL
|
|
73
|
+
pnpm dev "explain what this repo does" --yes # one-shot
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
(Not yet published to npm — `npm install -g tandem-mode` is a launch-day step,
|
|
77
|
+
not available today.)
|
|
78
|
+
|
|
79
|
+
To try it as a real installed `tandem` command instead of `pnpm dev` (useful
|
|
80
|
+
before publishing, or to use it in another project without cloning next to
|
|
81
|
+
it):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pnpm build
|
|
85
|
+
npm link # registers `tandem` globally, pointing at this checkout
|
|
86
|
+
tandem # now works from any directory
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
On first run you'll be asked for a DeepSeek API key (get one at
|
|
90
|
+
[platform.deepseek.com](https://platform.deepseek.com)) and a default
|
|
91
|
+
model/reasoning effort. On Windows the key is stored DPAPI-encrypted
|
|
92
|
+
(tied to your OS user account, same protection Credential Manager itself
|
|
93
|
+
relies on) in `%LOCALAPPDATA%\TandemMode\credentials.dat` — never in a
|
|
94
|
+
config file or log. Linux/macOS storage isn't implemented yet (`DEEPSEEK_API_KEY`
|
|
95
|
+
works everywhere in the meantime) — see good-first-issues.
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
tandem # interactive REPL
|
|
101
|
+
tandem "fix the off-by-one in foo.ts"
|
|
102
|
+
tandem --plan "add dark mode to the settings page"
|
|
103
|
+
tandem "task" --model deepseek-v4-flash --effort low --budget 0.50 --yes
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Flag | Effect |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `--resume` / `-r` | Continue the latest session in this directory |
|
|
109
|
+
| `--yes` / `-y` | Auto-approve destructive actions (prints what it approved) |
|
|
110
|
+
| `--plan` | Run planner/worker/reviewer orchestration instead of a single turn |
|
|
111
|
+
| `--model` | `deepseek-v4-pro` or `deepseek-v4-flash` |
|
|
112
|
+
| `--effort` | `low`, `high`, or `max` reasoning effort |
|
|
113
|
+
| `--budget` | Hard USD cap for the run |
|
|
114
|
+
| `--max-review-loops` | Cap on orchestration correction cycles (default 3) |
|
|
115
|
+
|
|
116
|
+
### REPL commands
|
|
117
|
+
|
|
118
|
+
| Command | Effect |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `/plan <task>` | Planner/worker/reviewer orchestration |
|
|
121
|
+
| `/model [name]` | Show or switch model |
|
|
122
|
+
| `/thinking [on\|off]` | Toggle thinking mode |
|
|
123
|
+
| `/effort [low\|high\|max]` | Show or set reasoning effort |
|
|
124
|
+
| `/status` | Current session/settings |
|
|
125
|
+
| `/usage` | Tokens, cost, off-peak projection |
|
|
126
|
+
| `/budget [amount]` | Show or set the session budget |
|
|
127
|
+
| `/new` | Start a fresh session |
|
|
128
|
+
| `/fork` | New session, carrying over the current history |
|
|
129
|
+
| `/resume [id]` | Continue the latest (or a named) session |
|
|
130
|
+
| `/paste [message]` | Send a Windows-clipboard image to the agent |
|
|
131
|
+
| `/clear` | Clear the screen |
|
|
132
|
+
| `/help` | This list |
|
|
133
|
+
| `/exit` | Quit |
|
|
134
|
+
|
|
135
|
+
Press **ESC** at any time during a turn to interrupt it.
|
|
136
|
+
|
|
137
|
+
## Architecture
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
tandem (no args) ─┬─> interactive REPL ─┬─> single turn: agent loop (tools)
|
|
141
|
+
│ └─> /plan: planner → worker(s) → reviewer
|
|
142
|
+
tandem "task" ──┘ (single-shot, same agent loop, for scripts/CI)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- `src/deepseek/` — thin client for DeepSeek's OpenAI-compatible API
|
|
146
|
+
(streaming + non-streaming chat completions, vision, web search via
|
|
147
|
+
Responses API). See [`docs/api-notes.en.md`](./docs/api-notes.en.md)
|
|
148
|
+
(Serbian original: [`docs/api-notes.md`](./docs/api-notes.md)) for the
|
|
149
|
+
API behavior this is built against, verified live rather than assumed.
|
|
150
|
+
- `src/agent/` — the tool-calling loop, tool implementations, session
|
|
151
|
+
persistence, usage/cost tracking.
|
|
152
|
+
- `src/orchestrator/` — planner/worker/reviewer, repo-map generation, the
|
|
153
|
+
cache-aware stable-prefix construction.
|
|
154
|
+
- `src/repl/` — slash commands, runtime config, the first-run wizard.
|
|
155
|
+
- `src/cli.tsx` — Ink-based terminal UI and the REPL/single-shot entry points.
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
Layered: **session** (in-REPL commands) > **project** (`.tandem/config.json`
|
|
160
|
+
in the current directory) > **global** (`~/.tandem/config.json`).
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"defaultModel": "deepseek-v4-pro",
|
|
165
|
+
"defaultReasoningEffort": "high",
|
|
166
|
+
"budgetUsd": 5,
|
|
167
|
+
"maxReviewLoops": 3
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The API key is never stored in these files — it lives DPAPI-encrypted on
|
|
172
|
+
Windows (or `DEEPSEEK_API_KEY` env var, which takes priority, for CI use).
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
pnpm install
|
|
180
|
+
pnpm typecheck
|
|
181
|
+
pnpm dev "some task"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Disclaimer
|
|
185
|
+
|
|
186
|
+
This is an independent, community-run project. It is not affiliated with,
|
|
187
|
+
endorsed by, or sponsored by DeepSeek.
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
[MIT](./LICENSE)
|
package/README.sr.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Tandem Mode
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | **Srpski**
|
|
4
|
+
|
|
5
|
+
Terminalski coding agent napravljen posebno za DeepSeek API — ne generički
|
|
6
|
+
LLM harness u koji je DeepSeek naknadno ugurana. Interaktivni REPL,
|
|
7
|
+
planner/worker/reviewer orkestracija, cache-aware arhitektura prompta, i
|
|
8
|
+
Windows kao prioritet od prvog dana.
|
|
9
|
+
|
|
10
|
+
> **Nezavisan open-source projekat. Nije povezan sa DeepSeek-om niti od
|
|
11
|
+
> njega podržan.**
|
|
12
|
+
|
|
13
|
+
## Status
|
|
14
|
+
|
|
15
|
+
Rana faza (M0–M3 iz [plana razvoja](./deepseek-cli-development-plan.md) su
|
|
16
|
+
gotovi, M4 u toku). Upotrebljiv već danas za stvarne coding zadatke nad
|
|
17
|
+
lokalnim repozitorijumom.
|
|
18
|
+
|
|
19
|
+
Prvi benchmark je gotov — pun izveštaj u
|
|
20
|
+
[docs/benchmark-results.md](./docs/benchmark-results.md) (na engleskom;
|
|
21
|
+
srpska verzija: [docs/benchmark-results.sr.md](./docs/benchmark-results.sr.md)).
|
|
22
|
+
Ukratko: sve tri konfiguracije (Pro-only, Flash-only, orkestracija) su
|
|
23
|
+
ispravno rešile 4/4 ubačena bага u pravom WooCommerce pluginu, sa čistim
|
|
24
|
+
`php -l` i nula nepotrebnih izmena. Ali za ovu klasu zadatka (mali,
|
|
25
|
+
jednodatotečni fixevi) orkestracija je koštala ~9.5× više i trajala ~19×
|
|
26
|
+
duže od Pro-only, za isti kvalitet — suprotno onome zbog čega bi se
|
|
27
|
+
orkestracija tražila. Čitati kao: overhead orkestracije treba veći,
|
|
28
|
+
razdvojiv zadatak da bi se isplatio, a ovaj prvi test to nije proverio.
|
|
29
|
+
Prijavljeno onako kako jeste, ne prilagođeno očekivanjima.
|
|
30
|
+
|
|
31
|
+
## Šta ima
|
|
32
|
+
|
|
33
|
+
- **Agentic petlja** — `deepseek-v4-pro`/`deepseek-v4-flash` sa tool-calling:
|
|
34
|
+
`read_file`, `list_dir`, `search`, `edit`, `git_diff`, `shell`, `view_image`,
|
|
35
|
+
`web_search`.
|
|
36
|
+
- **Dva načina rada** — interaktivni REPL za svakodnevni rad, single-shot
|
|
37
|
+
mod (`tandem "task" --yes`) za skriptovanje i CI.
|
|
38
|
+
- **Orkestracija** (`/plan`) — planner pravi JSON listu taskova, workeri
|
|
39
|
+
izvršavaju svaki task sa svežim minimalnim kontekstom, reviewer proverava
|
|
40
|
+
git diff naspram originalnog zadatka i ili odobrava ili traži korekcije
|
|
41
|
+
(ograničeno sa `max_review_loops`).
|
|
42
|
+
- **Cache-aware promptovi** — sistemski prompt workera + repo mapa + plan
|
|
43
|
+
ostaju bajt-za-bajt identični kroz pozive u jednoj orkestraciji, tako da
|
|
44
|
+
DeepSeek-ov keš stvarno pogađa. Izmereno uživo: cache-hit tokeni su rasli
|
|
45
|
+
iz poziva u poziv unutar jednog `/plan` pokretanja.
|
|
46
|
+
- **Vision** — `view_image` omogućava agentu da pogleda screenshot na koji
|
|
47
|
+
ga uputiš; `/paste` uzima sliku direktno iz Windows clipboard-a.
|
|
48
|
+
- **Web search** — izolovani pozivi ka DeepSeek Responses API-ju kad agentu
|
|
49
|
+
treba nešto van repozitorijuma (error poruka, dokumentacija biblioteke).
|
|
50
|
+
- **Perzistencija sesije** — svaka sesija je JSONL fajl; `/resume` nastavlja
|
|
51
|
+
poslednju (ili navedenu), `/fork` grana bez diranja originalne.
|
|
52
|
+
- **Bezbednost** — destruktivne akcije (izmene fajlova, shell komande van
|
|
53
|
+
male read-only bele liste) uvek traže potvrdu; `y`/`n`/`a` gde `a`
|
|
54
|
+
pamti izbor za taj alat do kraja sesije. `--yes` preskače ovo za CI, ali
|
|
55
|
+
uvek ispisuje šta je automatski odobreno.
|
|
56
|
+
- **Kontrola troškova** — usage se čita isključivo iz API-jevog `usage`
|
|
57
|
+
polja, nikad se ne procenjuje. Budžet (`/budget`, `--budget`) zaustavlja
|
|
58
|
+
petlju pre sledećeg poziva ako se pređe, bez gubitka sesije. `/usage`
|
|
59
|
+
takođe pokazuje koliko bi sesija koštala off-peak, a REPL upozorava pri
|
|
60
|
+
startu ako si trenutno u peak periodu.
|
|
61
|
+
- **ESC za prekid** — čisto prekida poziv u toku (i bilo koju shell komandu
|
|
62
|
+
koja radi), čuvajući ono što je do tada generisano.
|
|
63
|
+
- **Windows prioritet** — shell alat pokreće PowerShell nativno, izmene
|
|
64
|
+
fajlova čuvaju originalni CRLF/LF stil, putanje se svuda tretiraju preko
|
|
65
|
+
`path.resolve`.
|
|
66
|
+
|
|
67
|
+
## Brzi početak
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/zdrave08/Tandem-Mode.git
|
|
71
|
+
cd Tandem-Mode
|
|
72
|
+
pnpm install
|
|
73
|
+
pnpm dev # interaktivni REPL
|
|
74
|
+
pnpm dev "objasni sta radi ovaj repo" --yes # jedan potez
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
(Još nije objavljeno na npm-u — `npm install -g tandem-mode` je korak za
|
|
78
|
+
dan lansiranja, danas nije dostupan.)
|
|
79
|
+
|
|
80
|
+
Da probaš kao pravu instaliranu `tandem` komandu umesto `pnpm dev` (korisno
|
|
81
|
+
pre objave, ili da koristiš alat u drugom projektu bez kloniranja pored
|
|
82
|
+
njega):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pnpm build
|
|
86
|
+
npm link # registruje `tandem` globalno, pokazuje na ovaj checkout
|
|
87
|
+
tandem # sad radi iz bilo kog direktorijuma
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Pri prvom pokretanju bićeš pitan za DeepSeek API ključ (napravi ga na
|
|
91
|
+
[platform.deepseek.com](https://platform.deepseek.com)) i podrazumevani
|
|
92
|
+
model/reasoning effort. Na Windows-u se ključ čuva DPAPI-šifrovan (vezan za
|
|
93
|
+
tvoj OS nalog, ista zaštita na kojoj se zasniva i sam Credential Manager) u
|
|
94
|
+
`%LOCALAPPDATA%\TandemMode\credentials.dat` — nikad u config fajlu ili logu.
|
|
95
|
+
Linux/macOS čuvanje još nije implementirano (`DEEPSEEK_API_KEY` radi svuda
|
|
96
|
+
u međuvremenu) — videti good-first-issues.
|
|
97
|
+
|
|
98
|
+
## Upotreba
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
tandem # interaktivni REPL
|
|
102
|
+
tandem "popravi off-by-one u foo.ts"
|
|
103
|
+
tandem --plan "dodaj dark mode na stranicu podešavanja"
|
|
104
|
+
tandem "task" --model deepseek-v4-flash --effort low --budget 0.50 --yes
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Flag | Efekat |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `--resume` / `-r` | Nastavi poslednju sesiju u ovom direktorijumu |
|
|
110
|
+
| `--yes` / `-y` | Automatski odobri destruktivne akcije (ispisuje šta je odobreno) |
|
|
111
|
+
| `--plan` | Pokreni planner/worker/reviewer orkestraciju umesto jednog poteza |
|
|
112
|
+
| `--model` | `deepseek-v4-pro` ili `deepseek-v4-flash` |
|
|
113
|
+
| `--effort` | `low`, `high`, ili `max` reasoning effort |
|
|
114
|
+
| `--budget` | Tvrd USD limit za pokretanje |
|
|
115
|
+
| `--max-review-loops` | Limit korekcionih ciklusa u orkestraciji (podrazumevano 3) |
|
|
116
|
+
|
|
117
|
+
### REPL komande
|
|
118
|
+
|
|
119
|
+
| Komanda | Efekat |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `/plan <zadatak>` | Planner/worker/reviewer orkestracija |
|
|
122
|
+
| `/model [ime]` | Prikaži ili promeni model |
|
|
123
|
+
| `/thinking [on\|off]` | Uključi/isključi thinking mode |
|
|
124
|
+
| `/effort [low\|high\|max]` | Prikaži ili postavi reasoning effort |
|
|
125
|
+
| `/status` | Trenutna sesija/podešavanja |
|
|
126
|
+
| `/usage` | Tokeni, cena, off-peak projekcija |
|
|
127
|
+
| `/budget [iznos]` | Prikaži ili postavi budžet sesije |
|
|
128
|
+
| `/new` | Nova sesija |
|
|
129
|
+
| `/fork` | Nova sesija sa kopijom trenutne istorije |
|
|
130
|
+
| `/resume [id]` | Nastavi poslednju (ili navedenu) sesiju |
|
|
131
|
+
| `/paste [poruka]` | Pošalji sliku iz Windows clipboard-a agentu |
|
|
132
|
+
| `/clear` | Očisti ekran |
|
|
133
|
+
| `/help` | Ova lista |
|
|
134
|
+
| `/exit` | Izlaz |
|
|
135
|
+
|
|
136
|
+
Pritisni **ESC** u bilo kom trenutku tokom poteza da ga prekineš.
|
|
137
|
+
|
|
138
|
+
## Arhitektura
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
tandem (bez argumenata) ─┬─> interaktivni REPL ─┬─> jedan potez: agent petlja (alati)
|
|
142
|
+
│ └─> /plan: planner → worker(i) → reviewer
|
|
143
|
+
tandem "task" ────────┘ (single-shot, ista agent petlja, za skripte/CI)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `src/deepseek/` — tanak klijent za DeepSeek-ov OpenAI-kompatibilan API
|
|
147
|
+
(streaming + non-streaming chat completions, vision, web search preko
|
|
148
|
+
Responses API-ja). Vidi [`docs/api-notes.md`](./docs/api-notes.md) za
|
|
149
|
+
ponašanje API-ja protiv kog je ovo napravljeno, verifikovano uživo, ne
|
|
150
|
+
pretpostavljeno.
|
|
151
|
+
- `src/agent/` — tool-calling petlja, implementacije alata, perzistencija
|
|
152
|
+
sesije, praćenje usage/troška.
|
|
153
|
+
- `src/orchestrator/` — planner/worker/reviewer, generisanje repo mape,
|
|
154
|
+
cache-aware konstrukcija stabilnog prefiksa.
|
|
155
|
+
- `src/repl/` — slash komande, runtime konfiguracija, first-run wizard.
|
|
156
|
+
- `src/cli.tsx` — Ink terminalski UI i REPL/single-shot ulazne tačke.
|
|
157
|
+
|
|
158
|
+
## Konfiguracija
|
|
159
|
+
|
|
160
|
+
Slojevito: **sesija** (komande u REPL-u) > **projekat**
|
|
161
|
+
(`.tandem/config.json` u trenutnom direktorijumu) > **globalno**
|
|
162
|
+
(`~/.tandem/config.json`).
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"defaultModel": "deepseek-v4-pro",
|
|
167
|
+
"defaultReasoningEffort": "high",
|
|
168
|
+
"budgetUsd": 5,
|
|
169
|
+
"maxReviewLoops": 3
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
API ključ se nikad ne čuva u ovim fajlovima — živi DPAPI-šifrovan na
|
|
174
|
+
Windows-u (ili u `DEEPSEEK_API_KEY` env promenljivoj, koja ima prioritet,
|
|
175
|
+
za CI upotrebu).
|
|
176
|
+
|
|
177
|
+
## Razvoj
|
|
178
|
+
|
|
179
|
+
Vidi [CONTRIBUTING.sr.md](./CONTRIBUTING.sr.md).
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
pnpm install
|
|
183
|
+
pnpm typecheck
|
|
184
|
+
pnpm dev "neki zadatak"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Disclaimer
|
|
188
|
+
|
|
189
|
+
Ovo je nezavisan projekat koji vodi zajednica. Nije povezan sa DeepSeek-om,
|
|
190
|
+
niti od njega podržan ili sponzorisan.
|
|
191
|
+
|
|
192
|
+
## Licenca
|
|
193
|
+
|
|
194
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
/**
|
|
5
|
+
* Samo Windows za sada — Linux/macOS clipboard pristup ide u good-first-issues
|
|
6
|
+
* (xclip/pngpaste), u duhu "Windows first-class" principa iz plana.
|
|
7
|
+
*/
|
|
8
|
+
export async function saveClipboardImage(destPath) {
|
|
9
|
+
if (process.platform !== "win32") {
|
|
10
|
+
throw new Error("Clipboard paste slike je trenutno podržan samo na Windows-u.");
|
|
11
|
+
}
|
|
12
|
+
const escapedPath = destPath.replace(/'/g, "''");
|
|
13
|
+
const script = `
|
|
14
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
15
|
+
Add-Type -AssemblyName System.Drawing
|
|
16
|
+
if ([System.Windows.Forms.Clipboard]::ContainsImage()) {
|
|
17
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
18
|
+
$img.Save('${escapedPath}', [System.Drawing.Imaging.ImageFormat]::Png)
|
|
19
|
+
Write-Output "OK"
|
|
20
|
+
} else {
|
|
21
|
+
Write-Output "NO_IMAGE"
|
|
22
|
+
}`;
|
|
23
|
+
const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script]);
|
|
24
|
+
return stdout.trim() === "OK";
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=clipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/agent/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG;;;;;eAKF,WAAW;;;;EAIxB,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,gBAAgB,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAChH,OAAO,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { streamChatCompletion } from "../deepseek/client.js";
|
|
2
|
+
import { findTool, toolSpecs } from "./tools/index.js";
|
|
3
|
+
import { appendSessionMessage } from "./session.js";
|
|
4
|
+
function isAbortError(err) {
|
|
5
|
+
return err instanceof Error && err.name === "AbortError";
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* `messages` mora već sadržati system + početni user zahtev (i istoriju ako
|
|
9
|
+
* se nastavlja sesija) — pozivalac je odgovoran da TE poruke upiše u sesiju
|
|
10
|
+
* pre poziva. Petlja perzistuje samo poruke koje sama generiše (assistant/tool).
|
|
11
|
+
*/
|
|
12
|
+
export async function* runAgentLoop(messages, opts) {
|
|
13
|
+
const ctx = { cwd: opts.cwd, env: opts.env, usage: opts.usage, ...(opts.signal ? { signal: opts.signal } : {}) };
|
|
14
|
+
const maxIterations = opts.maxIterations ?? 25;
|
|
15
|
+
for (let iteration = 0; iteration < maxIterations; iteration++) {
|
|
16
|
+
if (opts.signal?.aborted) {
|
|
17
|
+
yield { type: "interrupted" };
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (opts.budgetUsd !== undefined) {
|
|
21
|
+
const spent = opts.usage.estimatedCostUsd();
|
|
22
|
+
if (spent >= opts.budgetUsd) {
|
|
23
|
+
yield { type: "budget_exceeded", spentUsd: spent, budgetUsd: opts.budgetUsd };
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
let reasoning = "";
|
|
28
|
+
let content = "";
|
|
29
|
+
const toolCallBuf = new Map();
|
|
30
|
+
try {
|
|
31
|
+
for await (const ev of streamChatCompletion(opts.env, {
|
|
32
|
+
model: opts.model,
|
|
33
|
+
messages,
|
|
34
|
+
tools: toolSpecs,
|
|
35
|
+
...(opts.thinking !== undefined ? { thinking: opts.thinking } : {}),
|
|
36
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
37
|
+
})) {
|
|
38
|
+
switch (ev.type) {
|
|
39
|
+
case "reasoning_delta":
|
|
40
|
+
reasoning += ev.delta;
|
|
41
|
+
yield { type: "reasoning_delta", delta: ev.delta };
|
|
42
|
+
break;
|
|
43
|
+
case "content_delta":
|
|
44
|
+
content += ev.delta;
|
|
45
|
+
yield { type: "content_delta", delta: ev.delta };
|
|
46
|
+
break;
|
|
47
|
+
case "tool_call_delta": {
|
|
48
|
+
const buf = toolCallBuf.get(ev.index) ?? { args: "" };
|
|
49
|
+
if (ev.id)
|
|
50
|
+
buf.id = ev.id;
|
|
51
|
+
if (ev.name)
|
|
52
|
+
buf.name = ev.name;
|
|
53
|
+
if (ev.argumentsDelta)
|
|
54
|
+
buf.args += ev.argumentsDelta;
|
|
55
|
+
toolCallBuf.set(ev.index, buf);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "usage":
|
|
59
|
+
opts.usage.add(opts.model, ev.usage);
|
|
60
|
+
yield { type: "usage", usage: ev.usage };
|
|
61
|
+
break;
|
|
62
|
+
case "done":
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (!isAbortError(err))
|
|
69
|
+
throw err;
|
|
70
|
+
// Prekinuto usred streama — sačuvaj šta god je stiglo do sad kao poruku,
|
|
71
|
+
// ali bez tool_calls (nepotpuni argumenti se ne mogu pouzdano izvršiti).
|
|
72
|
+
if (content || reasoning) {
|
|
73
|
+
const partialMessage = {
|
|
74
|
+
role: "assistant",
|
|
75
|
+
content: content || null,
|
|
76
|
+
...(reasoning ? { reasoningContent: reasoning } : {}),
|
|
77
|
+
};
|
|
78
|
+
messages.push(partialMessage);
|
|
79
|
+
await appendSessionMessage(opts.session, partialMessage);
|
|
80
|
+
}
|
|
81
|
+
yield { type: "interrupted" };
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const toolCalls = [...toolCallBuf.entries()]
|
|
85
|
+
.sort(([a], [b]) => a - b)
|
|
86
|
+
.map(([, v]) => ({
|
|
87
|
+
id: v.id ?? "",
|
|
88
|
+
type: "function",
|
|
89
|
+
function: { name: v.name ?? "", arguments: v.args },
|
|
90
|
+
}));
|
|
91
|
+
// `tools` se šalje na SVAKI poziv u ovoj petlji, pa dokumentacija traži
|
|
92
|
+
// da se reasoning_content svakog prethodnog poteza vrati — ne samo onih
|
|
93
|
+
// koji su zvali alat (inače dokumentovano 400, videti docs/api-notes.md #2).
|
|
94
|
+
const assistantMessage = {
|
|
95
|
+
role: "assistant",
|
|
96
|
+
content: content || null,
|
|
97
|
+
...(reasoning ? { reasoningContent: reasoning } : {}),
|
|
98
|
+
...(toolCalls.length > 0 ? { toolCalls } : {}),
|
|
99
|
+
};
|
|
100
|
+
messages.push(assistantMessage);
|
|
101
|
+
await appendSessionMessage(opts.session, assistantMessage);
|
|
102
|
+
if (toolCalls.length === 0) {
|
|
103
|
+
yield { type: "final", content };
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
for (const call of toolCalls) {
|
|
107
|
+
if (opts.signal?.aborted) {
|
|
108
|
+
yield { type: "interrupted" };
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let args = {};
|
|
112
|
+
try {
|
|
113
|
+
args = JSON.parse(call.function.arguments || "{}");
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Ostaje {} — alat će prijaviti nedostajuće/neispravne argumente.
|
|
117
|
+
}
|
|
118
|
+
const tool = findTool(call.function.name);
|
|
119
|
+
if (!tool) {
|
|
120
|
+
const toolMsg = {
|
|
121
|
+
role: "tool",
|
|
122
|
+
toolCallId: call.id,
|
|
123
|
+
content: `Nepoznat alat: ${call.function.name}`,
|
|
124
|
+
};
|
|
125
|
+
messages.push(toolMsg);
|
|
126
|
+
await appendSessionMessage(opts.session, toolMsg);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
yield { type: "tool_call_start", id: call.id, name: tool.name, args };
|
|
130
|
+
if (tool.isDestructive(args)) {
|
|
131
|
+
const approved = await opts.approve(tool.name, args);
|
|
132
|
+
if (!approved) {
|
|
133
|
+
yield { type: "tool_call_denied", id: call.id, name: tool.name };
|
|
134
|
+
const toolMsg = {
|
|
135
|
+
role: "tool",
|
|
136
|
+
toolCallId: call.id,
|
|
137
|
+
content: "Korisnik je odbio izvršavanje ovog alata.",
|
|
138
|
+
};
|
|
139
|
+
messages.push(toolMsg);
|
|
140
|
+
await appendSessionMessage(opts.session, toolMsg);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const result = await tool.execute(args, ctx);
|
|
145
|
+
if (opts.signal?.aborted) {
|
|
146
|
+
yield { type: "interrupted" };
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
yield { type: "tool_call_result", id: call.id, name: tool.name, result };
|
|
150
|
+
const toolMsg = { role: "tool", toolCallId: call.id, content: result.output };
|
|
151
|
+
messages.push(toolMsg);
|
|
152
|
+
await appendSessionMessage(opts.session, toolMsg);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
yield { type: "max_iterations_reached" };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const PRICING = {
|
|
2
|
+
"deepseek-v4-flash": { inputPerMillion: 0.22, outputPerMillion: 0.66, cacheHitInputPerMillion: 0.007 },
|
|
3
|
+
"deepseek-v4-pro": { inputPerMillion: 0.66, outputPerMillion: 1.98, cacheHitInputPerMillion: 0.022 },
|
|
4
|
+
};
|
|
5
|
+
const PEAK_WINDOWS_UTC = [
|
|
6
|
+
[1, 4],
|
|
7
|
+
[6, 10],
|
|
8
|
+
];
|
|
9
|
+
export function isPeakHour(date) {
|
|
10
|
+
const hour = date.getUTCHours();
|
|
11
|
+
return PEAK_WINDOWS_UTC.some(([start, end]) => hour >= start && hour < end);
|
|
12
|
+
}
|
|
13
|
+
export function estimateCallCost(model, usage, isPeak) {
|
|
14
|
+
const p = PRICING[model];
|
|
15
|
+
const peakMultiplier = isPeak ? 2 : 1;
|
|
16
|
+
const inputCost = (usage.promptCacheMissTokens / 1_000_000) * p.inputPerMillion * peakMultiplier +
|
|
17
|
+
(usage.promptCacheHitTokens / 1_000_000) * p.cacheHitInputPerMillion * peakMultiplier;
|
|
18
|
+
const outputCost = (usage.completionTokens / 1_000_000) * p.outputPerMillion * peakMultiplier;
|
|
19
|
+
return inputCost + outputCost;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/agent/pricing.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,OAAO,GAAkE;IACpF,mBAAmB,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE;IACtG,iBAAiB,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE;CACrG,CAAC;AAEF,MAAM,gBAAgB,GAA4B;IAChD,CAAC,CAAC,EAAE,CAAC,CAAC;IACN,CAAC,CAAC,EAAE,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAA2B,EAC3B,KAAgG,EAChG,MAAe;IAEf,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,SAAS,GACb,CAAC,KAAK,CAAC,qBAAqB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,eAAe,GAAG,cAAc;QAC9E,CAAC,KAAK,CAAC,oBAAoB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,uBAAuB,GAAG,cAAc,CAAC;IACxF,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,gBAAgB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,gBAAgB,GAAG,cAAc,CAAC;IAC9F,OAAO,SAAS,GAAG,UAAU,CAAC;AAChC,CAAC"}
|