zames_pro 2.95.5 → 2.96.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 +22 -0
- package/README.md +2 -2
- package/dist/agent-loop.js +33 -5
- package/dist/changelog.js +16 -4
- package/dist/commands.js +80 -5
- package/dist/compact.js +9 -2
- package/dist/dev-commands.js +13 -1
- package/dist/extraTools.js +20 -4
- package/dist/i18n/common.js +4 -0
- package/dist/i18n/help.js +24 -0
- package/dist/index.js +37 -0
- package/dist/loop-prompts.js +61 -0
- package/dist/release-policy.js +100 -0
- package/llms-full.txt +1 -1
- package/package.json +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.96.0] - 2026-11-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **loop:** dev-only /loop improvement cycles with a release policy
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **extraTools:** apply an insertion-only ApplyPatch hunk from its @@ header
|
|
19
|
+
- **dev:** keep the unparsed-call dump directory bounded
|
|
20
|
+
|
|
21
|
+
## [2.95.6] - 2026-11-05
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **compact:** carry the todo checklist and the /goal condition into the new chat
|
|
26
|
+
- **site:** a generated changelog page and one source for RU/EN layout strings
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **changelog:** keep a drafted group heading off the previous bullet
|
|
31
|
+
|
|
10
32
|
## [2.95.5] - 2026-11-05
|
|
11
33
|
|
|
12
34
|
### Added
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ and runs right after it, in the same chat. `Esc` aborts the current generation,
|
|
|
72
72
|
- [Why zames?](#why-zames) · [Requirements](#requirements) · [Signing in](#signing-in)
|
|
73
73
|
- [Switching providers (DeepSeek / Qwen / GLM)](#switching-providers-deepseek--qwen--glm)
|
|
74
74
|
- [Installation](#installation) · [Usage](#usage) · [FAQ](#faq)
|
|
75
|
-
- Reference: [Commands and tools](docs/commands.md) · [MCP](docs/mcp.md) · [Configuration and themes](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md)
|
|
75
|
+
- Reference: [Commands and tools](docs/commands.md) · [MCP](docs/mcp.md) · [Configuration and themes](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md) · [Changelog](https://viqto0r.github.io/zames_pro/changelog.html)
|
|
76
76
|
- [Keywords and alternatives](#keywords-and-alternatives) · [Links](#links) · [License](#license)
|
|
77
77
|
|
|
78
78
|
## How it works
|
|
@@ -534,7 +534,7 @@ zames # один раз войти, дальше прост
|
|
|
534
534
|
- [Почему zames?](#почему-zames) · [Требования](#требования) · [Вход в аккаунт](#вход-в-аккаунт)
|
|
535
535
|
- [Переключение провайдера (DeepSeek / Qwen / GLM)](#переключение-провайдера-deepseek--qwen--glm)
|
|
536
536
|
- [Установка](#установка) · [Использование](#использование) · [FAQ](#faq)
|
|
537
|
-
- Справочник: [Команды и инструменты](docs/commands-ru.md) · [MCP](docs/mcp-ru.md) · [Конфигурация и темы](docs/configuration-ru.md) · [Troubleshooting](docs/troubleshooting-ru.md)
|
|
537
|
+
- Справочник: [Команды и инструменты](docs/commands-ru.md) · [MCP](docs/mcp-ru.md) · [Конфигурация и темы](docs/configuration-ru.md) · [Troubleshooting](docs/troubleshooting-ru.md) · [История изменений](https://viqto0r.github.io/zames_pro/changelog-ru.html)
|
|
538
538
|
- [Ключевые слова и аналоги](#ключевые-слова-и-аналоги) · [Ссылки](#ссылки) · [Лицензия](#лицензия)
|
|
539
539
|
|
|
540
540
|
## Как это работает
|
package/dist/agent-loop.js
CHANGED
|
@@ -1392,9 +1392,38 @@ export function _setParserBindings(m) {
|
|
|
1392
1392
|
parseToolCall = m.parseToolCall;
|
|
1393
1393
|
responseLooksLikeToolCall = m.responseLooksLikeToolCall;
|
|
1394
1394
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1395
|
+
// The dev dump is a POST-MORTEM aid, not an archive. `npm run dev` turns it
|
|
1396
|
+
// on, and a /loop run is ONE process: the old per-process counter (stop after
|
|
1397
|
+
// 20, never delete) left every earlier file in place, so the project
|
|
1398
|
+
// `.zames/unparsed` grew without bound (93 files against a cap of 20). The
|
|
1399
|
+
// net-log dump already rotates for the same reason, so this directory keeps
|
|
1400
|
+
// the newest UNPARSED_DUMP_KEEP dumps.
|
|
1401
|
+
const UNPARSED_DUMP_KEEP = 20;
|
|
1397
1402
|
const UNPARSED_DUMP_BYTES = 64 * 1024;
|
|
1403
|
+
/** Our names start with an ISO stamp, so a plain sort is chronological. */
|
|
1404
|
+
const UNPARSED_DUMP_RE = /^\d{4}-\d{2}-\d{2}T[\d-]+Z-/;
|
|
1405
|
+
/**
|
|
1406
|
+
* Keeps only the newest `keep` dumps in `dir`. A foreign `.txt` is neither
|
|
1407
|
+
* deleted nor allowed to eat a slot of the keep window. Exported for the test:
|
|
1408
|
+
* this rotation is the only thing bounding the directory.
|
|
1409
|
+
*/
|
|
1410
|
+
export async function rotateUnparsedDumps(dir, keep = UNPARSED_DUMP_KEEP) {
|
|
1411
|
+
const fs = await import('node:fs/promises');
|
|
1412
|
+
const p = await import('node:path');
|
|
1413
|
+
const names = (await fs.readdir(dir).catch(() => [])).filter((n) => n.endsWith('.txt') && UNPARSED_DUMP_RE.test(n));
|
|
1414
|
+
if (names.length <= keep)
|
|
1415
|
+
return 0;
|
|
1416
|
+
names.sort();
|
|
1417
|
+
const stale = names.slice(0, names.length - keep);
|
|
1418
|
+
let removed = 0;
|
|
1419
|
+
for (const n of stale) {
|
|
1420
|
+
await fs
|
|
1421
|
+
.rm(p.join(dir, n), { force: true })
|
|
1422
|
+
.then(() => removed++)
|
|
1423
|
+
.catch(() => { });
|
|
1424
|
+
}
|
|
1425
|
+
return removed;
|
|
1426
|
+
}
|
|
1398
1427
|
/** Dev mode: save an unrecognized (but call-looking) answer for analysis. */
|
|
1399
1428
|
/**
|
|
1400
1429
|
* True when the raw answer is a bare `{"tool":"BashOutput","args":{"id":...}}`
|
|
@@ -1421,8 +1450,6 @@ export function isEmptyPollCall(raw) {
|
|
|
1421
1450
|
return !!a && typeof a.id === 'string';
|
|
1422
1451
|
}
|
|
1423
1452
|
export async function dumpUnparsedCall(raw, meta) {
|
|
1424
|
-
if (unparsedDumps >= UNPARSED_DUMP_MAX)
|
|
1425
|
-
return '';
|
|
1426
1453
|
const text = String(raw || '');
|
|
1427
1454
|
if (!text.trim())
|
|
1428
1455
|
return '';
|
|
@@ -1431,7 +1458,6 @@ export async function dumpUnparsedCall(raw, meta) {
|
|
|
1431
1458
|
const dir = p.join(meta.workdir || process.cwd(), '.zames', 'unparsed');
|
|
1432
1459
|
try {
|
|
1433
1460
|
await fs.mkdir(dir, { recursive: true });
|
|
1434
|
-
unparsedDumps++;
|
|
1435
1461
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1436
1462
|
const name = stamp + '-' + String(meta.where || 'x').replace(/[^a-z0-9_-]+/gi, '_');
|
|
1437
1463
|
const file = p.join(dir, name + '.txt');
|
|
@@ -1455,6 +1481,8 @@ export async function dumpUnparsedCall(raw, meta) {
|
|
|
1455
1481
|
NL +
|
|
1456
1482
|
NL;
|
|
1457
1483
|
await fs.writeFile(file, head + text.slice(0, UNPARSED_DUMP_BYTES), 'utf-8');
|
|
1484
|
+
// Rotate AFTER the write, so the dump just produced survives.
|
|
1485
|
+
await rotateUnparsedDumps(dir);
|
|
1458
1486
|
return file;
|
|
1459
1487
|
}
|
|
1460
1488
|
catch {
|
package/dist/changelog.js
CHANGED
|
@@ -97,13 +97,25 @@ export function syncUnreleased(changelog, body) {
|
|
|
97
97
|
// carries several groups (`## Added` … `## Fixed`) without a blank line
|
|
98
98
|
// between them, the next group used to stay glued to the last bullet
|
|
99
99
|
// (`- a### Fixed`) — it was not a heading at all, so the item vanished
|
|
100
|
-
// from the rendered changelog.
|
|
101
|
-
|
|
100
|
+
// from the rendered changelog. The lookahead is what keeps a CONTINUATION
|
|
101
|
+
// line of a multi-line bullet in place: gluing on `\n` as well joined the
|
|
102
|
+
// indent line to the next bullet and truncated the note.
|
|
103
|
+
// The draft's groups are still `## ` at this point (they become `### `
|
|
104
|
+
// below), and the FIRST group is already preceded by the blank line of
|
|
105
|
+
// the draft — so a group always starts a line, and this rule only has to
|
|
106
|
+
// punch in the missing blank line after a bullet.
|
|
107
|
+
.replace(/([^\n])(?=## )/g, '$1' + nl + nl)
|
|
102
108
|
.trim()
|
|
103
109
|
// The draft renders groups as `## Added` (top-level), but INSIDE the file
|
|
104
110
|
// a version's groups are `### Added` (a subsection of the version heading).
|
|
105
|
-
// `## [` (the next version) is left alone.
|
|
106
|
-
|
|
111
|
+
// `## [` (the next version) is left alone. The FIRST group follows the
|
|
112
|
+
// version heading directly (no bullet before it), so the blank-line rule
|
|
113
|
+
// above never fired for it — `## [Unreleased]` came out as a bare `#`
|
|
114
|
+
// followed by `# Added`. The heading owns a blank line, always.
|
|
115
|
+
.replace(/^## (?!\[)/gm, '### ')
|
|
116
|
+
// One blank line between the version heading and its first group, not
|
|
117
|
+
// the three the rules above can stack up.
|
|
118
|
+
.replace(/^\n+(?=### )/gm, '');
|
|
107
119
|
const section = cleanBody ? nl + nl + cleanBody + nl : nl;
|
|
108
120
|
return changelog.slice(0, afterAnchor) + section + tail;
|
|
109
121
|
}
|
package/dist/commands.js
CHANGED
|
@@ -512,20 +512,80 @@ export function isUsableCompactSummary(text) {
|
|
|
512
512
|
return false;
|
|
513
513
|
return true;
|
|
514
514
|
}
|
|
515
|
+
function todoMark(s) {
|
|
516
|
+
return s === 'completed' ? '[x]' : s === 'in_progress' ? '[~]' : '[ ]';
|
|
517
|
+
}
|
|
518
|
+
/** The TODO block carried into the new chat. Empty when there are no tasks. */
|
|
519
|
+
export function renderCompactTodos(todos = []) {
|
|
520
|
+
const items = Array.isArray(todos) ? todos : [];
|
|
521
|
+
if (!items.length)
|
|
522
|
+
return '';
|
|
523
|
+
const lines = items.map((x, i) => String(i + 1) +
|
|
524
|
+
'. ' +
|
|
525
|
+
todoMark(String(x.status)) +
|
|
526
|
+
' ' +
|
|
527
|
+
String(x.content));
|
|
528
|
+
const done = items.filter((x) => x.status === 'completed').length;
|
|
529
|
+
return ('## Task checklist (carried over by the agent, still in force)' +
|
|
530
|
+
NL +
|
|
531
|
+
'This list is SESSION state: it survived the compaction unchanged. ' +
|
|
532
|
+
'Do NOT recreate it from the summary — keep it up to date with ' +
|
|
533
|
+
'TodoWrite instead.' +
|
|
534
|
+
NL +
|
|
535
|
+
'Todo list (' +
|
|
536
|
+
String(done) +
|
|
537
|
+
'/' +
|
|
538
|
+
String(items.length) +
|
|
539
|
+
' done):' +
|
|
540
|
+
NL +
|
|
541
|
+
lines.join(NL));
|
|
542
|
+
}
|
|
543
|
+
/** The goal block carried into the new chat. Empty when no goal is set. */
|
|
544
|
+
export function renderCompactGoal(goal) {
|
|
545
|
+
const g = String(goal?.goal ?? '').trim();
|
|
546
|
+
if (!g)
|
|
547
|
+
return '';
|
|
548
|
+
const head = goal?.isCondition
|
|
549
|
+
? '## Completion goal (carried over, still in force)'
|
|
550
|
+
: '## Session goal (carried over, still in force)';
|
|
551
|
+
return (head +
|
|
552
|
+
NL +
|
|
553
|
+
'This goal is still active in this session; the compaction did not ' +
|
|
554
|
+
'cancel it. Keep working toward it.' +
|
|
555
|
+
NL +
|
|
556
|
+
NL +
|
|
557
|
+
g);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Re-pin the SESSION state after a compaction. The chat is new, but the todo
|
|
561
|
+
* store and the /goal file live in the process and on disk, so they survive —
|
|
562
|
+
* only the model's KNOWLEDGE of them does not. Sent as its own message right
|
|
563
|
+
* after the carryover, so it reads as current state, not as history.
|
|
564
|
+
*/
|
|
565
|
+
export function buildCompactToolResult(todos = [], goal = null) {
|
|
566
|
+
return [renderCompactTodos(todos), renderCompactGoal(goal)]
|
|
567
|
+
.filter(Boolean)
|
|
568
|
+
.join(NL + NL);
|
|
569
|
+
}
|
|
515
570
|
/**
|
|
516
571
|
* Wrap the model's summary into the text posted as the first message of the
|
|
517
572
|
* NEW chat. The system prompt is sent separately (sendSystemPrompt), so here
|
|
518
573
|
* we only mark the block as a carried-over context and add the operator's
|
|
519
|
-
* original
|
|
574
|
+
* original task. The todo checklist and the /goal condition are appended too:
|
|
575
|
+
* they are session state the model-written summary is not guaranteed to keep.
|
|
520
576
|
*/
|
|
521
|
-
export function buildCompactCarryover(summary, task) {
|
|
577
|
+
export function buildCompactCarryover(summary, task, todos = [], goal = null) {
|
|
522
578
|
const body = String(summary ?? '').trim();
|
|
523
|
-
const
|
|
579
|
+
const goalText = String(task ?? '').trim();
|
|
524
580
|
let out = 'Context carried over from a previous chat (compacted). ' +
|
|
525
581
|
'Treat it as the history of our work so far and continue from the current state.';
|
|
526
582
|
out += NL + NL + body;
|
|
527
|
-
if (
|
|
528
|
-
out += NL + NL + 'Original task: ' +
|
|
583
|
+
if (goalText) {
|
|
584
|
+
out += NL + NL + 'Original task: ' + goalText;
|
|
585
|
+
}
|
|
586
|
+
for (const extra of [renderCompactTodos(todos), renderCompactGoal(goal)]) {
|
|
587
|
+
if (extra)
|
|
588
|
+
out += NL + NL + extra;
|
|
529
589
|
}
|
|
530
590
|
return out;
|
|
531
591
|
}
|
|
@@ -1036,6 +1096,13 @@ export function buildImprovePrompt(item) {
|
|
|
1036
1096
|
export const DEV_ONLY_COMMANDS = [
|
|
1037
1097
|
'/improve',
|
|
1038
1098
|
'/backlog',
|
|
1099
|
+
// The /loop improvement cycles (src/loop-prompts.ts): they edit QUEUE.md,
|
|
1100
|
+
// read the dev dumps and push to master — a regular user has none of that.
|
|
1101
|
+
'/bughunt',
|
|
1102
|
+
'/textaudit',
|
|
1103
|
+
'/coverage',
|
|
1104
|
+
'/dumps',
|
|
1105
|
+
'/smoke',
|
|
1039
1106
|
'/self-review',
|
|
1040
1107
|
'/self-fix',
|
|
1041
1108
|
'/self-done',
|
|
@@ -1108,6 +1175,14 @@ export const SLASH_COMMANDS = [
|
|
|
1108
1175
|
{ name: '/review', key: 'help.cmd.review' },
|
|
1109
1176
|
{ name: '/improve', key: 'help.cmd.improve' },
|
|
1110
1177
|
{ name: '/backlog', key: 'help.cmd.backlog' },
|
|
1178
|
+
// The DEV-ONLY improvement cycles (src/loop-prompts.ts). printHelp filters
|
|
1179
|
+
// them out for a regular user (isDevOnlyCommand); the «/» list drops them
|
|
1180
|
+
// in buildSlashCommands, so a release build never advertises them.
|
|
1181
|
+
{ name: '/bughunt', key: 'help.cmd.bughunt' },
|
|
1182
|
+
{ name: '/textaudit', key: 'help.cmd.textaudit' },
|
|
1183
|
+
{ name: '/coverage', key: 'help.cmd.coverage' },
|
|
1184
|
+
{ name: '/dumps', key: 'help.cmd.dumps' },
|
|
1185
|
+
{ name: '/smoke', key: 'help.cmd.smoke' },
|
|
1111
1186
|
{ name: '/goal', key: 'help.cmd.goal' },
|
|
1112
1187
|
{ name: '/loop', key: 'help.cmd.loop' },
|
|
1113
1188
|
{ name: '/cron', key: 'help.cmd.cron' },
|
package/dist/compact.js
CHANGED
|
@@ -16,7 +16,7 @@ export const COMPACT_FALLBACK_LIMIT = 40;
|
|
|
16
16
|
* The caller owns the browser and the throttle; this function only sends.
|
|
17
17
|
*/
|
|
18
18
|
export async function performCompact(opts) {
|
|
19
|
-
const { browser, workdir, locale, task, transcript = null, ui = null, buildSystemPrompt, tools, answerTimeoutMs, fallbackLimit = COMPACT_FALLBACK_LIMIT, skipUiLock = false, quiet = false, selfImprovement = false, contextExhausted = false, } = opts;
|
|
19
|
+
const { browser, workdir, locale, task, transcript = null, ui = null, buildSystemPrompt, tools, answerTimeoutMs, fallbackLimit = COMPACT_FALLBACK_LIMIT, skipUiLock = false, quiet = false, selfImprovement = false, contextExhausted = false, todos = [], goal = null, } = opts;
|
|
20
20
|
const t = translate(locale);
|
|
21
21
|
// The active provider's LABEL for operator-facing text: performCompact is
|
|
22
22
|
// shared by every backend, so it cannot hardcode "DeepSeek". `providerArg`
|
|
@@ -189,7 +189,14 @@ export async function performCompact(opts) {
|
|
|
189
189
|
timeout: 60_000,
|
|
190
190
|
agent: false,
|
|
191
191
|
});
|
|
192
|
-
|
|
192
|
+
// The checklist and the goal are SESSION state (todoStore /
|
|
193
|
+
// .zames-goal), not chat state: they survive a compaction by themselves,
|
|
194
|
+
// but the model must be told they are still in force, otherwise it
|
|
195
|
+
// re-creates the tasks or treats the goal as gone because the summary
|
|
196
|
+
// does not mention it. They ride INSIDE the carryover, not as a separate
|
|
197
|
+
// turn: an extra send is another rate-limit pause AND every extra
|
|
198
|
+
// message of the new chat is another turn the model may answer.
|
|
199
|
+
await browser.ask(buildCompactCarryover(summary, task, todos, goal), {
|
|
193
200
|
timeout: 60_000,
|
|
194
201
|
agent: false,
|
|
195
202
|
});
|
package/dist/dev-commands.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
// N2 group (e): the DEV-ONLY self-development commands — /improve, /backlog,
|
|
2
|
-
// /self-review, /self-fix, /self-done, /self-list, /self-diff, /self-apply
|
|
2
|
+
// /self-review, /self-fix, /self-done, /self-list, /self-diff, /self-apply,
|
|
3
|
+
// plus the `/loop` improvement CYCLES (/bughunt, /textaudit, /coverage,
|
|
4
|
+
// /dumps, /smoke).
|
|
3
5
|
//
|
|
4
6
|
// They are registry entries with `devOnly: true`, so the guard that used to be
|
|
5
7
|
// a separate pre-check in the main loop is the SAME data the dispatch reads:
|
|
6
8
|
// a regular user gets `msg.dev_only`, dev mode runs the handler. The list here
|
|
7
9
|
// and `DEV_ONLY_COMMANDS` in src/commands.ts are kept in sync by a unit test.
|
|
10
|
+
import { LOOP_CYCLES } from './loop-prompts.js';
|
|
8
11
|
export function createDevCommands(deps) {
|
|
9
12
|
return [
|
|
10
13
|
{ name: 'improve', devOnly: true, handler: (c) => deps.improve(c.rest) },
|
|
11
14
|
{ name: 'backlog', devOnly: true, handler: (c) => deps.backlog(c.rest) },
|
|
15
|
+
// `backlog` has BOTH a dev command (record a note) and a cycle (implement
|
|
16
|
+
// an item): the command keeps its name, the cycle is reachable as
|
|
17
|
+
// `/backlog` only when no explicit entry exists, so the registry list is
|
|
18
|
+
// deduplicated by name below.
|
|
19
|
+
...LOOP_CYCLES.filter((id) => id !== 'backlog').map((id) => ({
|
|
20
|
+
name: id,
|
|
21
|
+
devOnly: true,
|
|
22
|
+
handler: (c) => deps.cycle(id, c.rest),
|
|
23
|
+
})),
|
|
12
24
|
{
|
|
13
25
|
name: 'self-review',
|
|
14
26
|
devOnly: true,
|
package/dist/extraTools.js
CHANGED
|
@@ -198,10 +198,26 @@ export function applyUpdateHunk(content, hunkLines) {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
if (!oldLines.length) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
// An INSERTION-ONLY hunk (`@@ -N,0 +N,M @@` + `+line`s) has nothing to
|
|
202
|
+
// match, so it was rejected outright — yet appending a line is a normal
|
|
203
|
+
// unified-diff form and the model uses it. Position it from the header's
|
|
204
|
+
// old-side start (a `-N,0` hunk inserts AFTER line N, hence the index N);
|
|
205
|
+
// without a usable header there is no anchor and we still refuse.
|
|
206
|
+
const hinted = block.oldStart !== null ? block.oldStart : -1;
|
|
207
|
+
if (hinted < 0) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
error: 'Insertion-only hunk needs a @@ -N,0 +N,M @@ header to know where to insert.',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const atInsert = Math.min(Math.max(hinted, searchFrom), fileLines.length);
|
|
214
|
+
fileLines = [
|
|
215
|
+
...fileLines.slice(0, atInsert),
|
|
216
|
+
...newLines,
|
|
217
|
+
...fileLines.slice(atInsert),
|
|
218
|
+
];
|
|
219
|
+
searchFrom = atInsert + newLines.length;
|
|
220
|
+
continue;
|
|
205
221
|
}
|
|
206
222
|
const at = locateBlock(fileLines, oldLines, block.oldStart, searchFrom);
|
|
207
223
|
if (at === -1) {
|
package/dist/i18n/common.js
CHANGED
|
@@ -169,6 +169,10 @@ export const commonMessages = {
|
|
|
169
169
|
ru: 'Команда {v} доступна только в dev-режиме (--dev или hotReload).',
|
|
170
170
|
en: 'The {v} command is only available in dev mode (--dev or hotReload).',
|
|
171
171
|
},
|
|
172
|
+
'cycle.unknown': {
|
|
173
|
+
ru: 'Неизвестный цикл {v}: ожидается /bughunt, /textaudit, /coverage, /dumps, /backlog или /smoke.',
|
|
174
|
+
en: 'Unknown cycle {v}: expected /bughunt, /textaudit, /coverage, /dumps, /backlog or /smoke.',
|
|
175
|
+
},
|
|
172
176
|
'msg.bye': { ru: 'Выход.', en: 'Bye.' },
|
|
173
177
|
'shell.empty': {
|
|
174
178
|
ru: 'После ! нужна команда, например !git status.',
|
package/dist/i18n/help.js
CHANGED
|
@@ -320,6 +320,30 @@ export const helpMessages = {
|
|
|
320
320
|
ru: 'стартовать в режиме плана (только чтение)',
|
|
321
321
|
en: 'start in plan mode (read-only)',
|
|
322
322
|
},
|
|
323
|
+
'help.sec.cycles': {
|
|
324
|
+
ru: 'Циклы улучшения (/loop, только dev)',
|
|
325
|
+
en: 'Improvement cycles (/loop, dev only)',
|
|
326
|
+
},
|
|
327
|
+
'help.cmd.bughunt': {
|
|
328
|
+
ru: '/bughunt [интервал] dev-only: /loop с охотой за багами по docs/BUGHUNT.md',
|
|
329
|
+
en: '/bughunt [interval] dev-only: /loop the bug hunt from docs/BUGHUNT.md',
|
|
330
|
+
},
|
|
331
|
+
'help.cmd.textaudit': {
|
|
332
|
+
ru: '/textaudit [интервал] dev-only: /loop с аудитом текстов по docs/TEXT-AUDIT.md',
|
|
333
|
+
en: '/textaudit [interval] dev-only: /loop the text audit from docs/TEXT-AUDIT.md',
|
|
334
|
+
},
|
|
335
|
+
'help.cmd.coverage': {
|
|
336
|
+
ru: '/coverage [интервал] dev-only: /loop с заполнением пробелов покрытия тестами',
|
|
337
|
+
en: '/coverage [interval] dev-only: /loop filling test-coverage gaps',
|
|
338
|
+
},
|
|
339
|
+
'help.cmd.dumps': {
|
|
340
|
+
ru: '/dumps [интервал] dev-only: /loop с разбором дампов непойманных tool-вызовов',
|
|
341
|
+
en: '/dumps [interval] dev-only: /loop triaging the unparsed-call dumps',
|
|
342
|
+
},
|
|
343
|
+
'help.cmd.smoke': {
|
|
344
|
+
ru: '/smoke [интервал] dev-only: /loop с живым smoke-прогоном провайдера',
|
|
345
|
+
en: '/smoke [interval] dev-only: /loop a live provider smoke run',
|
|
346
|
+
},
|
|
323
347
|
'help.cmd.loop': {
|
|
324
348
|
ru: '/loop <интервал> <задача> повторять задачу (s/m/h; без единицы — секунды; первый запуск сразу; минимум 15с)',
|
|
325
349
|
en: '/loop <interval> <task> repeat a task (s/m/h; no unit means seconds; the first run is immediate; minimum 15s)',
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { createSessionCommands } from './session-commands.js';
|
|
|
17
17
|
import { createTreeCommands } from './tree-commands.js';
|
|
18
18
|
import { createSchedulerCommands } from './scheduler-commands.js';
|
|
19
19
|
import { createDevCommands } from './dev-commands.js';
|
|
20
|
+
import { buildCycleLoopLine } from './loop-prompts.js';
|
|
20
21
|
import { shouldRunWizard, chooseLanguage, chooseProvider, providerNote, } from './first-run.js';
|
|
21
22
|
import { DeepSeekBrowser, headlessUACacheReady } from './browser.js';
|
|
22
23
|
import { QwenBrowser } from './qwen-browser.js';
|
|
@@ -481,6 +482,18 @@ function printHelp() {
|
|
|
481
482
|
t('help.self.diff') +
|
|
482
483
|
'\n ' +
|
|
483
484
|
t('help.self.apply') +
|
|
485
|
+
'\n\n' +
|
|
486
|
+
theme.bold(t('help.sec.cycles')) +
|
|
487
|
+
'\n ' +
|
|
488
|
+
t('help.cmd.bughunt') +
|
|
489
|
+
'\n ' +
|
|
490
|
+
t('help.cmd.textaudit') +
|
|
491
|
+
'\n ' +
|
|
492
|
+
t('help.cmd.coverage') +
|
|
493
|
+
'\n ' +
|
|
494
|
+
t('help.cmd.dumps') +
|
|
495
|
+
'\n ' +
|
|
496
|
+
t('help.cmd.smoke') +
|
|
484
497
|
'\n\n'
|
|
485
498
|
: ' ' +
|
|
486
499
|
t('help.cmd.debug_dom') +
|
|
@@ -1302,6 +1315,8 @@ async function main() {
|
|
|
1302
1315
|
skipUiLock: true,
|
|
1303
1316
|
selfImprovement: devMode,
|
|
1304
1317
|
contextExhausted: true,
|
|
1318
|
+
todos: getTodos(todoStore),
|
|
1319
|
+
goal: sessionGoal ? { goal: sessionGoal, isCondition: true } : null,
|
|
1305
1320
|
});
|
|
1306
1321
|
if (!res.ok)
|
|
1307
1322
|
return false;
|
|
@@ -3125,6 +3140,8 @@ async function main() {
|
|
|
3125
3140
|
answerTimeoutMs: providerAnswerTimeoutMs(),
|
|
3126
3141
|
fallbackLimit: COMPACT_FALLBACK_LIMIT,
|
|
3127
3142
|
selfImprovement: devMode,
|
|
3143
|
+
todos: getTodos(todoStore),
|
|
3144
|
+
goal: sessionGoal ? { goal: sessionGoal, isCondition: true } : null,
|
|
3128
3145
|
});
|
|
3129
3146
|
if (res.ok) {
|
|
3130
3147
|
currentChatId = res.chatId;
|
|
@@ -3134,6 +3151,21 @@ async function main() {
|
|
|
3134
3151
|
sendSystemPromptNext = false;
|
|
3135
3152
|
}
|
|
3136
3153
|
};
|
|
3154
|
+
/**
|
|
3155
|
+
* A dev-only /loop improvement cycle (`/bughunt 10m`, `/coverage`): build
|
|
3156
|
+
* the ONE `/loop <interval> <task>` line and arm the scheduler through the
|
|
3157
|
+
* SAME path `/loop` uses, so the interval/job bookkeeping has one owner.
|
|
3158
|
+
*/
|
|
3159
|
+
const cycleCommand = (id, rest) => {
|
|
3160
|
+
const arg = rest.trim();
|
|
3161
|
+
const interval = arg.split(/\s+/)[0] || '';
|
|
3162
|
+
const line = buildCycleLoopLine(id, interval);
|
|
3163
|
+
if (!line) {
|
|
3164
|
+
console.error(theme.error(t('cycle.unknown', { v: '/' + id })));
|
|
3165
|
+
return;
|
|
3166
|
+
}
|
|
3167
|
+
loopCommand(line.slice('/loop '.length));
|
|
3168
|
+
};
|
|
3137
3169
|
/** /goal: set/show/clear the condition. Returns whether it was SET. */
|
|
3138
3170
|
const goalCommand = async (line) => {
|
|
3139
3171
|
const sub = await handleGoal(line);
|
|
@@ -3599,6 +3631,7 @@ async function main() {
|
|
|
3599
3631
|
selfList: () => selfListCommand(''),
|
|
3600
3632
|
selfDiff: (name) => selfDiffCommand(name),
|
|
3601
3633
|
selfApply: (name) => selfApplyCommand(name),
|
|
3634
|
+
cycle: (id, rest) => cycleCommand(id, rest),
|
|
3602
3635
|
});
|
|
3603
3636
|
if (await dispatchCommand(devCommands, trimmed, {
|
|
3604
3637
|
print: (line) => console.log(line),
|
|
@@ -4730,6 +4763,10 @@ async function main() {
|
|
|
4730
4763
|
quiet: true,
|
|
4731
4764
|
skipUiLock: true,
|
|
4732
4765
|
selfImprovement: devMode,
|
|
4766
|
+
todos: getTodos(todoStore),
|
|
4767
|
+
goal: sessionGoal
|
|
4768
|
+
? { goal: sessionGoal, isCondition: true }
|
|
4769
|
+
: null,
|
|
4733
4770
|
});
|
|
4734
4771
|
if (res.ok) {
|
|
4735
4772
|
currentChatId = res.chatId;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The DEV-ONLY `/loop` cycles of zames_pro itself: bughunt, textaudit,
|
|
2
|
+
// coverage, dumps, backlog, smoke. Each command expands into ONE `/loop`
|
|
3
|
+
// line, so the interval lives in the scheduler (a fired tick is queued and
|
|
4
|
+
// skipped while the same job is still queued) and the operator never re-pastes
|
|
5
|
+
// a long prompt.
|
|
6
|
+
//
|
|
7
|
+
// WHY dev-only: these cycles edit BACKLOG.md/QUEUE.md, read the dev dumps and
|
|
8
|
+
// push to `master` — a regular npm user has none of that, and `/bughunt` on an
|
|
9
|
+
// arbitrary project would hunt bugs in THEIR code with OUR protocol. The list
|
|
10
|
+
// here and `DEV_ONLY_COMMANDS` in src/commands.ts are kept in sync by a test.
|
|
11
|
+
export const LOOP_CYCLES = [
|
|
12
|
+
'bughunt',
|
|
13
|
+
'textaudit',
|
|
14
|
+
'coverage',
|
|
15
|
+
'dumps',
|
|
16
|
+
'backlog',
|
|
17
|
+
'smoke',
|
|
18
|
+
];
|
|
19
|
+
/** Default period of a cycle: 5m. The floor is MIN_LOOP_MS (15s). */
|
|
20
|
+
export const DEFAULT_LOOP_INTERVAL = '5m';
|
|
21
|
+
const RULES = 'run the gates (typecheck, test, lint, format:check, build), append a step entry to QUEUE.md with the finding + gates + commit SHA, update the journal list, commit (see the subject rule below) and push to master (git push origin master), then check the release policy: git describe --tags --abbrev=0, git log <tag>..HEAD --pretty=format:%s and src/release-policy.ts decideRelease() — if it reports a READY patch/minor, run `npm run release <bump>`, push the branch AND the tag (git push && git push origin vX.Y.Z), and record the new version in QUEUE.md; a major or a BREAKING change is NEVER released by the loop, only reported. Do NOT bump the version by hand.';
|
|
22
|
+
const BODIES = {
|
|
23
|
+
bughunt: 'Bug hunt, one step. Read docs/BUGHUNT.md and follow it exactly: pick the next NOT-yet-covered module from its scope table (skip the ones under "Покрытые области" in QUEUE.md unless their code changed after the covering date), find ONE real reproducible defect, write the FAILING test first (RED), fix the root cause (GREEN), ' +
|
|
24
|
+
RULES +
|
|
25
|
+
' If the module is clean, add it to "Покрытые области", write a `чисто` entry with what you verified, and commit nothing. Stop the loop with /jobs clear when every module in the scope table is covered and no covered module has changed since its covering date.',
|
|
26
|
+
textaudit: 'Text audit, one step. Read docs/TEXT-AUDIT.md and follow it exactly: pick the next NOT-yet-covered file from its scope table (skip the ones listed under "Covered areas" in QUEUE.md), audit it against the code as the source of truth, fix ONE defect family in the source file(s) plus both languages, regenerate the generated files (npm run build:readme && npm run build:llms), ' +
|
|
27
|
+
RULES +
|
|
28
|
+
' If the file is clean, add it to "Covered areas", write a `чисто` entry with what you verified, and commit nothing. Stop the loop with /jobs clear when every file in the scope table is covered.',
|
|
29
|
+
coverage: 'Coverage step, one step: run `npm run coverage:gate`, find the module or branch whose line coverage is lowest or has NO test file at all (list `src/*.ts` without a matching `test/*.test.ts`), pick ONE of them, and write pure unit tests that pin the CURRENT behaviour — do not change the source unless a test uncovers a real bug (then switch to a fix: commit and cite docs/BUGHUNT.md). ' +
|
|
30
|
+
RULES +
|
|
31
|
+
' Stop the loop with /jobs clear when every source module has a test file and no gate module is below its floor.',
|
|
32
|
+
dumps: 'Unparsed-dump step, one step: list `.zames/unparsed/*.txt` (dev dumps of answers that looked like a tool call but were NOT parsed) and `~/.zames/net-log` if ZAMES_NET_DEBUG was on; take the NEWEST dump not yet analysed in QUEUE.md, classify WHY parseToolCall (src/answer-parse.ts) and the XML/DSML path (src/xml-toolcall.ts) missed it, then either extend the parser with a failing test FIRST (test/answer-parse*.test.ts or test/xml-toolcall*.test.ts) or record in QUEUE.md that the answer is genuinely unparseable (a reasoning paragraph, not a call) and why. ' +
|
|
33
|
+
RULES +
|
|
34
|
+
' Do NOT delete a dump you have not classified. Stop the loop with /jobs clear when every dump in the directory is classified.',
|
|
35
|
+
backlog: 'Backlog step, one item: read BACKLOG.md, take the highest-priority item that is NOT archived in a <details> block and is small enough for one step, implement it (a failing test first when the item is testable), ' +
|
|
36
|
+
RULES +
|
|
37
|
+
" Move the finished item under the archived <details> block with the /backlog helpers. Skip an item that needs the operator's decision and note WHY in QUEUE.md. Stop the loop with /jobs clear when BACKLOG.md has no open item left.",
|
|
38
|
+
smoke: "Live smoke step, one step: run the matching smoke script (`npm run self-smoke` for deepseek, `npm run qwen-smoke`, `npm run glm-smoke -- --headed`); it launches an ISOLATED zames instance (throwaway HOME) and must NEVER touch the operator's ~/.zames/profile or another agent's browser. If a scenario FAILS, read its output, reproduce the failing unit-level behaviour with a focused test if possible, fix the root cause (src/browser*.ts / src/qwen-*.ts / src/glm-*.ts / src/*-net-capture.ts), rerun the smoke and " +
|
|
39
|
+
RULES +
|
|
40
|
+
' If every scenario PASSES, write a `чисто` entry with the exit code and commit nothing. Stop the loop with /jobs clear after 3 consecutive PASS runs with no new failure.',
|
|
41
|
+
};
|
|
42
|
+
/** True when `id` is one of the cycles. */
|
|
43
|
+
export function isLoopCycle(id) {
|
|
44
|
+
return LOOP_CYCLES.includes(String(id ?? '').toLowerCase());
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the ONE line a cycle sends to `/loop`. `interval` is the FIRST token
|
|
48
|
+
* (the scheduler parses it), so a bare `/bughunt 10m` retunes the period.
|
|
49
|
+
* Returns null for an unknown cycle.
|
|
50
|
+
*/
|
|
51
|
+
export function buildCycleLoopLine(id, interval = DEFAULT_LOOP_INTERVAL) {
|
|
52
|
+
const cycle = String(id ?? '').toLowerCase();
|
|
53
|
+
if (!isLoopCycle(cycle))
|
|
54
|
+
return null;
|
|
55
|
+
// Collapse whitespace: a newline would split the interval off the command.
|
|
56
|
+
const every = String(interval ?? '')
|
|
57
|
+
.trim()
|
|
58
|
+
.replace(/\s+/g, '');
|
|
59
|
+
const period = every || DEFAULT_LOOP_INTERVAL;
|
|
60
|
+
return '/loop ' + period + ' ' + BODIES[cycle];
|
|
61
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// WHEN a /loop improvement cycle may cut a release. PURE and unit-tested: the
|
|
2
|
+
// decision is made from the commits since the last tag, and it is the one rule
|
|
3
|
+
// that keeps an unattended run from either spamming npm with a patch per fix
|
|
4
|
+
// or sitting on a pile of unreleased work forever.
|
|
5
|
+
//
|
|
6
|
+
// The policy is deliberately COARSE. DEVELOPING.md says several fixes land as
|
|
7
|
+
// ONE patch release (a 142-patch 2.82.x branch made the CHANGELOG and npm
|
|
8
|
+
// unreadable), so a lone fix is NOT enough: at least MIN_COMMITS user-visible
|
|
9
|
+
// changes must have accumulated. A feature or a breaking change is the
|
|
10
|
+
// operator's own signal and releases alone.
|
|
11
|
+
//
|
|
12
|
+
// docs/chore/test/ci/build commits do not count (they never appear in a
|
|
13
|
+
// user-facing changelog), but they also must not BLOCK a release: they are
|
|
14
|
+
// simply ignored.
|
|
15
|
+
import { parseConventionalCommit, groupCommits, } from './changelog.js';
|
|
16
|
+
/** User-visible commits (feat/fix/perf/refactor/revert/remove) that release together. */
|
|
17
|
+
export const MIN_COMMITS = 5;
|
|
18
|
+
/**
|
|
19
|
+
* Decide whether the commits in `subjects` (git log --pretty=format:%s since
|
|
20
|
+
* the last tag) justify a release, and which bump it is.
|
|
21
|
+
*
|
|
22
|
+
* - a `!` marker -> major (alone);
|
|
23
|
+
* - any `feat:` -> minor (alone);
|
|
24
|
+
* - otherwise at least MIN_COMMITS visible commits -> patch;
|
|
25
|
+
* - anything less -> `bump: null` (wait; the next step adds to the pile).
|
|
26
|
+
*/
|
|
27
|
+
export function decideRelease(subjects, opts = {}) {
|
|
28
|
+
const min = opts.minCommits ?? MIN_COMMITS;
|
|
29
|
+
const parsed = (subjects || [])
|
|
30
|
+
.map((s) => parseConventionalCommit(String(s ?? '')))
|
|
31
|
+
.filter((c) => c !== null);
|
|
32
|
+
// A chore(release) commit is the release itself; it must not be counted as
|
|
33
|
+
// work and must not trigger a second release.
|
|
34
|
+
const work = parsed.filter((c) => !(c.type === 'chore' && c.scope === 'release'));
|
|
35
|
+
const breaking = work.filter((c) => c.breaking);
|
|
36
|
+
if (breaking.length) {
|
|
37
|
+
return {
|
|
38
|
+
bump: 'major',
|
|
39
|
+
counted: work.length,
|
|
40
|
+
kinds: kindsOf(work),
|
|
41
|
+
blocked: breakingNote(breaking),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const feats = work.filter((c) => c.type === 'feat');
|
|
45
|
+
if (feats.length) {
|
|
46
|
+
return { bump: 'minor', counted: work.length, kinds: kindsOf(work) };
|
|
47
|
+
}
|
|
48
|
+
// The changelog groups are the source of truth for "user-visible": a type
|
|
49
|
+
// that renders NO line (docs/test/chore/ci/build) is not work worth a
|
|
50
|
+
// release on its own.
|
|
51
|
+
const groups = groupCommits(work);
|
|
52
|
+
const visible = groups.Added.length +
|
|
53
|
+
groups.Fixed.length +
|
|
54
|
+
groups.Changed.length +
|
|
55
|
+
groups.Removed.length;
|
|
56
|
+
if (visible < min) {
|
|
57
|
+
return { bump: null, counted: visible, kinds: kindsOf(work) };
|
|
58
|
+
}
|
|
59
|
+
return { bump: 'patch', counted: visible, kinds: kindsOf(work) };
|
|
60
|
+
}
|
|
61
|
+
function kindsOf(list) {
|
|
62
|
+
const seen = [];
|
|
63
|
+
for (const c of list)
|
|
64
|
+
if (!seen.includes(c.type))
|
|
65
|
+
seen.push(c.type);
|
|
66
|
+
return seen;
|
|
67
|
+
}
|
|
68
|
+
function breakingNote(list) {
|
|
69
|
+
const first = list[0];
|
|
70
|
+
return ('a BREAKING change (' +
|
|
71
|
+
(first.scope ? first.scope + '): ' : '') +
|
|
72
|
+
first.description +
|
|
73
|
+
') must be released by the operator, not by an unattended /loop');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A `/loop` must NEVER publish. `npm run release` only commits and tags, but
|
|
77
|
+
* the tag is what triggers the npm publish workflow — so a cycle that tagged
|
|
78
|
+
* would publish on its own.
|
|
79
|
+
*/
|
|
80
|
+
export const LOOP_RELEASE_BLOCKED = 'an unattended /loop must not tag: a tag push publishes to npm. ' +
|
|
81
|
+
'Report the ready bump and let the operator run `npm run release <bump>`.';
|
|
82
|
+
/**
|
|
83
|
+
* The one line a cycle prints (and writes into QUEUE.md) when the pile is big
|
|
84
|
+
* enough for a release.
|
|
85
|
+
*/
|
|
86
|
+
export function releaseHint(d, version) {
|
|
87
|
+
if (d.blocked)
|
|
88
|
+
return 'release blocked: ' + d.blocked;
|
|
89
|
+
if (!d.bump) {
|
|
90
|
+
const left = Math.max(0, MIN_COMMITS - d.counted);
|
|
91
|
+
return ('release: not yet (' +
|
|
92
|
+
d.counted +
|
|
93
|
+
'/' +
|
|
94
|
+
MIN_COMMITS +
|
|
95
|
+
' user-visible commits; ' +
|
|
96
|
+
left +
|
|
97
|
+
' more)');
|
|
98
|
+
}
|
|
99
|
+
return 'release READY: npm run release ' + d.bump + ' (from ' + version + ')';
|
|
100
|
+
}
|
package/llms-full.txt
CHANGED
|
@@ -57,7 +57,7 @@ and runs right after it, in the same chat. `Esc` aborts the current generation,
|
|
|
57
57
|
- [Why zames?](#why-zames) · [Requirements](#requirements) · [Signing in](#signing-in)
|
|
58
58
|
- [Switching providers (DeepSeek / Qwen / GLM)](#switching-providers-deepseek--qwen--glm)
|
|
59
59
|
- [Installation](#installation) · [Usage](#usage) · [FAQ](#faq)
|
|
60
|
-
- Reference: [Commands and tools](docs/commands.md) · [MCP](docs/mcp.md) · [Configuration and themes](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md)
|
|
60
|
+
- Reference: [Commands and tools](docs/commands.md) · [MCP](docs/mcp.md) · [Configuration and themes](docs/configuration.md) · [Troubleshooting](docs/troubleshooting.md) · [Changelog](https://viqto0r.github.io/zames_pro/changelog.html)
|
|
61
61
|
- [Keywords and alternatives](#keywords-and-alternatives) · [Links](#links) · [License](#license)
|
|
62
62
|
|
|
63
63
|
## How it works
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.96.0",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"render-how": "tsx scripts/render-how-it-works.mts",
|
|
45
45
|
"build:readme": "tsx scripts/build-readme.mts",
|
|
46
46
|
"build:llms": "tsx scripts/build-llms.mts",
|
|
47
|
+
"build:changelog": "tsx scripts/build-changelog.mts",
|
|
48
|
+
"build:site-data": "tsx scripts/build-site-data.mts",
|
|
47
49
|
"postinstall": "node scripts/postinstall.mjs",
|
|
48
50
|
"prepare": "husky",
|
|
49
51
|
"glm-smoke": "tsx scripts/glm-smoke.mts",
|
|
@@ -170,5 +172,10 @@
|
|
|
170
172
|
"funding": {
|
|
171
173
|
"type": "github",
|
|
172
174
|
"url": "https://github.com/sponsors/Viqto0r"
|
|
175
|
+
},
|
|
176
|
+
"overrides": {
|
|
177
|
+
"@typescript-eslint/typescript-estree": {
|
|
178
|
+
"minimatch": "^11.0.4"
|
|
179
|
+
}
|
|
173
180
|
}
|
|
174
181
|
}
|