tandem-mode 0.1.4 → 0.4.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 +37 -1
- package/README.sr.md +37 -1
- package/dist/agent/clipboard.js +4 -5
- package/dist/agent/clipboard.js.map +1 -1
- package/dist/agent/compact.js +80 -0
- package/dist/agent/compact.js.map +1 -0
- package/dist/agent/loop.js +2 -2
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/tools/edit.js +11 -11
- package/dist/agent/tools/edit.js.map +1 -1
- package/dist/agent/tools/gitDiff.js +41 -18
- package/dist/agent/tools/gitDiff.js.map +1 -1
- package/dist/agent/tools/listDir.js +4 -4
- package/dist/agent/tools/listDir.js.map +1 -1
- package/dist/agent/tools/readFile.js +7 -7
- package/dist/agent/tools/readFile.js.map +1 -1
- package/dist/agent/tools/search.js +10 -10
- package/dist/agent/tools/search.js.map +1 -1
- package/dist/agent/tools/shell.js +32 -14
- package/dist/agent/tools/shell.js.map +1 -1
- package/dist/agent/tools/viewImage.js +4 -4
- package/dist/agent/tools/viewImage.js.map +1 -1
- package/dist/agent/tools/webSearch.js +3 -3
- package/dist/agent/tools/webSearch.js.map +1 -1
- package/dist/benchmark/runner.js.map +1 -1
- package/dist/benchmark/tasks/{bitwiseBulkPriceWizard.js → bulkPriceWizard.js} +3 -3
- package/dist/benchmark/tasks/bulkPriceWizard.js.map +1 -0
- package/dist/cli.js +48 -22
- package/dist/cli.js.map +1 -1
- package/dist/config/credentials.js +5 -5
- package/dist/config/credentials.js.map +1 -1
- package/dist/config/profiles.js +108 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/deepseek/vision.js +4 -4
- package/dist/deepseek/vision.js.map +1 -1
- package/dist/deepseek/webSearch.js +1 -1
- package/dist/i18n/en.js +103 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/index.js +23 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/sr.js +101 -0
- package/dist/i18n/sr.js.map +1 -0
- package/dist/orchestrator/notify.js +38 -0
- package/dist/orchestrator/notify.js.map +1 -0
- package/dist/orchestrator/orchestrate.js +50 -19
- package/dist/orchestrator/orchestrate.js.map +1 -1
- package/dist/orchestrator/planner.js +10 -10
- package/dist/orchestrator/planner.js.map +1 -1
- package/dist/orchestrator/repoMap.js +1 -1
- package/dist/orchestrator/repoMap.js.map +1 -1
- package/dist/orchestrator/reviewer.js +20 -12
- package/dist/orchestrator/reviewer.js.map +1 -1
- package/dist/repl/commands.js +122 -37
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/system-prompt.js +13 -11
- package/dist/repl/system-prompt.js.map +1 -1
- package/dist/repl/wizard.js +17 -9
- package/dist/repl/wizard.js.map +1 -1
- package/package.json +1 -1
- package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js.map +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { runShell } from "../agent/tools/shell.js";
|
|
2
|
+
import { t } from "../i18n/index.js";
|
|
3
|
+
// The notification must never hold up the end of a run for long.
|
|
4
|
+
const NOTIFY_TIMEOUT_MS = 15_000;
|
|
5
|
+
const MAX_TASK_CHARS = 500;
|
|
6
|
+
const MAX_NOTES_CHARS = 1_000;
|
|
7
|
+
// Environment values cannot contain NUL bytes.
|
|
8
|
+
function envValue(text, max) {
|
|
9
|
+
return text.replaceAll("\0", "").slice(0, max);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Runs the user's `notifyCommand` after a /plan run. Details go in as environment
|
|
13
|
+
* variables, never spliced into the command text, so a task or review note cannot
|
|
14
|
+
* inject shell code. A failing command only prints a warning.
|
|
15
|
+
*/
|
|
16
|
+
export async function notifyCompletion(command, cwd, info) {
|
|
17
|
+
if (!command)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
const { exitCode, output } = await runShell(command, cwd, undefined, {
|
|
21
|
+
timeoutMs: NOTIFY_TIMEOUT_MS,
|
|
22
|
+
env: {
|
|
23
|
+
TANDEM_OUTCOME: info.outcome,
|
|
24
|
+
TANDEM_TASK: envValue(info.task, MAX_TASK_CHARS),
|
|
25
|
+
TANDEM_NOTES: envValue(info.notes, MAX_NOTES_CHARS),
|
|
26
|
+
TANDEM_REVIEW_LOOPS: String(info.reviewLoops),
|
|
27
|
+
TANDEM_COST_USD: info.costUsd.toFixed(4),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
if (exitCode !== 0) {
|
|
31
|
+
console.warn(t("notifyFailed", { reason: `exit code ${exitCode}: ${output.trim().slice(0, 200)}` }));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
console.warn(t("notifyFailed", { reason: err instanceof Error ? err.message : String(err) }));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/orchestrator/notify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAC;AAcrC,iEAAiE;AACjE,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B,+CAA+C;AAC/C,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAW;IACzC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAA2B,EAAE,GAAW,EAAE,IAAgB;IAC/F,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE;YACnE,SAAS,EAAE,iBAAiB;YAC5B,GAAG,EAAE;gBACH,cAAc,EAAE,IAAI,CAAC,OAAO;gBAC5B,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;gBAChD,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC;gBACnD,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;gBAC7C,eAAe,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;aACzC;SACF,CAAC,CAAC;QACH,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,aAAa,QAAQ,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACvG,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC;AACH,CAAC"}
|
|
@@ -2,30 +2,46 @@ import { gitDiffTool } from "../agent/tools/gitDiff.js";
|
|
|
2
2
|
import { buildRepoMap } from "./repoMap.js";
|
|
3
3
|
import { runPlanner } from "./planner.js";
|
|
4
4
|
import { runReviewer } from "./reviewer.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import { runShell } from "../agent/tools/shell.js";
|
|
6
|
+
import { notifyCompletion } from "./notify.js";
|
|
7
|
+
import { t } from "../i18n/index.js";
|
|
8
|
+
const WORKER_SYSTEM_PROMPT = `You are a worker in a Tandem Mode orchestration. You get ONE concrete task from
|
|
9
|
+
the plan below, with a repository map. You have no access to the conversation
|
|
10
|
+
that produced the plan — everything you need is here. Carry out the task using the tools
|
|
11
|
+
(read_file, list_dir, search, edit, git_diff, shell, view_image). When
|
|
12
|
+
you are done, answer concisely without tool calls so the loop stops.`;
|
|
10
13
|
function buildStablePrefix(plan, repoMap) {
|
|
11
|
-
return `${WORKER_SYSTEM_PROMPT}\n\nPlan (
|
|
14
|
+
return `${WORKER_SYSTEM_PROMPT}\n\nPlan (full, for context):\n${JSON.stringify(plan)}\n\nRepository map:\n${repoMap}`;
|
|
15
|
+
}
|
|
16
|
+
async function runVerify(command, cwd, signal) {
|
|
17
|
+
console.log(t("orchVerify", { command }));
|
|
18
|
+
try {
|
|
19
|
+
const { output, exitCode } = await runShell(command, cwd, signal);
|
|
20
|
+
console.log(exitCode === 0 ? t("orchVerifyPassed") : t("orchVerifyFailed", { code: String(exitCode) }));
|
|
21
|
+
return { command, exitCode, output };
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
if (err instanceof Error && err.name === "AbortError")
|
|
25
|
+
throw err;
|
|
26
|
+
return { command, exitCode: null, output: err.message };
|
|
27
|
+
}
|
|
12
28
|
}
|
|
13
29
|
export async function runOrchestration(task, opts) {
|
|
14
|
-
console.log("
|
|
30
|
+
console.log(t("orchPlanning"));
|
|
15
31
|
const repoMap = await buildRepoMap(opts.cwd);
|
|
16
32
|
const { plan, usage: plannerUsage } = await runPlanner(opts.env, task, repoMap, opts.signal);
|
|
17
33
|
opts.usage.add("deepseek-v4-pro", plannerUsage);
|
|
18
|
-
console.log(
|
|
19
|
-
plan.tasks.forEach((
|
|
34
|
+
console.log(t("orchPlan", { summary: plan.summary }));
|
|
35
|
+
plan.tasks.forEach((planTask, i) => console.log(` ${i + 1}. [${planTask.id}] ${planTask.description}`));
|
|
20
36
|
const stablePrefix = buildStablePrefix(plan, repoMap);
|
|
21
37
|
const workerThinking = { reasoningEffort: "high" };
|
|
22
|
-
for (const
|
|
38
|
+
for (const planTask of plan.tasks) {
|
|
23
39
|
if (opts.signal?.aborted)
|
|
24
40
|
return;
|
|
25
|
-
console.log(
|
|
41
|
+
console.log(t("orchWorker", { description: planTask.description }));
|
|
26
42
|
const messages = [
|
|
27
43
|
{ role: "system", content: stablePrefix },
|
|
28
|
-
{ role: "user", content: `Task: ${
|
|
44
|
+
{ role: "user", content: `Task: ${planTask.description}\nAcceptance criteria: ${planTask.acceptanceCriteria}` },
|
|
29
45
|
];
|
|
30
46
|
await opts.runWorkerTurn({ messages, model: "deepseek-v4-flash", thinking: workerThinking, ...(opts.signal ? { signal: opts.signal } : {}) });
|
|
31
47
|
}
|
|
@@ -33,28 +49,43 @@ export async function runOrchestration(task, opts) {
|
|
|
33
49
|
for (;;) {
|
|
34
50
|
if (opts.signal?.aborted)
|
|
35
51
|
return;
|
|
36
|
-
console.log("
|
|
52
|
+
console.log(t("orchReviewer"));
|
|
37
53
|
const diffResult = await gitDiffTool.execute({}, { cwd: opts.cwd, env: opts.env, usage: opts.usage });
|
|
38
|
-
const
|
|
54
|
+
const verify = opts.verifyCommand ? await runVerify(opts.verifyCommand, opts.cwd, opts.signal) : undefined;
|
|
55
|
+
const { result: review, usage: reviewUsage } = await runReviewer(opts.env, task, plan, diffResult.output, verify, opts.signal);
|
|
39
56
|
opts.usage.add("deepseek-v4-pro", reviewUsage);
|
|
40
57
|
if (review.approved) {
|
|
41
|
-
console.log(
|
|
58
|
+
console.log(t("orchApproved", { notes: review.notes }));
|
|
59
|
+
await notifyCompletion(opts.notifyCommand, opts.cwd, {
|
|
60
|
+
outcome: "approved",
|
|
61
|
+
task,
|
|
62
|
+
notes: review.notes,
|
|
63
|
+
reviewLoops: loops,
|
|
64
|
+
costUsd: opts.usage.estimatedCostUsd(),
|
|
65
|
+
});
|
|
42
66
|
return;
|
|
43
67
|
}
|
|
44
68
|
loops++;
|
|
45
|
-
console.log(
|
|
69
|
+
console.log(t("orchChangesRequested", { loops, max: opts.maxReviewLoops, notes: review.notes }));
|
|
46
70
|
review.corrections.forEach((c) => console.log(` - ${c.description}`));
|
|
47
71
|
if (loops >= opts.maxReviewLoops) {
|
|
48
|
-
console.log(
|
|
72
|
+
console.log(t("orchMaxLoops", { max: opts.maxReviewLoops }));
|
|
73
|
+
await notifyCompletion(opts.notifyCommand, opts.cwd, {
|
|
74
|
+
outcome: "max_review_loops",
|
|
75
|
+
task,
|
|
76
|
+
notes: review.notes,
|
|
77
|
+
reviewLoops: loops,
|
|
78
|
+
costUsd: opts.usage.estimatedCostUsd(),
|
|
79
|
+
});
|
|
49
80
|
return;
|
|
50
81
|
}
|
|
51
82
|
for (const correction of review.corrections) {
|
|
52
83
|
if (opts.signal?.aborted)
|
|
53
84
|
return;
|
|
54
|
-
console.log(
|
|
85
|
+
console.log(t("orchWorkerCorrection", { description: correction.description }));
|
|
55
86
|
const messages = [
|
|
56
87
|
{ role: "system", content: stablePrefix },
|
|
57
|
-
{ role: "user", content: `
|
|
88
|
+
{ role: "user", content: `Correction from the reviewer: ${correction.description}` },
|
|
58
89
|
];
|
|
59
90
|
await opts.runWorkerTurn({ messages, model: "deepseek-v4-flash", thinking: workerThinking, ...(opts.signal ? { signal: opts.signal } : {}) });
|
|
60
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/orchestrator/orchestrate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/orchestrator/orchestrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { chatCompletionOnce } from "../deepseek/client.js";
|
|
2
2
|
import { isPlan } from "./types.js";
|
|
3
|
-
const PLANNER_SYSTEM_PROMPT = `
|
|
4
|
-
|
|
5
|
-
(
|
|
6
|
-
git_diff/shell/view_image)
|
|
3
|
+
const PLANNER_SYSTEM_PROMPT = `You are the planner in the Tandem Mode coding agent. You receive a task and a map
|
|
4
|
+
of the repository. Split the task into small, independent steps that a worker model
|
|
5
|
+
(with no access to this conversation, only the tools read_file/list_dir/search/edit/
|
|
6
|
+
git_diff/shell/view_image) can carry out on its own.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Respond ONLY with valid JSON, with no markdown fences, in exactly this schema:
|
|
9
9
|
{
|
|
10
|
-
"summary": "
|
|
10
|
+
"summary": "short description of the approach",
|
|
11
11
|
"tasks": [
|
|
12
12
|
{ "id": "t1", "description": "...", "files": ["..."], "acceptanceCriteria": "..." }
|
|
13
13
|
]
|
|
@@ -22,7 +22,7 @@ export async function runPlanner(env, task, repoMap, signal) {
|
|
|
22
22
|
jsonOutput: true,
|
|
23
23
|
messages: [
|
|
24
24
|
{ role: "system", content: PLANNER_SYSTEM_PROMPT },
|
|
25
|
-
{ role: "user", content: `
|
|
25
|
+
{ role: "user", content: `Task:\n${task}\n\nRepository map:\n${repoMap}` },
|
|
26
26
|
],
|
|
27
27
|
...(signal ? { signal } : {}),
|
|
28
28
|
});
|
|
@@ -31,11 +31,11 @@ export async function runPlanner(env, task, repoMap, signal) {
|
|
|
31
31
|
parsed = JSON.parse(result.content);
|
|
32
32
|
}
|
|
33
33
|
catch {
|
|
34
|
-
lastError = new Error(`Planner
|
|
34
|
+
lastError = new Error(`Planner did not return valid JSON (attempt ${attempt}):\n${result.content.slice(0, 500)}`);
|
|
35
35
|
continue;
|
|
36
36
|
}
|
|
37
37
|
if (!isPlan(parsed)) {
|
|
38
|
-
lastError = new Error(`Planner JSON
|
|
38
|
+
lastError = new Error(`Planner JSON does not follow the expected schema (attempt ${attempt}):\n${result.content.slice(0, 500)}`);
|
|
39
39
|
continue;
|
|
40
40
|
}
|
|
41
41
|
return { plan: parsed, usage: result.usage };
|
|
@@ -43,6 +43,6 @@ export async function runPlanner(env, task, repoMap, signal) {
|
|
|
43
43
|
// Poznato ponašanje: json_object mod povremeno vrati prazan/nevalidan
|
|
44
44
|
// sadržaj (verovatno prolazna API nestabilnost, ne dosledna greška —
|
|
45
45
|
// videti docs/api-notes.md). Jedan retry pokriva to bez rušenja pozivaoca.
|
|
46
|
-
throw lastError ?? new Error("Planner
|
|
46
|
+
throw lastError ?? new Error("Planner failed after multiple attempts.");
|
|
47
47
|
}
|
|
48
48
|
//# sourceMappingURL=planner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/orchestrator/planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,MAAM,EAAa,MAAM,YAAY,CAAC;AAE/C,MAAM,qBAAqB,GAAG;;;;;;;;;;;EAW5B,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAc,EACd,IAAY,EACZ,OAAe,EACf,MAAoB;IAEpB,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE;YAC3C,KAAK,EAAE,iBAAiB;YACxB,QAAQ,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;YACpC,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,EAAE;gBAClD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/orchestrator/planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,MAAM,EAAa,MAAM,YAAY,CAAC;AAE/C,MAAM,qBAAqB,GAAG;;;;;;;;;;;EAW5B,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAc,EACd,IAAY,EACZ,OAAe,EACf,MAAoB;IAEpB,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE;YAC3C,KAAK,EAAE,iBAAiB;YACxB,QAAQ,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;YACpC,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,EAAE;gBAClD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,IAAI,wBAAwB,OAAO,EAAE,EAAE;aAC3E;YACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,IAAI,KAAK,CAAC,8CAA8C,OAAO,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YAClH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,SAAS,GAAG,IAAI,KAAK,CAAC,6DAA6D,OAAO,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACjI,SAAS;QACX,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,sEAAsE;IACtE,qEAAqE;IACrE,2EAA2E;IAC3E,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -47,6 +47,6 @@ export async function buildRepoMap(cwd) {
|
|
|
47
47
|
const files = (await listViaGit(cwd)) ?? (await listViaFs(cwd));
|
|
48
48
|
const truncated = files.length > MAX_FILES;
|
|
49
49
|
const listed = files.slice(0, MAX_FILES).sort();
|
|
50
|
-
return (listed.join("\n") + (truncated ? `\n... (
|
|
50
|
+
return (listed.join("\n") + (truncated ? `\n... (truncated, ${files.length} files in total)` : ""));
|
|
51
51
|
}
|
|
52
52
|
//# sourceMappingURL=repoMap.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repoMap.js","sourceRoot":"","sources":["../../src/orchestrator/repoMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,KAAK,UAAU,UAAU,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QAClG,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,UAAU,IAAI,CAAC,GAAW;QAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;YAAE,OAAO;QACxC,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;gBAAE,OAAO;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS;YAC9F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,KAAK,GAAG,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"repoMap.js","sourceRoot":"","sources":["../../src/orchestrator/repoMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,KAAK,UAAU,UAAU,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QAClG,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,UAAU,IAAI,CAAC,GAAW;QAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;YAAE,OAAO;QACxC,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;gBAAE,OAAO;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS;YAC9F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,KAAK,GAAG,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,KAAK,CAAC,MAAM,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC,CAC3F,CAAC;AACJ,CAAC"}
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { chatCompletionOnce } from "../deepseek/client.js";
|
|
2
2
|
import { isReviewResult } from "./types.js";
|
|
3
|
-
const REVIEWER_SYSTEM_PROMPT = `
|
|
4
|
-
plan
|
|
5
|
-
diff
|
|
6
|
-
|
|
3
|
+
const REVIEWER_SYSTEM_PROMPT = `You are the reviewer in the Tandem Mode coding agent. You receive the original task,
|
|
4
|
+
the plan the worker(s) carried out, and the git diff of the actual changes. Check whether the
|
|
5
|
+
diff really fulfills the task and the plan's acceptance criteria, contains no
|
|
6
|
+
unnecessary changes, and that the code is reasonable. If a check result
|
|
7
|
+
(build/test) is attached, a non-zero exit code means the change does not pass — in that case do not
|
|
8
|
+
approve, and name the concrete error from the output in "corrections".
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
Respond ONLY with valid JSON, with no markdown fences, in exactly this schema:
|
|
9
11
|
{
|
|
10
12
|
"approved": true|false,
|
|
11
|
-
"notes": "
|
|
13
|
+
"notes": "brief justification",
|
|
12
14
|
"corrections": [{ "description": "...", "files": ["..."] }]
|
|
13
15
|
}
|
|
14
|
-
"corrections"
|
|
16
|
+
"corrections" is an empty array if approved is true.`;
|
|
15
17
|
const MAX_ATTEMPTS = 2;
|
|
16
|
-
|
|
18
|
+
const MAX_VERIFY_CHARS = 6000;
|
|
19
|
+
function formatVerify(verify) {
|
|
20
|
+
// Greške build/test alata su obično na kraju izlaza, pa čuvamo rep, ne glavu.
|
|
21
|
+
const out = verify.output.length > MAX_VERIFY_CHARS ? "...(truncated)\n" + verify.output.slice(-MAX_VERIFY_CHARS) : verify.output;
|
|
22
|
+
return `\n\nCheck result (${verify.command}), exit code ${verify.exitCode ?? "n/a (not run)"}:\n${out || "(no output)"}`;
|
|
23
|
+
}
|
|
24
|
+
export async function runReviewer(env, originalTask, plan, gitDiff, verify, signal) {
|
|
17
25
|
let lastError;
|
|
18
26
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
19
27
|
const response = await chatCompletionOnce(env, {
|
|
@@ -24,7 +32,7 @@ export async function runReviewer(env, originalTask, plan, gitDiff, signal) {
|
|
|
24
32
|
{ role: "system", content: REVIEWER_SYSTEM_PROMPT },
|
|
25
33
|
{
|
|
26
34
|
role: "user",
|
|
27
|
-
content: `
|
|
35
|
+
content: `Original task:\n${originalTask}\n\nPlan:\n${JSON.stringify(plan, null, 2)}\n\nGit diff:\n${gitDiff.slice(0, 15000)}${verify ? formatVerify(verify) : ""}`,
|
|
28
36
|
},
|
|
29
37
|
],
|
|
30
38
|
...(signal ? { signal } : {}),
|
|
@@ -34,11 +42,11 @@ export async function runReviewer(env, originalTask, plan, gitDiff, signal) {
|
|
|
34
42
|
parsed = JSON.parse(response.content);
|
|
35
43
|
}
|
|
36
44
|
catch {
|
|
37
|
-
lastError = new Error(`Reviewer
|
|
45
|
+
lastError = new Error(`Reviewer did not return valid JSON (attempt ${attempt}):\n${response.content.slice(0, 500)}`);
|
|
38
46
|
continue;
|
|
39
47
|
}
|
|
40
48
|
if (!isReviewResult(parsed)) {
|
|
41
|
-
lastError = new Error(`Reviewer JSON
|
|
49
|
+
lastError = new Error(`Reviewer JSON does not follow the expected schema (attempt ${attempt}):\n${response.content.slice(0, 500)}`);
|
|
42
50
|
continue;
|
|
43
51
|
}
|
|
44
52
|
const result = {
|
|
@@ -48,6 +56,6 @@ export async function runReviewer(env, originalTask, plan, gitDiff, signal) {
|
|
|
48
56
|
};
|
|
49
57
|
return { result, usage: response.usage };
|
|
50
58
|
}
|
|
51
|
-
throw lastError ?? new Error("Reviewer
|
|
59
|
+
throw lastError ?? new Error("Reviewer failed after multiple attempts.");
|
|
52
60
|
}
|
|
53
61
|
//# sourceMappingURL=reviewer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../src/orchestrator/reviewer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAgC,MAAM,YAAY,CAAC;AAE1E,MAAM,sBAAsB,GAAG
|
|
1
|
+
{"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../src/orchestrator/reviewer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAgC,MAAM,YAAY,CAAC;AAE1E,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;qDAasB,CAAC;AAEtD,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAQ9B,SAAS,YAAY,CAAC,MAAoB;IACxC,8EAA8E;IAC9E,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IAClI,OAAO,qBAAqB,MAAM,CAAC,OAAO,gBAAgB,MAAM,CAAC,QAAQ,IAAI,eAAe,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;AAC3H,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAc,EACd,YAAoB,EACpB,IAAU,EACV,OAAe,EACf,MAAqB,EACrB,MAAoB;IAEpB,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE;YAC7C,KAAK,EAAE,iBAAiB;YACxB,QAAQ,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE;YACrC,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,sBAAsB,EAAE;gBACnD;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,mBAAmB,YAAY,cAAc,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,kBAAkB,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;iBACpK;aACF;YACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,IAAI,KAAK,CAAC,+CAA+C,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACrH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,SAAS,GAAG,IAAI,KAAK,CAAC,8DAA8D,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACpI,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAiB;YAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE;YACzB,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;SACtC,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;AAC3E,CAAC"}
|
package/dist/repl/commands.js
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import { createSession, findLatestSession, findSessionById, loadSessionMessages, appendSessionMessage, } from "../agent/session.js";
|
|
2
|
+
import { compactConversation, SUMMARY_MODEL } from "../agent/compact.js";
|
|
3
|
+
import { gitDiffTool } from "../agent/tools/gitDiff.js";
|
|
4
|
+
import { describeProfile, listProfiles, loadProfile, normalizeProfileName, saveProfile, } from "../config/profiles.js";
|
|
2
5
|
import { SYSTEM_PROMPT } from "./system-prompt.js";
|
|
3
|
-
|
|
4
|
-
/plan <zadatak> planner/worker/reviewer orkestracija
|
|
5
|
-
/model [deepseek-v4-pro|deepseek-v4-flash] prikaži ili promeni model
|
|
6
|
-
/thinking [on|off] uključi/isključi thinking mode
|
|
7
|
-
/effort [low|high|max] prikaži ili promeni reasoning effort
|
|
8
|
-
/status trenutna podešavanja i sesija
|
|
9
|
-
/usage utrošak tokena, procenjena cena, off-peak projekcija
|
|
10
|
-
/budget [iznos] prikaži ili postavi budžet u USD
|
|
11
|
-
/new nova sesija
|
|
12
|
-
/fork nova sesija sa kopijom trenutne istorije
|
|
13
|
-
/resume [id] nastavi poslednju ili navedenu sesiju
|
|
14
|
-
/paste [poruka] pošalji sliku iz clipboard-a (Windows)
|
|
15
|
-
/clear očisti ekran
|
|
16
|
-
/help ova lista
|
|
17
|
-
/exit izlaz`;
|
|
6
|
+
import { t } from "../i18n/index.js";
|
|
18
7
|
export async function handleCommand(line, state) {
|
|
19
8
|
const [cmd = "", ...rest] = line.slice(1).trim().split(/\s+/);
|
|
20
9
|
const arg = rest.join(" ").trim();
|
|
21
10
|
switch (cmd) {
|
|
22
11
|
case "help":
|
|
23
|
-
console.log(
|
|
12
|
+
console.log(t("helpText"));
|
|
24
13
|
return "continue";
|
|
25
14
|
case "exit":
|
|
26
15
|
case "quit":
|
|
@@ -31,10 +20,10 @@ export async function handleCommand(line, state) {
|
|
|
31
20
|
console.log(`Model: ${arg}`);
|
|
32
21
|
}
|
|
33
22
|
else if (!arg) {
|
|
34
|
-
console.log(
|
|
23
|
+
console.log(t("modelCurrent", { model: state.model }));
|
|
35
24
|
}
|
|
36
25
|
else {
|
|
37
|
-
console.log("
|
|
26
|
+
console.log(t("usageLine", { syntax: "/model deepseek-v4-pro|deepseek-v4-flash" }));
|
|
38
27
|
}
|
|
39
28
|
return "continue";
|
|
40
29
|
case "thinking":
|
|
@@ -46,7 +35,7 @@ export async function handleCommand(line, state) {
|
|
|
46
35
|
console.log(`Thinking: ${state.thinkingEnabled ? "on" : "off"}`);
|
|
47
36
|
}
|
|
48
37
|
else {
|
|
49
|
-
console.log("
|
|
38
|
+
console.log(t("usageLine", { syntax: "/thinking on|off" }));
|
|
50
39
|
}
|
|
51
40
|
return "continue";
|
|
52
41
|
case "effort":
|
|
@@ -55,33 +44,42 @@ export async function handleCommand(line, state) {
|
|
|
55
44
|
console.log(`Reasoning effort: ${arg}`);
|
|
56
45
|
}
|
|
57
46
|
else if (!arg) {
|
|
58
|
-
console.log(
|
|
47
|
+
console.log(t("effortCurrent", { effort: state.reasoningEffort }));
|
|
59
48
|
}
|
|
60
49
|
else {
|
|
61
|
-
console.log("
|
|
50
|
+
console.log(t("usageLine", { syntax: "/effort low|high|max" }));
|
|
62
51
|
}
|
|
63
52
|
return "continue";
|
|
64
53
|
case "status": {
|
|
65
|
-
console.log(
|
|
54
|
+
console.log(t("statusSession", { id: state.session.id }));
|
|
66
55
|
console.log(`Model: ${state.model}`);
|
|
67
56
|
console.log(`Thinking: ${state.thinkingEnabled ? `on (${state.reasoningEffort})` : "off"}`);
|
|
68
|
-
console.log(
|
|
57
|
+
console.log(t("statusBudget", { value: state.budgetUsd !== undefined ? `$${state.budgetUsd.toFixed(2)}` : t("statusNotSet") }));
|
|
69
58
|
console.log(`Max review loops: ${state.maxReviewLoops}`);
|
|
70
|
-
console.log(
|
|
59
|
+
console.log(t("statusVerify", { value: state.verifyCommand ?? t("statusVerifyNone") }));
|
|
60
|
+
console.log(t("statusNotify", { value: state.notifyCommand ?? t("statusVerifyNone") }));
|
|
61
|
+
console.log(t("statusMessages", { count: state.messages.length }));
|
|
62
|
+
return "continue";
|
|
63
|
+
}
|
|
64
|
+
case "diff": {
|
|
65
|
+
const staged = rest[0] === "--staged";
|
|
66
|
+
const scopePath = (staged ? rest.slice(1) : rest).join(" ").trim();
|
|
67
|
+
const result = await gitDiffTool.execute({ staged, ...(scopePath ? { path: scopePath } : {}) }, { cwd: state.cwd, env: state.env, usage: state.usage });
|
|
68
|
+
console.log(result.output);
|
|
71
69
|
return "continue";
|
|
72
70
|
}
|
|
73
71
|
case "usage": {
|
|
74
72
|
const totals = state.usage.totals();
|
|
75
73
|
const cost = state.usage.estimatedCostUsd();
|
|
76
74
|
const offPeakCost = state.usage.offPeakEquivalentCostUsd();
|
|
77
|
-
console.log(
|
|
78
|
-
console.log(
|
|
79
|
-
console.log(
|
|
75
|
+
console.log(t("usageCalls", { count: state.usage.callCount }));
|
|
76
|
+
console.log(t("usageTokens", { input: totals.promptTokens, cached: totals.promptCacheHitTokens, output: totals.completionTokens }));
|
|
77
|
+
console.log(t("usageCost", { cost: `$${cost.toFixed(4)}` }));
|
|
80
78
|
if (Math.abs(cost - offPeakCost) > 0.0000001) {
|
|
81
|
-
console.log(
|
|
79
|
+
console.log(t("usageOffPeak", { cost: `$${offPeakCost.toFixed(4)}` }));
|
|
82
80
|
}
|
|
83
81
|
if (state.budgetUsd !== undefined) {
|
|
84
|
-
console.log(
|
|
82
|
+
console.log(t("usageBudget", { budget: `$${state.budgetUsd.toFixed(2)}`, percent: ((cost / state.budgetUsd) * 100).toFixed(0) }));
|
|
85
83
|
}
|
|
86
84
|
return "continue";
|
|
87
85
|
}
|
|
@@ -90,14 +88,14 @@ export async function handleCommand(line, state) {
|
|
|
90
88
|
const n = Number(arg);
|
|
91
89
|
if (Number.isFinite(n) && n > 0) {
|
|
92
90
|
state.budgetUsd = n;
|
|
93
|
-
console.log(
|
|
91
|
+
console.log(t("budgetSet", { amount: `$${n.toFixed(2)}` }));
|
|
94
92
|
}
|
|
95
93
|
else {
|
|
96
|
-
console.log("
|
|
94
|
+
console.log(t("usageLine", { syntax: "/budget <USD>" }));
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
else {
|
|
100
|
-
console.log(state.budgetUsd !== undefined ?
|
|
98
|
+
console.log(state.budgetUsd !== undefined ? t("budgetCurrent", { amount: `$${state.budgetUsd.toFixed(2)}` }) : t("budgetNone"));
|
|
101
99
|
}
|
|
102
100
|
return "continue";
|
|
103
101
|
case "new": {
|
|
@@ -105,7 +103,94 @@ export async function handleCommand(line, state) {
|
|
|
105
103
|
const systemMsg = { role: "system", content: SYSTEM_PROMPT };
|
|
106
104
|
state.messages = [systemMsg];
|
|
107
105
|
await appendSessionMessage(state.session, systemMsg);
|
|
108
|
-
console.log(
|
|
106
|
+
console.log(t("sessionNew", { id: state.session.id }));
|
|
107
|
+
return "continue";
|
|
108
|
+
}
|
|
109
|
+
case "save-profile": {
|
|
110
|
+
const name = normalizeProfileName(arg);
|
|
111
|
+
if (!name) {
|
|
112
|
+
console.log(arg ? t("profileInvalidName") : t("usageLine", { syntax: "/save-profile <name>" }));
|
|
113
|
+
return "continue";
|
|
114
|
+
}
|
|
115
|
+
const profile = {
|
|
116
|
+
model: state.model,
|
|
117
|
+
reasoningEffort: state.reasoningEffort,
|
|
118
|
+
maxReviewLoops: state.maxReviewLoops,
|
|
119
|
+
...(state.budgetUsd !== undefined ? { budgetUsd: state.budgetUsd } : {}),
|
|
120
|
+
};
|
|
121
|
+
try {
|
|
122
|
+
const filePath = await saveProfile(name, profile);
|
|
123
|
+
console.log(t("profileSaved", { name, path: filePath }));
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
console.log(t("errorPrefix", { message: err instanceof Error ? err.message : String(err) }));
|
|
127
|
+
}
|
|
128
|
+
return "continue";
|
|
129
|
+
}
|
|
130
|
+
case "profile": {
|
|
131
|
+
if (!arg) {
|
|
132
|
+
try {
|
|
133
|
+
const names = await listProfiles();
|
|
134
|
+
console.log(names.length > 0 ? t("profileList", { names: names.join(", ") }) : t("profileNone"));
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.log(t("errorPrefix", { message: err instanceof Error ? err.message : String(err) }));
|
|
138
|
+
}
|
|
139
|
+
return "continue";
|
|
140
|
+
}
|
|
141
|
+
const name = normalizeProfileName(arg);
|
|
142
|
+
if (!name) {
|
|
143
|
+
console.log(t("profileInvalidName"));
|
|
144
|
+
return "continue";
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const profile = await loadProfile(name);
|
|
148
|
+
if (!profile) {
|
|
149
|
+
console.log(t("profileNotFound", { name }));
|
|
150
|
+
return "continue";
|
|
151
|
+
}
|
|
152
|
+
// Only the fields present in the profile are applied; the rest of the session is left alone.
|
|
153
|
+
if (profile.model)
|
|
154
|
+
state.model = profile.model;
|
|
155
|
+
if (profile.reasoningEffort)
|
|
156
|
+
state.reasoningEffort = profile.reasoningEffort;
|
|
157
|
+
if (profile.budgetUsd !== undefined)
|
|
158
|
+
state.budgetUsd = profile.budgetUsd;
|
|
159
|
+
if (profile.maxReviewLoops !== undefined)
|
|
160
|
+
state.maxReviewLoops = profile.maxReviewLoops;
|
|
161
|
+
console.log(t("profileLoaded", { name, summary: describeProfile(profile) }));
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.log(t("profileInvalid", { name, reason: err instanceof Error ? err.message : String(err) }));
|
|
165
|
+
}
|
|
166
|
+
return "continue";
|
|
167
|
+
}
|
|
168
|
+
case "compact": {
|
|
169
|
+
if (state.messages.filter((m) => m.role !== "system").length < 2) {
|
|
170
|
+
console.log(t("compactNothing"));
|
|
171
|
+
return "continue";
|
|
172
|
+
}
|
|
173
|
+
console.log(t("compactWorking", { count: state.messages.length }));
|
|
174
|
+
try {
|
|
175
|
+
const first = state.messages[0];
|
|
176
|
+
const systemPrompt = first?.role === "system" && first.content ? first.content : SYSTEM_PROMPT;
|
|
177
|
+
const result = await compactConversation(state.env, state.messages, systemPrompt);
|
|
178
|
+
state.usage.add(SUMMARY_MODEL, result.usage);
|
|
179
|
+
// New session instead of rewriting the old file: the original history stays on
|
|
180
|
+
// disk untouched, and /resume of the new session loads the compact version.
|
|
181
|
+
const previous = state.session;
|
|
182
|
+
const next = await createSession(state.cwd);
|
|
183
|
+
for (const msg of result.messages) {
|
|
184
|
+
await appendSessionMessage(next, msg);
|
|
185
|
+
}
|
|
186
|
+
const before = state.messages.length;
|
|
187
|
+
state.session = next;
|
|
188
|
+
state.messages = result.messages;
|
|
189
|
+
console.log(t("compactDone", { before, after: result.messages.length, id: next.id, previous: previous.id }));
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
console.log(t("compactFailed", { message: err instanceof Error ? err.message : String(err) }));
|
|
193
|
+
}
|
|
109
194
|
return "continue";
|
|
110
195
|
}
|
|
111
196
|
case "fork": {
|
|
@@ -114,25 +199,25 @@ export async function handleCommand(line, state) {
|
|
|
114
199
|
await appendSessionMessage(forked, msg);
|
|
115
200
|
}
|
|
116
201
|
state.session = forked;
|
|
117
|
-
console.log(
|
|
202
|
+
console.log(t("sessionForked", { id: forked.id, count: state.messages.length }));
|
|
118
203
|
return "continue";
|
|
119
204
|
}
|
|
120
205
|
case "resume": {
|
|
121
206
|
const target = arg ? await findSessionById(state.cwd, arg) : await findLatestSession(state.cwd);
|
|
122
207
|
if (!target) {
|
|
123
|
-
console.log(arg ?
|
|
208
|
+
console.log(arg ? t("sessionNotFound", { id: arg }) : t("sessionNoneInDir"));
|
|
124
209
|
return "continue";
|
|
125
210
|
}
|
|
126
211
|
state.messages = await loadSessionMessages(target.filePath);
|
|
127
212
|
state.session = target;
|
|
128
|
-
console.log(
|
|
213
|
+
console.log(t("sessionResumed", { id: target.id, count: state.messages.length }));
|
|
129
214
|
return "continue";
|
|
130
215
|
}
|
|
131
216
|
case "clear":
|
|
132
217
|
console.clear();
|
|
133
218
|
return "continue";
|
|
134
219
|
default:
|
|
135
|
-
console.log(
|
|
220
|
+
console.log(t("unknownCommand", { cmd }));
|
|
136
221
|
return "continue";
|
|
137
222
|
}
|
|
138
223
|
}
|