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.
Files changed (61) hide show
  1. package/README.md +37 -1
  2. package/README.sr.md +37 -1
  3. package/dist/agent/clipboard.js +4 -5
  4. package/dist/agent/clipboard.js.map +1 -1
  5. package/dist/agent/compact.js +80 -0
  6. package/dist/agent/compact.js.map +1 -0
  7. package/dist/agent/loop.js +2 -2
  8. package/dist/agent/loop.js.map +1 -1
  9. package/dist/agent/tools/edit.js +11 -11
  10. package/dist/agent/tools/edit.js.map +1 -1
  11. package/dist/agent/tools/gitDiff.js +41 -18
  12. package/dist/agent/tools/gitDiff.js.map +1 -1
  13. package/dist/agent/tools/listDir.js +4 -4
  14. package/dist/agent/tools/listDir.js.map +1 -1
  15. package/dist/agent/tools/readFile.js +7 -7
  16. package/dist/agent/tools/readFile.js.map +1 -1
  17. package/dist/agent/tools/search.js +10 -10
  18. package/dist/agent/tools/search.js.map +1 -1
  19. package/dist/agent/tools/shell.js +32 -14
  20. package/dist/agent/tools/shell.js.map +1 -1
  21. package/dist/agent/tools/viewImage.js +4 -4
  22. package/dist/agent/tools/viewImage.js.map +1 -1
  23. package/dist/agent/tools/webSearch.js +3 -3
  24. package/dist/agent/tools/webSearch.js.map +1 -1
  25. package/dist/benchmark/runner.js.map +1 -1
  26. package/dist/benchmark/tasks/{bitwiseBulkPriceWizard.js → bulkPriceWizard.js} +3 -3
  27. package/dist/benchmark/tasks/bulkPriceWizard.js.map +1 -0
  28. package/dist/cli.js +48 -22
  29. package/dist/cli.js.map +1 -1
  30. package/dist/config/credentials.js +5 -5
  31. package/dist/config/credentials.js.map +1 -1
  32. package/dist/config/profiles.js +108 -0
  33. package/dist/config/profiles.js.map +1 -0
  34. package/dist/config/schema.js.map +1 -1
  35. package/dist/deepseek/vision.js +4 -4
  36. package/dist/deepseek/vision.js.map +1 -1
  37. package/dist/deepseek/webSearch.js +1 -1
  38. package/dist/i18n/en.js +103 -0
  39. package/dist/i18n/en.js.map +1 -0
  40. package/dist/i18n/index.js +23 -0
  41. package/dist/i18n/index.js.map +1 -0
  42. package/dist/i18n/sr.js +101 -0
  43. package/dist/i18n/sr.js.map +1 -0
  44. package/dist/orchestrator/notify.js +38 -0
  45. package/dist/orchestrator/notify.js.map +1 -0
  46. package/dist/orchestrator/orchestrate.js +50 -19
  47. package/dist/orchestrator/orchestrate.js.map +1 -1
  48. package/dist/orchestrator/planner.js +10 -10
  49. package/dist/orchestrator/planner.js.map +1 -1
  50. package/dist/orchestrator/repoMap.js +1 -1
  51. package/dist/orchestrator/repoMap.js.map +1 -1
  52. package/dist/orchestrator/reviewer.js +20 -12
  53. package/dist/orchestrator/reviewer.js.map +1 -1
  54. package/dist/repl/commands.js +122 -37
  55. package/dist/repl/commands.js.map +1 -1
  56. package/dist/repl/system-prompt.js +13 -11
  57. package/dist/repl/system-prompt.js.map +1 -1
  58. package/dist/repl/wizard.js +17 -9
  59. package/dist/repl/wizard.js.map +1 -1
  60. package/package.json +1 -1
  61. 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
- const WORKER_SYSTEM_PROMPT = `Ti si worker u Tandem Mode orkestraciji. Dobijaš JEDAN konkretan task iz
6
- plana ispod, sa mapom repozitorijuma. Nemaš pristup razgovoru koji je
7
- proizveo plan — sve što ti treba je ovde. Izvrši task koristeći alate
8
- (read_file, list_dir, search, edit, git_diff, shell, view_image). Kad
9
- završiš, odgovori sažeto bez tool poziva da se petlja zaustavi.`;
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 (ceo, za kontekst):\n${JSON.stringify(plan)}\n\nMapa repozitorijuma:\n${repoMap}`;
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("\n📋 Planiram...\n");
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(`Plan: ${plan.summary}`);
19
- plan.tasks.forEach((t, i) => console.log(` ${i + 1}. [${t.id}] ${t.description}`));
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 t of plan.tasks) {
38
+ for (const planTask of plan.tasks) {
23
39
  if (opts.signal?.aborted)
24
40
  return;
25
- console.log(`\n🔧 Worker: ${t.description}\n`);
41
+ console.log(t("orchWorker", { description: planTask.description }));
26
42
  const messages = [
27
43
  { role: "system", content: stablePrefix },
28
- { role: "user", content: `Task: ${t.description}\nKriterijum prihvatanja: ${t.acceptanceCriteria}` },
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("\n🔍 Reviewer proverava...\n");
52
+ console.log(t("orchReviewer"));
37
53
  const diffResult = await gitDiffTool.execute({}, { cwd: opts.cwd, env: opts.env, usage: opts.usage });
38
- const { result: review, usage: reviewUsage } = await runReviewer(opts.env, task, plan, diffResult.output, opts.signal);
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(`✅ Reviewer odobrio. ${review.notes}\n`);
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(`⚠ Reviewer traži izmene (${loops}/${opts.maxReviewLoops}): ${review.notes}`);
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(`\n❌ Dostignut max_review_loops (${opts.maxReviewLoops}) bez odobrenja — proveri ručno.\n`);
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(`\n🔧 Worker (korekcija): ${correction.description}\n`);
85
+ console.log(t("orchWorkerCorrection", { description: correction.description }));
55
86
  const messages = [
56
87
  { role: "system", content: stablePrefix },
57
- { role: "user", content: `Korekcija od reviewera: ${correction.description}` },
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":"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"}
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 = `Ti si planner u Tandem Mode coding agentu. Dobijaš zadatak i mapu
4
- repozitorijuma. Podeli zadatak na male, nezavisne korake koje worker model
5
- (bez pristupa ovom razgovoru, samo alatima read_file/list_dir/search/edit/
6
- git_diff/shell/view_image) može samostalno izvršiti.
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
- Odgovori ISKLJUČIVO validnim JSON-om, bez markdown ograda, tačno ove šeme:
8
+ Respond ONLY with valid JSON, with no markdown fences, in exactly this schema:
9
9
  {
10
- "summary": "kratak opis pristupa",
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: `Zadatak:\n${task}\n\nMapa repozitorijuma:\n${repoMap}` },
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 nije vratio validan JSON (pokušaj ${attempt}):\n${result.content.slice(0, 500)}`);
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 ne prati očekivanu šemu (pokušaj ${attempt}):\n${result.content.slice(0, 500)}`);
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 nije uspeo posle više pokušaja.");
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,aAAa,IAAI,6BAA6B,OAAO,EAAE,EAAE;aACnF;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,6CAA6C,OAAO,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACjH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,SAAS,GAAG,IAAI,KAAK,CAAC,iDAAiD,OAAO,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACrH,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"}
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... (isečeno, ukupno ${files.length} fajlova)` : ""));
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,0BAA0B,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CACzF,CAAC;AACJ,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 = `Ti si reviewer u Tandem Mode coding agentu. Dobijaš originalni zadatak,
4
- plan koji je worker(i) izvršio, i git diff stvarnih izmena. Proveri da li
5
- diff zaista ispunjava zadatak i kriterijume prihvatanja iz plana, da nema
6
- nepotrebnih izmena, i da je kod razuman.
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
- Odgovori ISKLJUČIVO validnim JSON-om, bez markdown ograda, tačno ove šeme:
10
+ Respond ONLY with valid JSON, with no markdown fences, in exactly this schema:
9
11
  {
10
12
  "approved": true|false,
11
- "notes": "kratko obrazloženje",
13
+ "notes": "brief justification",
12
14
  "corrections": [{ "description": "...", "files": ["..."] }]
13
15
  }
14
- "corrections" je prazan niz ako je approved true.`;
16
+ "corrections" is an empty array if approved is true.`;
15
17
  const MAX_ATTEMPTS = 2;
16
- export async function runReviewer(env, originalTask, plan, gitDiff, signal) {
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: `Originalni zadatak:\n${originalTask}\n\nPlan:\n${JSON.stringify(plan, null, 2)}\n\nGit diff:\n${gitDiff.slice(0, 15000)}`,
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 nije vratio validan JSON (pokušaj ${attempt}):\n${response.content.slice(0, 500)}`);
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 ne prati očekivanu šemu (pokušaj ${attempt}):\n${response.content.slice(0, 500)}`);
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 nije uspeo posle više pokušaja.");
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;;;;;;;;;;;kDAWmB,CAAC;AAEnD,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAc,EACd,YAAoB,EACpB,IAAU,EACV,OAAe,EACf,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,wBAAwB,YAAY,cAAc,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,kBAAkB,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE;iBACpI;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,8CAA8C,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACpH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,SAAS,GAAG,IAAI,KAAK,CAAC,kDAAkD,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACxH,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"}
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"}
@@ -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
- const HELP_TEXT = `Komande:
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(HELP_TEXT);
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(`Trenutni model: ${state.model}`);
23
+ console.log(t("modelCurrent", { model: state.model }));
35
24
  }
36
25
  else {
37
- console.log("Upotreba: /model deepseek-v4-pro|deepseek-v4-flash");
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("Upotreba: /thinking on|off");
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(`Trenutni effort: ${state.reasoningEffort}`);
47
+ console.log(t("effortCurrent", { effort: state.reasoningEffort }));
59
48
  }
60
49
  else {
61
- console.log("Upotreba: /effort low|high|max");
50
+ console.log(t("usageLine", { syntax: "/effort low|high|max" }));
62
51
  }
63
52
  return "continue";
64
53
  case "status": {
65
- console.log(`Sesija: ${state.session.id}`);
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(`Budžet: ${state.budgetUsd !== undefined ? `$${state.budgetUsd.toFixed(2)}` : "nije postavljen"}`);
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(`Poruka u istoriji: ${state.messages.length}`);
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(`Pozivi: ${state.usage.callCount}`);
78
- console.log(`Tokeni: ${totals.promptTokens} in (${totals.promptCacheHitTokens} cached) / ${totals.completionTokens} out`);
79
- console.log(`Procenjena cena: $${cost.toFixed(4)}`);
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(`Ova sesija bi off-peak koštala: $${offPeakCost.toFixed(4)}`);
79
+ console.log(t("usageOffPeak", { cost: `$${offPeakCost.toFixed(4)}` }));
82
80
  }
83
81
  if (state.budgetUsd !== undefined) {
84
- console.log(`Budžet: $${state.budgetUsd.toFixed(2)} (iskorišćeno ${((cost / state.budgetUsd) * 100).toFixed(0)}%)`);
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(`Budžet postavljen na $${n.toFixed(2)}.`);
91
+ console.log(t("budgetSet", { amount: `$${n.toFixed(2)}` }));
94
92
  }
95
93
  else {
96
- console.log("Upotreba: /budget <iznos u USD>");
94
+ console.log(t("usageLine", { syntax: "/budget <USD>" }));
97
95
  }
98
96
  }
99
97
  else {
100
- console.log(state.budgetUsd !== undefined ? `Trenutni budžet: $${state.budgetUsd.toFixed(2)}` : "Budžet nije postavljen.");
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(`Nova sesija: ${state.session.id}`);
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(`Sesija forkovana: ${forked.id} (${state.messages.length} poruka preneto, originalna sesija netaknuta)`);
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 ? `Sesija '${arg}' nije pronađena.` : "Nema prethodne sesije u ovom direktorijumu.");
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(`Nastavljena sesija: ${target.id} (${state.messages.length} poruka)`);
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(`Nepoznata komanda: /${cmd}. Ukucaj /help.`);
220
+ console.log(t("unknownCommand", { cmd }));
136
221
  return "continue";
137
222
  }
138
223
  }