zames_pro 2.35.0 → 2.36.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/dist/i18n.js CHANGED
@@ -491,6 +491,7 @@ const CATALOG = {
491
491
  'status.undo': { ru: 'Undo: {v}', en: 'Undo: {v}' },
492
492
  'status.transcript': { ru: 'Транскрипт: {v}', en: 'Transcript: {v}' },
493
493
  'status.locale': { ru: 'Язык: {v}', en: 'Language: {v}' },
494
+ 'status.goal': { ru: 'Цель сессии: {v}', en: 'Session goal: {v}' },
494
495
  // ---------- messages ----------
495
496
  'msg.working_dir': {
496
497
  ru: 'Рабочая директория: {v}',
package/dist/index.js CHANGED
@@ -1609,6 +1609,11 @@ async function main() {
1609
1609
  void handleConfigCommand(text);
1610
1610
  return;
1611
1611
  }
1612
+ // `/goal` is safe mid-run: it only sets a variable and writes a file.
1613
+ if (/^\/goal(\s|$)/i.test(text.trim())) {
1614
+ void handleGoal(text);
1615
+ return;
1616
+ }
1612
1617
  }
1613
1618
  const attachments = (items || []).map((a) => ({
1614
1619
  path: a.path,
@@ -1778,6 +1783,36 @@ async function main() {
1778
1783
  else
1779
1784
  console.log(theme.system(line));
1780
1785
  }
1786
+ // Handle a `/goal` command shared by the main loop and the live interception
1787
+ // (so a goal can be set even while the agent is busy — it only touches a
1788
+ // variable and a file, never the chat). Output goes above the input line
1789
+ // when the editor is active, otherwise to stdout.
1790
+ async function handleGoal(input) {
1791
+ const g = parseGoalCommand(input);
1792
+ if (!g)
1793
+ return;
1794
+ const emit = (line) => {
1795
+ if (editor)
1796
+ editor.printAbove(line);
1797
+ else
1798
+ console.log(line);
1799
+ };
1800
+ if (g.sub === 'clear') {
1801
+ sessionGoal = null;
1802
+ await saveGoal(currentWorkdir, null);
1803
+ emit(theme.system(t('goal.cleared')));
1804
+ }
1805
+ else if (g.sub === 'show') {
1806
+ emit(sessionGoal
1807
+ ? theme.system(t('goal.current', { goal: sessionGoal }))
1808
+ : theme.dim(t('goal.none')));
1809
+ }
1810
+ else {
1811
+ sessionGoal = g.goal;
1812
+ await saveGoal(currentWorkdir, g.goal);
1813
+ emit(theme.system(t('goal.set', { goal: g.goal })));
1814
+ }
1815
+ }
1781
1816
  function configSetRaw(field, raw) {
1782
1817
  const value = validateConfigValue(field, raw);
1783
1818
  writeConfigValue('home', field.path, raw);
@@ -2328,25 +2363,8 @@ async function main() {
2328
2363
  continue;
2329
2364
  }
2330
2365
  if (lower === '/goal' || lower.startsWith('/goal ')) {
2331
- const g = parseGoalCommand(trimmed);
2332
- if (g) {
2333
- if (g.sub === 'clear') {
2334
- sessionGoal = null;
2335
- await saveGoal(currentWorkdir, null);
2336
- console.log(theme.system(t('goal.cleared')));
2337
- }
2338
- else if (g.sub === 'show') {
2339
- console.log(sessionGoal
2340
- ? theme.system(t('goal.current', { goal: sessionGoal }))
2341
- : theme.dim(t('goal.none')));
2342
- }
2343
- else {
2344
- sessionGoal = g.goal;
2345
- await saveGoal(currentWorkdir, g.goal);
2346
- console.log(theme.system(t('goal.set', { goal: g.goal })));
2347
- }
2348
- continue;
2349
- }
2366
+ await handleGoal(trimmed);
2367
+ continue;
2350
2368
  }
2351
2369
  if (lower === '/loop' || lower.startsWith('/loop ')) {
2352
2370
  const rest = trimmed.slice('/loop'.length).trim();
@@ -2667,6 +2685,7 @@ async function main() {
2667
2685
  console.log(theme.system(t('status.transcript', {
2668
2686
  v: transcript.file || t('common.off'),
2669
2687
  })));
2688
+ console.log(theme.system(t('status.goal', { v: sessionGoal || t('common.none') })));
2670
2689
  console.log(theme.system(t('status.locale', { v: localeDisplayName(currentLocale) })));
2671
2690
  console.log(theme.system(t('status.mcp', {
2672
2691
  v: mcpPool ? String(mcpPool.status().toolCount) : t('common.none'),
package/dist/input.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { theme } from './theme.js';
2
2
  import { renderMarkdown } from './markdown.js';
3
- import { randomThinkingPhrase, stripEllipsis } from './spinner.js';
3
+ import { randomThinkingPhrase, stripEllipsis, DOTS, renderDots, } from './spinner.js';
4
4
  import { translate } from './i18n.js';
5
5
  import { AttachmentStore, parseImagePaste, looksLikeFilePath, } from './attachments.js';
6
6
  // A permanent input line at the bottom of the terminal + a status/output area above it.
@@ -75,10 +75,8 @@ export function expandPastes(pastes, text) {
75
75
  }
76
76
  return out;
77
77
  }
78
- // Dot animation: start from an empty string (0 dots), then grow.
79
- // We align the width to the maximum (3) so the hint doesn't shift.
80
- const DOTS = ['', '.', '..', '...'];
81
- const DOTS_PAD = ' ';
78
+ // Dot animation is shared with the ora spinner (see src/spinner.ts): DOTS and
79
+ // renderDots live there so both status lines animate identically.
82
80
  function safeJson(v) {
83
81
  try {
84
82
  return JSON.stringify(v);
@@ -921,9 +919,7 @@ export class LineEditor {
921
919
  this._animating = false;
922
920
  }
923
921
  _dots(n) {
924
- // The dots are the same color as the base and of fixed width — otherwise
925
- // the hint on the right "jumps" when the animation phase changes.
926
- return theme.brown(DOTS[n] + DOTS_PAD.slice(DOTS[n].length));
922
+ return renderDots(n);
927
923
  }
928
924
  // Status-line hint ("Esc — stop"), localized.
929
925
  _hint() {
package/dist/spinner.js CHANGED
@@ -15,6 +15,17 @@ export function randomThinkingPhrase(locale = 'ru') {
15
15
  export function stripEllipsis(phrase) {
16
16
  return phrase.replace(/[.…]+\s*$/, '');
17
17
  }
18
+ // Dot animation shared by BOTH UIs (the ora spinner and the LineEditor): start
19
+ // from an empty string (0 dots), then grow. The width is padded to the maximum
20
+ // (3 dots) so the trailing hint does not "jump" when the phase changes. Kept in
21
+ // one place so the two status lines animate identically.
22
+ export const DOTS = ['', '.', '..', '...'];
23
+ export const DOTS_PAD = ' ';
24
+ // The colored dot string for a phase (brown, matching the base text).
25
+ export function renderDots(n) {
26
+ const d = DOTS[n] ?? '';
27
+ return theme.brown(d + DOTS_PAD.slice(d.length));
28
+ }
18
29
  export function createSpinner(locale = 'ru') {
19
30
  let spinner = null;
20
31
  let dotTimer = null;
@@ -26,10 +37,6 @@ export function createSpinner(locale = 'ru') {
26
37
  // zero every second and look frozen on a single dot.
27
38
  let animBase = '';
28
39
  let animating = false;
29
- // Dot animation: start from an empty string (0 dots), then grow.
30
- // We align the width to the maximum (3) so the hint doesn't shift.
31
- const DOTS = ['', '.', '..', '...'];
32
- const DOTS_PAD = ' ';
33
40
  const start = (text) => {
34
41
  if (!spinner)
35
42
  spinner = ora(text).start();
@@ -59,10 +66,7 @@ export function createSpinner(locale = 'ru') {
59
66
  const startAnimated = (baseText, showHint = true) => {
60
67
  animBase = theme.brown(stripEllipsis(baseText));
61
68
  const hint = showHint ? HINT : '';
62
- // The dots are the same color as the base and of fixed width — otherwise
63
- // the hint on the right "jumps" when the animation phase changes.
64
- const dots = (n) => theme.brown(DOTS[n] + DOTS_PAD.slice(DOTS[n].length));
65
- start(animBase + dots(0) + hint);
69
+ start(animBase + renderDots(0) + hint);
66
70
  dotPhase = 0;
67
71
  if (dotTimer)
68
72
  clearInterval(dotTimer);
@@ -70,7 +74,7 @@ export function createSpinner(locale = 'ru') {
70
74
  if (!spinner)
71
75
  return;
72
76
  dotPhase = (dotPhase + 1) % DOTS.length;
73
- spinner.text = animBase + dots(dotPhase) + hint;
77
+ spinner.text = animBase + renderDots(dotPhase) + hint;
74
78
  }, 400);
75
79
  animating = true;
76
80
  if (dotTimer.unref)
@@ -113,10 +117,7 @@ export function createSpinner(locale = 'ru') {
113
117
  // so the countdown refreshes without resetting the dots.
114
118
  if (animating && dotTimer && spinner) {
115
119
  animBase = theme.brown(stripEllipsis(label));
116
- spinner.text =
117
- animBase +
118
- theme.brown(DOTS[dotPhase] + DOTS_PAD.slice(DOTS[dotPhase].length)) +
119
- HINT;
120
+ spinner.text = animBase + renderDots(dotPhase) + HINT;
120
121
  return;
121
122
  }
122
123
  startAnimated(label);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.35.0",
3
+ "version": "2.36.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {