zames_pro 2.58.0 → 2.59.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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.59.0]
11
+
12
+ ### Added
13
+
14
+ - `/improve [id]` — a backlog-driven self-improvement loop. It picks the
15
+ next open item from `BACKLOG.md` (highest priority, or an explicit id),
16
+ runs the standard task loop on it (implement, typecheck/lint/test, mark
17
+ the item done, add a CHANGELOG entry) and leaves the changes uncommitted
18
+ for review. This is the reproducible hand-off path for a fresh agent.
19
+
10
20
  ## [2.58.0]
11
21
 
12
22
  ### Added
@@ -198,7 +208,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
198
208
  - Session banner warns when no saved DeepSeek session exists.
199
209
  - `--no-color` flag (explicit `NO_COLOR`).
200
210
 
201
- [Unreleased]: https://github.com/Viqto0r/zames_pro/compare/v2.58.0...HEAD
211
+ [Unreleased]: https://github.com/Viqto0r/zames_pro/compare/v2.59.0...HEAD
212
+ [2.59.0]: https://github.com/Viqto0r/zames_pro/compare/v2.58.0...v2.59.0
202
213
  [2.58.0]: https://github.com/Viqto0r/zames_pro/compare/v2.57.1...v2.58.0
203
214
  [2.57.1]: https://github.com/Viqto0r/zames_pro/compare/v2.57.0...v2.57.1
204
215
  [2.57.0]: https://github.com/Viqto0r/zames_pro/compare/v2.56.0...v2.57.0
package/README.md CHANGED
@@ -247,6 +247,10 @@ Codex CLI:
247
247
  instead of the whole list.
248
248
  - /review [focus] [--staged] — ask the agent to review uncommitted changes
249
249
  and report findings (no code changes).
250
+ - /improve [id] — self-improvement loop: take the next open item from
251
+ `BACKLOG.md` (or a specific id, e.g. `/improve B3`), implement it, run the
252
+ typecheck/lint/tests, mark it done and add a CHANGELOG entry. Nothing is
253
+ committed — the changes stay in the working tree for review.
250
254
  - /plan [on|off] — plan (read-only) mode. While it is on, the mutating tools
251
255
  (Write/Edit/MultiEdit/ApplyPatch/Bash, GitAdd/GitCommit/GitPush) are removed
252
256
  from the tool set, so the agent can investigate without touching the tree.
package/dist/commands.js CHANGED
@@ -707,6 +707,79 @@ export function mergeMessages(msgs) {
707
707
  const text = header + NL + NL + parts.join(NL + NL + '---' + NL + NL);
708
708
  return { text, attachments };
709
709
  }
710
+ // Parse BACKLOG.md into actionable items. Recognizes headings of the form
711
+ // `### A1. ...`, `### D7. ...` and marks an item done when its heading carries
712
+ // a `[x]` marker. Pure; unit-tested. We deliberately do NOT parse the whole
713
+ // markdown structure — only the headings the file contract promises.
714
+ export function parseBacklogItems(text) {
715
+ const out = [];
716
+ let priority = '';
717
+ for (const raw of String(text ?? '').split(NL)) {
718
+ const line = raw.trimEnd();
719
+ const p = /^##\s+(P[0-3])\b/.exec(line);
720
+ if (p) {
721
+ priority = p[1];
722
+ continue;
723
+ }
724
+ const m = /^###\s+([A-Z]\d+)\.\s+(.*)$/.exec(line);
725
+ if (!m)
726
+ continue;
727
+ const id = m[1];
728
+ const rest = m[2];
729
+ // The `[x]` rewrite in BACKLOG keeps the ORIGINAL heading under a
730
+ // <details> block wrapped in `~~`. That archived copy must not be counted
731
+ // as a second, open item — skip a heading whose text starts with `~~`.
732
+ if (rest.trim().startsWith('~~'))
733
+ continue;
734
+ const done = /^\[x\]/i.test(rest.trim());
735
+ // The title is the heading text with the [x] marker and the `~~` strike
736
+ // wrappers removed, so a done item still has a clean label.
737
+ const title = rest
738
+ .replace(/^\[x\]\s*/i, '')
739
+ .replace(/~~/g, '')
740
+ .trim();
741
+ out.push({ id, title, status: done ? 'done' : 'open', priority });
742
+ }
743
+ return out;
744
+ }
745
+ // The next item to work on: the highest-priority OPEN item, in file order.
746
+ // P0 first, then P1..P3. An explicit id overrides the priority search.
747
+ export function nextBacklogItem(items, wanted) {
748
+ const open = items.filter((i) => i.status === 'open');
749
+ if (wanted) {
750
+ const w = wanted.trim().toUpperCase();
751
+ return items.find((i) => i.id.toUpperCase() === w) || null;
752
+ }
753
+ if (!open.length)
754
+ return null;
755
+ const rank = (p) => {
756
+ const m = /P(\d)/.exec(p || '');
757
+ return m ? Number(m[1]) : 9;
758
+ };
759
+ return open.slice().sort((a, b) => rank(a.priority) - rank(b.priority))[0];
760
+ }
761
+ // The task text sent to the agent for a chosen item. Agent-facing English,
762
+ // like the rest of the loop. Keeps the working rules explicit so a fresh agent
763
+ // does not need any external context.
764
+ export function buildImprovePrompt(item) {
765
+ return ('Implement BACKLOG item ' +
766
+ item.id +
767
+ ' (priority ' +
768
+ (item.priority || '?') +
769
+ '): ' +
770
+ item.title +
771
+ NL +
772
+ NL +
773
+ 'Read the full item text in BACKLOG.md for the details and the rationale. ' +
774
+ 'Then: (1) implement it with the smallest reasonable change; ' +
775
+ '(2) run `npm run typecheck`, `npm run lint`, `npm run format:check` and ' +
776
+ '`npm test` and fix any failure; (3) mark the item done in BACKLOG.md by ' +
777
+ 'prefixing its heading with `[x]` and wrapping the original heading text ' +
778
+ 'in `~~ ~~`; (4) add a CHANGELOG entry if the change is user-visible; ' +
779
+ '(5) do NOT commit or push — leave the changes staged in the working tree ' +
780
+ 'for the operator to review. When done, call respond with a short report ' +
781
+ '(files changed, tests run, whether BACKLOG/CHANGELOG were updated).');
782
+ }
710
783
  // ---------- /review ----------
711
784
  export function buildReviewPrompt(focus, hasStaged = false) {
712
785
  const scope = hasStaged ? 'staged' : 'uncommitted';
package/dist/i18n.js CHANGED
@@ -239,6 +239,26 @@ export const CATALOG = {
239
239
  ru: '/review [focus] ревью незакоммиченных изменений',
240
240
  en: '/review [focus] review uncommitted changes',
241
241
  },
242
+ 'help.cmd.improve': {
243
+ ru: '/improve [id] взять следующий пункт BACKLOG и довести до тестов',
244
+ en: '/improve [id] take the next BACKLOG item and drive it to green tests',
245
+ },
246
+ 'improve.no_backlog': {
247
+ ru: 'BACKLOG.md не найден в рабочей директории.',
248
+ en: 'BACKLOG.md was not found in the working directory.',
249
+ },
250
+ 'improve.all_done': {
251
+ ru: 'Открытых пунктов в BACKLOG нет.',
252
+ en: 'There are no open BACKLOG items.',
253
+ },
254
+ 'improve.not_found': {
255
+ ru: 'Пункт {v} не найден в BACKLOG.',
256
+ en: 'Item {v} was not found in BACKLOG.',
257
+ },
258
+ 'improve.start': {
259
+ ru: 'Улучшение {id}: {title}',
260
+ en: 'Improving {id}: {title}',
261
+ },
242
262
  'help.cmd.compact': {
243
263
  ru: '/compact сжать историю и открыть новый чат с резюме',
244
264
  en: '/compact compact the history and open a new chat with the summary',
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { translate, normalizeLocale, localeDisplayName, isLocale, } from './i18n
18
18
  import { Transcript } from './transcript.js';
19
19
  import { UndoStore } from './undo.js';
20
20
  import { selfReview, selfDiff, selfApply, selfList } from './self-review.js';
21
- import { formatDiff, formatDiffStat, formatContextSources, diffGitArgs, parseTranscript, summarizeTranscript, renderCost, formatExport, defaultExportPath, renderDoctor, resolveExtraDir, buildReviewPrompt, formatDuration, formatRelativeTime, trimRestoredMessages, RESTORED_HISTORY_LIMIT, mergeMessages, hasQueuedJob, isSlashCommand, parseQueueCommand, parseLiveToggle, parseGoalCommand, isLiveConfigCommand, withGoal, formatQueueList, ctrlCEscalation, } from './commands.js';
21
+ import { formatDiff, formatDiffStat, formatContextSources, diffGitArgs, parseTranscript, summarizeTranscript, renderCost, formatExport, defaultExportPath, renderDoctor, resolveExtraDir, buildReviewPrompt, parseBacklogItems, nextBacklogItem, buildImprovePrompt, formatDuration, formatRelativeTime, trimRestoredMessages, RESTORED_HISTORY_LIMIT, mergeMessages, hasQueuedJob, isSlashCommand, parseQueueCommand, parseLiveToggle, parseGoalCommand, isLiveConfigCommand, withGoal, formatQueueList, ctrlCEscalation, } from './commands.js';
22
22
  import { performCompact } from './compact.js';
23
23
  import { Scheduler, parseInterval, formatInterval, formatJobLine, parseCron, } from './scheduler.js';
24
24
  import { renderMarkdown, setAnswerWidth } from './markdown.js';
@@ -388,6 +388,7 @@ ${theme.bold(t('help.sec.files'))}
388
388
  ${t('help.cmd.doctor')}
389
389
  ${t('help.cmd.add_dir')}
390
390
  ${t('help.cmd.review')}
391
+ ${t('help.cmd.improve')}
391
392
  ${t('help.cmd.debug_dom')}
392
393
  ${t('help.cmd.help')}
393
394
  ${t('help.cmd.exit')}
@@ -448,6 +449,7 @@ const SLASH_COMMANDS = [
448
449
  { name: '/doctor', key: 'help.cmd.doctor' },
449
450
  { name: '/add-dir', key: 'help.cmd.add_dir' },
450
451
  { name: '/review', key: 'help.cmd.review' },
452
+ { name: '/improve', key: 'help.cmd.improve' },
451
453
  { name: '/goal', key: 'help.cmd.goal' },
452
454
  { name: '/loop', key: 'help.cmd.loop' },
453
455
  { name: '/cron', key: 'help.cmd.cron' },
@@ -3336,6 +3338,58 @@ async function main() {
3336
3338
  }
3337
3339
  continue;
3338
3340
  }
3341
+ if (lower === '/improve' || lower.startsWith('/improve ')) {
3342
+ // Backlog-driven self-improvement: pick the next open BACKLOG item (or a
3343
+ // specific id) and run the standard task loop on it. The agent reads
3344
+ // BACKLOG.md itself for the details, so a FRESH agent needs no external
3345
+ // context — this is the reproducible hand-off path.
3346
+ const arg = trimmed.slice('/improve'.length).trim();
3347
+ let backlogText;
3348
+ try {
3349
+ backlogText = await fs.readFile(path.join(currentWorkdir, 'BACKLOG.md'), 'utf-8');
3350
+ }
3351
+ catch {
3352
+ console.error(theme.warn(t('improve.no_backlog')));
3353
+ continue;
3354
+ }
3355
+ const items = parseBacklogItems(backlogText);
3356
+ const item = nextBacklogItem(items, arg || undefined);
3357
+ if (!item) {
3358
+ console.log(theme.dim(arg ? t('improve.not_found', { v: arg }) : t('improve.all_done')));
3359
+ continue;
3360
+ }
3361
+ console.log(theme.system(t('improve.start', { id: item.id, title: item.title })));
3362
+ const improveTools = mod.createTools(currentWorkdir, {
3363
+ undo,
3364
+ todos: todoStore,
3365
+ readOnly: planMode,
3366
+ });
3367
+ if (mcpPool)
3368
+ improveTools.push(...mcpPool.tools);
3369
+ if (editor)
3370
+ editor.busy = true;
3371
+ try {
3372
+ await runTask(browser, improveTools, buildImprovePrompt(item), currentWorkdir, {
3373
+ transcript,
3374
+ freshChat: false,
3375
+ sendSystemPrompt: false,
3376
+ queue: pendingQueue,
3377
+ ui: editor || null,
3378
+ onChatReady: (chatId) => {
3379
+ if (chatId) {
3380
+ currentChatId = chatId;
3381
+ saveLastChat(chatId, currentWorkdir);
3382
+ }
3383
+ },
3384
+ getTokenUsage: () => browser.getLastTokenUsage(),
3385
+ });
3386
+ }
3387
+ finally {
3388
+ if (editor)
3389
+ editor.busy = false;
3390
+ }
3391
+ continue;
3392
+ }
3339
3393
  if (lower === '/review' || lower.startsWith('/review ')) {
3340
3394
  const rest = trimmed.slice('/review'.length).trim();
3341
3395
  const staged = rest.indexOf('--staged') !== -1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.58.0",
3
+ "version": "2.59.0",
4
4
  "description": "Terminal coding agent that drives chat.deepseek.com through Playwright: reads and edits files, runs commands, commits to git.",
5
5
  "type": "module",
6
6
  "bin": {