zames_pro 2.61.0 → 2.63.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/dist/config.js CHANGED
@@ -22,6 +22,12 @@ export const DEFAULTS = {
22
22
  enabled: true,
23
23
  maxBackups: 200,
24
24
  },
25
+ // BACKLOG B3: snapshot the whole working tree at the start of each task so
26
+ // /rewind can roll a risky task back in one step.
27
+ checkpoint: {
28
+ enabled: true,
29
+ maxBackups: 50,
30
+ },
25
31
  transcript: {
26
32
  enabled: true,
27
33
  dir: path.join(os.homedir(), '.zames', 'logs'),
@@ -162,6 +168,20 @@ export const CONFIG_SCHEMA = [
162
168
  labelKey: 'cfg.f.undo_maxBackups',
163
169
  groupKey: 'cfg.group.undo',
164
170
  },
171
+ {
172
+ path: 'checkpoint.enabled',
173
+ type: 'boolean',
174
+ labelKey: 'cfg.f.checkpoint_enabled',
175
+ groupKey: 'cfg.group.checkpoint',
176
+ },
177
+ {
178
+ path: 'checkpoint.maxBackups',
179
+ type: 'number',
180
+ min: 1,
181
+ max: 1000,
182
+ labelKey: 'cfg.f.checkpoint_maxBackups',
183
+ groupKey: 'cfg.group.checkpoint',
184
+ },
165
185
  {
166
186
  path: 'transcript.enabled',
167
187
  type: 'boolean',
package/dist/context.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs/promises';
2
+ import { statSync } from 'fs';
2
3
  import path from 'path';
3
4
  import os from 'os';
4
5
  // Built-in skills, always available unless a project/global skill with the
@@ -169,11 +170,21 @@ function parseFrontmatter(raw) {
169
170
  const userInvokable = typeof out['user-invokable'] === 'string'
170
171
  ? out['user-invokable'].toLowerCase() !== 'false'
171
172
  : undefined;
173
+ const argumentsList = typeof out['arguments'] === 'string'
174
+ ? out['arguments']
175
+ .split(/[,\s]+/)
176
+ .map((s) => s.trim())
177
+ .filter(Boolean)
178
+ : undefined;
172
179
  return {
173
180
  name: out.name,
174
181
  description: out.description,
175
182
  allowedTools,
176
183
  userInvokable,
184
+ argumentHint: typeof out['argument-hint'] === 'string'
185
+ ? out['argument-hint']
186
+ : undefined,
187
+ arguments: argumentsList,
177
188
  };
178
189
  }
179
190
  export function skillBody(raw) {
@@ -302,6 +313,8 @@ async function loadCommandsFromDir(dir) {
302
313
  description: (fm.description || '').replace(/\s+/g, ' ').trim(),
303
314
  path: full,
304
315
  body: skillBody(raw),
316
+ argumentHint: fm.argumentHint,
317
+ arguments: fm.arguments,
305
318
  });
306
319
  }
307
320
  return out;
@@ -376,13 +389,14 @@ async function loadMemoryChain(workdir) {
376
389
  }
377
390
  return out;
378
391
  }
379
- export async function loadProjectContext(workdir) {
392
+ export async function loadProjectContext(workdir, touchPaths = []) {
380
393
  const [agents, memory, skills, commands] = await Promise.all([
381
394
  loadAgentsChain(workdir).catch(() => []),
382
395
  loadMemoryChain(workdir).catch(() => []),
383
396
  loadSkills(workdir).catch(() => []),
384
397
  loadCommands(workdir).catch(() => []),
385
398
  ]);
399
+ const scopedAgents = await loadScopedAgents(workdir, touchPaths).catch(() => []);
386
400
  let used = 0;
387
401
  const withinBudget = (files) => {
388
402
  const kept = [];
@@ -396,8 +410,81 @@ export async function loadProjectContext(workdir) {
396
410
  };
397
411
  return {
398
412
  agents: withinBudget(agents),
413
+ // Scoped files are dropped silently when the total budget is already spent
414
+ // on the main chain: a nested AGENTS.md is an optimization, not a hard
415
+ // requirement, and must never push out a top-level instruction.
416
+ scopedAgents: withinBudget(scopedAgents),
399
417
  memory: withinBudget(memory),
400
418
  skills,
401
419
  commands,
402
420
  };
403
421
  }
422
+ // A directory is "touched" by a task when the task text mentions a path inside
423
+ // it (a `@ref`, a quoted path, a bare `src/foo.ts`). We look for the deepest
424
+ // existing directories named in the text and collect AGENTS.md/MEMORY.md from
425
+ // them and their ancestors below `workdir`. Implemented by scanning word tokens
426
+ // rather than a regex over the whole text so a long prompt stays cheap.
427
+ function touchedDirs(workdir, text) {
428
+ const root = path.resolve(workdir);
429
+ const out = new Set();
430
+ const words = String(text || '')
431
+ .split(/\s+/)
432
+ .map((w) => w.replace(/^[\('"\[]+/, '').replace(/[\),.;:'"\]]+$/, ''));
433
+ for (const w of words) {
434
+ if (!w || w.length > 300)
435
+ continue;
436
+ // Only path-looking tokens (contain a separator or a known file extension).
437
+ if (!/[\\/]/.test(w) && !/\.[A-Za-z0-9]{1,8}$/.test(w))
438
+ continue;
439
+ const abs = path.isAbsolute(w) ? w : path.resolve(root, w);
440
+ if (abs !== root && !abs.startsWith(root + path.sep))
441
+ continue;
442
+ // Walk from the deepest directory that actually exists up to the root.
443
+ let dir = abs;
444
+ let st = statSyncSafe(dir);
445
+ while (!st && dir !== root && dir.startsWith(root + path.sep)) {
446
+ dir = path.dirname(dir);
447
+ st = statSyncSafe(dir);
448
+ }
449
+ if (!st || !st.isDirectory())
450
+ continue;
451
+ let cur = dir;
452
+ while (cur.startsWith(root + path.sep)) {
453
+ out.add(cur);
454
+ cur = path.dirname(cur);
455
+ }
456
+ }
457
+ return [...out];
458
+ }
459
+ function statSyncSafe(p) {
460
+ try {
461
+ return statSync(p);
462
+ }
463
+ catch {
464
+ return null;
465
+ }
466
+ }
467
+ async function loadScopedAgents(workdir, touchPaths) {
468
+ if (!touchPaths.length)
469
+ return [];
470
+ const root = path.resolve(workdir);
471
+ // Collect every directory named by the task, deepest first, so the most
472
+ // specific instructions come first and win the budget.
473
+ const dirs = touchedDirs(root, touchPaths.join(' '));
474
+ dirs.sort((a, b) => b.length - a.length);
475
+ const out = [];
476
+ const seen = new Set();
477
+ for (const dir of dirs) {
478
+ for (const names of [AGENT_NAMES, MEMORY_NAMES]) {
479
+ const p = await findNamedFile(dir, names);
480
+ if (!p || seen.has(p))
481
+ continue;
482
+ const f = await readIfFile(p, MAX_FILE_BASE);
483
+ if (!f)
484
+ continue;
485
+ seen.add(p);
486
+ out.push(f);
487
+ }
488
+ }
489
+ return out;
490
+ }
package/dist/i18n.js CHANGED
@@ -194,6 +194,14 @@ export const CATALOG = {
194
194
  ru: '/undo-list список того, что можно откатить',
195
195
  en: '/undo-list list revertable changes',
196
196
  },
197
+ 'help.cmd.rewind': {
198
+ ru: '/rewind [n] откатить рабочее дерево к чекпойнту',
199
+ en: '/rewind [n] roll the working tree back to a checkpoint',
200
+ },
201
+ 'help.cmd.rewind_list': {
202
+ ru: '/rewind-list список чекпойнтов',
203
+ en: '/rewind-list list checkpoints',
204
+ },
197
205
  'help.cmd.transcript': {
198
206
  ru: '/transcript путь к файлу транскрипта',
199
207
  en: '/transcript transcript file path',
@@ -565,6 +573,7 @@ export const CATALOG = {
565
573
  'help.files': { ru: 'Файлы:', en: 'Files:' },
566
574
  'help.files.logs': { ru: 'Логи:', en: 'Logs:' },
567
575
  'help.files.undo': { ru: 'Undo:', en: 'Undo:' },
576
+ 'help.files.checkpoints': { ru: 'Чекпойнты:', en: 'Checkpoints:' },
568
577
  'help.files.sessions': { ru: 'Сессии:', en: 'Sessions:' },
569
578
  'help.files.profile': { ru: 'Профиль:', en: 'Profile:' },
570
579
  'help.files.snapshots': { ru: 'Снапшоты:', en: 'Snapshots:' },
@@ -649,6 +658,7 @@ export const CATALOG = {
649
658
  'status.headless': { ru: 'Headless: {v}', en: 'Headless: {v}' },
650
659
  'status.debug': { ru: 'Debug: {v}', en: 'Debug: {v}' },
651
660
  'status.undo': { ru: 'Undo: {v}', en: 'Undo: {v}' },
661
+ 'status.checkpoint': { ru: 'Чекпойнты: {v}', en: 'Checkpoints: {v}' },
652
662
  'status.transcript': { ru: 'Транскрипт: {v}', en: 'Transcript: {v}' },
653
663
  'status.locale': { ru: 'Язык: {v}', en: 'Language: {v}' },
654
664
  'status.goal': { ru: 'Цель сессии: {v}', en: 'Session goal: {v}' },
@@ -708,6 +718,26 @@ export const CATALOG = {
708
718
  ru: 'Неизвестная команда: {v}. Набери /help.',
709
719
  en: 'Unknown command: {v}. Type /help.',
710
720
  },
721
+ 'msg.missing_args': {
722
+ ru: 'Не хватает обязательных аргументов: {v}.',
723
+ en: 'Missing required arguments: {v}.',
724
+ },
725
+ 'perm.ask': {
726
+ ru: '⚠ Запрос подтверждения: {tool} ({reason})',
727
+ en: '⚠ Approval required: {tool} ({reason})',
728
+ },
729
+ 'perm.confirm': {
730
+ ru: 'Разрешить выполнение?',
731
+ en: 'Allow this call?',
732
+ },
733
+ 'perm.allowed': {
734
+ ru: '✔ Разрешено оператором.',
735
+ en: '✔ Allowed by operator.',
736
+ },
737
+ 'perm.denied': {
738
+ ru: '✖ Отклонено оператором — вызов не выполнен.',
739
+ en: '✖ Denied by operator — the call was not run.',
740
+ },
711
741
  'msg.dev_only': {
712
742
  ru: 'Команда {v} доступна только в dev-режиме (--dev или hotReload).',
713
743
  en: 'The {v} command is only available in dev mode (--dev or hotReload).',
@@ -749,6 +779,11 @@ export const CATALOG = {
749
779
  ru: '📎 Вложен файл {marker} {name} ({size}) — {path}',
750
780
  en: '📎 Attached file {marker} {name} ({size}) — {path}',
751
781
  },
782
+ // B5: shown when `@path` references in a task were inlined as file contents.
783
+ 'msg.at_refs': {
784
+ ru: '📄 Встроено файлов по @-ссылкам: {n} —',
785
+ en: '📄 Inlined @-referenced files: {n} —',
786
+ },
752
787
  'msg.clip_empty': {
753
788
  ru: '⚠ В буфере обмена нет картинки (проверено: {via}).',
754
789
  en: '⚠ No image found in the clipboard (tried: {via}).',
@@ -849,6 +884,11 @@ export const CATALOG = {
849
884
  },
850
885
  'cost.auto_compacts': { ru: ' авто-сжатий: {n}', en: ' auto-compacts: {n}' },
851
886
  'cost.by_tool': { ru: ' по инструментам:', en: ' by tool:' },
887
+ // T-D3: total time spent inside tools, then a top-N per-tool breakdown.
888
+ 'cost.by_time': {
889
+ ru: ' время в инструментах: {dur}; топ по времени:',
890
+ en: ' time in tools: {dur}; top by time:',
891
+ },
852
892
  'cost.duration': { ru: ' длительность: {dur}', en: ' duration: {dur}' },
853
893
  'cost.started': { ru: ' начало: {v}', en: ' started: {v}' },
854
894
  'cost.transcript': { ru: ' транскрипт: {v}', en: ' transcript: {v}' },
@@ -1293,6 +1333,46 @@ export const CATALOG = {
1293
1333
  ru: 'undo отключён в конфиге',
1294
1334
  en: 'undo is disabled in the config',
1295
1335
  },
1336
+ 'rewind.empty': { ru: 'Чекпойнтов нет.', en: 'No checkpoints.' },
1337
+ 'rewind.created': {
1338
+ ru: '📸 Чекпойнт: {v}',
1339
+ en: '📸 Checkpoint: {v}',
1340
+ },
1341
+ 'rewind.list_title': { ru: 'Чекпойнты:', en: 'Checkpoints:' },
1342
+ 'rewind.reverted': {
1343
+ ru: '↶ Откатили рабочее дерево к чекпойнту {v}',
1344
+ en: '↶ Rolled the working tree back to checkpoint {v}',
1345
+ },
1346
+ 'rewind.backup': {
1347
+ ru: 'Текущее состояние сохранено как чекпойнт {v}',
1348
+ en: 'Current state saved as checkpoint {v}',
1349
+ },
1350
+ 'rewind.failed': {
1351
+ ru: 'Не удалось откатить: {v}',
1352
+ en: 'Failed to rewind: {v}',
1353
+ },
1354
+ 'rewind.reason_not_found': {
1355
+ ru: 'чекпойнт не найден',
1356
+ en: 'checkpoint not found',
1357
+ },
1358
+ 'rewind.reason_archive_missing': {
1359
+ ru: 'архив чекпойнта отсутствует',
1360
+ en: 'checkpoint archive is missing',
1361
+ },
1362
+ 'rewind.reason_extract_failed': {
1363
+ ru: 'не удалось распаковать архив',
1364
+ en: 'failed to unpack the archive',
1365
+ },
1366
+ 'rewind.reason_disabled': {
1367
+ ru: 'чекпойнты отключены в конфиге',
1368
+ en: 'checkpoints are disabled in the config',
1369
+ },
1370
+ 'rewind.confirm': {
1371
+ ru: 'Откатить рабочее дерево к чекпойнту {v}? Текущее состояние будет сохранено как бэкап.',
1372
+ en: 'Roll the working tree back to checkpoint {v}? The current state will be saved as a backup.',
1373
+ },
1374
+ 'rewind.cancelled': { ru: 'Отменено.', en: 'Cancelled.' },
1375
+ 'rewind.label_backup': { ru: 'бэкап', en: 'backup' },
1296
1376
  'dom.saved': { ru: 'HTML сохранён: {v}', en: 'HTML saved: {v}' },
1297
1377
  'dom.selectors': { ru: 'Селекторы:', en: 'Selectors:' },
1298
1378
  'dom.save_error': {
@@ -1366,6 +1446,7 @@ export const CATALOG = {
1366
1446
  'cfg.group.ui': { ru: 'Интерфейс', en: 'Interface' },
1367
1447
  'cfg.group.agent': { ru: 'Агент', en: 'Agent' },
1368
1448
  'cfg.group.undo': { ru: 'Откат (undo)', en: 'Undo' },
1449
+ 'cfg.group.checkpoint': { ru: 'Чекпойнты', en: 'Checkpoints' },
1369
1450
  'cfg.group.transcript': { ru: 'Транскрипт', en: 'Transcript' },
1370
1451
  'cfg.group.browser': { ru: 'Браузер / DeepSeek', en: 'Browser / DeepSeek' },
1371
1452
  'cfg.f.ui_contextLimit': {
@@ -1401,6 +1482,14 @@ export const CATALOG = {
1401
1482
  ru: 'Сколько бэкапов хранить',
1402
1483
  en: 'Max undo backups kept',
1403
1484
  },
1485
+ 'cfg.f.checkpoint_enabled': {
1486
+ ru: 'Чекпойнт в начале каждой задачи (для /rewind)',
1487
+ en: 'Checkpoint at the start of each task (for /rewind)',
1488
+ },
1489
+ 'cfg.f.checkpoint_maxBackups': {
1490
+ ru: 'Сколько чекпойнтов хранить',
1491
+ en: 'How many checkpoints to keep',
1492
+ },
1404
1493
  'cfg.f.transcript_enabled': {
1405
1494
  ru: 'Вести транскрипт',
1406
1495
  en: 'Write transcript log',