reasonix 0.30.5 → 0.32.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/README.md +17 -7
- package/README.zh-CN.md +16 -6
- package/dashboard/app.css +149 -0
- package/dashboard/dist/app.js +1039 -88
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/index.js +3743 -3115
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +43 -97
- package/dist/index.js +1927 -737
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -294,165 +294,6 @@ var DeepSeekClient = class {
|
|
|
294
294
|
}
|
|
295
295
|
};
|
|
296
296
|
|
|
297
|
-
// src/harvest.ts
|
|
298
|
-
function emptyPlanState() {
|
|
299
|
-
return { subgoals: [], hypotheses: [], uncertainties: [], rejectedPaths: [] };
|
|
300
|
-
}
|
|
301
|
-
function isPlanStateEmpty(s) {
|
|
302
|
-
if (!s) return true;
|
|
303
|
-
return s.subgoals.length === 0 && s.hypotheses.length === 0 && s.uncertainties.length === 0 && s.rejectedPaths.length === 0;
|
|
304
|
-
}
|
|
305
|
-
var SYSTEM_PROMPT = `You extract a typed plan state from a reasoning trace produced by another LLM.
|
|
306
|
-
Output ONLY a JSON object. No markdown, no prose, no backticks.
|
|
307
|
-
|
|
308
|
-
Schema:
|
|
309
|
-
{
|
|
310
|
-
"subgoals": string[], // concrete intermediate objectives the trace identifies
|
|
311
|
-
"hypotheses": string[], // candidate approaches or assumptions being weighed
|
|
312
|
-
"uncertainties": string[], // facts the trace flags as unclear / to verify
|
|
313
|
-
"rejectedPaths": string[] // approaches the trace considered and then abandoned
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
Constraints:
|
|
317
|
-
- Every field must be present. Use [] if not applicable.
|
|
318
|
-
- Each array has at most {maxItems} items.
|
|
319
|
-
- Each item is plain text, at most {maxItemLen} characters, no markdown.
|
|
320
|
-
- Write in the same language as the trace (Chinese in \u2192 Chinese out, etc.).
|
|
321
|
-
- Do not quote back the trace; write short, specific phrases.`;
|
|
322
|
-
async function harvest(reasoningContent, client, options = {}, signal) {
|
|
323
|
-
if (!client || !reasoningContent) return emptyPlanState();
|
|
324
|
-
if (signal?.aborted) return emptyPlanState();
|
|
325
|
-
const minLen = options.minReasoningLen ?? 40;
|
|
326
|
-
const trimmed = reasoningContent.trim();
|
|
327
|
-
if (trimmed.length < minLen) return emptyPlanState();
|
|
328
|
-
const model = options.model ?? "deepseek-v4-flash";
|
|
329
|
-
const maxItems = options.maxItems ?? 5;
|
|
330
|
-
const maxItemLen = options.maxItemLen ?? 80;
|
|
331
|
-
const system = SYSTEM_PROMPT.replace("{maxItems}", String(maxItems)).replace(
|
|
332
|
-
"{maxItemLen}",
|
|
333
|
-
String(maxItemLen)
|
|
334
|
-
);
|
|
335
|
-
try {
|
|
336
|
-
const resp = await client.chat({
|
|
337
|
-
model,
|
|
338
|
-
messages: [
|
|
339
|
-
{ role: "system", content: system },
|
|
340
|
-
{ role: "user", content: trimmed }
|
|
341
|
-
],
|
|
342
|
-
responseFormat: { type: "json_object" },
|
|
343
|
-
temperature: 0,
|
|
344
|
-
maxTokens: 600,
|
|
345
|
-
// Pin mode + effort so a future default-model swap (e.g. someone
|
|
346
|
-
// sets `options.model = "deepseek-v4-pro"`) can't accidentally
|
|
347
|
-
// turn this micro-extraction into a multi-thousand-reasoning-
|
|
348
|
-
// token call. DeepSeek ignores these on non-thinking models, so
|
|
349
|
-
// the request stays valid regardless of the chosen model.
|
|
350
|
-
thinking: "disabled",
|
|
351
|
-
reasoningEffort: "high",
|
|
352
|
-
signal
|
|
353
|
-
});
|
|
354
|
-
return parsePlanState(resp.content, maxItems, maxItemLen);
|
|
355
|
-
} catch {
|
|
356
|
-
return emptyPlanState();
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function parsePlanState(raw, maxItems, maxItemLen) {
|
|
360
|
-
const text = (raw ?? "").trim();
|
|
361
|
-
if (!text) return emptyPlanState();
|
|
362
|
-
let parsed;
|
|
363
|
-
try {
|
|
364
|
-
parsed = JSON.parse(text);
|
|
365
|
-
} catch {
|
|
366
|
-
const match = text.match(/\{[\s\S]*\}/);
|
|
367
|
-
if (!match) return emptyPlanState();
|
|
368
|
-
try {
|
|
369
|
-
parsed = JSON.parse(match[0]);
|
|
370
|
-
} catch {
|
|
371
|
-
return emptyPlanState();
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
if (!parsed || typeof parsed !== "object") return emptyPlanState();
|
|
375
|
-
const obj = parsed;
|
|
376
|
-
return {
|
|
377
|
-
subgoals: sanitizeArray(obj.subgoals, maxItems, maxItemLen),
|
|
378
|
-
hypotheses: sanitizeArray(obj.hypotheses, maxItems, maxItemLen),
|
|
379
|
-
uncertainties: sanitizeArray(obj.uncertainties, maxItems, maxItemLen),
|
|
380
|
-
rejectedPaths: sanitizeArray(obj.rejectedPaths ?? obj.rejected_paths, maxItems, maxItemLen)
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
function sanitizeArray(raw, maxItems, maxItemLen) {
|
|
384
|
-
if (!Array.isArray(raw)) return [];
|
|
385
|
-
const out = [];
|
|
386
|
-
for (const item of raw) {
|
|
387
|
-
if (out.length >= maxItems) break;
|
|
388
|
-
if (typeof item !== "string") continue;
|
|
389
|
-
const cleaned = item.trim().replace(/\s+/g, " ");
|
|
390
|
-
if (!cleaned) continue;
|
|
391
|
-
out.push(cleaned.length <= maxItemLen ? cleaned : `${cleaned.slice(0, maxItemLen - 1)}\u2026`);
|
|
392
|
-
}
|
|
393
|
-
return out;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// src/consistency.ts
|
|
397
|
-
var defaultSelector = (samples) => {
|
|
398
|
-
if (samples.length === 0) throw new Error("defaultSelector: samples is empty");
|
|
399
|
-
return samples.slice().sort((a, b) => {
|
|
400
|
-
const uDiff = a.planState.uncertainties.length - b.planState.uncertainties.length;
|
|
401
|
-
if (uDiff !== 0) return uDiff;
|
|
402
|
-
const aLen = a.response.content?.length ?? 0;
|
|
403
|
-
const bLen = b.response.content?.length ?? 0;
|
|
404
|
-
return aLen - bLen;
|
|
405
|
-
})[0];
|
|
406
|
-
};
|
|
407
|
-
async function runBranches(client, request, opts = {}) {
|
|
408
|
-
const budget = Math.max(1, opts.budget ?? 1);
|
|
409
|
-
const temperatures = resolveTemperatures(budget, opts.temperatures);
|
|
410
|
-
const selector = opts.selector ?? defaultSelector;
|
|
411
|
-
const samples = await Promise.all(
|
|
412
|
-
temperatures.map(async (temperature, index) => {
|
|
413
|
-
const response = await client.chat({ ...request, temperature });
|
|
414
|
-
const planState = await harvest(response.reasoningContent, client, opts.harvestOptions);
|
|
415
|
-
const sample = { index, temperature, response, planState };
|
|
416
|
-
try {
|
|
417
|
-
opts.onSampleDone?.(sample);
|
|
418
|
-
} catch {
|
|
419
|
-
}
|
|
420
|
-
return sample;
|
|
421
|
-
})
|
|
422
|
-
);
|
|
423
|
-
return { chosen: selector(samples), samples };
|
|
424
|
-
}
|
|
425
|
-
function aggregateBranchUsage(samples) {
|
|
426
|
-
let promptTokens = 0;
|
|
427
|
-
let completionTokens = 0;
|
|
428
|
-
let totalTokens = 0;
|
|
429
|
-
let promptCacheHitTokens = 0;
|
|
430
|
-
let promptCacheMissTokens = 0;
|
|
431
|
-
for (const s of samples) {
|
|
432
|
-
promptTokens += s.response.usage.promptTokens;
|
|
433
|
-
completionTokens += s.response.usage.completionTokens;
|
|
434
|
-
totalTokens += s.response.usage.totalTokens;
|
|
435
|
-
promptCacheHitTokens += s.response.usage.promptCacheHitTokens;
|
|
436
|
-
promptCacheMissTokens += s.response.usage.promptCacheMissTokens;
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
promptTokens,
|
|
440
|
-
completionTokens,
|
|
441
|
-
totalTokens,
|
|
442
|
-
promptCacheHitTokens,
|
|
443
|
-
promptCacheMissTokens
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
function resolveTemperatures(budget, custom) {
|
|
447
|
-
if (custom && custom.length >= budget) return [...custom.slice(0, budget)];
|
|
448
|
-
if (budget === 1) return [0];
|
|
449
|
-
const out = [];
|
|
450
|
-
for (let i = 0; i < budget; i++) {
|
|
451
|
-
out.push(Number((i / (budget - 1)).toFixed(2)));
|
|
452
|
-
}
|
|
453
|
-
return out;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
297
|
// src/core/pause-gate.ts
|
|
457
298
|
var PauseGate = class {
|
|
458
299
|
_nextId = 0;
|
|
@@ -540,14 +381,1604 @@ var PauseGate = class {
|
|
|
540
381
|
} catch {
|
|
541
382
|
}
|
|
542
383
|
}
|
|
543
|
-
};
|
|
544
|
-
var pauseGate = new PauseGate();
|
|
384
|
+
};
|
|
385
|
+
var pauseGate = new PauseGate();
|
|
386
|
+
|
|
387
|
+
// src/hooks.ts
|
|
388
|
+
import { spawn } from "child_process";
|
|
389
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
390
|
+
import { homedir as homedir2 } from "os";
|
|
391
|
+
import { join as join2 } from "path";
|
|
392
|
+
|
|
393
|
+
// src/config.ts
|
|
394
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
395
|
+
import { homedir } from "os";
|
|
396
|
+
import { dirname, join } from "path";
|
|
397
|
+
|
|
398
|
+
// src/index/config.ts
|
|
399
|
+
import picomatch from "picomatch";
|
|
400
|
+
var DEFAULT_INDEX_EXCLUDES = {
|
|
401
|
+
dirs: [
|
|
402
|
+
"node_modules",
|
|
403
|
+
".git",
|
|
404
|
+
".hg",
|
|
405
|
+
".svn",
|
|
406
|
+
"dist",
|
|
407
|
+
"build",
|
|
408
|
+
"out",
|
|
409
|
+
".next",
|
|
410
|
+
".nuxt",
|
|
411
|
+
"target",
|
|
412
|
+
".venv",
|
|
413
|
+
"venv",
|
|
414
|
+
"__pycache__",
|
|
415
|
+
".pytest_cache",
|
|
416
|
+
".mypy_cache",
|
|
417
|
+
".cache",
|
|
418
|
+
"coverage",
|
|
419
|
+
".turbo",
|
|
420
|
+
".vercel",
|
|
421
|
+
".reasonix"
|
|
422
|
+
],
|
|
423
|
+
files: [
|
|
424
|
+
"package-lock.json",
|
|
425
|
+
"yarn.lock",
|
|
426
|
+
"pnpm-lock.yaml",
|
|
427
|
+
"Cargo.lock",
|
|
428
|
+
"poetry.lock",
|
|
429
|
+
"Pipfile.lock",
|
|
430
|
+
"go.sum",
|
|
431
|
+
".DS_Store"
|
|
432
|
+
],
|
|
433
|
+
exts: [
|
|
434
|
+
".png",
|
|
435
|
+
".jpg",
|
|
436
|
+
".jpeg",
|
|
437
|
+
".gif",
|
|
438
|
+
".webp",
|
|
439
|
+
".bmp",
|
|
440
|
+
".ico",
|
|
441
|
+
".tiff",
|
|
442
|
+
".woff",
|
|
443
|
+
".woff2",
|
|
444
|
+
".ttf",
|
|
445
|
+
".otf",
|
|
446
|
+
".eot",
|
|
447
|
+
".zip",
|
|
448
|
+
".tar",
|
|
449
|
+
".gz",
|
|
450
|
+
".bz2",
|
|
451
|
+
".xz",
|
|
452
|
+
".rar",
|
|
453
|
+
".7z",
|
|
454
|
+
".exe",
|
|
455
|
+
".dll",
|
|
456
|
+
".so",
|
|
457
|
+
".dylib",
|
|
458
|
+
".bin",
|
|
459
|
+
".class",
|
|
460
|
+
".jar",
|
|
461
|
+
".war",
|
|
462
|
+
".wasm",
|
|
463
|
+
".o",
|
|
464
|
+
".obj",
|
|
465
|
+
".lib",
|
|
466
|
+
".a",
|
|
467
|
+
".pyc",
|
|
468
|
+
".pyo",
|
|
469
|
+
".mp3",
|
|
470
|
+
".mp4",
|
|
471
|
+
".wav",
|
|
472
|
+
".ogg",
|
|
473
|
+
".webm",
|
|
474
|
+
".mov",
|
|
475
|
+
".avi",
|
|
476
|
+
".pdf",
|
|
477
|
+
".sqlite",
|
|
478
|
+
".db"
|
|
479
|
+
]
|
|
480
|
+
};
|
|
481
|
+
var DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
482
|
+
|
|
483
|
+
// src/config.ts
|
|
484
|
+
function defaultConfigPath() {
|
|
485
|
+
return join(homedir(), ".reasonix", "config.json");
|
|
486
|
+
}
|
|
487
|
+
function readConfig(path2 = defaultConfigPath()) {
|
|
488
|
+
try {
|
|
489
|
+
const raw = readFileSync(path2, "utf8");
|
|
490
|
+
const parsed = JSON.parse(raw);
|
|
491
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
494
|
+
return {};
|
|
495
|
+
}
|
|
496
|
+
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
497
|
+
mkdirSync(dirname(path2), { recursive: true });
|
|
498
|
+
writeFileSync(path2, JSON.stringify(cfg, null, 2), "utf8");
|
|
499
|
+
try {
|
|
500
|
+
chmodSync(path2, 384);
|
|
501
|
+
} catch {
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function loadLanguage(path2 = defaultConfigPath()) {
|
|
505
|
+
return readConfig(path2).lang;
|
|
506
|
+
}
|
|
507
|
+
function loadApiKey(path2 = defaultConfigPath()) {
|
|
508
|
+
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
509
|
+
return readConfig(path2).apiKey;
|
|
510
|
+
}
|
|
511
|
+
function webSearchEngine(path2 = defaultConfigPath()) {
|
|
512
|
+
const cfg = readConfig(path2).webSearchEngine;
|
|
513
|
+
if (cfg === "searxng") return "searxng";
|
|
514
|
+
return "mojeek";
|
|
515
|
+
}
|
|
516
|
+
function webSearchEndpoint(path2 = defaultConfigPath()) {
|
|
517
|
+
const cfg = readConfig(path2).webSearchEndpoint;
|
|
518
|
+
if (cfg && typeof cfg === "string") return cfg;
|
|
519
|
+
return "http://localhost:8080";
|
|
520
|
+
}
|
|
521
|
+
function saveApiKey(key, path2 = defaultConfigPath()) {
|
|
522
|
+
const cfg = readConfig(path2);
|
|
523
|
+
cfg.apiKey = key.trim();
|
|
524
|
+
writeConfig(cfg, path2);
|
|
525
|
+
}
|
|
526
|
+
function findProjectKey(cfg, rootDir) {
|
|
527
|
+
const projects = cfg.projects;
|
|
528
|
+
if (!projects) return void 0;
|
|
529
|
+
if (Object.hasOwn(projects, rootDir)) return rootDir;
|
|
530
|
+
if (process.platform !== "win32") return void 0;
|
|
531
|
+
const lower = rootDir.toLowerCase();
|
|
532
|
+
for (const k of Object.keys(projects)) {
|
|
533
|
+
if (k.toLowerCase() === lower) return k;
|
|
534
|
+
}
|
|
535
|
+
return void 0;
|
|
536
|
+
}
|
|
537
|
+
function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
538
|
+
const trimmed = prefix.trim();
|
|
539
|
+
if (!trimmed) return;
|
|
540
|
+
const cfg = readConfig(path2);
|
|
541
|
+
if (!cfg.projects) cfg.projects = {};
|
|
542
|
+
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
543
|
+
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
544
|
+
const existing = cfg.projects[key].shellAllowed ?? [];
|
|
545
|
+
if (existing.includes(trimmed)) return;
|
|
546
|
+
cfg.projects[key].shellAllowed = [...existing, trimmed];
|
|
547
|
+
writeConfig(cfg, path2);
|
|
548
|
+
}
|
|
549
|
+
function isPlausibleKey(key) {
|
|
550
|
+
const trimmed = key.trim();
|
|
551
|
+
return /^sk-[A-Za-z0-9_-]{16,}$/.test(trimmed);
|
|
552
|
+
}
|
|
553
|
+
function redactKey(key) {
|
|
554
|
+
if (!key) return "";
|
|
555
|
+
if (key.length <= 12) return "****";
|
|
556
|
+
return `${key.slice(0, 6)}\u2026${key.slice(-4)}`;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/i18n/EN.ts
|
|
560
|
+
var EN = {
|
|
561
|
+
common: {
|
|
562
|
+
error: "Error",
|
|
563
|
+
warning: "Warning",
|
|
564
|
+
loading: "Loading...",
|
|
565
|
+
done: "Done",
|
|
566
|
+
cancel: "Cancel",
|
|
567
|
+
confirm: "Confirm",
|
|
568
|
+
back: "Back",
|
|
569
|
+
next: "Next"
|
|
570
|
+
},
|
|
571
|
+
cli: {
|
|
572
|
+
description: "DeepSeek-native agent framework \u2014 built for cache hits and cheap tokens.",
|
|
573
|
+
continue: "Resume the most recently used chat session without showing the picker.",
|
|
574
|
+
setup: "Interactive wizard \u2014 API key, preset, MCP servers. Re-run any time to reconfigure.",
|
|
575
|
+
code: "Code-editing chat \u2014 filesystem tools rooted at <dir> (default: cwd), coding system prompt, v4-flash baseline.",
|
|
576
|
+
chat: "Interactive Ink TUI with live cache/cost panel.",
|
|
577
|
+
run: "Run a single task non-interactively, streaming output.",
|
|
578
|
+
stats: "Show usage dashboard.",
|
|
579
|
+
doctor: "One-command health check.",
|
|
580
|
+
commit: "Draft a commit message from the staged diff.",
|
|
581
|
+
sessions: "List saved chat sessions, or inspect one by name.",
|
|
582
|
+
pruneSessions: "Delete saved sessions idle \u2265N days (default 90). Use --dry-run to preview.",
|
|
583
|
+
events: "Pretty-print the kernel event-log sidecar.",
|
|
584
|
+
replay: "Interactive Ink TUI to scrub through a transcript.",
|
|
585
|
+
diff: "Compare two transcripts in a split-pane Ink TUI.",
|
|
586
|
+
mcp: "Model Context Protocol helpers \u2014 discover servers, test your setup.",
|
|
587
|
+
version: "Print Reasonix version.",
|
|
588
|
+
update: "Check for a newer Reasonix and install it.",
|
|
589
|
+
index: "Build (or incrementally refresh) a local semantic search index."
|
|
590
|
+
},
|
|
591
|
+
ui: {
|
|
592
|
+
welcome: "Run `reasonix` any time to start chatting \u2014 your settings are remembered.",
|
|
593
|
+
taglineChat: "DeepSeek-native agent",
|
|
594
|
+
taglineCode: "DeepSeek-native coding agent",
|
|
595
|
+
taglineSub: "cache-first \xB7 flash-first",
|
|
596
|
+
startSessionHint: "type a message to start your session",
|
|
597
|
+
inputPlaceholder: "Ask anything... (type / for commands, @ for files)",
|
|
598
|
+
busy: "Thinking...",
|
|
599
|
+
thinking: "\u25B8 thinking...",
|
|
600
|
+
undo: "Undo",
|
|
601
|
+
undoHint: "press u within 5s to undo",
|
|
602
|
+
applied: "applied",
|
|
603
|
+
rejected: "rejected",
|
|
604
|
+
noDashboard: "Suppress the auto-launched embedded web dashboard.",
|
|
605
|
+
dashboardAutoStartFailed: "\u25B2 dashboard auto-start failed ({reason}) \u2014 try /dashboard, or pass --no-dashboard to silence",
|
|
606
|
+
systemAppendHint: "Append instructions to the code system prompt. Does NOT replace the default prompt \u2014 adds after it.",
|
|
607
|
+
systemAppendFileHint: "Append file contents to the code system prompt. Does NOT replace the default prompt. UTF-8, relative to cwd or absolute.",
|
|
608
|
+
resumedSession: '\u25B8 resumed session "{name}" with {count} prior messages \xB7 /forget to start over \xB7 /sessions to list',
|
|
609
|
+
newSession: '\u25B8 session "{name}" (new) \u2014 auto-saved as you chat \xB7 /forget to delete \xB7 /sessions to list',
|
|
610
|
+
ephemeralSession: "\u25B8 ephemeral chat (no session persistence) \u2014 drop --no-session to enable",
|
|
611
|
+
restoredEdits: "\u25B8 restored {count} pending edit block(s) from an interrupted prior run \u2014 /apply to commit or /discard to drop.",
|
|
612
|
+
resumedPlan: "Resumed plan \xB7 {when}{summary}",
|
|
613
|
+
tipEditBindings: "\u25B8 TIP: edit-gate keybindings\n y / n accept or drop pending edits\n Shift+Tab switch review \u2194 AUTO (persisted; AUTO applies instantly)\n u undo the last auto-applied batch (within the 5s banner)\n Current mode is shown in the bottom status bar. Run /keys anytime for the full list.\n (This tip shows once \u2014 suppressed after.)",
|
|
614
|
+
modelOverride: "override the default model",
|
|
615
|
+
noSession: "disable session persistence for this run",
|
|
616
|
+
resumeHint: "force-resume the named session (even if idle)",
|
|
617
|
+
newHint: "force a fresh session (ignore --session / --continue)",
|
|
618
|
+
transcriptHint: "path to write the JSONL transcript",
|
|
619
|
+
budgetHint: "session USD cap \u2014 warns at 80%, refuses next turn at 100%",
|
|
620
|
+
modelIdHint: "DeepSeek model id (e.g. deepseek-v4-flash)",
|
|
621
|
+
systemPromptHint: "override the default system prompt",
|
|
622
|
+
presetHint: "model bundle \u2014 auto|flash|pro",
|
|
623
|
+
sessionNameHint: "session name (default: 'default')",
|
|
624
|
+
ephemeralHint: "disable session persistence for this run",
|
|
625
|
+
mcpSpecHint: "MCP server spec (repeatable)",
|
|
626
|
+
mcpPrefixHint: "prefix MCP tool names with this string",
|
|
627
|
+
noConfigHint: "ignore ~/.reasonix/config.json for this run",
|
|
628
|
+
presetHintShort: "model bundle \u2014 auto|flash|pro",
|
|
629
|
+
budgetHintShort: "session USD cap",
|
|
630
|
+
transcriptHintShort: "JSONL transcript path",
|
|
631
|
+
mcpSpecHintShort: "MCP server spec (repeatable)",
|
|
632
|
+
mcpPrefixHintShort: "MCP tool name prefix",
|
|
633
|
+
dryRunHint: "show what would be installed without actually installing",
|
|
634
|
+
rebuildHint: "rebuild the index from scratch",
|
|
635
|
+
embedModelHint: "embedding model name",
|
|
636
|
+
projectDirHint: "project root directory",
|
|
637
|
+
ollamaUrlHint: "Ollama server URL",
|
|
638
|
+
skipPromptsHint: "skip confirmation prompts",
|
|
639
|
+
verboseHint: "show full session metadata",
|
|
640
|
+
pruneDaysHint: "delete sessions idle this many days or more (default 90)",
|
|
641
|
+
pruneDryRunHint: "list what would be deleted without removing anything",
|
|
642
|
+
eventTypeHint: "filter by event type",
|
|
643
|
+
eventSinceHint: "start from this event id",
|
|
644
|
+
eventTailHint: "show only the last N events",
|
|
645
|
+
jsonHint: "output as JSON",
|
|
646
|
+
projectionHint: "show projected state at each event",
|
|
647
|
+
printHint: "print to stdout instead of TUI",
|
|
648
|
+
headHint: "show only the first N events",
|
|
649
|
+
tailHint: "show only the last N events",
|
|
650
|
+
mdReportHint: "write a markdown diff report to this path",
|
|
651
|
+
printHintTable: "print a table to stdout",
|
|
652
|
+
tuiHint: "open the interactive TUI",
|
|
653
|
+
labelAHint: "label for the left pane",
|
|
654
|
+
labelBHint: "label for the right pane",
|
|
655
|
+
mcpListDescription: "browse the MCP registry (official \u2192 smithery \u2192 local fallback)",
|
|
656
|
+
mcpInspectDescription: "inspect an MCP server spec (tools, resources, prompts)",
|
|
657
|
+
mcpSearchDescription: "search the MCP registry for servers matching a query",
|
|
658
|
+
mcpInstallDescription: "install an MCP server by name (writes its spec to your config)",
|
|
659
|
+
mcpBrowseDescription: "interactive marketplace browser \u2014 type to filter, enter to install",
|
|
660
|
+
mcpLocalHint: "show only the bundled offline catalog",
|
|
661
|
+
mcpRefreshHint: "bypass the 24h cache and refetch",
|
|
662
|
+
mcpLimitHint: "max entries to show",
|
|
663
|
+
mcpPagesHint: "eagerly load this many pages (default 1)",
|
|
664
|
+
mcpAllHint: "load every page (slow on first run)",
|
|
665
|
+
mcpMaxPagesHint: "cap how many pages to walk while searching (default 20)",
|
|
666
|
+
jsonHintCatalog: "output as JSON",
|
|
667
|
+
jsonHintReport: "output the inspection report as JSON",
|
|
668
|
+
modelOverrideFlash: "override the model (default: deepseek-v4-flash)",
|
|
669
|
+
skipConfirmHint: "skip the confirmation prompt"
|
|
670
|
+
},
|
|
671
|
+
slash: {
|
|
672
|
+
help: { description: "show the full command reference" },
|
|
673
|
+
status: { description: "current model, flags, context, session" },
|
|
674
|
+
preset: {
|
|
675
|
+
description: "model bundle \u2014 auto escalates flash \u2192 pro, flash/pro lock",
|
|
676
|
+
argsHint: "<auto|flash|pro>"
|
|
677
|
+
},
|
|
678
|
+
model: { description: "switch DeepSeek model id", argsHint: "<id>" },
|
|
679
|
+
models: { description: "list available models fetched from DeepSeek /models" },
|
|
680
|
+
language: {
|
|
681
|
+
description: "switch the runtime language",
|
|
682
|
+
argsHint: "<EN|zh-CN>",
|
|
683
|
+
success: "Language switched to English.",
|
|
684
|
+
unsupported: "Unsupported language code: {code}. Supported: {supported}."
|
|
685
|
+
},
|
|
686
|
+
pro: {
|
|
687
|
+
description: "arm v4-pro for the NEXT turn only (one-shot \xB7 auto-disarms after turn)",
|
|
688
|
+
argsHint: "[off]"
|
|
689
|
+
},
|
|
690
|
+
budget: {
|
|
691
|
+
description: "session USD cap \u2014 warns at 80%, refuses next turn at 100%. Off by default. /budget alone shows status",
|
|
692
|
+
argsHint: "[usd|off]"
|
|
693
|
+
},
|
|
694
|
+
mcp: { description: "list MCP servers + tools attached to this session" },
|
|
695
|
+
resource: {
|
|
696
|
+
description: "browse + read MCP resources (no arg \u2192 list URIs; <uri> \u2192 fetch contents)",
|
|
697
|
+
argsHint: "[uri]"
|
|
698
|
+
},
|
|
699
|
+
prompt: {
|
|
700
|
+
description: "browse + fetch MCP prompts (no arg \u2192 list names; <name> \u2192 render prompt)",
|
|
701
|
+
argsHint: "[name]"
|
|
702
|
+
},
|
|
703
|
+
memory: {
|
|
704
|
+
description: "show / manage pinned memory (REASONIX.md + ~/.reasonix/memory)",
|
|
705
|
+
argsHint: "[list|show <name>|forget <name>|clear <scope> confirm]"
|
|
706
|
+
},
|
|
707
|
+
skill: {
|
|
708
|
+
description: "list / run user skills (<project>/.reasonix/skills + ~/.reasonix/skills)",
|
|
709
|
+
argsHint: "[list|show <name>|<name> [args]]"
|
|
710
|
+
},
|
|
711
|
+
hooks: {
|
|
712
|
+
description: "list active hooks (settings.json under .reasonix/) \xB7 reload re-reads from disk",
|
|
713
|
+
argsHint: "[reload]"
|
|
714
|
+
},
|
|
715
|
+
permissions: {
|
|
716
|
+
description: "show / edit shell allowlist (builtin read-only \xB7 per-project: ~/.reasonix/config.json)",
|
|
717
|
+
argsHint: "[list|add <prefix>|remove <prefix|N>|clear confirm]"
|
|
718
|
+
},
|
|
719
|
+
dashboard: {
|
|
720
|
+
description: "launch the embedded web dashboard (127.0.0.1, token-gated)",
|
|
721
|
+
argsHint: "[stop]"
|
|
722
|
+
},
|
|
723
|
+
update: { description: "show current vs latest version + the shell command to upgrade" },
|
|
724
|
+
stats: {
|
|
725
|
+
description: "cross-session cost dashboard (today / week / month / all-time \xB7 cache hit \xB7 vs Claude)"
|
|
726
|
+
},
|
|
727
|
+
cost: {
|
|
728
|
+
description: "bare \u2192 last turn's spend (Usage card); with text \u2192 estimate cost of sending it next (worst-case + likely-cache)",
|
|
729
|
+
argsHint: "[text]"
|
|
730
|
+
},
|
|
731
|
+
doctor: { description: "health check (api / config / api-reach / index / hooks / project)" },
|
|
732
|
+
context: { description: "show context-window breakdown (system / tools / log / input)" },
|
|
733
|
+
retry: { description: "truncate & resend your last message (fresh sample)" },
|
|
734
|
+
compact: {
|
|
735
|
+
description: "shrink oversized tool results AND tool-call args (edit_file search/replace) in the log; cap in tokens, default 4000",
|
|
736
|
+
argsHint: "[tokens]"
|
|
737
|
+
},
|
|
738
|
+
keys: { description: "show all keyboard shortcuts and prompt prefixes" },
|
|
739
|
+
plans: { description: "list this session's active + archived plans, newest first" },
|
|
740
|
+
replay: {
|
|
741
|
+
description: "load an archived plan as a read-only Time Travel snapshot (default: newest)",
|
|
742
|
+
argsHint: "[N]"
|
|
743
|
+
},
|
|
744
|
+
sessions: { description: "list saved sessions (current marked with \u25B8)" },
|
|
745
|
+
setup: { description: "reminds you to exit and run `reasonix setup`" },
|
|
746
|
+
semantic: {
|
|
747
|
+
description: "show semantic_search status \u2014 built? Ollama installed? how to enable"
|
|
748
|
+
},
|
|
749
|
+
clear: { description: "clear visible scrollback only (log/context kept)" },
|
|
750
|
+
new: { description: "start a fresh conversation (clear context + scrollback)" },
|
|
751
|
+
loop: {
|
|
752
|
+
description: "auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop",
|
|
753
|
+
argsHint: "<5s..6h> <prompt> \xB7 stop \xB7 (no args = status)"
|
|
754
|
+
},
|
|
755
|
+
exit: { description: "quit the TUI" },
|
|
756
|
+
init: {
|
|
757
|
+
description: "scan the project and synthesize a baseline REASONIX.md (model writes; review with /apply). `force` overwrites an existing file.",
|
|
758
|
+
argsHint: "[force]"
|
|
759
|
+
},
|
|
760
|
+
apply: {
|
|
761
|
+
description: "commit pending edit blocks to disk (no arg \u2192 all; `1`, `1,3`, or `1-4` \u2192 that subset, rest stay pending)",
|
|
762
|
+
argsHint: "[N|N,M|N-M]"
|
|
763
|
+
},
|
|
764
|
+
discard: {
|
|
765
|
+
description: "drop pending edit blocks without writing (no arg \u2192 all; indices \u2192 that subset)",
|
|
766
|
+
argsHint: "[N|N,M|N-M]"
|
|
767
|
+
},
|
|
768
|
+
walk: {
|
|
769
|
+
description: "step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)"
|
|
770
|
+
},
|
|
771
|
+
undo: { description: "roll back the last applied edit batch" },
|
|
772
|
+
history: { description: "list every edit batch this session (ids for /show, undone markers)" },
|
|
773
|
+
show: {
|
|
774
|
+
description: "dump a stored edit diff (omit id for newest non-undone)",
|
|
775
|
+
argsHint: "[id]"
|
|
776
|
+
},
|
|
777
|
+
commit: { description: "git add -A && git commit -m ...", argsHint: '"msg"' },
|
|
778
|
+
checkpoint: {
|
|
779
|
+
description: "snapshot every file the session has touched (Cursor-style internal store, not git). /checkpoint alone lists.",
|
|
780
|
+
argsHint: "[name|list|forget <id>]"
|
|
781
|
+
},
|
|
782
|
+
restore: {
|
|
783
|
+
description: "roll back files to a named checkpoint (see /checkpoint list)",
|
|
784
|
+
argsHint: "<name|id>"
|
|
785
|
+
},
|
|
786
|
+
plan: {
|
|
787
|
+
description: "toggle read-only plan mode (writes bounced until submit_plan + approval)",
|
|
788
|
+
argsHint: "[on|off]"
|
|
789
|
+
},
|
|
790
|
+
mode: {
|
|
791
|
+
description: "edit-gate: review (queue) \xB7 auto (apply+undo) \xB7 yolo (apply+auto-shell). Shift+Tab cycles.",
|
|
792
|
+
argsHint: "[review|auto|yolo]"
|
|
793
|
+
},
|
|
794
|
+
jobs: { description: "list background jobs started by run_background" },
|
|
795
|
+
kill: {
|
|
796
|
+
description: "stop a background job by id (SIGTERM \u2192 SIGKILL after grace)",
|
|
797
|
+
argsHint: "<id>"
|
|
798
|
+
},
|
|
799
|
+
logs: {
|
|
800
|
+
description: "tail a background job's output (default last 80 lines)",
|
|
801
|
+
argsHint: "<id> [lines]"
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
wizard: {
|
|
805
|
+
languageTitle: "Choose your language",
|
|
806
|
+
languageSubtitle: "Detected from your system locale. Switch later via /language.",
|
|
807
|
+
welcomeTitle: "Welcome to Reasonix.",
|
|
808
|
+
apiKeyPrompt: "Paste your DeepSeek API key to get started.",
|
|
809
|
+
apiKeyGetOne: "Get one at: https://platform.deepseek.com/api_keys",
|
|
810
|
+
apiKeySavedLocally: "Saved locally to {path}",
|
|
811
|
+
apiKeyInputLabel: "key \u203A ",
|
|
812
|
+
apiKeyInvalid: "Doesn't look like a DeepSeek key. They start with 'sk-' and are 30+ chars.",
|
|
813
|
+
apiKeyPreview: "preview: {redacted}",
|
|
814
|
+
presetTitle: "Pick a preset",
|
|
815
|
+
mcpTitle: "Which MCP servers should Reasonix wire up for you?",
|
|
816
|
+
mcpUserArgsHint: "(you'll provide {arg})",
|
|
817
|
+
mcpFooterMulti: "[\u2191\u2193] navigate \xB7 [Space] toggle \xB7 [Enter] confirm \xB7 [Esc] cancel \xB7 empty = skip",
|
|
818
|
+
mcpArgsTitle: "Configure {name}",
|
|
819
|
+
mcpArgsDirMissing: "Directory {path} doesn't exist.",
|
|
820
|
+
mcpArgsDirCreateHint: "[Y/Enter] create it (mkdir -p) \xB7 [N/Esc] enter a different path",
|
|
821
|
+
mcpArgsDirCreateFailed: "Couldn't create {path}: {message}",
|
|
822
|
+
mcpArgsRequiredParam: "Required parameter: ",
|
|
823
|
+
mcpArgsEmpty: "{name} needs a value \u2014 got an empty string.",
|
|
824
|
+
mcpArgsNotADir: "{path} exists but is not a directory.",
|
|
825
|
+
reviewTitle: "Ready to save",
|
|
826
|
+
reviewLabelApiKey: "API key",
|
|
827
|
+
reviewLabelLanguage: "Language",
|
|
828
|
+
reviewLabelPreset: "Preset",
|
|
829
|
+
reviewLabelMcp: "MCP",
|
|
830
|
+
reviewMcpNone: "(none)",
|
|
831
|
+
reviewMcpServers: "{count} server(s)",
|
|
832
|
+
reviewSavesTo: "Saves to {path}",
|
|
833
|
+
reviewSaveError: "Could not save config: {message}",
|
|
834
|
+
reviewFooter: "[Enter] save \xB7 [Esc] cancel",
|
|
835
|
+
savedTitle: "\u25B8 Saved.",
|
|
836
|
+
savedFooter: "[Enter] to exit",
|
|
837
|
+
selectFooter: "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel",
|
|
838
|
+
stepCounter: "Step {step}/{total} \xB7 "
|
|
839
|
+
},
|
|
840
|
+
app: {
|
|
841
|
+
walkCancelledRemaining: "\u25B8 walk cancelled \u2014 {count} block(s) still pending.",
|
|
842
|
+
walkCancelled: "\u25B8 walk cancelled.",
|
|
843
|
+
editModeYolo: "\u25B8 edit mode: YOLO \u2014 edits AND shell commands auto-run. /undo still rolls back edits. Use carefully.",
|
|
844
|
+
editModeAuto: "\u25B8 edit mode: AUTO \u2014 edits apply immediately; press u within 5s to undo (space pauses the timer). Shell commands still ask.",
|
|
845
|
+
editModeReview: "\u25B8 edit mode: review \u2014 edits queue for /apply (or y) / /discard (or n)",
|
|
846
|
+
rejectedEdit: "\u25B8 rejected edit to {path}{context}",
|
|
847
|
+
autoApprovingRest: "\u25B8 auto-approving remaining edits for this turn",
|
|
848
|
+
flippedAutoSession: "\u25B8 flipped to AUTO mode for the rest of the session (persisted)",
|
|
849
|
+
flippedAutoWalk: "\u25B8 flipped to AUTO mode \u2014 future edits will apply immediately. Walk exited.",
|
|
850
|
+
dashboardStopped: "\u25B8 dashboard stopped.",
|
|
851
|
+
notedMemory: "\u25B8 noted ({scope}) \u2014 {verb} {path}",
|
|
852
|
+
notedScopeProject: "project",
|
|
853
|
+
notedScopeGlobal: "global",
|
|
854
|
+
notedVerbCreated: "created",
|
|
855
|
+
notedVerbAppended: "appended to",
|
|
856
|
+
memoryWriteFailed: "# memory write failed",
|
|
857
|
+
commandFailed: "! command failed",
|
|
858
|
+
restoreCodeOnly: "\u25B8 /restore is code-mode only",
|
|
859
|
+
hookUserPromptSubmit: "UserPromptSubmit hook",
|
|
860
|
+
hookStop: "Stop hook",
|
|
861
|
+
atMentions: "\u25B8 @mentions: {parts}",
|
|
862
|
+
atUrl: "\u25B8 @url: {parts}",
|
|
863
|
+
atUrlFailed: "@url expansion failed",
|
|
864
|
+
denied: "\u25B8 denied: {cmd}{context}",
|
|
865
|
+
alwaysAllowed: '\u25B8 always allowed "{prefix}" for {dir}',
|
|
866
|
+
runningCommand: "\u25B8 running: {cmd}",
|
|
867
|
+
startingBackground: "\u25B8 starting (background): {cmd}",
|
|
868
|
+
checkpointSaved: "\u26C1 checkpoint saved \xB7 {id} \xB7 {count} file{s} \xB7 /restore {id} to roll back this step",
|
|
869
|
+
continuingAfter: "\u25B8 continuing after {label}{counter}",
|
|
870
|
+
planStoppedAt: "\u25B8 plan stopped at {label}{counter}",
|
|
871
|
+
revisingAfter: "\u25B8 revising after {label} \u2014 {feedback}"
|
|
872
|
+
},
|
|
873
|
+
hooks: {
|
|
874
|
+
head: "hook {tag} `{cmd}` {decision}{truncTag}",
|
|
875
|
+
headWithDetail: "hook {tag} `{cmd}` {decision}{truncTag}: {detail}",
|
|
876
|
+
truncated: " (output truncated at 256KB)",
|
|
877
|
+
decisionBlock: "block",
|
|
878
|
+
decisionWarn: "warn",
|
|
879
|
+
decisionTimeout: "timeout",
|
|
880
|
+
decisionError: "error"
|
|
881
|
+
},
|
|
882
|
+
summary: {
|
|
883
|
+
status: "summarizing what was gathered\u2026",
|
|
884
|
+
hallucinatedFallback: "(model emitted fake tool-call markup instead of a prose summary \u2014 try /retry with a narrower question, or /think to inspect R1's reasoning)",
|
|
885
|
+
failedAfterReason: "{label} and the fallback summary call failed: {message}. Run /clear and retry with a narrower question, or raise --max-tool-iters."
|
|
886
|
+
},
|
|
887
|
+
loop: {
|
|
888
|
+
budgetExhausted: "session budget exhausted \u2014 spent ${spent} \u2265 cap ${cap}. Bump the cap with /budget <usd>, clear it with /budget off, or end the session.",
|
|
889
|
+
budget80Pct: "\u25B2 budget 80% used \u2014 ${spent} of ${cap}. Next turn or two likely trips the cap.",
|
|
890
|
+
proArmed: "\u21E7 /pro armed \u2014 this turn runs on deepseek-v4-pro (one-shot \xB7 disarms after turn)",
|
|
891
|
+
abortedAtIter: "aborted at iter {iter}/{cap} \u2014 stopped without producing a summary (press \u2191 + Enter or /retry to resume)",
|
|
892
|
+
toolUploadStatus: "tool result uploaded \xB7 model thinking before next response\u2026",
|
|
893
|
+
toolBudgetWarning: "{iter}/{cap} tool calls used \u2014 approaching budget. Press Esc to force a summary now.",
|
|
894
|
+
preflightFoldStatus: "preflight: context near full, attempting fold\u2026",
|
|
895
|
+
preflightFolded: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) \u2014 folded {beforeMessages} messages \u2192 {afterMessages} (summary {summaryChars} chars). Sending.",
|
|
896
|
+
preflightNoFold: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) and nothing left to fold \u2014 DeepSeek will likely 400. Run /clear or /new to start fresh.",
|
|
897
|
+
flashEscalation: "\u21E7 flash requested escalation \u2014 retrying this turn on {model}{reasonSuffix}",
|
|
898
|
+
harvestStatus: "extracting plan state from reasoning\u2026",
|
|
899
|
+
autoEscalation: "\u21E7 auto-escalating to {model} for the rest of this turn \u2014 flash hit {breakdown}. Next turn falls back to {fallback} unless /pro is armed.",
|
|
900
|
+
repeatToolCallWarning: "Caught a repeated tool call \u2014 let the model see the issue and retry with a different approach.",
|
|
901
|
+
stormStuck: "Stopped a stuck retry loop \u2014 the model kept calling the same tool with identical args after a self-correction nudge. Try /retry, rephrase, or rule out the underlying blocker.",
|
|
902
|
+
stormSuppressed: "Suppressed {count} repeated tool call(s) \u2014 same name + args fired 3+ times.",
|
|
903
|
+
compactingHistoryStatus: "compacting history{aggressiveTag}\u2026",
|
|
904
|
+
aggressiveTag: " (aggressive)",
|
|
905
|
+
foldedHistory: "context {before}/{ctxMax} ({pct}%) \u2014 folded {beforeMessages} messages \u2192 {afterMessages} (summary {summaryChars} chars). Continuing.",
|
|
906
|
+
aggressivelyFoldedHistory: "context {before}/{ctxMax} ({pct}%) \u2014 aggressively folded {beforeMessages} messages \u2192 {afterMessages} (summary {summaryChars} chars). Continuing.",
|
|
907
|
+
forcingSummary: "context {before}/{ctxMax} ({pct}%) \u2014 forcing summary from what was gathered. Run /compact, /clear, or /new to reset."
|
|
908
|
+
},
|
|
909
|
+
errors: {
|
|
910
|
+
contextOverflow: "Context overflow (DeepSeek 400): session history is {requested}, past the model's prompt limit (V4: 1M tokens; legacy chat/reasoner: 131k). Usually a single tool result grew too big. Reasonix caps new tool results at 8k tokens and auto-heals oversized history on session load \u2014 a restart often clears it. If it still overflows, run /forget (delete the session) or /clear (drop the displayed history) to start fresh.",
|
|
911
|
+
contextOverflowTooMany: "too many tokens",
|
|
912
|
+
auth401: "Authentication failed (DeepSeek 401): {inner}. Your API key is rejected. Fix with `reasonix setup` or `export DEEPSEEK_API_KEY=sk-...`. Get one at https://platform.deepseek.com/api_keys.",
|
|
913
|
+
balance402: "Out of balance (DeepSeek 402): {inner}. Top up at https://platform.deepseek.com/top_up \u2014 the panel header shows your balance once it's non-zero.",
|
|
914
|
+
badparam422: "Invalid parameter (DeepSeek 422): {inner}",
|
|
915
|
+
badrequest400: "Bad request (DeepSeek 400): {inner}",
|
|
916
|
+
deepseek5xxHead: "DeepSeek service unavailable ({status}) \u2014 this is a DeepSeek-side problem, not Reasonix. Already retried 4\xD7 with backoff.",
|
|
917
|
+
deepseek5xxReachable: " DeepSeek's main API answered our health check, but /chat/completions is failing \u2014 partial outage on their side.",
|
|
918
|
+
deepseek5xxUnreachable: " DeepSeek API is unreachable from your network \u2014 could be a wider DS outage or a local network issue.",
|
|
919
|
+
deepseek5xxActionNetwork: " Try: (1) check your network, (2) wait 30s and retry, (3) status page: https://status.deepseek.com.",
|
|
920
|
+
deepseek5xxActionRetry: " Try: (1) wait 30s and retry, (2) /preset to switch model, (3) status page: https://status.deepseek.com.",
|
|
921
|
+
innerNoMessage: "(no message)",
|
|
922
|
+
reasonAborted: "[aborted by user (Esc) \u2014 summarizing what I found so far]",
|
|
923
|
+
reasonContextGuard: "[context budget running low \u2014 summarizing before the next call would overflow]",
|
|
924
|
+
reasonStuck: "[stuck on a repeated tool call \u2014 explaining what was tried and what's blocking progress]",
|
|
925
|
+
reasonBudget: "[tool-call budget ({iterCap}) reached \u2014 forcing summary from what I found]",
|
|
926
|
+
labelAborted: "aborted by user",
|
|
927
|
+
labelContextGuard: "context-guard triggered (prompt > 80% of window)",
|
|
928
|
+
labelStuck: "stuck (repeated tool call suppressed by storm-breaker)",
|
|
929
|
+
labelBudget: "tool-call budget ({iterCap}) reached"
|
|
930
|
+
},
|
|
931
|
+
handlers: {
|
|
932
|
+
basic: {
|
|
933
|
+
newInfo: "\u25B8 new conversation \u2014 dropped {count} message(s) from context. Same session, fresh slate.",
|
|
934
|
+
helpTitle: "Commands:",
|
|
935
|
+
helpShellTitle: "Shell shortcut:",
|
|
936
|
+
helpShell: " !<cmd> run <cmd> in the sandbox root; output goes into",
|
|
937
|
+
helpShellDetail: " the conversation so the model sees it next turn.",
|
|
938
|
+
helpShellConsent: " No allowlist gate \u2014 user-typed = explicit consent.",
|
|
939
|
+
helpShellExample: " Example: !git status !ls src/ !npm test",
|
|
940
|
+
helpMemoryTitle: "Quick memory:",
|
|
941
|
+
helpMemoryPin: " #<note> append <note> to <project>/REASONIX.md (committable).",
|
|
942
|
+
helpMemoryPinEx: " Example: #findByEmail must be case-insensitive",
|
|
943
|
+
helpMemoryGlobal: " #g <note> append <note> to ~/.reasonix/REASONIX.md (global, never committed).",
|
|
944
|
+
helpMemoryGlobalEx: " Example: #g always run pnpm not npm",
|
|
945
|
+
helpMemoryPinBoth: " Both pin into every future session's prefix. Faster than /memory.",
|
|
946
|
+
helpMemoryEscape: " Use `\\#text` to send a literal `#text` to the model.",
|
|
947
|
+
helpFileTitle: "File references (code mode):",
|
|
948
|
+
helpFile: " @path/to/file inline file content under [Referenced files] on send.",
|
|
949
|
+
helpFilePicker: " Type `@` to open the picker (\u2191\u2193 navigate, Tab/Enter pick).",
|
|
950
|
+
helpUrlTitle: "URL references:",
|
|
951
|
+
helpUrl: " @https://example.com fetch the URL, strip HTML, inline under [Referenced URLs].",
|
|
952
|
+
helpUrlCache: " Same URL twice in one session fetches once (in-mem cache).",
|
|
953
|
+
helpUrlPunct: " Trailing sentence punctuation (./,/)) is stripped automatically.",
|
|
954
|
+
helpPresetsTitle: "Presets (branch + harvest are NEVER auto-enabled \u2014 opt-in only):",
|
|
955
|
+
helpPresetAuto: " auto v4-flash \u2192 v4-pro on hard turns \u2190 default \xB7 cheap when easy, smart when hard",
|
|
956
|
+
helpPresetFlash: " flash v4-flash always cheapest \xB7 predictable per-turn cost",
|
|
957
|
+
helpPresetPro: " pro v4-pro always ~3\xD7 flash (5/31) \xB7 hard multi-turn work",
|
|
958
|
+
helpSessionsTitle: "Sessions (auto-enabled by default, named 'default'):",
|
|
959
|
+
helpSessionCustom: " reasonix chat --session <name> use a different named session",
|
|
960
|
+
helpSessionNone: " reasonix chat --no-session disable persistence for this run",
|
|
961
|
+
retryNone: "nothing to retry \u2014 no prior user message in this session's log.",
|
|
962
|
+
retryInfo: '\u25B8 retrying: "{preview}"',
|
|
963
|
+
loopTuiOnly: "/loop is only available in the interactive TUI (not in run/replay).",
|
|
964
|
+
loopStopped: "\u25B8 loop stopped.",
|
|
965
|
+
loopNoActive: "no active loop to stop.",
|
|
966
|
+
loopNoActiveHint: "no active loop. Start one with `/loop <interval> <prompt>` (e.g. /loop 30s npm test).\nCancels on: /loop stop \xB7 Esc \xB7 /clear /new \xB7 any user-typed prompt.",
|
|
967
|
+
loopStarted: '\u25B8 loop started \u2014 re-submitting "{prompt}" every {duration}. Type anything (or /loop stop) to cancel.'
|
|
968
|
+
},
|
|
969
|
+
admin: {
|
|
970
|
+
doctorNeedsTui: "/doctor needs a TUI context (postDoctor wired).",
|
|
971
|
+
doctorRunning: "\u2695 Doctor \u2014 running health checks\u2026",
|
|
972
|
+
hooksReloadUnavailable: "/hooks reload is not available in this context (no reload callback wired).",
|
|
973
|
+
hooksReloaded: "\u25B8 reloaded hooks \xB7 {count} active",
|
|
974
|
+
hooksUsage: "usage: /hooks list active hooks\n /hooks reload re-read settings.json files",
|
|
975
|
+
hooksNone: "no hooks configured.",
|
|
976
|
+
hooksDropHint: "drop a settings.json with a `hooks` key into either of:",
|
|
977
|
+
hooksProject: " \xB7 {path} (project)",
|
|
978
|
+
hooksProjectFallback: " \xB7 <project>/.reasonix/settings.json (project)",
|
|
979
|
+
hooksGlobal: " \xB7 {path} (global)",
|
|
980
|
+
hooksEvents: "events: PreToolUse, PostToolUse, UserPromptSubmit, Stop",
|
|
981
|
+
hooksExitCodes: "exit 0 = pass \xB7 exit 2 = block (Pre*) \xB7 other = warn",
|
|
982
|
+
hooksLoaded: "\u25B8 {count} hook(s) loaded",
|
|
983
|
+
hooksSources: "sources: project={project} \xB7 global={global}",
|
|
984
|
+
updateCurrent: "current: reasonix {version}",
|
|
985
|
+
updateLatestPending: "latest: (not yet resolved \u2014 background check in flight or offline)",
|
|
986
|
+
updateRetryHint: "triggered a fresh registry fetch \u2014 retry `/update` in a few seconds,",
|
|
987
|
+
updateRetryHint2: "or run `reasonix update` in another terminal to force it synchronously.",
|
|
988
|
+
updateLatest: "latest: reasonix {version}",
|
|
989
|
+
updateUpToDate: "you're on the latest. nothing to do.",
|
|
990
|
+
updateNpxHint: "you're running via npx \u2014 the next `npx reasonix ...` launch will auto-fetch.",
|
|
991
|
+
updateNpxForce: "to force a refresh sooner: `npm cache clean --force`.",
|
|
992
|
+
updateUpgradeHint: "to upgrade, exit this session and run:",
|
|
993
|
+
updateUpgradeCmd1: " reasonix update (interactive, dry-run supported via --dry-run)",
|
|
994
|
+
updateUpgradeCmd2: " npm install -g reasonix@latest (direct)",
|
|
995
|
+
updateInSessionDisabled: "in-session install is deliberately disabled \u2014 the npm spawn would",
|
|
996
|
+
updateInSessionDisabled2: "corrupt this TUI's rendering and Windows can lock the running binary.",
|
|
997
|
+
statsNoData: "no usage data yet.",
|
|
998
|
+
statsEveryTurn: "every turn you run here appends one record \u2014 this session's turns",
|
|
999
|
+
statsWillAppear: "will show up in the dashboard once you send a message."
|
|
1000
|
+
},
|
|
1001
|
+
edits: {
|
|
1002
|
+
undoCodeOnly: "/undo is only available inside `reasonix code` \u2014 chat mode doesn't apply edits.",
|
|
1003
|
+
historyCodeOnly: "/history is only available inside `reasonix code`.",
|
|
1004
|
+
showCodeOnly: "/show is only available inside `reasonix code`.",
|
|
1005
|
+
applyCodeOnly: "/apply is only available inside `reasonix code` (nothing to apply here).",
|
|
1006
|
+
discardCodeOnly: "/discard is only available inside `reasonix code`.",
|
|
1007
|
+
planCodeOnly: "/plan is only available inside `reasonix code` \u2014 chat mode doesn't gate tool writes.",
|
|
1008
|
+
planOn: "\u25B8 plan mode ON \u2014 write tools are gated; the model MUST call `submit_plan` before anything executes. (The model can also call submit_plan on its own for big tasks even when plan mode is off \u2014 this toggle is the stronger, explicit constraint.) Type /plan off to leave.",
|
|
1009
|
+
planOff: "\u25B8 plan mode OFF \u2014 write tools are live again. Model can still propose plans autonomously for large tasks.",
|
|
1010
|
+
modeCodeOnly: "/mode is only available inside `reasonix code`.",
|
|
1011
|
+
modeUsage: "usage: /mode <review|auto|yolo> (Shift+Tab also cycles)",
|
|
1012
|
+
modeYolo: "\u25B8 edit mode: YOLO \u2014 edits AND shell commands auto-run with no prompt. /undo still rolls back edits. Use carefully.",
|
|
1013
|
+
modeAuto: "\u25B8 edit mode: AUTO \u2014 edits apply immediately; press u within 5s to undo, or /undo later. Shell commands still ask.",
|
|
1014
|
+
modeReview: "\u25B8 edit mode: review \u2014 edits queue for /apply (or y) / /discard (or n)",
|
|
1015
|
+
commitCodeOnly: "/commit is only available inside `reasonix code` (needs a rooted git repo).",
|
|
1016
|
+
commitUsage: 'usage: /commit "your commit message" \u2014 runs `git add -A && git commit -m "\u2026"` in {root}',
|
|
1017
|
+
walkCodeOnly: "/walk is only available inside `reasonix code`.",
|
|
1018
|
+
checkpointCodeOnly: "/checkpoint is only available inside `reasonix code` \u2014 chat mode doesn't apply edits.",
|
|
1019
|
+
checkpointNone: "no checkpoints yet \u2014 `/checkpoint <name>` snapshots every file the session has touched. Restore later with `/restore <name>`.",
|
|
1020
|
+
checkpointHeader: "\u25C8 checkpoints \xB7 {count} stored",
|
|
1021
|
+
checkpointRestoreHint: " /restore <name|id> \xB7 /checkpoint forget <id> \xB7 /checkpoint <name> to add",
|
|
1022
|
+
checkpointForgetUsage: "usage: /checkpoint forget <id|name>",
|
|
1023
|
+
checkpointNoMatch: '\u25B8 no checkpoint matching "{name}" \u2014 see /checkpoint list',
|
|
1024
|
+
checkpointDeleted: "\u25B8 deleted checkpoint {id} ({name})",
|
|
1025
|
+
checkpointDeleteFailed: "\u25B8 failed to delete {id} (already gone?)",
|
|
1026
|
+
checkpointSaveUsage: "usage: /checkpoint <name> (or /checkpoint list to see existing)",
|
|
1027
|
+
checkpointSavedEmpty: `\u25B8 checkpoint "{name}" saved ({id}) \u2014 but no files have been touched yet, so it's an empty baseline. Edits made after this point will be revertable.`,
|
|
1028
|
+
checkpointSaved: '\u25B8 checkpoint "{name}" saved ({id}) \u2014 {files} file{s}, {size} KB. Restore: /restore {name}',
|
|
1029
|
+
restoreCodeOnly: "/restore is only available inside `reasonix code`.",
|
|
1030
|
+
restoreUsage: "usage: /restore <name|id> (see /checkpoint list for ids)",
|
|
1031
|
+
restoreNoMatch: '\u25B8 no checkpoint matching "{target}" \u2014 try /checkpoint list',
|
|
1032
|
+
restoreInfo: '\u25B8 restored "{name}" ({id}) from {when}',
|
|
1033
|
+
restoreWrote: " \xB7 wrote back {count} file{s}",
|
|
1034
|
+
restoreRemoved: " \xB7 removed {count} file{s} (didn't exist at checkpoint time)",
|
|
1035
|
+
restoreSkipped: " \u2717 {count} file{s} skipped:"
|
|
1036
|
+
},
|
|
1037
|
+
model: {
|
|
1038
|
+
modelHint: "try deepseek-v4-flash or deepseek-v4-pro \u2014 run /models to fetch the live list",
|
|
1039
|
+
modelUsage: "usage: /model <id> ({hint})",
|
|
1040
|
+
modelNotInCatalog: "model \u2192 {id} (\u26A0 not in the fetched catalog: {list}. If this is wrong the next call will 400 \u2014 run /models to refresh.)",
|
|
1041
|
+
modelSet: "model \u2192 {id}",
|
|
1042
|
+
presetAuto: "preset \u2192 auto (v4-flash \u2192 v4-pro on hard turns \xB7 default)",
|
|
1043
|
+
presetFlash: "preset \u2192 flash (v4-flash always \xB7 cheapest \xB7 /pro still bumps one turn)",
|
|
1044
|
+
presetPro: "preset \u2192 pro (v4-pro always \xB7 ~3\xD7 flash \xB7 for hard multi-turn work)",
|
|
1045
|
+
presetUsage: "usage: /preset <auto|flash|pro>",
|
|
1046
|
+
proNothingArmed: "nothing armed \u2014 /pro with no args will arm pro for your next turn",
|
|
1047
|
+
proDisarmed: "\u25B8 /pro disarmed \u2014 next turn falls back to the current preset",
|
|
1048
|
+
proUsage: "usage: /pro arm pro for the next turn (one-shot, auto-disarms after)\n /pro off cancel armed state before the next turn",
|
|
1049
|
+
proArmed: "\u25B8 /pro armed \u2014 your NEXT message runs on {model} regardless of preset. Auto-disarms after one turn. Use /preset max for a persistent switch.",
|
|
1050
|
+
budgetNoCap: "no session budget set \u2014 Reasonix will keep going until you stop it. Set one with: /budget <usd> (e.g. /budget 5)",
|
|
1051
|
+
budgetStatus: "budget: ${spent} of ${cap} ({pct}%) \xB7 /budget off to clear, /budget <usd> to change",
|
|
1052
|
+
budgetOff: "budget \u2192 off (no cap)",
|
|
1053
|
+
budgetUsage: 'usage: /budget <usd> (got "{arg}" \u2014 must be a positive number, e.g. /budget 5 or /budget 12.50)',
|
|
1054
|
+
budgetExhausted: "\u25B2 budget \u2192 ${cap} but already spent ${spent}. Next turn will be refused \u2014 bump the cap higher to keep going, or end the session.",
|
|
1055
|
+
budgetSet: "budget \u2192 ${cap} (so far: ${spent} \xB7 warns at 80%, refuses next turn at 100% \xB7 /budget off to clear)"
|
|
1056
|
+
},
|
|
1057
|
+
permissions: {
|
|
1058
|
+
mutateCodeOnly: "/permissions add / remove / clear are only available inside `reasonix code` \u2014 they edit the project-scoped allowlist (`~/.reasonix/config.json` projects[<root>].shellAllowed).",
|
|
1059
|
+
addUsage: 'usage: /permissions add <prefix> (multi-token OK: /permissions add "git push origin")',
|
|
1060
|
+
addAlready: "\u25B8 already allowed: {prefix}",
|
|
1061
|
+
addBuiltin: "\u25B8 `{prefix}` is already in the builtin allowlist \u2014 no per-project entry needed. (Builtin entries are always on.)",
|
|
1062
|
+
addInfo: "\u25B8 added: {prefix}\n \u2192 next `{prefix}` invocation runs without prompting in this project.",
|
|
1063
|
+
removeUsage: "usage: /permissions remove <prefix-or-index> (e.g. /permissions remove 3, or /permissions remove npm)",
|
|
1064
|
+
removeEmpty: "\u25B8 no project allowlist entries to remove.",
|
|
1065
|
+
removeIndexOob: "\u25B8 index out of range: {idx} (project list has {count} entries)",
|
|
1066
|
+
removeNothing: "\u25B8 nothing to remove.",
|
|
1067
|
+
removeBuiltin: "\u25B8 `{prefix}` is in the builtin allowlist (read-only). Builtin entries can't be removed at runtime \u2014 they're baked into the binary.",
|
|
1068
|
+
removeInfo: "\u25B8 removed: {prefix}",
|
|
1069
|
+
removeNotFound: "\u25B8 no such project entry: {prefix} (try /permissions list to see what's stored)",
|
|
1070
|
+
clearAlready: "\u25B8 project allowlist is already empty.",
|
|
1071
|
+
clearConfirm: "about to drop {count} project allowlist entr{plural} for {root}. Re-run with the word 'confirm' to proceed: /permissions clear confirm",
|
|
1072
|
+
clearedNone: "\u25B8 project allowlist was already empty \u2014 nothing changed.",
|
|
1073
|
+
cleared: "\u25B8 cleared {count} project allowlist entr{plural}.",
|
|
1074
|
+
usage: 'usage: /permissions [list] show current state\n /permissions add <prefix> persist (e.g. "npm run build")\n /permissions remove <prefix-or-N> drop one entry\n /permissions clear confirm wipe every project entry',
|
|
1075
|
+
modeYolo: "\u25B8 edit mode: YOLO \u2014 every shell command auto-runs, allowlist is bypassed. /mode review to re-enable prompts.",
|
|
1076
|
+
modeAuto: "\u25B8 edit mode: auto \u2014 edits auto-apply, shell still gated by allowlist (or ShellConfirm prompt for non-allowlisted).",
|
|
1077
|
+
modeReview: "\u25B8 edit mode: review \u2014 both edits and non-allowlisted shell commands ask before running.",
|
|
1078
|
+
projectHeader: "Project allowlist ({count}) \u2014 {root}",
|
|
1079
|
+
projectNone1: ' (none \u2014 pick "always allow" on a ShellConfirm prompt to add one,',
|
|
1080
|
+
projectNone2: " or `/permissions add <prefix>` directly.)",
|
|
1081
|
+
projectNoRoot: "Project allowlist \u2014 (no project root; chat mode shows builtin entries only)",
|
|
1082
|
+
builtinHeader: "Builtin allowlist ({count}) \u2014 read-only, baked in",
|
|
1083
|
+
subcommands: "Subcommands: /permissions add <prefix> \xB7 /permissions remove <prefix-or-N> \xB7 /permissions clear confirm"
|
|
1084
|
+
},
|
|
1085
|
+
dashboard: {
|
|
1086
|
+
notAvailable: "/dashboard is not available in this context (no startDashboard callback wired).",
|
|
1087
|
+
stopNoCallback: "/dashboard stop: no stop callback wired.",
|
|
1088
|
+
notRunning: "\u25B8 dashboard is not running.",
|
|
1089
|
+
stopping: "\u25B8 dashboard stopping\u2026",
|
|
1090
|
+
alreadyRunning: "\u25B8 dashboard is already running:",
|
|
1091
|
+
alreadyRunningHint: "Open it in any browser. Type `/dashboard stop` to tear it down.",
|
|
1092
|
+
ready: "\u25B8 dashboard ready:",
|
|
1093
|
+
readyHint: "127.0.0.1 only \xB7 token-gated. Type `/dashboard stop` to shut down.",
|
|
1094
|
+
failed: "\u25B8 dashboard failed to start: {reason}",
|
|
1095
|
+
starting: "\u25B8 starting dashboard server\u2026"
|
|
1096
|
+
},
|
|
1097
|
+
observability: {
|
|
1098
|
+
contextInfo: "context: ~{total} of {max} ({pct}%) \xB7 system {sys} \xB7 tools {tools} \xB7 log {log}",
|
|
1099
|
+
compactStarting: "\u25B8 folding older turns into a summary\u2026",
|
|
1100
|
+
compactNoop: "\u25B8 nothing to fold \u2014 log already small or recent turns alone exceed the budget.",
|
|
1101
|
+
compactDone: "\u25B8 folded {before} messages \u2192 {after} (summary {chars} chars). Continuing.",
|
|
1102
|
+
compactFailed: "\u25B8 fold failed: {reason}",
|
|
1103
|
+
costNoTurn: "no turn yet \u2014 `/cost` shows the most recent turn's token + spend breakdown.",
|
|
1104
|
+
costNeedsTui: "/cost needs a TUI context (postUsage wired).",
|
|
1105
|
+
costNoPricing: '\u25B8 /cost: no pricing table for model "{model}". Add one to telemetry/stats.ts.',
|
|
1106
|
+
costEstimate: "\u25B8 /cost estimate \xB7 {model} \xB7 {prompt} prompt tokens (sys {sys} + tools {tools} + log {log} + msg {msg})",
|
|
1107
|
+
costWorstCase: " worst case (full miss): {input} input + ~{output} output ({avg} avg) \u2248 {total}",
|
|
1108
|
+
costLikely: " likely ({pct}% session cache hit): {input} input + ~{output} output \u2248 {total}",
|
|
1109
|
+
costLikelyCold: " likely: matches worst case until cache fills (no completed turns yet)",
|
|
1110
|
+
statusModel: " model {model}",
|
|
1111
|
+
statusFlags: " flags stream={stream} \xB7 effort={effort}",
|
|
1112
|
+
statusCtx: " ctx {bar} {used}/{max} ({pct}%)",
|
|
1113
|
+
statusCtxNone: " ctx no turns yet",
|
|
1114
|
+
statusCost: " cost ${cost} \xB7 cache {bar} {pct}% \xB7 turns {turns}",
|
|
1115
|
+
statusCostCold: " cost ${cost} \xB7 turns {turns} (cache warming up)",
|
|
1116
|
+
statusBudget: " budget ${spent} / ${cap} ({pct}%){tag}",
|
|
1117
|
+
statusSession: ' session "{name}" \xB7 {count} messages in log (resumed {resumed})',
|
|
1118
|
+
statusSessionEphemeral: " session (ephemeral \u2014 no persistence)",
|
|
1119
|
+
statusWorkspace: " workspace {path} \xB7 pinned at launch (relaunch with --dir <path> to switch)",
|
|
1120
|
+
statusMcp: " mcp {servers} server(s), {tools} tool(s) in registry",
|
|
1121
|
+
statusEdits: " edits {count} pending (/apply to commit, /discard to drop)",
|
|
1122
|
+
statusPlan: " plan ON \u2014 writes gated (submit_plan + approval)",
|
|
1123
|
+
statusModeYolo: " mode YOLO \u2014 edits + shell auto-run with no prompt (/undo still rolls back \xB7 Shift+Tab to flip)",
|
|
1124
|
+
statusModeAuto: " mode AUTO \u2014 edits apply immediately (u to undo within 5s \xB7 Shift+Tab to flip)",
|
|
1125
|
+
statusModeReview: " mode review \u2014 edits queue for /apply or y (Shift+Tab to flip)",
|
|
1126
|
+
statusDash: " dash {url} (open in browser \xB7 /dashboard stop)"
|
|
1127
|
+
},
|
|
1128
|
+
plans: {
|
|
1129
|
+
noSession: "no session attached \u2014 `/plans` is per-session. Run `reasonix code` in a project to get a session.",
|
|
1130
|
+
activePlan: "\u25B8 active plan{label} \u2014 {done}/{total} step{s} done \xB7 last touched {when}",
|
|
1131
|
+
activeNone: "\u25B8 active plan: (none)",
|
|
1132
|
+
noArchives: "no archived plans yet for this session \u2014 they auto-archive when every step is done",
|
|
1133
|
+
archivedHeader: "Archived ({count}):",
|
|
1134
|
+
replayNoSession: "no session attached \u2014 `/replay` is per-session. Run `reasonix code` in a project to get a session.",
|
|
1135
|
+
replayNoArchives: "no archived plans yet for this session \u2014 `/replay` lights up once a plan completes (auto-archives when every step is done).",
|
|
1136
|
+
replayInvalidIndex: "invalid index \u2014 `/replay` takes 1..{max} (newest = 1). Use `/plans` to see the list.",
|
|
1137
|
+
archivedRow: " \u2713 {when} {total} step{s} \xB7 {completion} {label}",
|
|
1138
|
+
completionComplete: "complete",
|
|
1139
|
+
stopAborted: "\u25B8 plan stopped \u2014 model aborted; type a follow-up to continue or start a new task."
|
|
1140
|
+
},
|
|
1141
|
+
jobs: {
|
|
1142
|
+
codeOnly: "/jobs is only available inside `reasonix code`.",
|
|
1143
|
+
killCodeOnly: "/kill is only available inside `reasonix code`.",
|
|
1144
|
+
logsCodeOnly: "/logs is only available inside `reasonix code`.",
|
|
1145
|
+
empty: "\u25C8 jobs \xB7 0 running \xB7 0 total\n (run_background spawns one \u2014 dev servers, watchers, long-running scripts)",
|
|
1146
|
+
header: "\u25C8 jobs \xB7 {running} running \xB7 {total} total",
|
|
1147
|
+
footer: " /logs <id> tail \xB7 /kill <id> SIGTERM \u2192 SIGKILL",
|
|
1148
|
+
killUsage: "usage: /kill <id> (see /jobs for ids)",
|
|
1149
|
+
killNotFound: "job {id}: not found",
|
|
1150
|
+
killAlreadyExited: "job {id} already exited ({code})",
|
|
1151
|
+
killStopping: "\u25B8 stopping job {id} (tree kill: SIGTERM \u2192 SIGKILL after 2s grace; Windows: taskkill /T /F)",
|
|
1152
|
+
killStatus: "\u25B8 job {id} {status}",
|
|
1153
|
+
killStillAlive: "still alive after SIGKILL (!) \u2014 report this as a bug",
|
|
1154
|
+
logsUsage: "usage: /logs <id> [lines] (default last 80 lines)",
|
|
1155
|
+
logsNotFound: "job {id}: not found",
|
|
1156
|
+
logsStatus: "[job {id} \xB7 {status}]\n$ {command}",
|
|
1157
|
+
logsRunning: "running \xB7 pid {pid}",
|
|
1158
|
+
logsExited: "exited {code}",
|
|
1159
|
+
logsFailed: "failed ({reason})",
|
|
1160
|
+
logsStopped: "stopped"
|
|
1161
|
+
},
|
|
1162
|
+
memory: {
|
|
1163
|
+
disabled: "memory is disabled (REASONIX_MEMORY=off in env). Unset the var to re-enable \u2014 no REASONIX.md or ~/.reasonix/memory content will be pinned in the meantime.",
|
|
1164
|
+
noRoot: "no working directory on this session \u2014 `/memory` needs a root to resolve REASONIX.md from. (Running in a test harness?)",
|
|
1165
|
+
listEmpty: "no user memories yet. The model can call `remember` to save one, or you can create files by hand in ~/.reasonix/memory/global/ or the per-project subdir.",
|
|
1166
|
+
listHeader: "User memories ({count}):",
|
|
1167
|
+
listFooter: "View body: /memory show <name> Delete: /memory forget <name>",
|
|
1168
|
+
showUsage: "usage: /memory show <name> or /memory show <scope>/<name>",
|
|
1169
|
+
showNotFound: "no memory found: {target}",
|
|
1170
|
+
showFailed: "show failed: {reason}",
|
|
1171
|
+
forgetUsage: "usage: /memory forget <name> or /memory forget <scope>/<name>",
|
|
1172
|
+
forgetNotFound: "no memory found: {target}",
|
|
1173
|
+
forgetInfo: "\u25B8 forgot {scope}/{name}. Next /new or launch won't see it.",
|
|
1174
|
+
forgetFailed: "could not forget {scope}/{name} (already gone?)",
|
|
1175
|
+
forgetError: "forget failed: {reason}",
|
|
1176
|
+
clearUsage: "usage: /memory clear <global|project> confirm",
|
|
1177
|
+
clearConfirm: "about to delete every memory in scope={scope}. Re-run with the word 'confirm' to proceed: /memory clear {scope} confirm",
|
|
1178
|
+
cleared: "\u25B8 cleared scope={scope} \u2014 deleted {count} memory file(s).",
|
|
1179
|
+
noMemory: "no memory pinned in {root}.",
|
|
1180
|
+
layers: "Three layers are available:",
|
|
1181
|
+
layerProject: " 1. {file} \u2014 committable team memory (in the repo).",
|
|
1182
|
+
layerGlobal: " 2. ~/.reasonix/memory/global/ \u2014 your cross-project private memory.",
|
|
1183
|
+
layerProjectHash: " 3. ~/.reasonix/memory/<project-hash>/ \u2014 this project's private memory.",
|
|
1184
|
+
askModel: "Ask the model to `remember` something, or hand-edit files directly.",
|
|
1185
|
+
changesNote: "Changes take effect on next /new or launch \u2014 the system prompt is hashed once per session to keep the prefix cache warm.",
|
|
1186
|
+
subcommands: "Subcommands: /memory list | /memory show <name> | /memory forget <name> | /memory clear <scope> confirm",
|
|
1187
|
+
changesNoteShort: "Changes take effect on next /new or launch. Subcommands: /memory list | show | forget | clear"
|
|
1188
|
+
},
|
|
1189
|
+
mcp: {
|
|
1190
|
+
noServers: 'no MCP servers attached. Run `reasonix setup` to pick some, or launch with --mcp "<spec>". `reasonix mcp list` shows the catalog.',
|
|
1191
|
+
toolsLabel: " tools {count}",
|
|
1192
|
+
resourcesHint: "`/resource` to browse+read",
|
|
1193
|
+
promptsHint: "`/prompt` to browse+fetch",
|
|
1194
|
+
awarenessOnly: "Chat mode consumes tools today; resources+prompts are surfaced here for awareness.",
|
|
1195
|
+
catalogHint: "Full catalog: `reasonix mcp list` \xB7 deeper diagnosis: `reasonix mcp inspect <spec>`.",
|
|
1196
|
+
fallbackServers: "MCP servers ({count}):",
|
|
1197
|
+
fallbackTools: "Tools in registry ({count}):",
|
|
1198
|
+
fallbackChange: "To change this set, exit and run `reasonix setup`.",
|
|
1199
|
+
usageDisableEnable: "usage: /mcp {action} <name> \xB7 pick a name shown in /mcp (anonymous servers can't be named-toggled).",
|
|
1200
|
+
usageReconnect: "usage: /mcp reconnect <name> \xB7 pick a name shown in /mcp.",
|
|
1201
|
+
unknownServer: 'unknown MCP server "{name}". Known: {list}.',
|
|
1202
|
+
noneList: "(none)",
|
|
1203
|
+
reconnectNoTui: "/mcp reconnect requires the interactive TUI (postInfo not wired)."
|
|
1204
|
+
},
|
|
1205
|
+
init: {
|
|
1206
|
+
codeOnly: "/init only works in code mode (it needs filesystem tools).\nRun `reasonix code [path]` to start a session rooted at the\nproject you want to initialize, then run /init.",
|
|
1207
|
+
exists: "\u25B8 REASONIX.md already exists at {path}",
|
|
1208
|
+
existsForce: " /init force regenerate from scratch (overwrites)",
|
|
1209
|
+
existsEdit: " Or edit it by hand \u2014 it's just markdown. The current file is",
|
|
1210
|
+
existsPinned: " pinned into the system prompt every launch as-is.",
|
|
1211
|
+
info: "\u25B8 /init \u2014 model will scan the project and synthesize REASONIX.md.\n The result lands as a pending edit; review with /apply or /walk."
|
|
1212
|
+
},
|
|
1213
|
+
webSearchEngine: {
|
|
1214
|
+
currentEngine: "Current web search engine: {engine}",
|
|
1215
|
+
endpoint: "SearXNG endpoint: {url}",
|
|
1216
|
+
usageHeader: "Usage:",
|
|
1217
|
+
usageMojeek: " /search-engine mojeek use Mojeek (default, no external deps)",
|
|
1218
|
+
usageSearxng: " /search-engine searxng use SearXNG at default endpoint",
|
|
1219
|
+
usageSearxngUrl: " /search-engine searxng <url> use SearXNG at custom endpoint",
|
|
1220
|
+
alias: "Alias: /se",
|
|
1221
|
+
searxngInfo: "SearXNG is a self-hosted metasearch engine (https://github.com/searxng/searxng).",
|
|
1222
|
+
searxngInstall: "Install it with: docker run -d -p 8080:8080 searxng/searxng",
|
|
1223
|
+
switched: 'Switched web search engine to "{engine}".{note}',
|
|
1224
|
+
switchedSearxngNote: " Make sure SearXNG is running at {endpoint}.",
|
|
1225
|
+
confirmed: '\u2713 Web search engine set to "{engine}"{detail}. Next assistant turn will pick up the change.',
|
|
1226
|
+
confirmedDetail: " ({endpoint})"
|
|
1227
|
+
},
|
|
1228
|
+
skill: {
|
|
1229
|
+
listEmpty: "no skills found. Reasonix reads skills from:",
|
|
1230
|
+
listProjectScope: " \xB7 <project>/.reasonix/skills/<name>/SKILL.md (or <name>.md) \u2014 project scope",
|
|
1231
|
+
listGlobalScope: " \xB7 ~/.reasonix/skills/<name>/SKILL.md (or <name>.md) \u2014 global scope",
|
|
1232
|
+
listProjectOnly: " (project scope is only active in `reasonix code`)",
|
|
1233
|
+
listFrontmatter: "Each file's frontmatter needs at least `name` and `description`.",
|
|
1234
|
+
listInvoke: "Invoke a skill with `/skill <name> [args]` or by asking the model to call `run_skill`.",
|
|
1235
|
+
listHeader: "User skills ({count}):",
|
|
1236
|
+
listFooter: "View: /skill show <name> Run: /skill <name> [args] New: /skill new <name>",
|
|
1237
|
+
listEmptyNewHint: "Scaffold one with: /skill new <name> (project scope) \u2014 there's no remote registry yet; you author skills directly.",
|
|
1238
|
+
showUsage: "usage: /skill show <name>",
|
|
1239
|
+
showNotFound: "no skill found: {name}",
|
|
1240
|
+
runNotFound: "no skill found: {name} (try /skill list)",
|
|
1241
|
+
runInfo: "\u25B8 running skill: {name}{args}",
|
|
1242
|
+
newUsage: "usage: /skill new <name> [--global]",
|
|
1243
|
+
newCreated: "\u25B8 created skill: {name}\n {path}\n edit it, then `/skill {name}` to invoke",
|
|
1244
|
+
newError: "\u25B2 /skill new failed: {reason}"
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
// src/i18n/zh-CN.ts
|
|
1250
|
+
var zhCN = {
|
|
1251
|
+
common: {
|
|
1252
|
+
error: "\u9519\u8BEF",
|
|
1253
|
+
warning: "\u8B66\u544A",
|
|
1254
|
+
loading: "\u52A0\u8F7D\u4E2D...",
|
|
1255
|
+
done: "\u5B8C\u6210",
|
|
1256
|
+
cancel: "\u53D6\u6D88",
|
|
1257
|
+
confirm: "\u786E\u8BA4",
|
|
1258
|
+
back: "\u8FD4\u56DE",
|
|
1259
|
+
next: "\u4E0B\u4E00\u6B65"
|
|
1260
|
+
},
|
|
1261
|
+
cli: {
|
|
1262
|
+
description: "DeepSeek \u539F\u751F\u667A\u80FD\u4F53\u6846\u67B6 \u2014 \u4E13\u4E3A\u7F13\u5B58\u547D\u4E2D\u548C\u4F4E\u6210\u672C\u4EE4\u724C\u6784\u5EFA\u3002",
|
|
1263
|
+
continue: "\u6062\u590D\u6700\u8FD1\u4F7F\u7528\u7684\u804A\u5929\u4F1A\u8BDD\uFF0C\u4E0D\u663E\u793A\u9009\u62E9\u5668\u3002",
|
|
1264
|
+
setup: "\u4EA4\u4E92\u5F0F\u5411\u5BFC \u2014 API \u5BC6\u94A5\u3001\u9884\u8BBE\u3001MCP \u670D\u52A1\u5668\u3002\u968F\u65F6\u91CD\u65B0\u8FD0\u884C\u4EE5\u91CD\u65B0\u914D\u7F6E\u3002",
|
|
1265
|
+
code: "\u4EE3\u7801\u7F16\u8F91\u804A\u5929 \u2014 \u4EE5 <dir>\uFF08\u9ED8\u8BA4\uFF1Acwd\uFF09\u4E3A\u6839\u7684\u6587\u4EF6\u7CFB\u7EDF\u5DE5\u5177\uFF0C\u7F16\u7801\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF0Cv4-flash \u57FA\u7EBF\u3002",
|
|
1266
|
+
chat: "\u5177\u6709\u5B9E\u65F6\u7F13\u5B58/\u6210\u672C\u9762\u677F\u7684\u4EA4\u4E92\u5F0F Ink TUI\u3002",
|
|
1267
|
+
run: "\u4EE5\u975E\u4EA4\u4E92\u65B9\u5F0F\u8FD0\u884C\u5355\u4E2A\u4EFB\u52A1\uFF0C\u6D41\u5F0F\u8F93\u51FA\u3002",
|
|
1268
|
+
stats: "\u663E\u793A\u4F7F\u7528\u60C5\u51B5\u4EEA\u8868\u677F\u3002",
|
|
1269
|
+
doctor: "\u4E00\u952E\u5065\u5EB7\u68C0\u67E5\u3002",
|
|
1270
|
+
commit: "\u4ECE\u6682\u5B58\u7684\u5DEE\u5F02\u4E2D\u8D77\u8349\u63D0\u4EA4\u6D88\u606F\u3002",
|
|
1271
|
+
sessions: "\u5217\u51FA\u4FDD\u5B58\u7684\u804A\u5929\u4F1A\u8BDD\uFF0C\u6216\u6309\u540D\u79F0\u68C0\u67E5\u3002",
|
|
1272
|
+
pruneSessions: "\u5220\u9664\u7A7A\u95F2 \u2265N \u5929\u7684\u5DF2\u4FDD\u5B58\u4F1A\u8BDD\uFF08\u9ED8\u8BA4 90\uFF09\u3002\u4F7F\u7528 --dry-run \u9884\u89C8\u3002",
|
|
1273
|
+
events: "\u7F8E\u5316\u6253\u5370\u5185\u6838\u4E8B\u4EF6\u65E5\u5FD7\u4FA7\u8FB9\u6587\u4EF6\u3002",
|
|
1274
|
+
replay: "\u4EA4\u4E92\u5F0F Ink TUI\uFF0C\u7528\u4E8E\u6D4F\u89C8\u8F6C\u5F55\u7A3F\u3002",
|
|
1275
|
+
diff: "\u5728\u5206\u680F Ink TUI \u4E2D\u6BD4\u8F83\u4E24\u4E2A\u8F6C\u5F55\u7A3F\u3002",
|
|
1276
|
+
mcp: "\u6A21\u578B\u4E0A\u4E0B\u6587\u534F\u8BAE (MCP) \u52A9\u624B \u2014 \u53D1\u73B0\u670D\u52A1\u5668\uFF0C\u6D4B\u8BD5\u60A8\u7684\u8BBE\u7F6E\u3002",
|
|
1277
|
+
version: "\u6253\u5370 Reasonix \u7248\u672C\u3002",
|
|
1278
|
+
update: "\u68C0\u67E5\u8F83\u65B0\u7248\u672C\u7684 Reasonix \u5E76\u5B89\u88C5\u3002",
|
|
1279
|
+
index: "\u6784\u5EFA\uFF08\u6216\u589E\u91CF\u5237\u65B0\uFF09\u672C\u5730\u8BED\u4E49\u641C\u7D22\u7D22\u5F15\u3002"
|
|
1280
|
+
},
|
|
1281
|
+
ui: {
|
|
1282
|
+
welcome: "\u968F\u65F6\u8FD0\u884C `reasonix` \u5F00\u59CB\u804A\u5929 \u2014 \u60A8\u7684\u8BBE\u7F6E\u5C06\u88AB\u8BB0\u4F4F\u3002",
|
|
1283
|
+
taglineChat: "DeepSeek \u539F\u751F\u667A\u80FD\u4F53",
|
|
1284
|
+
taglineCode: "DeepSeek \u539F\u751F\u4EE3\u7801\u667A\u80FD\u4F53",
|
|
1285
|
+
taglineSub: "\u7F13\u5B58\u4F18\u5148 \xB7 Flash \u4F18\u5148",
|
|
1286
|
+
startSessionHint: "\u8F93\u5165\u6D88\u606F\u4EE5\u5F00\u59CB\u60A8\u7684\u4F1A\u8BDD",
|
|
1287
|
+
inputPlaceholder: "\u8F93\u5165\u4EFB\u4F55\u5185\u5BB9... (\u8F93\u5165 / \u4F7F\u7528\u547D\u4EE4, @ \u5F15\u7528\u6587\u4EF6)",
|
|
1288
|
+
busy: "\u601D\u8003\u4E2D...",
|
|
1289
|
+
thinking: "\u25B8 \u601D\u8003\u4E2D...",
|
|
1290
|
+
undo: "\u64A4\u6D88",
|
|
1291
|
+
undoHint: "\u5728 5 \u79D2\u5185\u6309 u \u64A4\u6D88",
|
|
1292
|
+
applied: "\u5DF2\u5E94\u7528",
|
|
1293
|
+
rejected: "\u5DF2\u62D2\u7EDD",
|
|
1294
|
+
noDashboard: "\u7981\u6B62\u81EA\u52A8\u542F\u52A8\u5D4C\u5165\u5F0F Web \u4EEA\u8868\u677F\u3002",
|
|
1295
|
+
dashboardAutoStartFailed: "\u25B2 \u4EEA\u8868\u677F\u81EA\u52A8\u542F\u52A8\u5931\u8D25 ({reason}) \u2014 \u5C1D\u8BD5 /dashboard\uFF0C\u6216\u4F20\u9012 --no-dashboard \u4EE5\u9759\u9ED8",
|
|
1296
|
+
systemAppendHint: "\u8FFD\u52A0\u6307\u4EE4\u5230\u4EE3\u7801\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3002\u4E0D\u66FF\u6362\u9ED8\u8BA4\u63D0\u793A\u8BCD \u2014 \u5728\u5176\u540E\u6DFB\u52A0\u3002",
|
|
1297
|
+
systemAppendFileHint: "\u8FFD\u52A0\u6587\u4EF6\u5185\u5BB9\u5230\u4EE3\u7801\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3002\u4E0D\u66FF\u6362\u9ED8\u8BA4\u63D0\u793A\u8BCD\u3002UTF-8\uFF0C\u76F8\u5BF9\u4E8E cwd \u6216\u7EDD\u5BF9\u8DEF\u5F84\u3002",
|
|
1298
|
+
resumedSession: '\u25B8 \u5DF2\u6062\u590D\u4F1A\u8BDD "{name}"\uFF0C\u5305\u542B {count} \u6761\u5386\u53F2\u6D88\u606F \xB7 /forget \u91CD\u65B0\u5F00\u59CB \xB7 /sessions \u5217\u51FA',
|
|
1299
|
+
newSession: '\u25B8 \u4F1A\u8BDD "{name}" (\u65B0) \u2014 \u968F\u804A\u968F\u5B58 \xB7 /forget \u5220\u9664 \xB7 /sessions \u5217\u51FA',
|
|
1300
|
+
ephemeralSession: "\u25B8 \u4E34\u65F6\u804A\u5929 (\u4E0D\u4FDD\u5B58\u4F1A\u8BDD) \u2014 \u53BB\u6389 --no-session \u4EE5\u542F\u7528\u4FDD\u5B58",
|
|
1301
|
+
restoredEdits: "\u25B8 \u4ECE\u4E2D\u65AD\u7684\u8FD0\u884C\u4E2D\u6062\u590D\u4E86 {count} \u4E2A\u5F85\u5904\u7406\u7684\u7F16\u8F91\u5757 \u2014 /apply \u63D0\u4EA4\u6216 /discard \u653E\u5F03\u3002",
|
|
1302
|
+
resumedPlan: "\u5DF2\u6062\u590D\u8BA1\u5212 \xB7 {when}{summary}",
|
|
1303
|
+
tipEditBindings: "\u25B8 \u63D0\u793A\uFF1A\u7F16\u8F91\u95E8\u63A7\u5FEB\u6377\u952E\n y / n \u63A5\u53D7\u6216\u653E\u5F03\u5F85\u5904\u7406\u7684\u7F16\u8F91\n Shift+Tab \u5207\u6362 \u9884\u89C8 \u2194 \u81EA\u52A8 (\u6301\u4E45\u5316\uFF1B\u81EA\u52A8\u6A21\u5F0F\u7ACB\u5373\u5E94\u7528)\n u \u64A4\u6D88\u4E0A\u6B21\u81EA\u52A8\u5E94\u7528\u7684\u6279\u5904\u7406 (\u5728 5 \u79D2\u6A2A\u5E45\u5185)\n \u5F53\u524D\u6A21\u5F0F\u663E\u793A\u5728\u5E95\u90E8\u72B6\u6001\u680F\u3002\u968F\u65F6\u8FD0\u884C /keys \u67E5\u770B\u5B8C\u6574\u5217\u8868\u3002\n (\u6B64\u63D0\u793A\u4EC5\u663E\u793A\u4E00\u6B21 \u2014 \u4E4B\u540E\u5C06\u9690\u85CF\u3002)",
|
|
1304
|
+
modelOverride: "\u8986\u76D6\u9ED8\u8BA4\u6A21\u578B",
|
|
1305
|
+
noSession: "\u7981\u7528\u672C\u6B21\u8FD0\u884C\u7684\u4F1A\u8BDD\u6301\u4E45\u5316",
|
|
1306
|
+
resumeHint: "\u5F3A\u5236\u6062\u590D\u6307\u5B9A\u4F1A\u8BDD\uFF08\u5373\u4F7F\u7A7A\u95F2\uFF09",
|
|
1307
|
+
newHint: "\u5F3A\u5236\u521B\u5EFA\u65B0\u4F1A\u8BDD\uFF08\u5FFD\u7565 --session / --continue\uFF09",
|
|
1308
|
+
transcriptHint: "JSONL \u8F6C\u5F55\u7A3F\u7684\u5199\u5165\u8DEF\u5F84",
|
|
1309
|
+
budgetHint: "\u4F1A\u8BDD\u7F8E\u5143\u4E0A\u9650 \u2014 80% \u65F6\u8B66\u544A\uFF0C100% \u65F6\u62D2\u7EDD\u4E0B\u4E00\u8F6E",
|
|
1310
|
+
modelIdHint: "DeepSeek \u6A21\u578B ID\uFF08\u4F8B\u5982 deepseek-v4-flash\uFF09",
|
|
1311
|
+
systemPromptHint: "\u8986\u76D6\u9ED8\u8BA4\u7CFB\u7EDF\u63D0\u793A\u8BCD",
|
|
1312
|
+
presetHint: "\u6A21\u578B\u7EC4\u5408 \u2014 auto|flash|pro",
|
|
1313
|
+
sessionNameHint: "\u4F1A\u8BDD\u540D\u79F0\uFF08\u9ED8\u8BA4\uFF1A'default'\uFF09",
|
|
1314
|
+
ephemeralHint: "\u7981\u7528\u672C\u6B21\u8FD0\u884C\u7684\u4F1A\u8BDD\u6301\u4E45\u5316",
|
|
1315
|
+
mcpSpecHint: "MCP \u670D\u52A1\u5668\u89C4\u683C\uFF08\u53EF\u91CD\u590D\uFF09",
|
|
1316
|
+
mcpPrefixHint: "\u7528\u6B64\u5B57\u7B26\u4E32\u4E3A MCP \u5DE5\u5177\u540D\u6DFB\u52A0\u524D\u7F00",
|
|
1317
|
+
noConfigHint: "\u672C\u6B21\u8FD0\u884C\u5FFD\u7565 ~/.reasonix/config.json",
|
|
1318
|
+
presetHintShort: "\u6A21\u578B\u7EC4\u5408 \u2014 auto|flash|pro",
|
|
1319
|
+
budgetHintShort: "\u4F1A\u8BDD\u7F8E\u5143\u4E0A\u9650",
|
|
1320
|
+
transcriptHintShort: "JSONL \u8F6C\u5F55\u7A3F\u8DEF\u5F84",
|
|
1321
|
+
mcpSpecHintShort: "MCP \u670D\u52A1\u5668\u89C4\u683C\uFF08\u53EF\u91CD\u590D\uFF09",
|
|
1322
|
+
mcpPrefixHintShort: "MCP \u5DE5\u5177\u540D\u524D\u7F00",
|
|
1323
|
+
dryRunHint: "\u663E\u793A\u5C06\u8981\u5B89\u88C5\u7684\u5185\u5BB9\u4F46\u4E0D\u5B9E\u9645\u5B89\u88C5",
|
|
1324
|
+
rebuildHint: "\u4ECE\u5934\u91CD\u5EFA\u7D22\u5F15",
|
|
1325
|
+
embedModelHint: "\u5D4C\u5165\u6A21\u578B\u540D\u79F0",
|
|
1326
|
+
projectDirHint: "\u9879\u76EE\u6839\u76EE\u5F55",
|
|
1327
|
+
ollamaUrlHint: "Ollama \u670D\u52A1\u5668 URL",
|
|
1328
|
+
skipPromptsHint: "\u8DF3\u8FC7\u786E\u8BA4\u63D0\u793A",
|
|
1329
|
+
verboseHint: "\u663E\u793A\u5B8C\u6574\u7684\u4F1A\u8BDD\u5143\u6570\u636E",
|
|
1330
|
+
pruneDaysHint: "\u5220\u9664\u7A7A\u95F2\u6B64\u5929\u6570\u6216\u66F4\u591A\u7684\u4F1A\u8BDD\uFF08\u9ED8\u8BA4 90\uFF09",
|
|
1331
|
+
pruneDryRunHint: "\u5217\u51FA\u5C06\u8981\u5220\u9664\u7684\u5185\u5BB9\u4F46\u4E0D\u5B9E\u9645\u5220\u9664",
|
|
1332
|
+
eventTypeHint: "\u6309\u4E8B\u4EF6\u7C7B\u578B\u8FC7\u6EE4",
|
|
1333
|
+
eventSinceHint: "\u4ECE\u6B64\u4E8B\u4EF6 ID \u5F00\u59CB",
|
|
1334
|
+
eventTailHint: "\u4EC5\u663E\u793A\u6700\u540E N \u4E2A\u4E8B\u4EF6",
|
|
1335
|
+
jsonHint: "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA",
|
|
1336
|
+
projectionHint: "\u663E\u793A\u6BCF\u4E2A\u4E8B\u4EF6\u7684\u6295\u5F71\u72B6\u6001",
|
|
1337
|
+
printHint: "\u6253\u5370\u5230\u6807\u51C6\u8F93\u51FA\u800C\u975E TUI",
|
|
1338
|
+
headHint: "\u4EC5\u663E\u793A\u524D N \u4E2A\u4E8B\u4EF6",
|
|
1339
|
+
tailHint: "\u4EC5\u663E\u793A\u6700\u540E N \u4E2A\u4E8B\u4EF6",
|
|
1340
|
+
mdReportHint: "\u5C06 markdown \u5DEE\u5F02\u62A5\u544A\u5199\u5165\u6B64\u8DEF\u5F84",
|
|
1341
|
+
printHintTable: "\u6253\u5370\u8868\u683C\u5230\u6807\u51C6\u8F93\u51FA",
|
|
1342
|
+
tuiHint: "\u6253\u5F00\u4EA4\u4E92\u5F0F TUI",
|
|
1343
|
+
labelAHint: "\u5DE6\u4FA7\u9762\u677F\u7684\u6807\u7B7E",
|
|
1344
|
+
labelBHint: "\u53F3\u4FA7\u9762\u677F\u7684\u6807\u7B7E",
|
|
1345
|
+
mcpListDescription: "\u6D4F\u89C8 MCP \u6CE8\u518C\u8868\uFF08\u5B98\u65B9 \u2192 smithery \u2192 \u672C\u5730 fallback\uFF09",
|
|
1346
|
+
mcpInspectDescription: "\u68C0\u67E5 MCP \u670D\u52A1\u5668\u89C4\u683C\uFF08\u5DE5\u5177\u3001\u8D44\u6E90\u3001\u63D0\u793A\uFF09",
|
|
1347
|
+
mcpSearchDescription: "\u5728 MCP \u6CE8\u518C\u8868\u4E2D\u641C\u7D22\u5339\u914D\u7684\u670D\u52A1\u5668",
|
|
1348
|
+
mcpInstallDescription: "\u6309\u540D\u79F0\u5B89\u88C5 MCP \u670D\u52A1\u5668\uFF08\u5C06\u5176\u89C4\u683C\u5199\u5165\u914D\u7F6E\uFF09",
|
|
1349
|
+
mcpBrowseDescription: "\u4EA4\u4E92\u5F0F\u5E02\u573A\u6D4F\u89C8\u5668 \u2014 \u8F93\u5165\u8FC7\u6EE4\u3001\u56DE\u8F66\u5B89\u88C5",
|
|
1350
|
+
mcpLocalHint: "\u53EA\u663E\u793A\u5185\u7F6E\u7684\u79BB\u7EBF\u76EE\u5F55",
|
|
1351
|
+
mcpRefreshHint: "\u5FFD\u7565 24 \u5C0F\u65F6\u7F13\u5B58\uFF0C\u5F3A\u5236\u5237\u65B0",
|
|
1352
|
+
mcpLimitHint: "\u6700\u591A\u663E\u793A\u591A\u5C11\u6761",
|
|
1353
|
+
mcpPagesHint: "\u4E00\u6B21\u6027\u9884\u52A0\u8F7D\u591A\u5C11\u9875\uFF08\u9ED8\u8BA4 1\uFF09",
|
|
1354
|
+
mcpAllHint: "\u52A0\u8F7D\u5168\u90E8\u9875\uFF08\u9996\u6B21\u8F83\u6162\uFF09",
|
|
1355
|
+
mcpMaxPagesHint: "\u641C\u7D22\u65F6\u6700\u591A\u8D70\u591A\u5C11\u9875\uFF08\u9ED8\u8BA4 20\uFF09",
|
|
1356
|
+
jsonHintCatalog: "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA",
|
|
1357
|
+
jsonHintReport: "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA\u68C0\u67E5\u62A5\u544A",
|
|
1358
|
+
modelOverrideFlash: "\u8986\u76D6\u6A21\u578B\uFF08\u9ED8\u8BA4\uFF1Adeepseek-v4-flash\uFF09",
|
|
1359
|
+
skipConfirmHint: "\u8DF3\u8FC7\u786E\u8BA4\u63D0\u793A"
|
|
1360
|
+
},
|
|
1361
|
+
slash: {
|
|
1362
|
+
help: { description: "\u663E\u793A\u5B8C\u6574\u547D\u4EE4\u53C2\u8003" },
|
|
1363
|
+
status: { description: "\u5F53\u524D\u6A21\u578B\u3001\u6807\u5FD7\u3001\u4E0A\u4E0B\u6587\u3001\u4F1A\u8BDD" },
|
|
1364
|
+
preset: {
|
|
1365
|
+
description: "\u6A21\u578B\u7EC4\u5408 \u2014 \u81EA\u52A8\u5728 flash \u2192 pro \u4E4B\u95F4\u5207\u6362\uFF0C\u6216\u9501\u5B9A flash/pro",
|
|
1366
|
+
argsHint: "<auto|flash|pro>"
|
|
1367
|
+
},
|
|
1368
|
+
model: { description: "\u5207\u6362 DeepSeek \u6A21\u578B ID", argsHint: "<id>" },
|
|
1369
|
+
models: { description: "\u5217\u51FA\u4ECE DeepSeek /models \u83B7\u53D6\u7684\u53EF\u7528\u6A21\u578B" },
|
|
1370
|
+
language: {
|
|
1371
|
+
description: "\u5207\u6362\u8FD0\u884C\u65F6\u8BED\u8A00",
|
|
1372
|
+
argsHint: "<en|zh-CN>",
|
|
1373
|
+
success: "\u8BED\u8A00\u5DF2\u5207\u6362\u4E3A\u7B80\u4F53\u4E2D\u6587\u3002",
|
|
1374
|
+
unsupported: "\u4E0D\u652F\u6301\u7684\u8BED\u8A00\u4EE3\u7801\uFF1A{code}\u3002\u652F\u6301\u7684\u8BED\u8A00\uFF1A{supported}\u3002"
|
|
1375
|
+
},
|
|
1376
|
+
pro: {
|
|
1377
|
+
description: "\u4EC5\u4E3A\u4E0B\u4E00\u8F6E\u542F\u7528 v4-pro\uFF08\u4E00\u6B21\u6027 \xB7 \u81EA\u52A8\u89E3\u9664\uFF09",
|
|
1378
|
+
argsHint: "[off]"
|
|
1379
|
+
},
|
|
1380
|
+
budget: {
|
|
1381
|
+
description: "\u4F1A\u8BDD\u7F8E\u5143\u4E0A\u9650 \u2014 80% \u65F6\u8B66\u544A\uFF0C100% \u65F6\u62D2\u7EDD\u4E0B\u4E00\u8F6E\u3002\u9ED8\u8BA4\u5173\u95ED\u3002\u5355\u72EC /budget \u663E\u793A\u72B6\u6001",
|
|
1382
|
+
argsHint: "[usd|off]"
|
|
1383
|
+
},
|
|
1384
|
+
mcp: { description: "\u5217\u51FA\u9644\u52A0\u5230\u6B64\u4F1A\u8BDD\u7684 MCP \u670D\u52A1\u5668 + \u5DE5\u5177" },
|
|
1385
|
+
resource: {
|
|
1386
|
+
description: "\u6D4F\u89C8 + \u8BFB\u53D6 MCP \u8D44\u6E90\uFF08\u65E0\u53C2\u6570 \u2192 \u5217\u51FA URI\uFF1B<uri> \u2192 \u83B7\u53D6\u5185\u5BB9\uFF09",
|
|
1387
|
+
argsHint: "[uri]"
|
|
1388
|
+
},
|
|
1389
|
+
prompt: {
|
|
1390
|
+
description: "\u6D4F\u89C8 + \u83B7\u53D6 MCP \u63D0\u793A\uFF08\u65E0\u53C2\u6570 \u2192 \u5217\u51FA\u540D\u79F0\uFF1B<name> \u2192 \u6E32\u67D3\u63D0\u793A\uFF09",
|
|
1391
|
+
argsHint: "[name]"
|
|
1392
|
+
},
|
|
1393
|
+
memory: {
|
|
1394
|
+
description: "\u663E\u793A / \u7BA1\u7406\u56FA\u5B9A\u8BB0\u5FC6\uFF08REASONIX.md + ~/.reasonix/memory\uFF09",
|
|
1395
|
+
argsHint: "[list|show <name>|forget <name>|clear <scope> confirm]"
|
|
1396
|
+
},
|
|
1397
|
+
skill: {
|
|
1398
|
+
description: "\u5217\u51FA / \u8FD0\u884C\u7528\u6237\u6280\u80FD\uFF08<project>/.reasonix/skills + ~/.reasonix/skills\uFF09",
|
|
1399
|
+
argsHint: "[list|show <name>|<name> [args]]"
|
|
1400
|
+
},
|
|
1401
|
+
hooks: {
|
|
1402
|
+
description: "\u5217\u51FA\u6D3B\u8DC3\u7684 hooks\uFF08.reasonix/ \u4E0B\u7684 settings.json\uFF09\xB7 reload \u4ECE\u78C1\u76D8\u91CD\u65B0\u8BFB\u53D6",
|
|
1403
|
+
argsHint: "[reload]"
|
|
1404
|
+
},
|
|
1405
|
+
permissions: {
|
|
1406
|
+
description: "\u663E\u793A / \u7F16\u8F91 shell \u5141\u8BB8\u5217\u8868\uFF08\u5185\u7F6E\u53EA\u8BFB \xB7 \u9879\u76EE\u7EA7\uFF1A~/.reasonix/config.json\uFF09",
|
|
1407
|
+
argsHint: "[list|add <prefix>|remove <prefix|N>|clear confirm]"
|
|
1408
|
+
},
|
|
1409
|
+
dashboard: {
|
|
1410
|
+
description: "\u542F\u52A8\u5D4C\u5165\u5F0F Web \u4EEA\u8868\u677F\uFF08127.0.0.1\uFF0Ctoken \u4FDD\u62A4\uFF09",
|
|
1411
|
+
argsHint: "[stop]"
|
|
1412
|
+
},
|
|
1413
|
+
update: { description: "\u663E\u793A\u5F53\u524D\u7248\u672C\u4E0E\u6700\u65B0\u7248\u672C\u53CA\u5347\u7EA7\u547D\u4EE4" },
|
|
1414
|
+
stats: {
|
|
1415
|
+
description: "\u8DE8\u4F1A\u8BDD\u6210\u672C\u4EEA\u8868\u677F\uFF08\u4ECA\u65E5 / \u672C\u5468 / \u672C\u6708 / \u5168\u90E8 \xB7 \u7F13\u5B58\u547D\u4E2D \xB7 \u4E0E Claude \u5BF9\u6BD4\uFF09"
|
|
1416
|
+
},
|
|
1417
|
+
cost: {
|
|
1418
|
+
description: "\u7A7A \u2192 \u4E0A\u4E00\u8F6E\u82B1\u8D39\uFF08\u4F7F\u7528\u5361\u7247\uFF09\uFF1B\u5E26\u6587\u672C \u2192 \u4F30\u7B97\u53D1\u9001\u6210\u672C\uFF08\u6700\u574F\u60C5\u51B5 + \u53EF\u80FD\u7F13\u5B58\u547D\u4E2D\uFF09",
|
|
1419
|
+
argsHint: "[text]"
|
|
1420
|
+
},
|
|
1421
|
+
doctor: {
|
|
1422
|
+
description: "\u5065\u5EB7\u68C0\u67E5\uFF08api / config / api-reach / index / hooks / project\uFF09"
|
|
1423
|
+
},
|
|
1424
|
+
context: { description: "\u663E\u793A\u4E0A\u4E0B\u6587\u7A97\u53E3\u5206\u89E3\uFF08\u7CFB\u7EDF / \u5DE5\u5177 / \u65E5\u5FD7 / \u8F93\u5165\uFF09" },
|
|
1425
|
+
retry: { description: "\u622A\u65AD\u5E76\u91CD\u53D1\u60A8\u7684\u6700\u540E\u4E00\u6761\u6D88\u606F\uFF08\u91CD\u65B0\u91C7\u6837\uFF09" },
|
|
1426
|
+
compact: {
|
|
1427
|
+
description: "\u7F29\u5C0F\u65E5\u5FD7\u4E2D\u8FC7\u5927\u7684\u5DE5\u5177\u7ED3\u679C\u548C\u5DE5\u5177\u8C03\u7528\u53C2\u6570\uFF1B\u4E0A\u9650\u4E3A tokens\uFF0C\u9ED8\u8BA4 4000",
|
|
1428
|
+
argsHint: "[tokens]"
|
|
1429
|
+
},
|
|
1430
|
+
keys: { description: "\u663E\u793A\u6240\u6709\u952E\u76D8\u5FEB\u6377\u952E\u548C\u63D0\u793A\u524D\u7F00" },
|
|
1431
|
+
plans: { description: "\u5217\u51FA\u6B64\u4F1A\u8BDD\u7684\u6D3B\u8DC3 + \u5F52\u6863\u8BA1\u5212\uFF08\u6700\u65B0\u5728\u524D\uFF09" },
|
|
1432
|
+
replay: {
|
|
1433
|
+
description: "\u52A0\u8F7D\u5F52\u6863\u8BA1\u5212\u4E3A\u53EA\u8BFB\u7684\u65F6\u95F4\u65C5\u884C\u5FEB\u7167\uFF08\u9ED8\u8BA4\uFF1A\u6700\u65B0\uFF09",
|
|
1434
|
+
argsHint: "[N]"
|
|
1435
|
+
},
|
|
1436
|
+
sessions: { description: "\u5217\u51FA\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD\uFF08\u5F53\u524D\u6807\u8BB0\u4E3A \u25B8\uFF09" },
|
|
1437
|
+
setup: { description: "\u63D0\u9192\u60A8\u9000\u51FA\u5E76\u8FD0\u884C `reasonix setup`" },
|
|
1438
|
+
semantic: {
|
|
1439
|
+
description: "\u663E\u793A semantic_search \u72B6\u6001 \u2014 \u5DF2\u6784\u5EFA\uFF1FOllama \u5DF2\u5B89\u88C5\uFF1F\u5982\u4F55\u542F\u7528"
|
|
1440
|
+
},
|
|
1441
|
+
clear: { description: "\u4EC5\u6E05\u9664\u53EF\u89C1\u7684\u6EDA\u52A8\u56DE\u653E\uFF08\u65E5\u5FD7/\u4E0A\u4E0B\u6587\u4FDD\u7559\uFF09" },
|
|
1442
|
+
new: { description: "\u5F00\u59CB\u5168\u65B0\u5BF9\u8BDD\uFF08\u6E05\u9664\u4E0A\u4E0B\u6587 + \u6EDA\u52A8\u56DE\u653E\uFF09" },
|
|
1443
|
+
loop: {
|
|
1444
|
+
description: "\u6BCF <interval> \u81EA\u52A8\u91CD\u65B0\u63D0\u4EA4 <prompt>\uFF0C\u76F4\u5230\u60A8\u8F93\u5165 / Esc / /loop stop",
|
|
1445
|
+
argsHint: "<5s..6h> <prompt> \xB7 stop \xB7 \uFF08\u65E0\u53C2\u6570 = \u72B6\u6001\uFF09"
|
|
1446
|
+
},
|
|
1447
|
+
exit: { description: "\u9000\u51FA TUI" },
|
|
1448
|
+
init: {
|
|
1449
|
+
description: "\u626B\u63CF\u9879\u76EE\u5E76\u5408\u6210\u57FA\u7EBF REASONIX.md\uFF08\u6A21\u578B\u5199\u5165\uFF1B\u4F7F\u7528 /apply \u5BA1\u67E5\uFF09\u3002`force` \u8986\u76D6\u5DF2\u6709\u6587\u4EF6\u3002",
|
|
1450
|
+
argsHint: "[force]"
|
|
1451
|
+
},
|
|
1452
|
+
apply: {
|
|
1453
|
+
description: "\u5C06\u5F85\u5904\u7406\u7684\u7F16\u8F91\u5757\u63D0\u4EA4\u5230\u78C1\u76D8\uFF08\u65E0\u53C2\u6570 \u2192 \u5168\u90E8\uFF1B`1`\u3001`1,3` \u6216 `1-4` \u2192 \u8BE5\u5B50\u96C6\uFF0C\u5176\u4F59\u4FDD\u6301\u5F85\u5904\u7406\uFF09",
|
|
1454
|
+
argsHint: "[N|N,M|N-M]"
|
|
1455
|
+
},
|
|
1456
|
+
discard: {
|
|
1457
|
+
description: "\u4E22\u5F03\u5F85\u5904\u7406\u7684\u7F16\u8F91\u5757\u800C\u4E0D\u5199\u5165\uFF08\u65E0\u53C2\u6570 \u2192 \u5168\u90E8\uFF1B\u7D22\u5F15 \u2192 \u8BE5\u5B50\u96C6\uFF09",
|
|
1458
|
+
argsHint: "[N|N,M|N-M]"
|
|
1459
|
+
},
|
|
1460
|
+
walk: {
|
|
1461
|
+
description: "\u9010\u5757\u9010\u6B65\u5904\u7406\u5F85\u5904\u7406\u7684\u7F16\u8F91\uFF08git-add-p \u98CE\u683C\uFF1A\u6BCF\u5757 y/n\uFF0Ca \u5E94\u7528\u5269\u4F59\uFF0CA \u5207\u6362 AUTO\uFF09"
|
|
1462
|
+
},
|
|
1463
|
+
undo: { description: "\u56DE\u6EDA\u6700\u540E\u5E94\u7528\u7684\u7F16\u8F91\u6279\u5904\u7406" },
|
|
1464
|
+
history: {
|
|
1465
|
+
description: "\u5217\u51FA\u6B64\u4F1A\u8BDD\u7684\u6BCF\u4E2A\u7F16\u8F91\u6279\u5904\u7406\uFF08\u7528\u4E8E /show \u7684 ID\uFF0C\u64A4\u6D88\u6807\u8BB0\uFF09"
|
|
1466
|
+
},
|
|
1467
|
+
show: {
|
|
1468
|
+
description: "\u8F6C\u50A8\u5B58\u50A8\u7684\u7F16\u8F91\u5DEE\u5F02\uFF08\u7701\u7565 id \u65F6\u4E3A\u6700\u65B0\u672A\u64A4\u6D88\u7684\uFF09",
|
|
1469
|
+
argsHint: "[id]"
|
|
1470
|
+
},
|
|
1471
|
+
commit: { description: "git add -A && git commit -m ...", argsHint: '"msg"' },
|
|
1472
|
+
checkpoint: {
|
|
1473
|
+
description: "\u5FEB\u7167\u4F1A\u8BDD\u6D89\u53CA\u7684\u6BCF\u4E2A\u6587\u4EF6\uFF08Cursor \u98CE\u683C\u5185\u90E8\u5B58\u50A8\uFF0C\u975E git\uFF09\u3002\u5355\u72EC /checkpoint \u5217\u51FA\u3002",
|
|
1474
|
+
argsHint: "[name|list|forget <id>]"
|
|
1475
|
+
},
|
|
1476
|
+
restore: {
|
|
1477
|
+
description: "\u5C06\u6587\u4EF6\u56DE\u6EDA\u5230\u547D\u540D\u7684\u68C0\u67E5\u70B9\uFF08\u89C1 /checkpoint list\uFF09",
|
|
1478
|
+
argsHint: "<name|id>"
|
|
1479
|
+
},
|
|
1480
|
+
plan: {
|
|
1481
|
+
description: "\u5207\u6362\u53EA\u8BFB\u8BA1\u5212\u6A21\u5F0F\uFF08\u5199\u5165\u88AB\u5F39\u56DE\u76F4\u5230 submit_plan + \u5BA1\u6279\uFF09",
|
|
1482
|
+
argsHint: "[on|off]"
|
|
1483
|
+
},
|
|
1484
|
+
mode: {
|
|
1485
|
+
description: "\u7F16\u8F91\u95E8\u63A7\uFF1Areview\uFF08\u6392\u961F\uFF09\xB7 auto\uFF08\u5E94\u7528+\u64A4\u6D88\uFF09\xB7 yolo\uFF08\u5E94\u7528+\u81EA\u52A8 shell\uFF09\u3002Shift+Tab \u5FAA\u73AF\u3002",
|
|
1486
|
+
argsHint: "[review|auto|yolo]"
|
|
1487
|
+
},
|
|
1488
|
+
jobs: { description: "\u5217\u51FA run_background \u542F\u52A8\u7684\u540E\u53F0\u4F5C\u4E1A" },
|
|
1489
|
+
kill: {
|
|
1490
|
+
description: "\u6309 ID \u505C\u6B62\u540E\u53F0\u4F5C\u4E1A\uFF08SIGTERM \u2192 \u5BBD\u9650\u671F\u540E SIGKILL\uFF09",
|
|
1491
|
+
argsHint: "<id>"
|
|
1492
|
+
},
|
|
1493
|
+
logs: {
|
|
1494
|
+
description: "\u8DDF\u8E2A\u540E\u53F0\u4F5C\u4E1A\u7684\u8F93\u51FA\uFF08\u9ED8\u8BA4\u6700\u540E 80 \u884C\uFF09",
|
|
1495
|
+
argsHint: "<id> [lines]"
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1498
|
+
wizard: {
|
|
1499
|
+
languageTitle: "\u9009\u62E9\u8BED\u8A00",
|
|
1500
|
+
languageSubtitle: "\u5DF2\u6839\u636E\u7CFB\u7EDF\u8BED\u8A00\u81EA\u52A8\u9009\u4E2D\u3002\u4E4B\u540E\u53EF\u7528 /language \u5207\u6362\u3002",
|
|
1501
|
+
welcomeTitle: "\u6B22\u8FCE\u4F7F\u7528 Reasonix\u3002",
|
|
1502
|
+
apiKeyPrompt: "\u7C98\u8D34\u4F60\u7684 DeepSeek API key \u5F00\u59CB\u4F7F\u7528\u3002",
|
|
1503
|
+
apiKeyGetOne: "\u5728\u6B64\u83B7\u53D6\uFF1Ahttps://platform.deepseek.com/api_keys",
|
|
1504
|
+
apiKeySavedLocally: "\u4FDD\u5B58\u5728\u672C\u5730\uFF1A{path}",
|
|
1505
|
+
apiKeyInputLabel: "key \u203A ",
|
|
1506
|
+
apiKeyInvalid: "\u8FD9\u770B\u8D77\u6765\u4E0D\u50CF DeepSeek \u7684 key\u3002\u5B83\u4EEC\u4EE5 'sk-' \u5F00\u5934\uFF0C30 \u5B57\u7B26\u4EE5\u4E0A\u3002",
|
|
1507
|
+
apiKeyPreview: "\u9884\u89C8\uFF1A{redacted}",
|
|
1508
|
+
presetTitle: "\u9009\u62E9\u9884\u8BBE",
|
|
1509
|
+
mcpTitle: "Reasonix \u8981\u4E3A\u4F60\u63A5\u5165\u54EA\u4E9B MCP \u670D\u52A1\u5668\uFF1F",
|
|
1510
|
+
mcpUserArgsHint: "\uFF08\u9700\u8981\u4F60\u63D0\u4F9B {arg}\uFF09",
|
|
1511
|
+
mcpFooterMulti: "[\u2191\u2193] \u79FB\u52A8 \xB7 [\u7A7A\u683C] \u9009\u62E9 \xB7 [Enter] \u786E\u8BA4 \xB7 [Esc] \u53D6\u6D88 \xB7 \u5168\u4E0D\u9009 = \u8DF3\u8FC7",
|
|
1512
|
+
mcpArgsTitle: "\u914D\u7F6E {name}",
|
|
1513
|
+
mcpArgsDirMissing: "\u76EE\u5F55 {path} \u4E0D\u5B58\u5728\u3002",
|
|
1514
|
+
mcpArgsDirCreateHint: "[Y/Enter] \u521B\u5EFA\uFF08mkdir -p\uFF09\xB7 [N/Esc] \u8F93\u5165\u5176\u4ED6\u8DEF\u5F84",
|
|
1515
|
+
mcpArgsDirCreateFailed: "\u65E0\u6CD5\u521B\u5EFA {path}\uFF1A{message}",
|
|
1516
|
+
mcpArgsRequiredParam: "\u5FC5\u586B\u53C2\u6570\uFF1A",
|
|
1517
|
+
mcpArgsEmpty: "{name} \u9700\u8981\u4E00\u4E2A\u503C \u2014 \u4E0D\u80FD\u4E3A\u7A7A\u3002",
|
|
1518
|
+
mcpArgsNotADir: "{path} \u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55\u3002",
|
|
1519
|
+
reviewTitle: "\u786E\u8BA4\u4FDD\u5B58",
|
|
1520
|
+
reviewLabelApiKey: "API key",
|
|
1521
|
+
reviewLabelLanguage: "\u8BED\u8A00",
|
|
1522
|
+
reviewLabelPreset: "\u9884\u8BBE",
|
|
1523
|
+
reviewLabelMcp: "MCP",
|
|
1524
|
+
reviewMcpNone: "\uFF08\u65E0\uFF09",
|
|
1525
|
+
reviewMcpServers: "{count} \u4E2A\u670D\u52A1\u5668",
|
|
1526
|
+
reviewSavesTo: "\u4FDD\u5B58\u5230 {path}",
|
|
1527
|
+
reviewSaveError: "\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25\uFF1A{message}",
|
|
1528
|
+
reviewFooter: "[Enter] \u4FDD\u5B58 \xB7 [Esc] \u53D6\u6D88",
|
|
1529
|
+
savedTitle: "\u25B8 \u5DF2\u4FDD\u5B58\u3002",
|
|
1530
|
+
savedFooter: "[Enter] \u9000\u51FA",
|
|
1531
|
+
selectFooter: "[\u2191\u2193] \u79FB\u52A8 \xB7 [Enter] \u786E\u8BA4 \xB7 [Esc] \u53D6\u6D88",
|
|
1532
|
+
stepCounter: "\u6B65\u9AA4 {step}/{total} \xB7 "
|
|
1533
|
+
},
|
|
1534
|
+
app: {
|
|
1535
|
+
walkCancelledRemaining: "\u25B8 \u6D4F\u89C8\u5DF2\u53D6\u6D88 \u2014 \u8FD8\u6709 {count} \u4E2A\u5F85\u5904\u7406\u7F16\u8F91\u5757\u3002",
|
|
1536
|
+
walkCancelled: "\u25B8 \u6D4F\u89C8\u5DF2\u53D6\u6D88\u3002",
|
|
1537
|
+
editModeYolo: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1AYOLO \u2014 \u7F16\u8F91\u548C shell \u547D\u4EE4\u90FD\u81EA\u52A8\u6267\u884C\u3002/undo \u4ECD\u53EF\u64A4\u9500\u7F16\u8F91\u3002\u8BF7\u8C28\u614E\u4F7F\u7528\u3002",
|
|
1538
|
+
editModeAuto: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1AAUTO \u2014 \u7F16\u8F91\u7ACB\u5373\u5E94\u7528\uFF1B5 \u79D2\u5185\u6309 u \u64A4\u9500\uFF08\u7A7A\u683C\u6682\u505C\u8BA1\u65F6\uFF09\u3002shell \u547D\u4EE4\u4ECD\u4F1A\u8BE2\u95EE\u3002",
|
|
1539
|
+
editModeReview: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1Areview \u2014 \u7F16\u8F91\u5165\u961F\u5F85 /apply\uFF08\u6216 y\uFF09/ /discard\uFF08\u6216 n\uFF09",
|
|
1540
|
+
rejectedEdit: "\u25B8 \u62D2\u7EDD\u4E86\u5BF9 {path} \u7684\u7F16\u8F91{context}",
|
|
1541
|
+
autoApprovingRest: "\u25B8 \u672C\u8F6E\u5269\u4F59\u7F16\u8F91\u81EA\u52A8\u6279\u51C6",
|
|
1542
|
+
flippedAutoSession: "\u25B8 \u5DF2\u5207\u6362\u5230 AUTO \u6A21\u5F0F\uFF08\u672C\u4F1A\u8BDD\u5269\u4F59\u751F\u6548\uFF0C\u5DF2\u6301\u4E45\u5316\uFF09",
|
|
1543
|
+
flippedAutoWalk: "\u25B8 \u5DF2\u5207\u6362\u5230 AUTO \u6A21\u5F0F \u2014 \u540E\u7EED\u7F16\u8F91\u7ACB\u5373\u5E94\u7528\u3002\u6D4F\u89C8\u6A21\u5F0F\u9000\u51FA\u3002",
|
|
1544
|
+
dashboardStopped: "\u25B8 \u4EEA\u8868\u677F\u5DF2\u505C\u6B62\u3002",
|
|
1545
|
+
notedMemory: "\u25B8 \u5DF2\u8BB0\u5F55\uFF08{scope}\uFF09\u2014 {verb} {path}",
|
|
1546
|
+
notedScopeProject: "\u9879\u76EE",
|
|
1547
|
+
notedScopeGlobal: "\u5168\u5C40",
|
|
1548
|
+
notedVerbCreated: "\u521B\u5EFA",
|
|
1549
|
+
notedVerbAppended: "\u8FFD\u52A0\u5230",
|
|
1550
|
+
memoryWriteFailed: "# \u8BB0\u5FC6\u5199\u5165\u5931\u8D25",
|
|
1551
|
+
commandFailed: "! \u547D\u4EE4\u5931\u8D25",
|
|
1552
|
+
restoreCodeOnly: "\u25B8 /restore \u4EC5\u5728\u4EE3\u7801\u6A21\u5F0F\u53EF\u7528",
|
|
1553
|
+
hookUserPromptSubmit: "UserPromptSubmit \u94A9\u5B50",
|
|
1554
|
+
hookStop: "Stop \u94A9\u5B50",
|
|
1555
|
+
atMentions: "\u25B8 @mentions\uFF1A{parts}",
|
|
1556
|
+
atUrl: "\u25B8 @url\uFF1A{parts}",
|
|
1557
|
+
atUrlFailed: "@url \u5C55\u5F00\u5931\u8D25",
|
|
1558
|
+
denied: "\u25B8 \u5DF2\u62D2\u7EDD\uFF1A{cmd}{context}",
|
|
1559
|
+
alwaysAllowed: '\u25B8 \u5DF2\u5BF9 {dir} \u6C38\u4E45\u5141\u8BB8 "{prefix}"',
|
|
1560
|
+
runningCommand: "\u25B8 \u6B63\u5728\u6267\u884C\uFF1A{cmd}",
|
|
1561
|
+
startingBackground: "\u25B8 \u540E\u53F0\u542F\u52A8\uFF1A{cmd}",
|
|
1562
|
+
checkpointSaved: "\u26C1 \u5DF2\u4FDD\u5B58\u68C0\u67E5\u70B9 \xB7 {id} \xB7 {count} \u4E2A\u6587\u4EF6 \xB7 /restore {id} \u53EF\u56DE\u6EDA\u6B64\u6B65",
|
|
1563
|
+
continuingAfter: "\u25B8 \u5728 {label}{counter} \u4E4B\u540E\u7EE7\u7EED",
|
|
1564
|
+
planStoppedAt: "\u25B8 \u8BA1\u5212\u5728 {label}{counter} \u5904\u505C\u6B62",
|
|
1565
|
+
revisingAfter: "\u25B8 \u5728 {label} \u4E4B\u540E\u4FEE\u8BA2 \u2014 {feedback}"
|
|
1566
|
+
},
|
|
1567
|
+
hooks: {
|
|
1568
|
+
head: "\u94A9\u5B50 {tag} `{cmd}` {decision}{truncTag}",
|
|
1569
|
+
headWithDetail: "\u94A9\u5B50 {tag} `{cmd}` {decision}{truncTag}\uFF1A{detail}",
|
|
1570
|
+
truncated: "\uFF08\u8F93\u51FA\u5728 256KB \u5904\u622A\u65AD\uFF09",
|
|
1571
|
+
decisionBlock: "\u62E6\u622A",
|
|
1572
|
+
decisionWarn: "\u544A\u8B66",
|
|
1573
|
+
decisionTimeout: "\u8D85\u65F6",
|
|
1574
|
+
decisionError: "\u9519\u8BEF"
|
|
1575
|
+
},
|
|
1576
|
+
summary: {
|
|
1577
|
+
status: "\u6B63\u5728\u603B\u7ED3\u5DF2\u6536\u96C6\u7684\u5185\u5BB9\u2026",
|
|
1578
|
+
hallucinatedFallback: "\uFF08\u6A21\u578B\u751F\u6210\u4E86\u4F2A\u9020\u7684\u5DE5\u5177\u8C03\u7528\u6807\u8BB0\u800C\u975E\u7EAF\u6587\u672C\u603B\u7ED3 \u2014 \u8BD5\u8BD5 /retry \u6362\u4E2A\u66F4\u7A84\u7684\u95EE\u9898\uFF0C\u6216 /think \u67E5\u770B R1 \u7684\u63A8\u7406\uFF09",
|
|
1579
|
+
failedAfterReason: "{label}\uFF0C\u4E14\u56DE\u9000\u7684\u603B\u7ED3\u8C03\u7528\u4E5F\u5931\u8D25\uFF1A{message}\u3002\u8BF7\u8FD0\u884C /clear \u540E\u7528\u66F4\u7A84\u7684\u95EE\u9898\u91CD\u8BD5\uFF0C\u6216\u63D0\u9AD8 --max-tool-iters\u3002"
|
|
1580
|
+
},
|
|
1581
|
+
loop: {
|
|
1582
|
+
budgetExhausted: "\u4F1A\u8BDD\u9884\u7B97\u5DF2\u7528\u5B8C \u2014 \u5DF2\u82B1\u8D39 ${spent} \u2265 \u4E0A\u9650 ${cap}\u3002\u7528 /budget <usd> \u63D0\u9AD8\u4E0A\u9650\uFF0C/budget off \u6E05\u9664\u4E0A\u9650\uFF0C\u6216\u7ED3\u675F\u4F1A\u8BDD\u3002",
|
|
1583
|
+
budget80Pct: "\u25B2 \u9884\u7B97\u5DF2\u7528 80% \u2014 ${spent} / ${cap}\u3002\u4E0B\u4E00\u4E24\u8F6E\u53EF\u80FD\u5C31\u89E6\u9876\u3002",
|
|
1584
|
+
proArmed: "\u21E7 /pro \u5DF2\u88C5\u5907 \u2014 \u672C\u8F6E\u4F7F\u7528 deepseek-v4-pro\uFF08\u4E00\u6B21\u6027 \xB7 \u672C\u8F6E\u540E\u81EA\u52A8\u89E3\u9664\uFF09",
|
|
1585
|
+
abortedAtIter: "\u5728\u7B2C {iter}/{cap} \u6B21\u5DE5\u5177\u8C03\u7528\u5904\u4E2D\u65AD \u2014 \u672A\u751F\u6210\u603B\u7ED3\u5373\u505C\u6B62\uFF08\u6309 \u2191 + Enter \u6216 /retry \u6062\u590D\uFF09",
|
|
1586
|
+
toolUploadStatus: "\u5DE5\u5177\u7ED3\u679C\u5DF2\u4E0A\u4F20 \xB7 \u6A21\u578B\u5728\u751F\u6210\u4E0B\u4E00\u6761\u54CD\u5E94\u524D\u601D\u8003\u4E2D\u2026",
|
|
1587
|
+
toolBudgetWarning: "\u5DF2\u7528 {iter}/{cap} \u6B21\u5DE5\u5177\u8C03\u7528 \u2014 \u63A5\u8FD1\u4E0A\u9650\u3002\u6309 Esc \u7ACB\u5373\u5F3A\u5236\u603B\u7ED3\u3002",
|
|
1588
|
+
preflightFoldStatus: "\u9884\u68C0\uFF1A\u4E0A\u4E0B\u6587\u63A5\u8FD1\u4E0A\u9650\uFF0C\u5C1D\u8BD5\u6298\u53E0\u2026",
|
|
1589
|
+
preflightFolded: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\u2014 \u5DF2\u6298\u53E0 {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages}\uFF08\u603B\u7ED3 {summaryChars} \u5B57\uFF09\u3002\u53D1\u9001\u4E2D\u3002",
|
|
1590
|
+
preflightNoFold: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\u4E14\u6CA1\u6709\u53EF\u6298\u53E0\u7684\u5185\u5BB9 \u2014 DeepSeek \u5927\u6982\u7387\u4F1A\u8FD4\u56DE 400\u3002\u8BF7\u8FD0\u884C /clear \u6216 /new \u91CD\u65B0\u5F00\u59CB\u3002",
|
|
1591
|
+
flashEscalation: "\u21E7 flash \u8BF7\u6C42\u5347\u7EA7 \u2014 \u672C\u8F6E\u6539\u7528 {model}{reasonSuffix}",
|
|
1592
|
+
harvestStatus: "\u6B63\u5728\u4ECE\u63A8\u7406\u8FC7\u7A0B\u63D0\u53D6\u8BA1\u5212\u72B6\u6001\u2026",
|
|
1593
|
+
autoEscalation: "\u21E7 \u672C\u8F6E\u5269\u4F59\u8C03\u7528\u81EA\u52A8\u5347\u7EA7\u5230 {model} \u2014 flash \u547D\u4E2D {breakdown}\u3002\u4E0B\u4E00\u8F6E\u56DE\u9000\u5230 {fallback}\uFF0C\u9664\u975E\u5DF2\u88C5\u5907 /pro\u3002",
|
|
1594
|
+
repeatToolCallWarning: "\u62E6\u622A\u5230\u91CD\u590D\u5DE5\u5177\u8C03\u7528 \u2014 \u8BA9\u6A21\u578B\u5BDF\u89C9\u95EE\u9898\u5E76\u6362\u79CD\u65B9\u5F0F\u91CD\u8BD5\u3002",
|
|
1595
|
+
stormStuck: "\u5DF2\u505C\u6B62\u5361\u6B7B\u7684\u91CD\u8BD5\u5FAA\u73AF \u2014 \u6A21\u578B\u5728\u81EA\u7EA0\u63D0\u793A\u540E\u4ECD\u4EE5\u76F8\u540C\u53C2\u6570\u91CD\u590D\u8C03\u7528\u540C\u4E00\u5DE5\u5177\u3002\u8BF7\u5C1D\u8BD5 /retry\u3001\u6362\u79CD\u8BF4\u6CD5\uFF0C\u6216\u6392\u67E5\u5E95\u5C42\u963B\u585E\u3002",
|
|
1596
|
+
stormSuppressed: "\u5DF2\u6291\u5236 {count} \u6B21\u91CD\u590D\u5DE5\u5177\u8C03\u7528 \u2014 \u540C\u4E00\u540D\u79F0 + \u53C2\u6570\u89E6\u53D1 3 \u6B21\u4EE5\u4E0A\u3002",
|
|
1597
|
+
compactingHistoryStatus: "\u6B63\u5728\u538B\u7F29\u5386\u53F2{aggressiveTag}\u2026",
|
|
1598
|
+
aggressiveTag: "\uFF08\u6FC0\u8FDB\uFF09",
|
|
1599
|
+
foldedHistory: "\u4E0A\u4E0B\u6587 {before}/{ctxMax}\uFF08{pct}%\uFF09\u2014 \u5DF2\u6298\u53E0 {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages}\uFF08\u603B\u7ED3 {summaryChars} \u5B57\uFF09\u3002\u7EE7\u7EED\u3002",
|
|
1600
|
+
aggressivelyFoldedHistory: "\u4E0A\u4E0B\u6587 {before}/{ctxMax}\uFF08{pct}%\uFF09\u2014 \u5DF2\u6FC0\u8FDB\u6298\u53E0 {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages}\uFF08\u603B\u7ED3 {summaryChars} \u5B57\uFF09\u3002\u7EE7\u7EED\u3002",
|
|
1601
|
+
forcingSummary: "\u4E0A\u4E0B\u6587 {before}/{ctxMax}\uFF08{pct}%\uFF09\u2014 \u57FA\u4E8E\u5DF2\u6536\u96C6\u5230\u7684\u5185\u5BB9\u5F3A\u5236\u603B\u7ED3\u3002\u8BF7\u8FD0\u884C /compact\u3001/clear \u6216 /new \u91CD\u7F6E\u3002"
|
|
1602
|
+
},
|
|
1603
|
+
errors: {
|
|
1604
|
+
contextOverflow: "\u4E0A\u4E0B\u6587\u6EA2\u51FA\uFF08DeepSeek 400\uFF09\uFF1A\u4F1A\u8BDD\u5386\u53F2\u5DF2\u8FBE {requested}\uFF0C\u8D85\u51FA\u6A21\u578B prompt \u4E0A\u9650\uFF08V4\uFF1A1M tokens\uFF1B\u65E7\u7248 chat/reasoner\uFF1A131k\uFF09\u3002\u901A\u5E38\u662F\u5355\u4E2A\u5DE5\u5177\u7ED3\u679C\u592A\u5927\u3002Reasonix \u9ED8\u8BA4\u5C06\u65B0\u5DE5\u5177\u7ED3\u679C\u9650\u5236\u5728 8k tokens\uFF0C\u5E76\u5728\u4F1A\u8BDD\u52A0\u8F7D\u65F6\u81EA\u52A8\u4FEE\u590D\u8D85\u5927\u5386\u53F2 \u2014 \u91CD\u542F\u5E38\u80FD\u6E05\u6389\u3002\u5982\u679C\u4ECD\u7136\u6EA2\u51FA\uFF0C\u8FD0\u884C /forget\uFF08\u5220\u9664\u4F1A\u8BDD\uFF09\u6216 /clear\uFF08\u4E22\u5F03\u663E\u793A\u4E2D\u7684\u5386\u53F2\uFF09\u4ECE\u5934\u5F00\u59CB\u3002",
|
|
1605
|
+
contextOverflowTooMany: "tokens \u6570\u91CF\u8FC7\u591A",
|
|
1606
|
+
auth401: "\u8BA4\u8BC1\u5931\u8D25\uFF08DeepSeek 401\uFF09\uFF1A{inner}\u3002\u4F60\u7684 API key \u88AB\u62D2\u7EDD\u3002\u8FD0\u884C `reasonix setup` \u6216 `export DEEPSEEK_API_KEY=sk-...` \u4FEE\u590D\u3002\u5728 https://platform.deepseek.com/api_keys \u83B7\u53D6 key\u3002",
|
|
1607
|
+
balance402: "\u4F59\u989D\u4E0D\u8DB3\uFF08DeepSeek 402\uFF09\uFF1A{inner}\u3002\u5728 https://platform.deepseek.com/top_up \u5145\u503C \u2014 \u4F59\u989D\u975E\u96F6\u65F6\u9762\u677F\u9876\u680F\u4F1A\u663E\u793A\u3002",
|
|
1608
|
+
badparam422: "\u53C2\u6570\u9519\u8BEF\uFF08DeepSeek 422\uFF09\uFF1A{inner}",
|
|
1609
|
+
badrequest400: "\u8BF7\u6C42\u9519\u8BEF\uFF08DeepSeek 400\uFF09\uFF1A{inner}",
|
|
1610
|
+
deepseek5xxHead: "DeepSeek \u670D\u52A1\u4E0D\u53EF\u7528\uFF08{status}\uFF09 \u2014 \u8FD9\u662F DeepSeek \u670D\u52A1\u7AEF\u95EE\u9898\uFF0C\u4E0D\u662F Reasonix \u6545\u969C\u3002\u5DF2\u6309\u6307\u6570\u9000\u907F\u91CD\u8BD5 4 \u6B21\u3002",
|
|
1611
|
+
deepseek5xxReachable: " DeepSeek \u4E3B API \u5065\u5EB7\u68C0\u67E5\u901A\u8FC7\uFF0C\u4F46 /chat/completions \u5728\u6302 \u2014 \u4ED6\u4EEC\u90A3\u8FB9\u90E8\u5206\u670D\u52A1\u5F02\u5E38\u3002",
|
|
1612
|
+
deepseek5xxUnreachable: " \u65E0\u6CD5\u4ECE\u4F60\u7684\u7F51\u7EDC\u8BBF\u95EE DeepSeek API \u2014 \u53EF\u80FD\u662F DS \u6574\u4F53\u6545\u969C\uFF0C\u4E5F\u53EF\u80FD\u662F\u672C\u5730\u7F51\u7EDC\u95EE\u9898\u3002",
|
|
1613
|
+
deepseek5xxActionNetwork: " \u5EFA\u8BAE\uFF1A(1) \u68C0\u67E5\u7F51\u7EDC\uFF0C(2) \u7B49 30 \u79D2\u540E\u91CD\u8BD5\uFF0C(3) \u67E5\u770B\u72B6\u6001\u9875 https://status.deepseek.com\u3002",
|
|
1614
|
+
deepseek5xxActionRetry: " \u5EFA\u8BAE\uFF1A(1) \u7B49 30 \u79D2\u540E\u91CD\u8BD5\uFF0C(2) \u7528 /preset \u5207\u6362\u6A21\u578B\uFF0C(3) \u67E5\u770B\u72B6\u6001\u9875 https://status.deepseek.com\u3002",
|
|
1615
|
+
innerNoMessage: "\uFF08\u65E0\u9519\u8BEF\u4FE1\u606F\uFF09",
|
|
1616
|
+
reasonAborted: "[\u7528\u6237\u5DF2\u4E2D\u65AD\uFF08Esc\uFF09 \u2014 \u6B63\u5728\u603B\u7ED3\u5230\u76EE\u524D\u4E3A\u6B62\u7684\u53D1\u73B0]",
|
|
1617
|
+
reasonContextGuard: "[\u4E0A\u4E0B\u6587\u989D\u5EA6\u5373\u5C06\u8017\u5C3D \u2014 \u5728\u4E0B\u4E00\u6B21\u8C03\u7528\u6EA2\u51FA\u4E4B\u524D\u5148\u603B\u7ED3]",
|
|
1618
|
+
reasonStuck: "[\u5361\u5728\u91CD\u590D\u7684\u5DE5\u5177\u8C03\u7528\u4E0A \u2014 \u8BF4\u660E\u5DF2\u5C1D\u8BD5\u7684\u65B9\u6CD5\u4EE5\u53CA\u963B\u585E\u70B9]",
|
|
1619
|
+
reasonBudget: "[\u5DE5\u5177\u8C03\u7528\u914D\u989D\uFF08{iterCap}\uFF09\u5DF2\u7528\u5C3D \u2014 \u57FA\u4E8E\u5DF2\u53D1\u73B0\u7684\u5185\u5BB9\u5F3A\u5236\u603B\u7ED3]",
|
|
1620
|
+
labelAborted: "\u7528\u6237\u4E2D\u65AD",
|
|
1621
|
+
labelContextGuard: "\u89E6\u53D1\u4E0A\u4E0B\u6587\u4FDD\u62A4\uFF08prompt > 80% \u7A97\u53E3\uFF09",
|
|
1622
|
+
labelStuck: "\u5361\u6B7B\uFF08\u91CD\u590D\u5DE5\u5177\u8C03\u7528\u88AB\u53CD\u98CE\u66B4\u673A\u5236\u6291\u5236\uFF09",
|
|
1623
|
+
labelBudget: "\u5DE5\u5177\u8C03\u7528\u914D\u989D\uFF08{iterCap}\uFF09\u5DF2\u7528\u5C3D"
|
|
1624
|
+
},
|
|
1625
|
+
handlers: {
|
|
1626
|
+
basic: {
|
|
1627
|
+
newInfo: "\u25B8 \u65B0\u5BF9\u8BDD \u2014 \u5DF2\u4ECE\u4E0A\u4E0B\u6587\u4E2D\u4E22\u5F03 {count} \u6761\u6D88\u606F\u3002\u540C\u4E00\u4F1A\u8BDD\uFF0C\u5168\u65B0\u5F00\u59CB\u3002",
|
|
1628
|
+
helpTitle: "\u547D\u4EE4\uFF1A",
|
|
1629
|
+
helpShellTitle: "Shell \u5FEB\u6377\u65B9\u5F0F\uFF1A",
|
|
1630
|
+
helpShell: " !<cmd> \u5728\u6C99\u7BB1\u6839\u76EE\u5F55\u8FD0\u884C <cmd>\uFF1B\u8F93\u51FA\u8FDB\u5165\u5BF9\u8BDD",
|
|
1631
|
+
helpShellDetail: " \u4EE5\u4FBF\u6A21\u578B\u5728\u4E0B\u4E00\u8F6E\u770B\u5230\u3002\u65E0\u5141\u8BB8\u5217\u8868\u9650\u5236\u3002",
|
|
1632
|
+
helpShellConsent: " \u7528\u6237\u8F93\u5165 = \u660E\u786E\u540C\u610F\u3002",
|
|
1633
|
+
helpShellExample: " \u793A\u4F8B\uFF1A!git status !ls src/ !npm test",
|
|
1634
|
+
helpMemoryTitle: "\u5FEB\u901F\u8BB0\u5FC6\uFF1A",
|
|
1635
|
+
helpMemoryPin: " #<note> \u5C06 <note> \u8FFD\u52A0\u5230 <project>/REASONIX.md\uFF08\u53EF\u63D0\u4EA4\uFF09\u3002",
|
|
1636
|
+
helpMemoryPinEx: " \u793A\u4F8B\uFF1A#findByEmail \u5FC5\u987B\u533A\u5206\u5927\u5C0F\u5199",
|
|
1637
|
+
helpMemoryGlobal: " #g <note> \u5C06 <note> \u8FFD\u52A0\u5230 ~/.reasonix/REASONIX.md\uFF08\u5168\u5C40\uFF0C\u4E0D\u63D0\u4EA4\uFF09\u3002",
|
|
1638
|
+
helpMemoryGlobalEx: " \u793A\u4F8B\uFF1A#g \u59CB\u7EC8\u4F7F\u7528 pnpm \u800C\u975E npm",
|
|
1639
|
+
helpMemoryPinBoth: " \u4E24\u8005\u90FD\u56FA\u5B9A\u5230\u6BCF\u4E2A\u672A\u6765\u4F1A\u8BDD\u7684\u524D\u7F00\u4E2D\u3002\u6BD4 /memory \u66F4\u5FEB\u3002",
|
|
1640
|
+
helpMemoryEscape: " \u4F7F\u7528 `\\#text` \u53D1\u9001\u5B57\u9762\u91CF `#text` \u7ED9\u6A21\u578B\u3002",
|
|
1641
|
+
helpFileTitle: "\u6587\u4EF6\u5F15\u7528\uFF08\u4EE3\u7801\u6A21\u5F0F\uFF09\uFF1A",
|
|
1642
|
+
helpFile: " @path/to/file \u53D1\u9001\u65F6\u5C06\u6587\u4EF6\u5185\u5BB9\u5185\u8054\u5230 [Referenced files] \u4E0B\u3002",
|
|
1643
|
+
helpFilePicker: " \u8F93\u5165 `@` \u6253\u5F00\u9009\u62E9\u5668\uFF08\u2191\u2193 \u5BFC\u822A\uFF0CTab/Enter \u9009\u62E9\uFF09\u3002",
|
|
1644
|
+
helpUrlTitle: "URL \u5F15\u7528\uFF1A",
|
|
1645
|
+
helpUrl: " @https://example.com \u83B7\u53D6 URL\uFF0C\u5265\u79BB HTML\uFF0C\u5185\u8054\u5230 [Referenced URLs] \u4E0B\u3002",
|
|
1646
|
+
helpUrlCache: " \u540C\u4E00\u4F1A\u8BDD\u4E2D\u76F8\u540C URL \u53EA\u83B7\u53D6\u4E00\u6B21\uFF08\u5185\u5B58\u7F13\u5B58\uFF09\u3002",
|
|
1647
|
+
helpUrlPunct: " \u81EA\u52A8\u5265\u79BB\u5C3E\u90E8\u6807\u70B9\u7B26\u53F7\uFF08./,/\uFF09\uFF09\u3002",
|
|
1648
|
+
helpPresetsTitle: "\u9884\u8BBE\uFF08branch + harvest \u6C38\u8FDC\u4E0D\u4F1A\u81EA\u52A8\u542F\u7528 \u2014 \u4EC5\u624B\u52A8\u9009\u62E9\uFF09\uFF1A",
|
|
1649
|
+
helpPresetAuto: " auto v4-flash \u2192 v4-pro \u5728\u56F0\u96BE\u8F6E\u6B21\u5207\u6362 \u2190 \u9ED8\u8BA4 \xB7 \u7B80\u5355\u65F6\u4FBF\u5B9C\uFF0C\u56F0\u96BE\u65F6\u667A\u80FD",
|
|
1650
|
+
helpPresetFlash: " flash \u59CB\u7EC8\u4F7F\u7528 v4-flash \u6700\u4FBF\u5B9C \xB7 \u6BCF\u8F6E\u6210\u672C\u53EF\u9884\u6D4B",
|
|
1651
|
+
helpPresetPro: " pro \u59CB\u7EC8\u4F7F\u7528 v4-pro \u7EA6 3 \u500D flash \xB7 \u7528\u4E8E\u56F0\u96BE\u7684\u591A\u8F6E\u5DE5\u4F5C",
|
|
1652
|
+
helpSessionsTitle: "\u4F1A\u8BDD\uFF08\u9ED8\u8BA4\u81EA\u52A8\u542F\u7528\uFF0C\u547D\u540D\u4E3A 'default'\uFF09\uFF1A",
|
|
1653
|
+
helpSessionCustom: " reasonix chat --session <name> \u4F7F\u7528\u4E0D\u540C\u7684\u547D\u540D\u4F1A\u8BDD",
|
|
1654
|
+
helpSessionNone: " reasonix chat --no-session \u7981\u7528\u672C\u6B21\u8FD0\u884C\u7684\u6301\u4E45\u5316",
|
|
1655
|
+
retryNone: "\u6CA1\u6709\u53EF\u91CD\u8BD5\u7684\u5185\u5BB9 \u2014 \u6B64\u4F1A\u8BDD\u65E5\u5FD7\u4E2D\u6CA1\u6709\u5148\u524D\u7684\u7528\u6237\u6D88\u606F\u3002",
|
|
1656
|
+
retryInfo: '\u25B8 \u91CD\u8BD5\u4E2D\uFF1A"{preview}"',
|
|
1657
|
+
loopTuiOnly: "/loop \u4EC5\u5728\u4EA4\u4E92\u5F0F TUI \u4E2D\u53EF\u7528\uFF08\u4E0D\u5728 run/replay \u4E2D\uFF09\u3002",
|
|
1658
|
+
loopStopped: "\u25B8 \u5FAA\u73AF\u5DF2\u505C\u6B62\u3002",
|
|
1659
|
+
loopNoActive: "\u6CA1\u6709\u6D3B\u52A8\u7684\u5FAA\u73AF\u53EF\u505C\u6B62\u3002",
|
|
1660
|
+
loopNoActiveHint: "\u6CA1\u6709\u6D3B\u52A8\u7684\u5FAA\u73AF\u3002\u4F7F\u7528 `/loop <interval> <prompt>` \u542F\u52A8\u4E00\u4E2A\uFF08\u4F8B\u5982 /loop 30s npm test\uFF09\u3002\n\u53D6\u6D88\u65B9\u5F0F\uFF1A/loop stop \xB7 Esc \xB7 /clear /new \xB7 \u4EFB\u4F55\u7528\u6237\u8F93\u5165\u7684\u63D0\u793A\u3002",
|
|
1661
|
+
loopStarted: '\u25B8 \u5FAA\u73AF\u5DF2\u542F\u52A8 \u2014 \u6BCF {duration} \u91CD\u65B0\u63D0\u4EA4 "{prompt}"\u3002\u8F93\u5165\u4EFB\u4F55\u5185\u5BB9\uFF08\u6216 /loop stop\uFF09\u53D6\u6D88\u3002'
|
|
1662
|
+
},
|
|
1663
|
+
admin: {
|
|
1664
|
+
doctorNeedsTui: "/doctor \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postDoctor \u5DF2\u8FDE\u63A5\uFF09\u3002",
|
|
1665
|
+
doctorRunning: "\u2695 \u5065\u5EB7\u68C0\u67E5 \u2014 \u6B63\u5728\u8FD0\u884C\u2026",
|
|
1666
|
+
hooksReloadUnavailable: "/hooks reload \u5728\u6B64\u4E0A\u4E0B\u6587\u4E2D\u4E0D\u53EF\u7528\uFF08\u65E0\u91CD\u8F7D\u56DE\u8C03\uFF09\u3002",
|
|
1667
|
+
hooksReloaded: "\u25B8 \u5DF2\u91CD\u8F7D hooks \xB7 {count} \u4E2A\u6D3B\u8DC3",
|
|
1668
|
+
hooksUsage: "\u7528\u6CD5\uFF1A/hooks \u5217\u51FA\u6D3B\u8DC3\u7684 hooks\n /hooks reload \u91CD\u65B0\u8BFB\u53D6 settings.json \u6587\u4EF6",
|
|
1669
|
+
hooksNone: "\u672A\u914D\u7F6E hooks\u3002",
|
|
1670
|
+
hooksDropHint: "\u5C06\u5305\u542B `hooks` \u952E\u7684 settings.json \u653E\u5165\u4EE5\u4E0B\u4EFB\u4E00\u4F4D\u7F6E\uFF1A",
|
|
1671
|
+
hooksProject: " \xB7 {path}\uFF08\u9879\u76EE\uFF09",
|
|
1672
|
+
hooksProjectFallback: " \xB7 <project>/.reasonix/settings.json\uFF08\u9879\u76EE\uFF09",
|
|
1673
|
+
hooksGlobal: " \xB7 {path}\uFF08\u5168\u5C40\uFF09",
|
|
1674
|
+
hooksEvents: "\u4E8B\u4EF6\uFF1APreToolUse, PostToolUse, UserPromptSubmit, Stop",
|
|
1675
|
+
hooksExitCodes: "exit 0 = \u901A\u8FC7 \xB7 exit 2 = \u963B\u6B62\uFF08Pre*\uFF09\xB7 \u5176\u4ED6 = \u8B66\u544A",
|
|
1676
|
+
hooksLoaded: "\u25B8 \u5DF2\u52A0\u8F7D {count} \u4E2A hook",
|
|
1677
|
+
hooksSources: "\u6765\u6E90\uFF1Aproject={project} \xB7 global={global}",
|
|
1678
|
+
updateCurrent: "\u5F53\u524D\uFF1Areasonix {version}",
|
|
1679
|
+
updateLatestPending: "\u6700\u65B0\uFF1A\uFF08\u5C1A\u672A\u89E3\u6790 \u2014 \u540E\u53F0\u68C0\u67E5\u8FDB\u884C\u4E2D\u6216\u79BB\u7EBF\uFF09",
|
|
1680
|
+
updateRetryHint: "\u5DF2\u89E6\u53D1\u65B0\u7684\u6CE8\u518C\u8868\u83B7\u53D6 \u2014 \u51E0\u79D2\u540E\u91CD\u8BD5 `/update`\uFF0C",
|
|
1681
|
+
updateRetryHint2: "\u6216\u5728\u53E6\u4E00\u4E2A\u7EC8\u7AEF\u8FD0\u884C `reasonix update` \u5F3A\u5236\u540C\u6B65\u6267\u884C\u3002",
|
|
1682
|
+
updateLatest: "\u6700\u65B0\uFF1Areasonix {version}",
|
|
1683
|
+
updateUpToDate: "\u60A8\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002\u65E0\u9700\u64CD\u4F5C\u3002",
|
|
1684
|
+
updateNpxHint: "\u60A8\u6B63\u5728\u901A\u8FC7 npx \u8FD0\u884C \u2014 \u4E0B\u6B21 `npx reasonix ...` \u542F\u52A8\u65F6\u5C06\u81EA\u52A8\u83B7\u53D6\u3002",
|
|
1685
|
+
updateNpxForce: "\u8981\u5F3A\u5236\u5237\u65B0\uFF1A`npm cache clean --force`\u3002",
|
|
1686
|
+
updateUpgradeHint: "\u8981\u5347\u7EA7\uFF0C\u8BF7\u9000\u51FA\u6B64\u4F1A\u8BDD\u5E76\u8FD0\u884C\uFF1A",
|
|
1687
|
+
updateUpgradeCmd1: " reasonix update \uFF08\u4EA4\u4E92\u5F0F\uFF0C\u652F\u6301 --dry-run \u9884\u89C8\uFF09",
|
|
1688
|
+
updateUpgradeCmd2: " npm install -g reasonix@latest \uFF08\u76F4\u63A5\u5B89\u88C5\uFF09",
|
|
1689
|
+
updateInSessionDisabled: "\u4F1A\u8BDD\u5185\u5B89\u88C5\u88AB\u523B\u610F\u7981\u7528 \u2014 npm spawn \u4F1A",
|
|
1690
|
+
updateInSessionDisabled2: "\u7834\u574F\u6B64 TUI \u7684\u6E32\u67D3\uFF0C\u4E14 Windows \u53EF\u80FD\u9501\u5B9A\u8FD0\u884C\u4E2D\u7684\u4E8C\u8FDB\u5236\u6587\u4EF6\u3002",
|
|
1691
|
+
statsNoData: "\u5C1A\u65E0\u4F7F\u7528\u6570\u636E\u3002",
|
|
1692
|
+
statsEveryTurn: "\u60A8\u5728\u6B64\u8FD0\u884C\u7684\u6BCF\u4E00\u8F6E\u90FD\u4F1A\u8FFD\u52A0\u4E00\u6761\u8BB0\u5F55 \u2014 \u6B64\u4F1A\u8BDD\u7684\u8F6E\u6B21",
|
|
1693
|
+
statsWillAppear: "\u5C06\u5728\u60A8\u53D1\u9001\u6D88\u606F\u540E\u663E\u793A\u5728\u4EEA\u8868\u677F\u4E2D\u3002"
|
|
1694
|
+
},
|
|
1695
|
+
edits: {
|
|
1696
|
+
undoCodeOnly: "/undo \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528 \u2014 \u804A\u5929\u6A21\u5F0F\u4E0D\u5E94\u7528\u7F16\u8F91\u3002",
|
|
1697
|
+
historyCodeOnly: "/history \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1698
|
+
showCodeOnly: "/show \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1699
|
+
applyCodeOnly: "/apply \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\uFF08\u6B64\u5904\u65E0\u5185\u5BB9\u53EF\u5E94\u7528\uFF09\u3002",
|
|
1700
|
+
discardCodeOnly: "/discard \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1701
|
+
planCodeOnly: "/plan \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528 \u2014 \u804A\u5929\u6A21\u5F0F\u4E0D\u9650\u5236\u5DE5\u5177\u5199\u5165\u3002",
|
|
1702
|
+
planOn: "\u25B8 \u8BA1\u5212\u6A21\u5F0F\u5F00\u542F \u2014 \u5199\u5165\u5DE5\u5177\u88AB\u9650\u5236\uFF1B\u6A21\u578B\u5FC5\u987B\u5148\u8C03\u7528 `submit_plan` \u624D\u80FD\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C\u3002\uFF08\u6A21\u578B\u4E5F\u53EF\u4EE5\u5728\u8BA1\u5212\u6A21\u5F0F\u5173\u95ED\u65F6\u81EA\u4E3B\u8C03\u7528 submit_plan \u5904\u7406\u5927\u578B\u4EFB\u52A1 \u2014 \u6B64\u5F00\u5173\u662F\u66F4\u5F3A\u7684\u663E\u5F0F\u7EA6\u675F\u3002\uFF09\u8F93\u5165 /plan off \u9000\u51FA\u3002",
|
|
1703
|
+
planOff: "\u25B8 \u8BA1\u5212\u6A21\u5F0F\u5173\u95ED \u2014 \u5199\u5165\u5DE5\u5177\u518D\u6B21\u53EF\u7528\u3002\u6A21\u578B\u4ECD\u53EF\u4E3A\u5927\u578B\u4EFB\u52A1\u81EA\u4E3B\u63D0\u51FA\u8BA1\u5212\u3002",
|
|
1704
|
+
modeCodeOnly: "/mode \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1705
|
+
modeUsage: "\u7528\u6CD5\uFF1A/mode <review|auto|yolo> \uFF08Shift+Tab \u4E5F\u53EF\u5FAA\u73AF\uFF09",
|
|
1706
|
+
modeYolo: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1AYOLO \u2014 \u7F16\u8F91\u548C Shell \u547D\u4EE4\u81EA\u52A8\u8FD0\u884C\uFF0C\u65E0\u63D0\u793A\u3002/undo \u4ECD\u53EF\u56DE\u6EDA\u7F16\u8F91\u3002\u8BF7\u8C28\u614E\u4F7F\u7528\u3002",
|
|
1707
|
+
modeAuto: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1AAUTO \u2014 \u7F16\u8F91\u7ACB\u5373\u5E94\u7528\uFF1B\u5728 5 \u79D2\u5185\u6309 u \u64A4\u6D88\uFF0C\u6216\u7A0D\u540E\u4F7F\u7528 /undo\u3002Shell \u547D\u4EE4\u4ECD\u4F1A\u8BE2\u95EE\u3002",
|
|
1708
|
+
modeReview: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1Areview \u2014 \u7F16\u8F91\u6392\u961F\u7B49\u5F85 /apply\uFF08\u6216 y\uFF09/ /discard\uFF08\u6216 n\uFF09",
|
|
1709
|
+
commitCodeOnly: "/commit \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\uFF08\u9700\u8981\u6709\u6839\u7684 git \u4ED3\u5E93\uFF09\u3002",
|
|
1710
|
+
commitUsage: '\u7528\u6CD5\uFF1A/commit "\u63D0\u4EA4\u6D88\u606F" \u2014 \u5728 {root} \u4E2D\u8FD0\u884C `git add -A && git commit -m "\u2026"`',
|
|
1711
|
+
walkCodeOnly: "/walk \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1712
|
+
checkpointCodeOnly: "/checkpoint \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528 \u2014 \u804A\u5929\u6A21\u5F0F\u4E0D\u5E94\u7528\u7F16\u8F91\u3002",
|
|
1713
|
+
checkpointNone: "\u5C1A\u65E0\u68C0\u67E5\u70B9 \u2014 `/checkpoint <name>` \u5FEB\u7167\u4F1A\u8BDD\u6D89\u53CA\u7684\u6BCF\u4E2A\u6587\u4EF6\u3002\u7A0D\u540E\u4F7F\u7528 `/restore <name>` \u6062\u590D\u3002",
|
|
1714
|
+
checkpointHeader: "\u25C8 \u68C0\u67E5\u70B9 \xB7 \u5DF2\u5B58\u50A8 {count} \u4E2A",
|
|
1715
|
+
checkpointRestoreHint: " /restore <name|id> \xB7 /checkpoint forget <id> \xB7 /checkpoint <name> \u6DFB\u52A0",
|
|
1716
|
+
checkpointForgetUsage: "\u7528\u6CD5\uFF1A/checkpoint forget <id|name>",
|
|
1717
|
+
checkpointNoMatch: '\u25B8 \u672A\u627E\u5230\u5339\u914D "{name}" \u7684\u68C0\u67E5\u70B9 \u2014 \u89C1 /checkpoint list',
|
|
1718
|
+
checkpointDeleted: "\u25B8 \u5DF2\u5220\u9664\u68C0\u67E5\u70B9 {id}\uFF08{name}\uFF09",
|
|
1719
|
+
checkpointDeleteFailed: "\u25B8 \u5220\u9664 {id} \u5931\u8D25\uFF08\u5DF2\u6D88\u5931\uFF1F\uFF09",
|
|
1720
|
+
checkpointSaveUsage: "\u7528\u6CD5\uFF1A/checkpoint <name> \uFF08\u6216 /checkpoint list \u67E5\u770B\u73B0\u6709\uFF09",
|
|
1721
|
+
checkpointSavedEmpty: '\u25B8 \u68C0\u67E5\u70B9 "{name}" \u5DF2\u4FDD\u5B58\uFF08{id}\uFF09\u2014 \u4F46\u5C1A\u672A\u6D89\u53CA\u4EFB\u4F55\u6587\u4EF6\uFF0C\u56E0\u6B64\u662F\u7A7A\u57FA\u7EBF\u3002\u6B64\u540E\u7684\u7F16\u8F91\u5C06\u53EF\u64A4\u6D88\u3002',
|
|
1722
|
+
checkpointSaved: '\u25B8 \u68C0\u67E5\u70B9 "{name}" \u5DF2\u4FDD\u5B58\uFF08{id}\uFF09\u2014 {files} \u4E2A\u6587\u4EF6\uFF0C{size} KB\u3002\u6062\u590D\uFF1A/restore {name}',
|
|
1723
|
+
restoreCodeOnly: "/restore \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1724
|
+
restoreUsage: "\u7528\u6CD5\uFF1A/restore <name|id> \uFF08\u89C1 /checkpoint list \u83B7\u53D6 ID\uFF09",
|
|
1725
|
+
restoreNoMatch: '\u25B8 \u672A\u627E\u5230\u5339\u914D "{target}" \u7684\u68C0\u67E5\u70B9 \u2014 \u5C1D\u8BD5 /checkpoint list',
|
|
1726
|
+
restoreInfo: '\u25B8 \u5DF2\u6062\u590D "{name}"\uFF08{id}\uFF09\uFF0C\u6765\u81EA {when}',
|
|
1727
|
+
restoreWrote: " \xB7 \u5199\u56DE\u4E86 {count} \u4E2A\u6587\u4EF6",
|
|
1728
|
+
restoreRemoved: " \xB7 \u79FB\u9664\u4E86 {count} \u4E2A\u6587\u4EF6\uFF08\u68C0\u67E5\u70B9\u65F6\u4E0D\u5B58\u5728\uFF09",
|
|
1729
|
+
restoreSkipped: " \u2717 \u8DF3\u8FC7\u4E86 {count} \u4E2A\u6587\u4EF6\uFF1A"
|
|
1730
|
+
},
|
|
1731
|
+
model: {
|
|
1732
|
+
modelHint: "\u5C1D\u8BD5 deepseek-v4-flash \u6216 deepseek-v4-pro \u2014 \u8FD0\u884C /models \u83B7\u53D6\u5B9E\u65F6\u5217\u8868",
|
|
1733
|
+
modelUsage: "\u7528\u6CD5\uFF1A/model <id> \uFF08{hint}\uFF09",
|
|
1734
|
+
modelNotInCatalog: "model \u2192 {id} \uFF08\u26A0 \u4E0D\u5728\u83B7\u53D6\u7684\u76EE\u5F55\u4E2D\uFF1A{list}\u3002\u5982\u679C\u8FD9\u662F\u9519\u8BEF\u7684\uFF0C\u4E0B\u6B21\u8C03\u7528\u5C06\u8FD4\u56DE 400 \u2014 \u8FD0\u884C /models \u5237\u65B0\u3002\uFF09",
|
|
1735
|
+
modelSet: "model \u2192 {id}",
|
|
1736
|
+
presetAuto: "preset \u2192 auto \uFF08v4-flash \u2192 v4-pro \u5728\u56F0\u96BE\u8F6E\u6B21\u5207\u6362 \xB7 \u9ED8\u8BA4\uFF09",
|
|
1737
|
+
presetFlash: "preset \u2192 flash \uFF08\u59CB\u7EC8\u4F7F\u7528 v4-flash \xB7 \u6700\u4FBF\u5B9C \xB7 /pro \u4ECD\u53EF\u4E34\u65F6\u63D0\u5347\u4E00\u8F6E\uFF09",
|
|
1738
|
+
presetPro: "preset \u2192 pro \uFF08\u59CB\u7EC8\u4F7F\u7528 v4-pro \xB7 \u7EA6 3 \u500D flash \xB7 \u7528\u4E8E\u56F0\u96BE\u7684\u591A\u8F6E\u5DE5\u4F5C\uFF09",
|
|
1739
|
+
presetUsage: "\u7528\u6CD5\uFF1A/preset <auto|flash|pro>",
|
|
1740
|
+
proNothingArmed: "\u672A\u542F\u7528 \u2014 /pro \u4E0D\u5E26\u53C2\u6570\u5C06\u4E3A\u4E0B\u4E00\u8F6E\u542F\u7528 pro",
|
|
1741
|
+
proDisarmed: "\u25B8 /pro \u5DF2\u89E3\u9664 \u2014 \u4E0B\u4E00\u8F6E\u56DE\u9000\u5230\u5F53\u524D\u9884\u8BBE",
|
|
1742
|
+
proUsage: "\u7528\u6CD5\uFF1A/pro \u4E3A\u4E0B\u4E00\u8F6E\u542F\u7528 pro\uFF08\u4E00\u6B21\u6027\uFF0C\u81EA\u52A8\u89E3\u9664\uFF09\n /pro off \u5728\u4E0B\u4E00\u8F6E\u524D\u53D6\u6D88\u542F\u7528\u72B6\u6001",
|
|
1743
|
+
proArmed: "\u25B8 /pro \u5DF2\u542F\u7528 \u2014 \u60A8\u7684\u4E0B\u4E00\u6761\u6D88\u606F\u5C06\u5728 {model} \u4E0A\u8FD0\u884C\uFF0C\u65E0\u8BBA\u9884\u8BBE\u5982\u4F55\u3002\u4E00\u8F6E\u540E\u81EA\u52A8\u89E3\u9664\u3002\u4F7F\u7528 /preset max \u8FDB\u884C\u6301\u4E45\u5207\u6362\u3002",
|
|
1744
|
+
budgetNoCap: "\u672A\u8BBE\u7F6E\u4F1A\u8BDD\u9884\u7B97 \u2014 Reasonix \u5C06\u6301\u7EED\u8FD0\u884C\u76F4\u5230\u60A8\u505C\u6B62\u3002\u4F7F\u7528\u4EE5\u4E0B\u65B9\u5F0F\u8BBE\u7F6E\uFF1A/budget <usd> \uFF08\u4F8B\u5982 /budget 5\uFF09",
|
|
1745
|
+
budgetStatus: "\u9884\u7B97\uFF1A${spent} / ${cap}\uFF08{pct}%\uFF09\xB7 /budget off \u6E05\u9664\uFF0C/budget <usd> \u66F4\u6539",
|
|
1746
|
+
budgetOff: "budget \u2192 \u5173\u95ED\uFF08\u65E0\u4E0A\u9650\uFF09",
|
|
1747
|
+
budgetUsage: '\u7528\u6CD5\uFF1A/budget <usd> \uFF08\u6536\u5230 "{arg}" \u2014 \u5FC5\u987B\u662F\u6B63\u6570\uFF0C\u4F8B\u5982 /budget 5 \u6216 /budget 12.50\uFF09',
|
|
1748
|
+
budgetExhausted: "\u25B2 budget \u2192 ${cap} \u4F46\u5DF2\u82B1\u8D39 ${spent}\u3002\u4E0B\u4E00\u8F6E\u5C06\u88AB\u62D2\u7EDD \u2014 \u63D0\u9AD8\u4E0A\u9650\u4EE5\u7EE7\u7EED\uFF0C\u6216\u7ED3\u675F\u4F1A\u8BDD\u3002",
|
|
1749
|
+
budgetSet: "budget \u2192 ${cap} \uFF08\u8FC4\u4ECA\uFF1A${spent} \xB7 80% \u65F6\u8B66\u544A\uFF0C100% \u65F6\u62D2\u7EDD\u4E0B\u4E00\u8F6E \xB7 /budget off \u6E05\u9664\uFF09"
|
|
1750
|
+
},
|
|
1751
|
+
permissions: {
|
|
1752
|
+
mutateCodeOnly: "/permissions add / remove / clear \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528 \u2014 \u5B83\u4EEC\u7F16\u8F91\u9879\u76EE\u8303\u56F4\u7684\u5141\u8BB8\u5217\u8868\uFF08`~/.reasonix/config.json` projects[<root>].shellAllowed\uFF09\u3002",
|
|
1753
|
+
addUsage: '\u7528\u6CD5\uFF1A/permissions add <prefix> \uFF08\u591A token \u53EF\u7528\uFF1A/permissions add "git push origin"\uFF09',
|
|
1754
|
+
addAlready: "\u25B8 \u5DF2\u5141\u8BB8\uFF1A{prefix}",
|
|
1755
|
+
addBuiltin: "\u25B8 `{prefix}` \u5DF2\u5728\u5185\u7F6E\u5141\u8BB8\u5217\u8868\u4E2D \u2014 \u65E0\u9700\u9879\u76EE\u6761\u76EE\u3002\uFF08\u5185\u7F6E\u6761\u76EE\u59CB\u7EC8\u5F00\u542F\u3002\uFF09",
|
|
1756
|
+
addInfo: "\u25B8 \u5DF2\u6DFB\u52A0\uFF1A{prefix}\n \u2192 \u5728\u6B64\u9879\u76EE\u4E2D\uFF0C\u4E0B\u6B21 `{prefix}` \u8C03\u7528\u5C06\u65E0\u9700\u63D0\u793A\u3002",
|
|
1757
|
+
removeUsage: "\u7528\u6CD5\uFF1A/permissions remove <prefix-or-index> \uFF08\u4F8B\u5982 /permissions remove 3\uFF0C\u6216 /permissions remove npm\uFF09",
|
|
1758
|
+
removeEmpty: "\u25B8 \u6CA1\u6709\u9879\u76EE\u5141\u8BB8\u5217\u8868\u6761\u76EE\u53EF\u79FB\u9664\u3002",
|
|
1759
|
+
removeIndexOob: "\u25B8 \u7D22\u5F15\u8D85\u51FA\u8303\u56F4\uFF1A{idx}\uFF08\u9879\u76EE\u5217\u8868\u6709 {count} \u4E2A\u6761\u76EE\uFF09",
|
|
1760
|
+
removeNothing: "\u25B8 \u65E0\u5185\u5BB9\u53EF\u79FB\u9664\u3002",
|
|
1761
|
+
removeBuiltin: "\u25B8 `{prefix}` \u5728\u5185\u7F6E\u5141\u8BB8\u5217\u8868\u4E2D\uFF08\u53EA\u8BFB\uFF09\u3002\u5185\u7F6E\u6761\u76EE\u65E0\u6CD5\u5728\u8FD0\u884C\u65F6\u79FB\u9664 \u2014 \u5B83\u4EEC\u5DF2\u7F16\u8BD1\u5230\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E2D\u3002",
|
|
1762
|
+
removeInfo: "\u25B8 \u5DF2\u79FB\u9664\uFF1A{prefix}",
|
|
1763
|
+
removeNotFound: "\u25B8 \u65E0\u6B64\u9879\u76EE\u6761\u76EE\uFF1A{prefix} \uFF08\u5C1D\u8BD5 /permissions list \u67E5\u770B\u5DF2\u5B58\u50A8\u7684\u5185\u5BB9\uFF09",
|
|
1764
|
+
clearAlready: "\u25B8 \u9879\u76EE\u5141\u8BB8\u5217\u8868\u5DF2\u4E3A\u7A7A\u3002",
|
|
1765
|
+
clearConfirm: "\u5373\u5C06\u4E22\u5F03 {root} \u7684 {count} \u4E2A\u9879\u76EE\u5141\u8BB8\u5217\u8868\u6761\u76EE\u3002\u91CD\u65B0\u8FD0\u884C\u5E76\u9644\u5E26 'confirm' \u4E00\u8BCD\u4EE5\u7EE7\u7EED\uFF1A/permissions clear confirm",
|
|
1766
|
+
clearedNone: "\u25B8 \u9879\u76EE\u5141\u8BB8\u5217\u8868\u5DF2\u4E3A\u7A7A \u2014 \u65E0\u53D8\u5316\u3002",
|
|
1767
|
+
cleared: "\u25B8 \u5DF2\u6E05\u9664 {count} \u4E2A\u9879\u76EE\u5141\u8BB8\u5217\u8868\u6761\u76EE\u3002",
|
|
1768
|
+
usage: '\u7528\u6CD5\uFF1A/permissions [list] \u663E\u793A\u5F53\u524D\u72B6\u6001\n /permissions add <prefix> \u6301\u4E45\u5316\uFF08\u4F8B\u5982 "npm run build"\uFF09\n /permissions remove <prefix-or-N> \u5220\u9664\u4E00\u4E2A\u6761\u76EE\n /permissions clear confirm \u6E05\u9664\u6240\u6709\u9879\u76EE\u6761\u76EE',
|
|
1769
|
+
modeYolo: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1AYOLO \u2014 \u6BCF\u4E2A shell \u547D\u4EE4\u81EA\u52A8\u8FD0\u884C\uFF0C\u5141\u8BB8\u5217\u8868\u88AB\u7ED5\u8FC7\u3002/mode review \u91CD\u65B0\u542F\u7528\u63D0\u793A\u3002",
|
|
1770
|
+
modeAuto: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1Aauto \u2014 \u7F16\u8F91\u81EA\u52A8\u5E94\u7528\uFF0Cshell \u4ECD\u53D7\u5141\u8BB8\u5217\u8868\u9650\u5236\uFF08\u6216\u975E\u5141\u8BB8\u5217\u8868\u7684 ShellConfirm \u63D0\u793A\uFF09\u3002",
|
|
1771
|
+
modeReview: "\u25B8 \u7F16\u8F91\u6A21\u5F0F\uFF1Areview \u2014 \u7F16\u8F91\u548C\u975E\u5141\u8BB8\u5217\u8868\u7684 shell \u547D\u4EE4\u5728\u8FD0\u884C\u524D\u90FD\u4F1A\u8BE2\u95EE\u3002",
|
|
1772
|
+
projectHeader: "\u9879\u76EE\u5141\u8BB8\u5217\u8868\uFF08{count}\uFF09\u2014 {root}",
|
|
1773
|
+
projectNone1: ' \uFF08\u65E0 \u2014 \u5728 ShellConfirm \u63D0\u793A\u4E2D\u9009\u62E9 "always allow" \u6DFB\u52A0\u4E00\u4E2A\uFF0C',
|
|
1774
|
+
projectNone2: " \u6216\u76F4\u63A5 `/permissions add <prefix>`\u3002\uFF09",
|
|
1775
|
+
projectNoRoot: "\u9879\u76EE\u5141\u8BB8\u5217\u8868 \u2014 \uFF08\u65E0\u9879\u76EE\u6839\u76EE\u5F55\uFF1B\u804A\u5929\u6A21\u5F0F\u4EC5\u663E\u793A\u5185\u7F6E\u6761\u76EE\uFF09",
|
|
1776
|
+
builtinHeader: "\u5185\u7F6E\u5141\u8BB8\u5217\u8868\uFF08{count}\uFF09\u2014 \u53EA\u8BFB\uFF0C\u5DF2\u7F16\u8BD1",
|
|
1777
|
+
subcommands: "\u5B50\u547D\u4EE4\uFF1A/permissions add <prefix> \xB7 /permissions remove <prefix-or-N> \xB7 /permissions clear confirm"
|
|
1778
|
+
},
|
|
1779
|
+
dashboard: {
|
|
1780
|
+
notAvailable: "/dashboard \u5728\u6B64\u4E0A\u4E0B\u6587\u4E2D\u4E0D\u53EF\u7528\uFF08\u65E0 startDashboard \u56DE\u8C03\uFF09\u3002",
|
|
1781
|
+
stopNoCallback: "/dashboard stop\uFF1A\u65E0\u505C\u6B62\u56DE\u8C03\u3002",
|
|
1782
|
+
notRunning: "\u25B8 \u4EEA\u8868\u677F\u672A\u8FD0\u884C\u3002",
|
|
1783
|
+
stopping: "\u25B8 \u4EEA\u8868\u677F\u6B63\u5728\u505C\u6B62\u2026",
|
|
1784
|
+
alreadyRunning: "\u25B8 \u4EEA\u8868\u677F\u5DF2\u5728\u8FD0\u884C\uFF1A",
|
|
1785
|
+
alreadyRunningHint: "\u5728\u4EFB\u4F55\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u5B83\u3002\u8F93\u5165 `/dashboard stop` \u5173\u95ED\u3002",
|
|
1786
|
+
ready: "\u25B8 \u4EEA\u8868\u677F\u5C31\u7EEA\uFF1A",
|
|
1787
|
+
readyHint: "\u4EC5 127.0.0.1 \xB7 token \u4FDD\u62A4\u3002\u8F93\u5165 `/dashboard stop` \u5173\u95ED\u3002",
|
|
1788
|
+
failed: "\u25B8 \u4EEA\u8868\u677F\u542F\u52A8\u5931\u8D25\uFF1A{reason}",
|
|
1789
|
+
starting: "\u25B8 \u6B63\u5728\u542F\u52A8\u4EEA\u8868\u677F\u670D\u52A1\u5668\u2026"
|
|
1790
|
+
},
|
|
1791
|
+
observability: {
|
|
1792
|
+
contextInfo: "\u4E0A\u4E0B\u6587\uFF1A~{total} / {max}\uFF08{pct}%\uFF09\xB7 \u7CFB\u7EDF {sys} \xB7 \u5DE5\u5177 {tools} \xB7 \u65E5\u5FD7 {log}",
|
|
1793
|
+
compactStarting: "\u25B8 \u6B63\u5728\u6298\u53E0\u65E7\u8F6E\u6B21\u4E3A\u6458\u8981\u2026",
|
|
1794
|
+
compactNoop: "\u25B8 \u65E0\u9700\u6298\u53E0 \u2014 \u65E5\u5FD7\u5DF2\u8DB3\u591F\u5C0F\uFF0C\u6216\u6700\u8FD1\u8F6E\u6B21\u672C\u8EAB\u5DF2\u8D85\u8FC7\u9884\u7B97\u3002",
|
|
1795
|
+
compactDone: "\u25B8 \u5DF2\u6298\u53E0 {before} \u6761\u6D88\u606F \u2192 {after}\uFF08\u6458\u8981 {chars} \u5B57\u7B26\uFF09\u3002\u7EE7\u7EED\u3002",
|
|
1796
|
+
compactFailed: "\u25B8 \u6298\u53E0\u5931\u8D25\uFF1A{reason}",
|
|
1797
|
+
costNoTurn: "\u5C1A\u65E0\u8F6E\u6B21 \u2014 `/cost` \u663E\u793A\u6700\u8FD1\u4E00\u8F6E\u7684 token + \u82B1\u8D39\u660E\u7EC6\u3002",
|
|
1798
|
+
costNeedsTui: "/cost \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postUsage \u5DF2\u8FDE\u63A5\uFF09\u3002",
|
|
1799
|
+
costNoPricing: '\u25B8 /cost\uFF1A\u6A21\u578B "{model}" \u65E0\u5B9A\u4EF7\u8868\u3002\u8BF7\u5728 telemetry/stats.ts \u4E2D\u6DFB\u52A0\u3002',
|
|
1800
|
+
costEstimate: "\u25B8 /cost \u4F30\u7B97 \xB7 {model} \xB7 {prompt} prompt tokens\uFF08\u7CFB\u7EDF {sys} + \u5DE5\u5177 {tools} + \u65E5\u5FD7 {log} + \u6D88\u606F {msg}\uFF09",
|
|
1801
|
+
costWorstCase: " \u6700\u574F\u60C5\u51B5\uFF08\u5B8C\u5168\u672A\u547D\u4E2D\uFF09\uFF1A{input} \u8F93\u5165 + ~{output} \u8F93\u51FA\uFF08{avg} \u5E73\u5747\uFF09\u2248 {total}",
|
|
1802
|
+
costLikely: " \u53EF\u80FD\uFF08{pct}% \u4F1A\u8BDD\u7F13\u5B58\u547D\u4E2D\uFF09\uFF1A{input} \u8F93\u5165 + ~{output} \u8F93\u51FA \u2248 {total}",
|
|
1803
|
+
costLikelyCold: " \u53EF\u80FD\uFF1A\u5728\u7F13\u5B58\u586B\u5145\u524D\u4E0E\u6700\u574F\u60C5\u51B5\u76F8\u540C\uFF08\u65E0\u5DF2\u5B8C\u6210\u7684\u8F6E\u6B21\uFF09",
|
|
1804
|
+
statusModel: " \u6A21\u578B {model}",
|
|
1805
|
+
statusFlags: " \u6807\u5FD7 stream={stream} \xB7 effort={effort}",
|
|
1806
|
+
statusCtx: " \u4E0A\u4E0B\u6587 {bar} {used}/{max}\uFF08{pct}%\uFF09",
|
|
1807
|
+
statusCtxNone: " \u4E0A\u4E0B\u6587 \u5C1A\u65E0\u8F6E\u6B21",
|
|
1808
|
+
statusCost: " \u6210\u672C ${cost} \xB7 \u7F13\u5B58 {bar} {pct}% \xB7 \u8F6E\u6B21 {turns}",
|
|
1809
|
+
statusCostCold: " \u6210\u672C ${cost} \xB7 \u8F6E\u6B21 {turns}\uFF08\u7F13\u5B58\u9884\u70ED\u4E2D\uFF09",
|
|
1810
|
+
statusBudget: " \u9884\u7B97 ${spent} / ${cap}\uFF08{pct}%\uFF09{tag}",
|
|
1811
|
+
statusSession: ' \u4F1A\u8BDD "{name}" \xB7 \u65E5\u5FD7\u4E2D {count} \u6761\u6D88\u606F\uFF08\u6062\u590D\u4E86 {resumed} \u6761\uFF09',
|
|
1812
|
+
statusSessionEphemeral: " \u4F1A\u8BDD \uFF08\u4E34\u65F6 \u2014 \u65E0\u6301\u4E45\u5316\uFF09",
|
|
1813
|
+
statusWorkspace: " \u5DE5\u4F5C\u533A {path} \xB7 \u542F\u52A8\u65F6\u9501\u5B9A\uFF08\u7528 --dir <path> \u91CD\u65B0\u542F\u52A8\u4EE5\u5207\u6362\uFF09",
|
|
1814
|
+
statusMcp: " MCP {servers} \u4E2A\u670D\u52A1\u5668\uFF0C\u6CE8\u518C\u8868\u4E2D {tools} \u4E2A\u5DE5\u5177",
|
|
1815
|
+
statusEdits: " \u7F16\u8F91 {count} \u4E2A\u5F85\u5904\u7406\uFF08/apply \u63D0\u4EA4\uFF0C/discard \u4E22\u5F03\uFF09",
|
|
1816
|
+
statusPlan: " \u8BA1\u5212 \u5F00\u542F \u2014 \u5199\u5165\u53D7\u9650\uFF08submit_plan + \u5BA1\u6279\uFF09",
|
|
1817
|
+
statusModeYolo: " \u6A21\u5F0F YOLO \u2014 \u7F16\u8F91 + shell \u81EA\u52A8\u8FD0\u884C\uFF0C\u65E0\u63D0\u793A\uFF08/undo \u4ECD\u53EF\u56DE\u6EDA \xB7 Shift+Tab \u5207\u6362\uFF09",
|
|
1818
|
+
statusModeAuto: " \u6A21\u5F0F AUTO \u2014 \u7F16\u8F91\u7ACB\u5373\u5E94\u7528\uFF085 \u79D2\u5185\u6309 u \u64A4\u6D88 \xB7 Shift+Tab \u5207\u6362\uFF09",
|
|
1819
|
+
statusModeReview: " \u6A21\u5F0F review \u2014 \u7F16\u8F91\u6392\u961F\u7B49\u5F85 /apply \u6216 y\uFF08Shift+Tab \u5207\u6362\uFF09",
|
|
1820
|
+
statusDash: " \u4EEA\u8868\u677F {url}\uFF08\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 \xB7 /dashboard stop\uFF09"
|
|
1821
|
+
},
|
|
1822
|
+
plans: {
|
|
1823
|
+
noSession: "\u672A\u9644\u52A0\u4F1A\u8BDD \u2014 `/plans` \u662F\u6309\u4F1A\u8BDD\u7684\u3002\u5728\u9879\u76EE\u4E2D\u8FD0\u884C `reasonix code` \u4EE5\u83B7\u53D6\u4F1A\u8BDD\u3002",
|
|
1824
|
+
activePlan: "\u25B8 \u6D3B\u8DC3\u8BA1\u5212{label} \u2014 {done}/{total} \u6B65\u9AA4\u5DF2\u5B8C\u6210 \xB7 \u6700\u540E\u89E6\u53CA {when}",
|
|
1825
|
+
activeNone: "\u25B8 \u6D3B\u8DC3\u8BA1\u5212\uFF1A\uFF08\u65E0\uFF09",
|
|
1826
|
+
noArchives: "\u6B64\u4F1A\u8BDD\u5C1A\u65E0\u5F52\u6863\u8BA1\u5212 \u2014 \u5F53\u6BCF\u4E2A\u6B65\u9AA4\u5B8C\u6210\u65F6\u81EA\u52A8\u5F52\u6863",
|
|
1827
|
+
archivedHeader: "\u5DF2\u5F52\u6863\uFF08{count}\uFF09\uFF1A",
|
|
1828
|
+
replayNoSession: "\u672A\u9644\u52A0\u4F1A\u8BDD \u2014 `/replay` \u662F\u6309\u4F1A\u8BDD\u7684\u3002\u5728\u9879\u76EE\u4E2D\u8FD0\u884C `reasonix code` \u4EE5\u83B7\u53D6\u4F1A\u8BDD\u3002",
|
|
1829
|
+
replayNoArchives: "\u6B64\u4F1A\u8BDD\u5C1A\u65E0\u5F52\u6863\u8BA1\u5212 \u2014 `/replay` \u5728\u8BA1\u5212\u5B8C\u6210\u540E\u542F\u7528\uFF08\u6BCF\u4E2A\u6B65\u9AA4\u5B8C\u6210\u65F6\u81EA\u52A8\u5F52\u6863\uFF09\u3002",
|
|
1830
|
+
replayInvalidIndex: "\u65E0\u6548\u7D22\u5F15 \u2014 `/replay` \u63A5\u53D7 1..{max}\uFF08\u6700\u65B0 = 1\uFF09\u3002\u4F7F\u7528 `/plans` \u67E5\u770B\u5217\u8868\u3002",
|
|
1831
|
+
archivedRow: " \u2713 {when} {total}\u6B65 \xB7 {completion} {label}",
|
|
1832
|
+
completionComplete: "\u5DF2\u5B8C\u6210",
|
|
1833
|
+
stopAborted: "\u25B8 \u8BA1\u5212\u5DF2\u505C\u6B62 \u2014 \u6A21\u578B\u5DF2\u4E2D\u6B62\uFF1B\u8F93\u5165\u540E\u7EED\u5185\u5BB9\u7EE7\u7EED\uFF0C\u6216\u5F00\u59CB\u65B0\u4EFB\u52A1\u3002"
|
|
1834
|
+
},
|
|
1835
|
+
jobs: {
|
|
1836
|
+
codeOnly: "/jobs \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1837
|
+
killCodeOnly: "/kill \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1838
|
+
logsCodeOnly: "/logs \u4EC5\u5728 `reasonix code` \u4E2D\u53EF\u7528\u3002",
|
|
1839
|
+
empty: "\u25C8 \u4F5C\u4E1A \xB7 0 \u8FD0\u884C\u4E2D \xB7 \u5171 0 \u4E2A\n \uFF08run_background \u751F\u6210\u4E00\u4E2A \u2014 \u5F00\u53D1\u670D\u52A1\u5668\u3001\u76D1\u89C6\u5668\u3001\u957F\u65F6\u95F4\u8FD0\u884C\u7684\u811A\u672C\uFF09",
|
|
1840
|
+
header: "\u25C8 \u4F5C\u4E1A \xB7 {running} \u8FD0\u884C\u4E2D \xB7 \u5171 {total} \u4E2A",
|
|
1841
|
+
footer: " /logs <id> \u8DDF\u8E2A \xB7 /kill <id> SIGTERM \u2192 SIGKILL",
|
|
1842
|
+
killUsage: "\u7528\u6CD5\uFF1A/kill <id> \uFF08\u89C1 /jobs \u83B7\u53D6 ID\uFF09",
|
|
1843
|
+
killNotFound: "\u4F5C\u4E1A {id}\uFF1A\u672A\u627E\u5230",
|
|
1844
|
+
killAlreadyExited: "\u4F5C\u4E1A {id} \u5DF2\u9000\u51FA\uFF08{code}\uFF09",
|
|
1845
|
+
killStopping: "\u25B8 \u6B63\u5728\u505C\u6B62\u4F5C\u4E1A {id}\uFF08\u6811\u7EC8\u6B62\uFF1ASIGTERM \u2192 2 \u79D2\u5BBD\u9650\u671F\u540E SIGKILL\uFF1BWindows\uFF1Ataskkill /T /F\uFF09",
|
|
1846
|
+
killStatus: "\u25B8 \u4F5C\u4E1A {id} {status}",
|
|
1847
|
+
killStillAlive: "SIGKILL \u540E\u4ECD\u5B58\u6D3B (!) \u2014 \u8BF7\u5C06\u6B64\u4F5C\u4E3A bug \u62A5\u544A",
|
|
1848
|
+
logsUsage: "\u7528\u6CD5\uFF1A/logs <id> [lines] \uFF08\u9ED8\u8BA4\u6700\u540E 80 \u884C\uFF09",
|
|
1849
|
+
logsNotFound: "\u4F5C\u4E1A {id}\uFF1A\u672A\u627E\u5230",
|
|
1850
|
+
logsStatus: "[\u4F5C\u4E1A {id} \xB7 {status}]\n$ {command}",
|
|
1851
|
+
logsRunning: "\u8FD0\u884C\u4E2D \xB7 pid {pid}",
|
|
1852
|
+
logsExited: "\u5DF2\u9000\u51FA {code}",
|
|
1853
|
+
logsFailed: "\u5931\u8D25\uFF08{reason}\uFF09",
|
|
1854
|
+
logsStopped: "\u5DF2\u505C\u6B62"
|
|
1855
|
+
},
|
|
1856
|
+
memory: {
|
|
1857
|
+
disabled: "\u8BB0\u5FC6\u5DF2\u7981\u7528\uFF08\u73AF\u5883\u53D8\u91CF REASONIX_MEMORY=off\uFF09\u3002\u53D6\u6D88\u8BBE\u7F6E\u8BE5\u53D8\u91CF\u4EE5\u91CD\u65B0\u542F\u7528 \u2014 \u6B64\u671F\u95F4\u4E0D\u4F1A\u56FA\u5B9A\u4EFB\u4F55 REASONIX.md \u6216 ~/.reasonix/memory \u5185\u5BB9\u3002",
|
|
1858
|
+
noRoot: "\u6B64\u4F1A\u8BDD\u65E0\u5DE5\u4F5C\u76EE\u5F55 \u2014 `/memory` \u9700\u8981\u4E00\u4E2A\u6839\u76EE\u5F55\u6765\u89E3\u6790 REASONIX.md\u3002\uFF08\u5728\u6D4B\u8BD5\u73AF\u5883\u4E2D\u8FD0\u884C\uFF1F\uFF09",
|
|
1859
|
+
listEmpty: "\u5C1A\u65E0\u7528\u6237\u8BB0\u5FC6\u3002\u6A21\u578B\u53EF\u4EE5\u8C03\u7528 `remember` \u4FDD\u5B58\u4E00\u4E2A\uFF0C\u6216\u60A8\u53EF\u4EE5\u5728 ~/.reasonix/memory/global/ \u6216\u9879\u76EE\u5B50\u76EE\u5F55\u4E2D\u624B\u52A8\u521B\u5EFA\u6587\u4EF6\u3002",
|
|
1860
|
+
listHeader: "\u7528\u6237\u8BB0\u5FC6\uFF08{count}\uFF09\uFF1A",
|
|
1861
|
+
listFooter: "\u67E5\u770B\u6B63\u6587\uFF1A/memory show <name> \u5220\u9664\uFF1A/memory forget <name>",
|
|
1862
|
+
showUsage: "\u7528\u6CD5\uFF1A/memory show <name> \u6216 /memory show <scope>/<name>",
|
|
1863
|
+
showNotFound: "\u672A\u627E\u5230\u8BB0\u5FC6\uFF1A{target}",
|
|
1864
|
+
showFailed: "\u663E\u793A\u5931\u8D25\uFF1A{reason}",
|
|
1865
|
+
forgetUsage: "\u7528\u6CD5\uFF1A/memory forget <name> \u6216 /memory forget <scope>/<name>",
|
|
1866
|
+
forgetNotFound: "\u672A\u627E\u5230\u8BB0\u5FC6\uFF1A{target}",
|
|
1867
|
+
forgetInfo: "\u25B8 \u5DF2\u9057\u5FD8 {scope}/{name}\u3002\u4E0B\u6B21 /new \u6216\u542F\u52A8\u65F6\u5C06\u4E0D\u53EF\u89C1\u3002",
|
|
1868
|
+
forgetFailed: "\u65E0\u6CD5\u9057\u5FD8 {scope}/{name}\uFF08\u5DF2\u6D88\u5931\uFF1F\uFF09",
|
|
1869
|
+
forgetError: "\u9057\u5FD8\u5931\u8D25\uFF1A{reason}",
|
|
1870
|
+
clearUsage: "\u7528\u6CD5\uFF1A/memory clear <global|project> confirm",
|
|
1871
|
+
clearConfirm: "\u5373\u5C06\u5220\u9664 scope={scope} \u4E2D\u7684\u6BCF\u4E2A\u8BB0\u5FC6\u3002\u91CD\u65B0\u8FD0\u884C\u5E76\u9644\u5E26 'confirm' \u4E00\u8BCD\u4EE5\u7EE7\u7EED\uFF1A/memory clear {scope} confirm",
|
|
1872
|
+
cleared: "\u25B8 \u5DF2\u6E05\u9664 scope={scope} \u2014 \u5220\u9664\u4E86 {count} \u4E2A\u8BB0\u5FC6\u6587\u4EF6\u3002",
|
|
1873
|
+
noMemory: "\u5728 {root} \u4E2D\u672A\u56FA\u5B9A\u8BB0\u5FC6\u3002",
|
|
1874
|
+
layers: "\u53EF\u7528\u7684\u4E09\u4E2A\u5C42\u7EA7\uFF1A",
|
|
1875
|
+
layerProject: " 1. {file} \u2014 \u53EF\u63D0\u4EA4\u7684\u56E2\u961F\u8BB0\u5FC6\uFF08\u5728\u4ED3\u5E93\u4E2D\uFF09\u3002",
|
|
1876
|
+
layerGlobal: " 2. ~/.reasonix/memory/global/ \u2014 \u60A8\u7684\u8DE8\u9879\u76EE\u79C1\u6709\u8BB0\u5FC6\u3002",
|
|
1877
|
+
layerProjectHash: " 3. ~/.reasonix/memory/<project-hash>/ \u2014 \u6B64\u9879\u76EE\u7684\u79C1\u6709\u8BB0\u5FC6\u3002",
|
|
1878
|
+
askModel: "\u8BA9\u6A21\u578B `remember` \u67D0\u4E9B\u5185\u5BB9\uFF0C\u6216\u76F4\u63A5\u624B\u7F16\u8F91\u6587\u4EF6\u3002",
|
|
1879
|
+
changesNote: "\u66F4\u6539\u5728\u4E0B\u6B21 /new \u6216\u542F\u52A8\u65F6\u751F\u6548 \u2014 \u7CFB\u7EDF\u63D0\u793A\u8BCD\u6BCF\u4F1A\u8BDD\u54C8\u5E0C\u4E00\u6B21\u4EE5\u4FDD\u6301\u524D\u7F00\u7F13\u5B58\u70ED\u5EA6\u3002",
|
|
1880
|
+
subcommands: "\u5B50\u547D\u4EE4\uFF1A/memory list | /memory show <name> | /memory forget <name> | /memory clear <scope> confirm",
|
|
1881
|
+
changesNoteShort: "\u66F4\u6539\u5728\u4E0B\u6B21 /new \u6216\u542F\u52A8\u65F6\u751F\u6548\u3002\u5B50\u547D\u4EE4\uFF1A/memory list | show | forget | clear"
|
|
1882
|
+
},
|
|
1883
|
+
mcp: {
|
|
1884
|
+
noServers: '\u672A\u9644\u52A0 MCP \u670D\u52A1\u5668\u3002\u8FD0\u884C `reasonix setup` \u9009\u62E9\u4E00\u4E9B\uFF0C\u6216\u4F7F\u7528 --mcp "<spec>" \u542F\u52A8\u3002`reasonix mcp list` \u663E\u793A\u76EE\u5F55\u3002',
|
|
1885
|
+
toolsLabel: " \u5DE5\u5177 {count}",
|
|
1886
|
+
resourcesHint: "`/resource` \u6D4F\u89C8+\u8BFB\u53D6",
|
|
1887
|
+
promptsHint: "`/prompt` \u6D4F\u89C8+\u83B7\u53D6",
|
|
1888
|
+
awarenessOnly: "\u804A\u5929\u6A21\u5F0F\u76EE\u524D\u6D88\u8017\u5DE5\u5177\uFF1B\u8D44\u6E90+\u63D0\u793A\u5728\u6B64\u5C55\u793A\u4F9B\u4E86\u89E3\u3002",
|
|
1889
|
+
catalogHint: "\u5B8C\u6574\u76EE\u5F55\uFF1A`reasonix mcp list` \xB7 \u6DF1\u5EA6\u8BCA\u65AD\uFF1A`reasonix mcp inspect <spec>`\u3002",
|
|
1890
|
+
fallbackServers: "MCP \u670D\u52A1\u5668\uFF08{count}\uFF09\uFF1A",
|
|
1891
|
+
fallbackTools: "\u6CE8\u518C\u8868\u4E2D\u7684\u5DE5\u5177\uFF08{count}\uFF09\uFF1A",
|
|
1892
|
+
fallbackChange: "\u8981\u66F4\u6539\u6B64\u8BBE\u7F6E\uFF0C\u8BF7\u9000\u51FA\u5E76\u8FD0\u884C `reasonix setup`\u3002",
|
|
1893
|
+
usageDisableEnable: "\u7528\u6CD5\uFF1A/mcp {action} <name> \xB7 \u4ECE /mcp \u5217\u8868\u4E2D\u6311\u4E00\u4E2A\u540D\u5B57\uFF08\u533F\u540D\u670D\u52A1\u5668\u65E0\u6CD5\u6309\u540D\u5207\u6362\uFF09\u3002",
|
|
1894
|
+
usageReconnect: "\u7528\u6CD5\uFF1A/mcp reconnect <name> \xB7 \u4ECE /mcp \u5217\u8868\u4E2D\u6311\u4E00\u4E2A\u540D\u5B57\u3002",
|
|
1895
|
+
unknownServer: '\u672A\u77E5 MCP \u670D\u52A1\u5668 "{name}"\u3002\u5DF2\u77E5\uFF1A{list}\u3002',
|
|
1896
|
+
noneList: "\uFF08\u65E0\uFF09",
|
|
1897
|
+
reconnectNoTui: "/mcp reconnect \u9700\u8981\u4EA4\u4E92\u5F0F TUI\uFF08postInfo \u672A\u8FDE\u63A5\uFF09\u3002"
|
|
1898
|
+
},
|
|
1899
|
+
init: {
|
|
1900
|
+
codeOnly: "/init \u4EC5\u5728\u4EE3\u7801\u6A21\u5F0F\u4E0B\u5DE5\u4F5C\uFF08\u9700\u8981\u6587\u4EF6\u7CFB\u7EDF\u5DE5\u5177\uFF09\u3002\n\u8FD0\u884C `reasonix code [path]` \u542F\u52A8\u4E00\u4E2A\u4EE5\u60A8\u8981\u521D\u59CB\u5316\u7684\u9879\u76EE\u4E3A\u6839\u7684\u4F1A\u8BDD\uFF0C\n\u7136\u540E\u8FD0\u884C /init\u3002",
|
|
1901
|
+
exists: "\u25B8 REASONIX.md \u5DF2\u5B58\u5728\u4E8E {path}",
|
|
1902
|
+
existsForce: " /init force \u4ECE\u5934\u91CD\u65B0\u751F\u6210\uFF08\u8986\u76D6\uFF09",
|
|
1903
|
+
existsEdit: " \u6216\u624B\u52A8\u7F16\u8F91 \u2014 \u5B83\u53EA\u662F markdown\u3002\u5F53\u524D\u6587\u4EF6\u5DF2",
|
|
1904
|
+
existsPinned: " \u56FA\u5B9A\u5230\u6BCF\u6B21\u542F\u52A8\u7684\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u3002",
|
|
1905
|
+
info: "\u25B8 /init \u2014 \u6A21\u578B\u5C06\u626B\u63CF\u9879\u76EE\u5E76\u5408\u6210 REASONIX.md\u3002\n \u7ED3\u679C\u5C06\u4F5C\u4E3A\u5F85\u5904\u7406\u7684\u7F16\u8F91\uFF1B\u4F7F\u7528 /apply \u6216 /walk \u5BA1\u67E5\u3002"
|
|
1906
|
+
},
|
|
1907
|
+
webSearchEngine: {
|
|
1908
|
+
currentEngine: "\u5F53\u524D\u7F51\u9875\u641C\u7D22\u5F15\u64CE\uFF1A{engine}",
|
|
1909
|
+
endpoint: "SearXNG \u7AEF\u70B9\uFF1A{url}",
|
|
1910
|
+
usageHeader: "\u7528\u6CD5\uFF1A",
|
|
1911
|
+
usageMojeek: " /search-engine mojeek \u4F7F\u7528 Mojeek\uFF08\u9ED8\u8BA4\uFF0C\u65E0\u5916\u90E8\u4F9D\u8D56\uFF09",
|
|
1912
|
+
usageSearxng: " /search-engine searxng \u4F7F\u7528 SearXNG \u9ED8\u8BA4\u7AEF\u70B9",
|
|
1913
|
+
usageSearxngUrl: " /search-engine searxng <url> \u4F7F\u7528 SearXNG \u81EA\u5B9A\u4E49\u7AEF\u70B9",
|
|
1914
|
+
alias: "\u522B\u540D\uFF1A/se",
|
|
1915
|
+
searxngInfo: "SearXNG \u662F\u4E00\u4E2A\u81EA\u6258\u7BA1\u7684\u5143\u641C\u7D22\u5F15\u64CE\uFF08https://github.com/searxng/searxng\uFF09\u3002",
|
|
1916
|
+
searxngInstall: "\u5B89\u88C5\u547D\u4EE4\uFF1A docker run -d -p 8080:8080 searxng/searxng",
|
|
1917
|
+
switched: '\u5DF2\u5207\u6362\u7F51\u9875\u641C\u7D22\u5F15\u64CE\u4E3A "{engine}"\u3002{note}',
|
|
1918
|
+
switchedSearxngNote: " \u8BF7\u786E\u4FDD SearXNG \u5728 {endpoint} \u8FD0\u884C\u3002",
|
|
1919
|
+
confirmed: '\u2713 \u7F51\u9875\u641C\u7D22\u5F15\u64CE\u5DF2\u8BBE\u4E3A "{engine}"{detail}\u3002\u4E0B\u4E00\u8F6E\u6A21\u578B\u8C03\u7528\u5C06\u751F\u6548\u3002',
|
|
1920
|
+
confirmedDetail: "\uFF08{endpoint}\uFF09"
|
|
1921
|
+
},
|
|
1922
|
+
skill: {
|
|
1923
|
+
listEmpty: "\u672A\u627E\u5230\u6280\u80FD\u3002Reasonix \u4ECE\u4EE5\u4E0B\u4F4D\u7F6E\u8BFB\u53D6\u6280\u80FD\uFF1A",
|
|
1924
|
+
listProjectScope: " \xB7 <project>/.reasonix/skills/<name>/SKILL.md \uFF08\u6216 <name>.md\uFF09 \u2014 \u9879\u76EE\u8303\u56F4",
|
|
1925
|
+
listGlobalScope: " \xB7 ~/.reasonix/skills/<name>/SKILL.md \uFF08\u6216 <name>.md\uFF09 \u2014 \u5168\u5C40\u8303\u56F4",
|
|
1926
|
+
listProjectOnly: " \uFF08\u9879\u76EE\u8303\u56F4\u4EC5\u5728 `reasonix code` \u4E2D\u6D3B\u8DC3\uFF09",
|
|
1927
|
+
listFrontmatter: "\u6BCF\u4E2A\u6587\u4EF6\u7684 frontmatter \u81F3\u5C11\u9700\u8981 `name` \u548C `description`\u3002",
|
|
1928
|
+
listInvoke: "\u4F7F\u7528 `/skill <name> [args]` \u8C03\u7528\u6280\u80FD\uFF0C\u6216\u8BA9\u6A21\u578B\u8C03\u7528 `run_skill`\u3002",
|
|
1929
|
+
listHeader: "\u7528\u6237\u6280\u80FD\uFF08{count}\uFF09\uFF1A",
|
|
1930
|
+
listFooter: "\u67E5\u770B\uFF1A/skill show <name> \u8FD0\u884C\uFF1A/skill <name> [args] \u65B0\u5EFA\uFF1A/skill new <name>",
|
|
1931
|
+
listEmptyNewHint: "\u7528 `/skill new <name>` \u5728\u9879\u76EE\u8303\u56F4\u4E0B\u751F\u6210\u4E00\u4E2A\u7A7A\u767D\u6A21\u677F \u2014 \u6682\u65E0\u5728\u7EBF\u5E02\u573A\uFF0C\u6280\u80FD\u9700\u8981\u81EA\u5DF1\u5199\u3002",
|
|
1932
|
+
showUsage: "\u7528\u6CD5\uFF1A/skill show <name>",
|
|
1933
|
+
showNotFound: "\u672A\u627E\u5230\u6280\u80FD\uFF1A{name}",
|
|
1934
|
+
runNotFound: "\u672A\u627E\u5230\u6280\u80FD\uFF1A{name} \uFF08\u5C1D\u8BD5 /skill list\uFF09",
|
|
1935
|
+
runInfo: "\u25B8 \u6B63\u5728\u8FD0\u884C\u6280\u80FD\uFF1A{name}{args}",
|
|
1936
|
+
newUsage: "\u7528\u6CD5\uFF1A/skill new <name> [--global]",
|
|
1937
|
+
newCreated: "\u25B8 \u5DF2\u521B\u5EFA\u6280\u80FD\uFF1A{name}\n {path}\n \u7F16\u8F91\u540E\u7528 `/skill {name}` \u8C03\u7528",
|
|
1938
|
+
newError: "\u25B2 /skill new \u5931\u8D25\uFF1A{reason}"
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
// src/i18n/index.ts
|
|
1944
|
+
var translations = {
|
|
1945
|
+
EN,
|
|
1946
|
+
"zh-CN": zhCN
|
|
1947
|
+
};
|
|
1948
|
+
function detectSystemLanguage(locale = Intl.DateTimeFormat().resolvedOptions().locale) {
|
|
1949
|
+
if (locale.startsWith("zh")) return "zh-CN";
|
|
1950
|
+
if (locale.startsWith("en")) return "EN";
|
|
1951
|
+
return null;
|
|
1952
|
+
}
|
|
1953
|
+
var currentLang = loadLanguage() ?? detectSystemLanguage() ?? "EN";
|
|
1954
|
+
function t(path2, params) {
|
|
1955
|
+
const parts = path2.split(".");
|
|
1956
|
+
let val = translations[currentLang] || translations.EN;
|
|
1957
|
+
for (const part of parts) {
|
|
1958
|
+
val = val?.[part];
|
|
1959
|
+
if (val === void 0) break;
|
|
1960
|
+
}
|
|
1961
|
+
if (val === void 0 && currentLang !== "EN") {
|
|
1962
|
+
val = translations.EN;
|
|
1963
|
+
for (const part of parts) {
|
|
1964
|
+
val = val?.[part];
|
|
1965
|
+
if (val === void 0) break;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
if (typeof val !== "string") {
|
|
1969
|
+
return path2;
|
|
1970
|
+
}
|
|
1971
|
+
if (params) {
|
|
1972
|
+
let result = val;
|
|
1973
|
+
for (const [k, v] of Object.entries(params)) {
|
|
1974
|
+
result = result.replace(new RegExp(`\\{${k}\\}`, "g"), String(v));
|
|
1975
|
+
}
|
|
1976
|
+
return result;
|
|
1977
|
+
}
|
|
1978
|
+
return val;
|
|
1979
|
+
}
|
|
545
1980
|
|
|
546
1981
|
// src/hooks.ts
|
|
547
|
-
import { spawn } from "child_process";
|
|
548
|
-
import { existsSync, readFileSync } from "fs";
|
|
549
|
-
import { homedir } from "os";
|
|
550
|
-
import { join } from "path";
|
|
551
1982
|
var HOOK_EVENTS = [
|
|
552
1983
|
"PreToolUse",
|
|
553
1984
|
"PostToolUse",
|
|
@@ -564,15 +1995,15 @@ var DEFAULT_TIMEOUTS_MS = {
|
|
|
564
1995
|
var HOOK_SETTINGS_FILENAME = "settings.json";
|
|
565
1996
|
var HOOK_SETTINGS_DIRNAME = ".reasonix";
|
|
566
1997
|
function globalSettingsPath(homeDirOverride) {
|
|
567
|
-
return
|
|
1998
|
+
return join2(homeDirOverride ?? homedir2(), HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME);
|
|
568
1999
|
}
|
|
569
2000
|
function projectSettingsPath(projectRoot) {
|
|
570
|
-
return
|
|
2001
|
+
return join2(projectRoot, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME);
|
|
571
2002
|
}
|
|
572
2003
|
function readSettingsFile(path2) {
|
|
573
2004
|
if (!existsSync(path2)) return null;
|
|
574
2005
|
try {
|
|
575
|
-
const raw =
|
|
2006
|
+
const raw = readFileSync2(path2, "utf8");
|
|
576
2007
|
const parsed = JSON.parse(raw);
|
|
577
2008
|
if (parsed && typeof parsed === "object") return parsed;
|
|
578
2009
|
} catch {
|
|
@@ -691,9 +2122,12 @@ function formatHookOutcomeMessage(outcome) {
|
|
|
691
2122
|
const detail = (outcome.stderr || outcome.stdout || "").trim();
|
|
692
2123
|
const tag = `${outcome.hook.scope}/${outcome.hook.event}`;
|
|
693
2124
|
const cmd = outcome.hook.command.length > 60 ? `${outcome.hook.command.slice(0, 60)}\u2026` : outcome.hook.command;
|
|
694
|
-
const truncTag = outcome.truncated ? "
|
|
695
|
-
const
|
|
696
|
-
return detail ?
|
|
2125
|
+
const truncTag = outcome.truncated ? t("hooks.truncated") : "";
|
|
2126
|
+
const decision = t(`hooks.decision${capitalize(outcome.decision)}`);
|
|
2127
|
+
return detail ? t("hooks.headWithDetail", { tag, cmd, decision, truncTag, detail }) : t("hooks.head", { tag, cmd, decision, truncTag });
|
|
2128
|
+
}
|
|
2129
|
+
function capitalize(s) {
|
|
2130
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
697
2131
|
}
|
|
698
2132
|
function decideOutcome(event, raw) {
|
|
699
2133
|
if (raw.spawnError) return "error";
|
|
@@ -735,9 +2169,9 @@ async function runHooks(opts) {
|
|
|
735
2169
|
}
|
|
736
2170
|
|
|
737
2171
|
// src/tokenizer.ts
|
|
738
|
-
import { existsSync as existsSync2, readFileSync as
|
|
2172
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
739
2173
|
import { createRequire } from "module";
|
|
740
|
-
import { dirname, join as
|
|
2174
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
741
2175
|
import { fileURLToPath } from "url";
|
|
742
2176
|
import { gunzipSync } from "zlib";
|
|
743
2177
|
function buildByteToChar() {
|
|
@@ -765,26 +2199,26 @@ function resolveDataPath() {
|
|
|
765
2199
|
if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
|
|
766
2200
|
const candidates = [];
|
|
767
2201
|
try {
|
|
768
|
-
const here =
|
|
769
|
-
candidates.push(
|
|
770
|
-
candidates.push(
|
|
2202
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
2203
|
+
candidates.push(join3(here, "..", "data", "deepseek-tokenizer.json.gz"));
|
|
2204
|
+
candidates.push(join3(here, "..", "..", "data", "deepseek-tokenizer.json.gz"));
|
|
771
2205
|
} catch {
|
|
772
2206
|
}
|
|
773
2207
|
try {
|
|
774
2208
|
const req = createRequire(import.meta.url);
|
|
775
2209
|
candidates.push(
|
|
776
|
-
|
|
2210
|
+
join3(dirname2(req.resolve("reasonix/package.json")), "data", "deepseek-tokenizer.json.gz")
|
|
777
2211
|
);
|
|
778
2212
|
} catch {
|
|
779
2213
|
}
|
|
780
2214
|
for (const p of candidates) {
|
|
781
2215
|
if (existsSync2(p)) return p;
|
|
782
2216
|
}
|
|
783
|
-
return candidates[0] ??
|
|
2217
|
+
return candidates[0] ?? join3(process.cwd(), "data", "deepseek-tokenizer.json.gz");
|
|
784
2218
|
}
|
|
785
2219
|
function loadTokenizer() {
|
|
786
2220
|
if (cached) return cached;
|
|
787
|
-
const buf =
|
|
2221
|
+
const buf = readFileSync3(resolveDataPath());
|
|
788
2222
|
const json = gunzipSync(buf).toString("utf8");
|
|
789
2223
|
const data = JSON.parse(json);
|
|
790
2224
|
const mergeRank = /* @__PURE__ */ new Map();
|
|
@@ -799,10 +2233,10 @@ function loadTokenizer() {
|
|
|
799
2233
|
}
|
|
800
2234
|
const addedMap = /* @__PURE__ */ new Map();
|
|
801
2235
|
const addedContents = [];
|
|
802
|
-
for (const
|
|
803
|
-
if (!
|
|
804
|
-
addedMap.set(
|
|
805
|
-
addedContents.push(
|
|
2236
|
+
for (const t2 of data.added_tokens) {
|
|
2237
|
+
if (!t2.special) {
|
|
2238
|
+
addedMap.set(t2.content, t2.id);
|
|
2239
|
+
addedContents.push(t2.content);
|
|
806
2240
|
}
|
|
807
2241
|
}
|
|
808
2242
|
addedContents.sort((a, b) => b.length - a.length);
|
|
@@ -869,29 +2303,29 @@ function bpeEncode(piece, mergeRank) {
|
|
|
869
2303
|
}
|
|
870
2304
|
function encode(text) {
|
|
871
2305
|
if (!text) return [];
|
|
872
|
-
const
|
|
2306
|
+
const t2 = loadTokenizer();
|
|
873
2307
|
const ids = [];
|
|
874
2308
|
const process2 = (segment) => {
|
|
875
2309
|
if (!segment) return;
|
|
876
2310
|
let chunks = [segment];
|
|
877
|
-
for (const re of
|
|
2311
|
+
for (const re of t2.splitRegexes) chunks = applySplit(chunks, re);
|
|
878
2312
|
for (const chunk of chunks) {
|
|
879
2313
|
if (!chunk) continue;
|
|
880
|
-
const byteLevel = byteLevelEncode(chunk,
|
|
881
|
-
const pieces = bpeEncode(byteLevel,
|
|
2314
|
+
const byteLevel = byteLevelEncode(chunk, t2.byteToChar);
|
|
2315
|
+
const pieces = bpeEncode(byteLevel, t2.mergeRank);
|
|
882
2316
|
for (const p of pieces) {
|
|
883
|
-
const id =
|
|
2317
|
+
const id = t2.vocab[p];
|
|
884
2318
|
if (id !== void 0) ids.push(id);
|
|
885
2319
|
}
|
|
886
2320
|
}
|
|
887
2321
|
};
|
|
888
|
-
if (
|
|
889
|
-
|
|
2322
|
+
if (t2.addedPattern) {
|
|
2323
|
+
t2.addedPattern.lastIndex = 0;
|
|
890
2324
|
let last = 0;
|
|
891
|
-
for (const m of text.matchAll(
|
|
2325
|
+
for (const m of text.matchAll(t2.addedPattern)) {
|
|
892
2326
|
const idx = m.index ?? 0;
|
|
893
2327
|
if (idx > last) process2(text.slice(last, idx));
|
|
894
|
-
const id =
|
|
2328
|
+
const id = t2.addedMap.get(m[0]);
|
|
895
2329
|
if (id !== void 0) ids.push(id);
|
|
896
2330
|
last = idx + m[0].length;
|
|
897
2331
|
}
|
|
@@ -1051,12 +2485,12 @@ var ToolRegistry = class {
|
|
|
1051
2485
|
return this._tools.get(name)?.parallelSafe === true;
|
|
1052
2486
|
}
|
|
1053
2487
|
specs() {
|
|
1054
|
-
return [...this._tools.values()].map((
|
|
2488
|
+
return [...this._tools.values()].map((t2) => ({
|
|
1055
2489
|
type: "function",
|
|
1056
2490
|
function: {
|
|
1057
|
-
name:
|
|
1058
|
-
description:
|
|
1059
|
-
parameters:
|
|
2491
|
+
name: t2.name,
|
|
2492
|
+
description: t2.description ?? "",
|
|
2493
|
+
parameters: t2.flatSchema ?? t2.parameters ?? { type: "object", properties: {} }
|
|
1060
2494
|
}
|
|
1061
2495
|
}));
|
|
1062
2496
|
}
|
|
@@ -1308,23 +2742,23 @@ function blockToString(block) {
|
|
|
1308
2742
|
import { execFileSync } from "child_process";
|
|
1309
2743
|
import {
|
|
1310
2744
|
appendFileSync,
|
|
1311
|
-
chmodSync,
|
|
2745
|
+
chmodSync as chmodSync2,
|
|
1312
2746
|
existsSync as existsSync3,
|
|
1313
|
-
mkdirSync,
|
|
1314
|
-
readFileSync as
|
|
2747
|
+
mkdirSync as mkdirSync2,
|
|
2748
|
+
readFileSync as readFileSync4,
|
|
1315
2749
|
readdirSync,
|
|
1316
2750
|
renameSync,
|
|
1317
2751
|
statSync,
|
|
1318
2752
|
unlinkSync,
|
|
1319
|
-
writeFileSync
|
|
2753
|
+
writeFileSync as writeFileSync2
|
|
1320
2754
|
} from "fs";
|
|
1321
|
-
import { homedir as
|
|
1322
|
-
import { dirname as
|
|
2755
|
+
import { homedir as homedir3 } from "os";
|
|
2756
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
1323
2757
|
function sessionsDir() {
|
|
1324
|
-
return
|
|
2758
|
+
return join4(homedir3(), ".reasonix", "sessions");
|
|
1325
2759
|
}
|
|
1326
2760
|
function sessionPath(name) {
|
|
1327
|
-
return
|
|
2761
|
+
return join4(sessionsDir(), `${sanitizeName(name)}.jsonl`);
|
|
1328
2762
|
}
|
|
1329
2763
|
function sanitizeName(name) {
|
|
1330
2764
|
const cleaned = name.replace(/[^\w\-\u4e00-\u9fa5]/g, "_").slice(0, 64);
|
|
@@ -1334,7 +2768,7 @@ function loadSessionMessages(name) {
|
|
|
1334
2768
|
const path2 = sessionPath(name);
|
|
1335
2769
|
if (!existsSync3(path2)) return [];
|
|
1336
2770
|
try {
|
|
1337
|
-
const raw =
|
|
2771
|
+
const raw = readFileSync4(path2, "utf8");
|
|
1338
2772
|
const out = [];
|
|
1339
2773
|
for (const line of raw.split(/\r?\n/)) {
|
|
1340
2774
|
const trimmed = line.trim();
|
|
@@ -1352,11 +2786,11 @@ function loadSessionMessages(name) {
|
|
|
1352
2786
|
}
|
|
1353
2787
|
function appendSessionMessage(name, message) {
|
|
1354
2788
|
const path2 = sessionPath(name);
|
|
1355
|
-
|
|
2789
|
+
mkdirSync2(dirname3(path2), { recursive: true });
|
|
1356
2790
|
appendFileSync(path2, `${JSON.stringify(message)}
|
|
1357
2791
|
`, "utf8");
|
|
1358
2792
|
try {
|
|
1359
|
-
|
|
2793
|
+
chmodSync2(path2, 384);
|
|
1360
2794
|
} catch {
|
|
1361
2795
|
}
|
|
1362
2796
|
}
|
|
@@ -1368,7 +2802,7 @@ function listSessions() {
|
|
|
1368
2802
|
(f) => f.endsWith(".jsonl") && !f.endsWith(".events.jsonl")
|
|
1369
2803
|
);
|
|
1370
2804
|
return files.map((file) => {
|
|
1371
|
-
const path2 =
|
|
2805
|
+
const path2 = join4(dir, file);
|
|
1372
2806
|
const stat2 = statSync(path2);
|
|
1373
2807
|
const name = file.replace(/\.jsonl$/, "");
|
|
1374
2808
|
const messageCount = countLines(path2);
|
|
@@ -1386,13 +2820,13 @@ function listSessions() {
|
|
|
1386
2820
|
}
|
|
1387
2821
|
}
|
|
1388
2822
|
function metaPath(name) {
|
|
1389
|
-
return
|
|
2823
|
+
return join4(sessionsDir(), `${sanitizeName(name)}.meta.json`);
|
|
1390
2824
|
}
|
|
1391
2825
|
function loadSessionMeta(name) {
|
|
1392
2826
|
const p = metaPath(name);
|
|
1393
2827
|
if (!existsSync3(p)) return {};
|
|
1394
2828
|
try {
|
|
1395
|
-
const raw = JSON.parse(
|
|
2829
|
+
const raw = JSON.parse(readFileSync4(p, "utf8"));
|
|
1396
2830
|
return raw && typeof raw === "object" ? raw : {};
|
|
1397
2831
|
} catch {
|
|
1398
2832
|
return {};
|
|
@@ -1416,18 +2850,18 @@ function deleteSession(name) {
|
|
|
1416
2850
|
}
|
|
1417
2851
|
function rewriteSession(name, messages) {
|
|
1418
2852
|
const path2 = sessionPath(name);
|
|
1419
|
-
|
|
2853
|
+
mkdirSync2(dirname3(path2), { recursive: true });
|
|
1420
2854
|
const body = messages.map((m) => JSON.stringify(m)).join("\n");
|
|
1421
|
-
|
|
2855
|
+
writeFileSync2(path2, body ? `${body}
|
|
1422
2856
|
` : "", "utf8");
|
|
1423
2857
|
try {
|
|
1424
|
-
|
|
2858
|
+
chmodSync2(path2, 384);
|
|
1425
2859
|
} catch {
|
|
1426
2860
|
}
|
|
1427
2861
|
}
|
|
1428
2862
|
function countLines(path2) {
|
|
1429
2863
|
try {
|
|
1430
|
-
const raw =
|
|
2864
|
+
const raw = readFileSync4(path2, "utf8");
|
|
1431
2865
|
return raw.split(/\r?\n/).filter((l) => l.trim()).length;
|
|
1432
2866
|
} catch {
|
|
1433
2867
|
return 0;
|
|
@@ -1515,27 +2949,27 @@ var SessionStats = class {
|
|
|
1515
2949
|
return stats;
|
|
1516
2950
|
}
|
|
1517
2951
|
get totalCost() {
|
|
1518
|
-
return this._carryoverCost + this.turns.reduce((sum,
|
|
2952
|
+
return this._carryoverCost + this.turns.reduce((sum, t2) => sum + t2.cost, 0);
|
|
1519
2953
|
}
|
|
1520
2954
|
get totalClaudeEquivalent() {
|
|
1521
|
-
return this.turns.reduce((sum,
|
|
2955
|
+
return this.turns.reduce((sum, t2) => sum + claudeEquivalentCost(t2.usage), 0);
|
|
1522
2956
|
}
|
|
1523
2957
|
get savingsVsClaude() {
|
|
1524
2958
|
const c = this.totalClaudeEquivalent;
|
|
1525
2959
|
return c > 0 ? 1 - this.totalCost / c : 0;
|
|
1526
2960
|
}
|
|
1527
2961
|
get totalInputCost() {
|
|
1528
|
-
return this.turns.reduce((sum,
|
|
2962
|
+
return this.turns.reduce((sum, t2) => sum + inputCostUsd(t2.model, t2.usage), 0);
|
|
1529
2963
|
}
|
|
1530
2964
|
get totalOutputCost() {
|
|
1531
|
-
return this.turns.reduce((sum,
|
|
2965
|
+
return this.turns.reduce((sum, t2) => sum + outputCostUsd(t2.model, t2.usage), 0);
|
|
1532
2966
|
}
|
|
1533
2967
|
get aggregateCacheHitRatio() {
|
|
1534
2968
|
let hit = this._carryoverCacheHit;
|
|
1535
2969
|
let miss = this._carryoverCacheMiss;
|
|
1536
|
-
for (const
|
|
1537
|
-
hit +=
|
|
1538
|
-
miss +=
|
|
2970
|
+
for (const t2 of this.turns) {
|
|
2971
|
+
hit += t2.usage.promptCacheHitTokens;
|
|
2972
|
+
miss += t2.usage.promptCacheMissTokens;
|
|
1539
2973
|
}
|
|
1540
2974
|
const denom = hit + miss;
|
|
1541
2975
|
return denom > 0 ? hit / denom : 0;
|
|
@@ -1700,62 +3134,59 @@ var ContextManager = class {
|
|
|
1700
3134
|
}
|
|
1701
3135
|
};
|
|
1702
3136
|
|
|
1703
|
-
// src/loop/branch.ts
|
|
1704
|
-
function summarizeBranch(chosen, samples) {
|
|
1705
|
-
return {
|
|
1706
|
-
budget: samples.length,
|
|
1707
|
-
chosenIndex: chosen.index,
|
|
1708
|
-
uncertainties: samples.map((s) => s.planState.uncertainties.length),
|
|
1709
|
-
temperatures: samples.map((s) => s.temperature)
|
|
1710
|
-
};
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
3137
|
// src/loop/errors.ts
|
|
1714
|
-
function formatLoopError(err) {
|
|
3138
|
+
function formatLoopError(err, probe) {
|
|
1715
3139
|
const msg = err.message ?? "";
|
|
1716
3140
|
if (msg.includes("maximum context length")) {
|
|
1717
3141
|
const reqMatch = msg.match(/requested\s+(\d+)\s+tokens/);
|
|
1718
|
-
const requested = reqMatch ? `${Number(reqMatch[1]).toLocaleString()} tokens` : "
|
|
1719
|
-
return
|
|
3142
|
+
const requested = reqMatch ? `${Number(reqMatch[1]).toLocaleString()} tokens` : t("errors.contextOverflowTooMany");
|
|
3143
|
+
return t("errors.contextOverflow", { requested });
|
|
1720
3144
|
}
|
|
1721
3145
|
const m = /^DeepSeek (\d{3}):\s*([\s\S]*)$/.exec(msg);
|
|
1722
3146
|
if (!m) return msg;
|
|
1723
3147
|
const status = m[1] ?? "";
|
|
1724
3148
|
const body = m[2] ?? "";
|
|
1725
3149
|
const inner = extractDeepSeekErrorMessage(body);
|
|
1726
|
-
if (status === "401") {
|
|
1727
|
-
|
|
1728
|
-
}
|
|
1729
|
-
if (status === "
|
|
1730
|
-
|
|
1731
|
-
}
|
|
1732
|
-
if (status === "422") {
|
|
1733
|
-
return `Invalid parameter (DeepSeek 422): ${inner}`;
|
|
1734
|
-
}
|
|
1735
|
-
if (status === "400") {
|
|
1736
|
-
return `Bad request (DeepSeek 400): ${inner}`;
|
|
1737
|
-
}
|
|
3150
|
+
if (status === "401") return t("errors.auth401", { inner });
|
|
3151
|
+
if (status === "402") return t("errors.balance402", { inner });
|
|
3152
|
+
if (status === "422") return t("errors.badparam422", { inner });
|
|
3153
|
+
if (status === "400") return t("errors.badrequest400", { inner });
|
|
3154
|
+
if (is5xxStatus(status)) return formatDeepSeek5xx(status, probe);
|
|
1738
3155
|
return msg;
|
|
1739
3156
|
}
|
|
3157
|
+
function is5xxError(err) {
|
|
3158
|
+
if (!(err instanceof Error)) return false;
|
|
3159
|
+
const m = /^DeepSeek (5\d{2}):/.exec(err.message ?? "");
|
|
3160
|
+
return m !== null;
|
|
3161
|
+
}
|
|
3162
|
+
async function probeDeepSeekReachable(client, timeoutMs = 1500) {
|
|
3163
|
+
const balance = await client.getBalance({ signal: AbortSignal.timeout(timeoutMs) });
|
|
3164
|
+
return { reachable: balance !== null };
|
|
3165
|
+
}
|
|
3166
|
+
function is5xxStatus(status) {
|
|
3167
|
+
return status === "500" || status === "502" || status === "503" || status === "504";
|
|
3168
|
+
}
|
|
3169
|
+
function formatDeepSeek5xx(status, probe) {
|
|
3170
|
+
const head = t("errors.deepseek5xxHead", { status });
|
|
3171
|
+
const probeNote = probe === void 0 ? "" : probe.reachable ? t("errors.deepseek5xxReachable") : t("errors.deepseek5xxUnreachable");
|
|
3172
|
+
const action = probe?.reachable === false ? t("errors.deepseek5xxActionNetwork") : t("errors.deepseek5xxActionRetry");
|
|
3173
|
+
return `${head}${probeNote}${action}`;
|
|
3174
|
+
}
|
|
1740
3175
|
function reasonPrefixFor(reason, iterCap) {
|
|
1741
|
-
if (reason === "aborted") return "
|
|
1742
|
-
if (reason === "context-guard")
|
|
1743
|
-
|
|
1744
|
-
}
|
|
1745
|
-
if (reason === "stuck") {
|
|
1746
|
-
return "[stuck on a repeated tool call \u2014 explaining what was tried and what's blocking progress]";
|
|
1747
|
-
}
|
|
1748
|
-
return `[tool-call budget (${iterCap}) reached \u2014 forcing summary from what I found]`;
|
|
3176
|
+
if (reason === "aborted") return t("errors.reasonAborted");
|
|
3177
|
+
if (reason === "context-guard") return t("errors.reasonContextGuard");
|
|
3178
|
+
if (reason === "stuck") return t("errors.reasonStuck");
|
|
3179
|
+
return t("errors.reasonBudget", { iterCap });
|
|
1749
3180
|
}
|
|
1750
3181
|
function errorLabelFor(reason, iterCap) {
|
|
1751
|
-
if (reason === "aborted") return "
|
|
1752
|
-
if (reason === "context-guard") return "
|
|
1753
|
-
if (reason === "stuck") return "
|
|
1754
|
-
return
|
|
3182
|
+
if (reason === "aborted") return t("errors.labelAborted");
|
|
3183
|
+
if (reason === "context-guard") return t("errors.labelContextGuard");
|
|
3184
|
+
if (reason === "stuck") return t("errors.labelStuck");
|
|
3185
|
+
return t("errors.labelBudget", { iterCap });
|
|
1755
3186
|
}
|
|
1756
3187
|
function extractDeepSeekErrorMessage(body) {
|
|
1757
3188
|
const trimmed = body.trim();
|
|
1758
|
-
if (!trimmed) return "
|
|
3189
|
+
if (!trimmed) return t("errors.innerNoMessage");
|
|
1759
3190
|
try {
|
|
1760
3191
|
const parsed = JSON.parse(trimmed);
|
|
1761
3192
|
if (parsed && typeof parsed === "object") {
|
|
@@ -1782,13 +3213,13 @@ function isEscalationRequest(content) {
|
|
|
1782
3213
|
return parseEscalationMarker(content).matched;
|
|
1783
3214
|
}
|
|
1784
3215
|
function looksLikePartialEscalationMarker(buf) {
|
|
1785
|
-
const
|
|
1786
|
-
if (
|
|
1787
|
-
if (
|
|
1788
|
-
return NEEDS_PRO_MARKER_PREFIX.startsWith(
|
|
3216
|
+
const t2 = buf.trimStart();
|
|
3217
|
+
if (t2.length === 0) return true;
|
|
3218
|
+
if (t2.length <= NEEDS_PRO_MARKER_PREFIX.length) {
|
|
3219
|
+
return NEEDS_PRO_MARKER_PREFIX.startsWith(t2);
|
|
1789
3220
|
}
|
|
1790
|
-
if (!
|
|
1791
|
-
const rest =
|
|
3221
|
+
if (!t2.startsWith(NEEDS_PRO_MARKER_PREFIX)) return false;
|
|
3222
|
+
const rest = t2.slice(NEEDS_PRO_MARKER_PREFIX.length);
|
|
1792
3223
|
if (rest[0] !== ">" && rest[0] !== ":") return false;
|
|
1793
3224
|
return true;
|
|
1794
3225
|
}
|
|
@@ -1830,7 +3261,7 @@ function buildSyntheticAssistantMessage(content, fallbackModel) {
|
|
|
1830
3261
|
// src/loop/force-summary.ts
|
|
1831
3262
|
async function* forceSummaryAfterIterLimit(ctx, opts = { reason: "budget" }) {
|
|
1832
3263
|
try {
|
|
1833
|
-
yield { turn: ctx.turn, role: "status", content: "
|
|
3264
|
+
yield { turn: ctx.turn, role: "status", content: t("summary.status") };
|
|
1834
3265
|
const messages = ctx.buildMessages();
|
|
1835
3266
|
messages.push({
|
|
1836
3267
|
role: "user",
|
|
@@ -1847,7 +3278,7 @@ async function* forceSummaryAfterIterLimit(ctx, opts = { reason: "budget" }) {
|
|
|
1847
3278
|
});
|
|
1848
3279
|
const rawContent = resp.content?.trim() ?? "";
|
|
1849
3280
|
const cleaned = stripHallucinatedToolMarkup(rawContent);
|
|
1850
|
-
const summary = cleaned || "
|
|
3281
|
+
const summary = cleaned || t("summary.hallucinatedFallback");
|
|
1851
3282
|
const reasonPrefix = reasonPrefixFor(opts.reason, ctx.maxToolIters);
|
|
1852
3283
|
const annotated = `${reasonPrefix}
|
|
1853
3284
|
|
|
@@ -1868,7 +3299,7 @@ ${summary}`;
|
|
|
1868
3299
|
turn: ctx.turn,
|
|
1869
3300
|
role: "error",
|
|
1870
3301
|
content: "",
|
|
1871
|
-
error:
|
|
3302
|
+
error: t("summary.failedAfterReason", { label, message: err.message })
|
|
1872
3303
|
};
|
|
1873
3304
|
yield { turn: ctx.turn, role: "done", content: "" };
|
|
1874
3305
|
}
|
|
@@ -2058,19 +3489,19 @@ var ImmutablePrefix = class {
|
|
|
2058
3489
|
return [{ role: "system", content: this.system }, ...this.fewShots.map((m) => ({ ...m }))];
|
|
2059
3490
|
}
|
|
2060
3491
|
tools() {
|
|
2061
|
-
return this._toolSpecs.map((
|
|
3492
|
+
return this._toolSpecs.map((t2) => structuredClone(t2));
|
|
2062
3493
|
}
|
|
2063
3494
|
addTool(spec) {
|
|
2064
3495
|
const name = spec.function?.name;
|
|
2065
3496
|
if (!name) return false;
|
|
2066
|
-
if (this._toolSpecs.some((
|
|
3497
|
+
if (this._toolSpecs.some((t2) => t2.function?.name === name)) return false;
|
|
2067
3498
|
this._toolSpecs.push(spec);
|
|
2068
3499
|
this._fingerprintCache = null;
|
|
2069
3500
|
return true;
|
|
2070
3501
|
}
|
|
2071
3502
|
/** Mirror of addTool for MCP hot-unbridge. Same cache-miss cost — prefix changes shape. */
|
|
2072
3503
|
removeTool(name) {
|
|
2073
|
-
const idx = this._toolSpecs.findIndex((
|
|
3504
|
+
const idx = this._toolSpecs.findIndex((t2) => t2.function?.name === name);
|
|
2074
3505
|
if (idx < 0) return false;
|
|
2075
3506
|
this._toolSpecs.splice(idx, 1);
|
|
2076
3507
|
this._fingerprintCache = null;
|
|
@@ -2465,13 +3896,9 @@ var CacheFirstLoop = class {
|
|
|
2465
3896
|
stats = new SessionStats();
|
|
2466
3897
|
repair;
|
|
2467
3898
|
// Mutable via configure() — slash commands in the TUI / library callers tweak
|
|
2468
|
-
// these mid-session so users don't have to restart
|
|
3899
|
+
// these mid-session so users don't have to restart.
|
|
2469
3900
|
model;
|
|
2470
3901
|
stream;
|
|
2471
|
-
harvestEnabled;
|
|
2472
|
-
harvestOptions;
|
|
2473
|
-
branchEnabled;
|
|
2474
|
-
branchOptions;
|
|
2475
3902
|
reasoningEffort;
|
|
2476
3903
|
autoEscalate = true;
|
|
2477
3904
|
budgetUsd;
|
|
@@ -2509,19 +3936,8 @@ var CacheFirstLoop = class {
|
|
|
2509
3936
|
this.hooks = opts.hooks ?? [];
|
|
2510
3937
|
this.hookCwd = opts.hookCwd ?? process.cwd();
|
|
2511
3938
|
this.confirmationGate = opts.confirmationGate ?? pauseGate;
|
|
2512
|
-
if (typeof opts.branch === "number") {
|
|
2513
|
-
this.branchOptions = { budget: opts.branch };
|
|
2514
|
-
} else if (opts.branch && typeof opts.branch === "object") {
|
|
2515
|
-
this.branchOptions = opts.branch;
|
|
2516
|
-
} else {
|
|
2517
|
-
this.branchOptions = {};
|
|
2518
|
-
}
|
|
2519
|
-
this.branchEnabled = (this.branchOptions.budget ?? 1) > 1;
|
|
2520
|
-
const harvestForced = this.branchEnabled;
|
|
2521
|
-
this.harvestEnabled = harvestForced || opts.harvest === true || typeof opts.harvest === "object" && opts.harvest !== null;
|
|
2522
|
-
this.harvestOptions = typeof opts.harvest === "object" && opts.harvest !== null ? opts.harvest : this.branchOptions.harvestOptions ?? {};
|
|
2523
3939
|
this._streamPreference = opts.stream ?? true;
|
|
2524
|
-
this.stream = this.
|
|
3940
|
+
this.stream = this._streamPreference;
|
|
2525
3941
|
const allowedNames = /* @__PURE__ */ new Set([...this.prefix.toolSpecs.map((s) => s.function.name)]);
|
|
2526
3942
|
const registry = this.tools;
|
|
2527
3943
|
const isMutating = (call) => {
|
|
@@ -2639,29 +4055,12 @@ var CacheFirstLoop = class {
|
|
|
2639
4055
|
}
|
|
2640
4056
|
configure(opts) {
|
|
2641
4057
|
if (opts.model !== void 0) this.model = opts.model;
|
|
2642
|
-
if (opts.stream !== void 0)
|
|
4058
|
+
if (opts.stream !== void 0) {
|
|
4059
|
+
this._streamPreference = opts.stream;
|
|
4060
|
+
this.stream = opts.stream;
|
|
4061
|
+
}
|
|
2643
4062
|
if (opts.reasoningEffort !== void 0) this.reasoningEffort = opts.reasoningEffort;
|
|
2644
4063
|
if (opts.autoEscalate !== void 0) this.autoEscalate = opts.autoEscalate;
|
|
2645
|
-
if (opts.branch !== void 0) {
|
|
2646
|
-
if (typeof opts.branch === "number") {
|
|
2647
|
-
this.branchOptions = { budget: opts.branch };
|
|
2648
|
-
} else if (opts.branch && typeof opts.branch === "object") {
|
|
2649
|
-
this.branchOptions = opts.branch;
|
|
2650
|
-
} else {
|
|
2651
|
-
this.branchOptions = {};
|
|
2652
|
-
}
|
|
2653
|
-
this.branchEnabled = (this.branchOptions.budget ?? 1) > 1;
|
|
2654
|
-
}
|
|
2655
|
-
if (opts.harvest !== void 0) {
|
|
2656
|
-
const want = opts.harvest === true || typeof opts.harvest === "object" && opts.harvest !== null;
|
|
2657
|
-
this.harvestEnabled = want || this.branchEnabled;
|
|
2658
|
-
if (typeof opts.harvest === "object" && opts.harvest !== null) {
|
|
2659
|
-
this.harvestOptions = opts.harvest;
|
|
2660
|
-
}
|
|
2661
|
-
} else if (this.branchEnabled) {
|
|
2662
|
-
this.harvestEnabled = true;
|
|
2663
|
-
}
|
|
2664
|
-
this.stream = this.branchEnabled ? false : this._streamPreference;
|
|
2665
4064
|
}
|
|
2666
4065
|
/** `null` disables the cap; any change re-arms the 80% warning. */
|
|
2667
4066
|
setBudget(usd) {
|
|
@@ -2684,6 +4083,10 @@ var CacheFirstLoop = class {
|
|
|
2684
4083
|
get escalatedThisTurn() {
|
|
2685
4084
|
return this._escalateThisTurn;
|
|
2686
4085
|
}
|
|
4086
|
+
/** UI surface — model id of the call about to run (or running) right now, including escalation. */
|
|
4087
|
+
get currentCallModel() {
|
|
4088
|
+
return this.modelForCurrentCall();
|
|
4089
|
+
}
|
|
2687
4090
|
modelForCurrentCall() {
|
|
2688
4091
|
return this._escalateThisTurn ? ESCALATION_MODEL : this.model;
|
|
2689
4092
|
}
|
|
@@ -2776,7 +4179,10 @@ ${reason}`
|
|
|
2776
4179
|
turn: this._turn,
|
|
2777
4180
|
role: "error",
|
|
2778
4181
|
content: "",
|
|
2779
|
-
error:
|
|
4182
|
+
error: t("loop.budgetExhausted", {
|
|
4183
|
+
spent: spent.toFixed(4),
|
|
4184
|
+
cap: this.budgetUsd.toFixed(2)
|
|
4185
|
+
})
|
|
2780
4186
|
};
|
|
2781
4187
|
return;
|
|
2782
4188
|
}
|
|
@@ -2785,7 +4191,10 @@ ${reason}`
|
|
|
2785
4191
|
yield {
|
|
2786
4192
|
turn: this._turn,
|
|
2787
4193
|
role: "warning",
|
|
2788
|
-
content:
|
|
4194
|
+
content: t("loop.budget80Pct", {
|
|
4195
|
+
spent: spent.toFixed(4),
|
|
4196
|
+
cap: this.budgetUsd.toFixed(2)
|
|
4197
|
+
})
|
|
2789
4198
|
};
|
|
2790
4199
|
}
|
|
2791
4200
|
}
|
|
@@ -2810,7 +4219,7 @@ ${reason}`
|
|
|
2810
4219
|
yield {
|
|
2811
4220
|
turn: this._turn,
|
|
2812
4221
|
role: "warning",
|
|
2813
|
-
content: "
|
|
4222
|
+
content: t("loop.proArmed")
|
|
2814
4223
|
};
|
|
2815
4224
|
}
|
|
2816
4225
|
let pendingUser = userInput;
|
|
@@ -2822,7 +4231,7 @@ ${reason}`
|
|
|
2822
4231
|
yield {
|
|
2823
4232
|
turn: this._turn,
|
|
2824
4233
|
role: "warning",
|
|
2825
|
-
content:
|
|
4234
|
+
content: t("loop.abortedAtIter", { iter, cap: this.maxToolIters })
|
|
2826
4235
|
};
|
|
2827
4236
|
const stoppedMsg = "[aborted by user (Esc) \u2014 no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]";
|
|
2828
4237
|
this.appendAndPersist(buildSyntheticAssistantMessage(stoppedMsg, this.model));
|
|
@@ -2840,7 +4249,7 @@ ${reason}`
|
|
|
2840
4249
|
yield {
|
|
2841
4250
|
turn: this._turn,
|
|
2842
4251
|
role: "status",
|
|
2843
|
-
content: "
|
|
4252
|
+
content: t("loop.toolUploadStatus")
|
|
2844
4253
|
};
|
|
2845
4254
|
}
|
|
2846
4255
|
if (!warnedForIterBudget && iter >= warnAt) {
|
|
@@ -2848,7 +4257,7 @@ ${reason}`
|
|
|
2848
4257
|
yield {
|
|
2849
4258
|
turn: this._turn,
|
|
2850
4259
|
role: "warning",
|
|
2851
|
-
content:
|
|
4260
|
+
content: t("loop.toolBudgetWarning", { iter, cap: this.maxToolIters })
|
|
2852
4261
|
};
|
|
2853
4262
|
}
|
|
2854
4263
|
let messages = this.buildMessages(pendingUser);
|
|
@@ -2859,25 +4268,32 @@ ${reason}`
|
|
|
2859
4268
|
yield {
|
|
2860
4269
|
turn: this._turn,
|
|
2861
4270
|
role: "status",
|
|
2862
|
-
content: "
|
|
4271
|
+
content: t("loop.preflightFoldStatus")
|
|
2863
4272
|
};
|
|
2864
4273
|
const result = await this.context.fold(this.model);
|
|
2865
4274
|
if (result.folded) {
|
|
2866
4275
|
yield {
|
|
2867
4276
|
turn: this._turn,
|
|
2868
4277
|
role: "warning",
|
|
2869
|
-
content:
|
|
2870
|
-
estimate
|
|
2871
|
-
|
|
4278
|
+
content: t("loop.preflightFolded", {
|
|
4279
|
+
estimate: estimate.toLocaleString(),
|
|
4280
|
+
ctxMax: ctxMax.toLocaleString(),
|
|
4281
|
+
pct: Math.round(estimate / ctxMax * 100),
|
|
4282
|
+
beforeMessages: result.beforeMessages,
|
|
4283
|
+
afterMessages: result.afterMessages,
|
|
4284
|
+
summaryChars: result.summaryChars
|
|
4285
|
+
})
|
|
2872
4286
|
};
|
|
2873
4287
|
messages = this.buildMessages(pendingUser);
|
|
2874
4288
|
} else {
|
|
2875
4289
|
yield {
|
|
2876
4290
|
turn: this._turn,
|
|
2877
4291
|
role: "warning",
|
|
2878
|
-
content:
|
|
2879
|
-
estimate
|
|
2880
|
-
|
|
4292
|
+
content: t("loop.preflightNoFold", {
|
|
4293
|
+
estimate: estimate.toLocaleString(),
|
|
4294
|
+
ctxMax: ctxMax.toLocaleString(),
|
|
4295
|
+
pct: Math.round(estimate / ctxMax * 100)
|
|
4296
|
+
})
|
|
2881
4297
|
};
|
|
2882
4298
|
}
|
|
2883
4299
|
}
|
|
@@ -2886,89 +4302,8 @@ ${reason}`
|
|
|
2886
4302
|
let reasoningContent = "";
|
|
2887
4303
|
let toolCalls = [];
|
|
2888
4304
|
let usage = null;
|
|
2889
|
-
let branchSummary;
|
|
2890
|
-
let preHarvestedPlanState;
|
|
2891
4305
|
try {
|
|
2892
|
-
if (this.
|
|
2893
|
-
const budget = this.branchOptions.budget ?? 1;
|
|
2894
|
-
yield {
|
|
2895
|
-
turn: this._turn,
|
|
2896
|
-
role: "branch_start",
|
|
2897
|
-
content: "",
|
|
2898
|
-
branchProgress: {
|
|
2899
|
-
completed: 0,
|
|
2900
|
-
total: budget,
|
|
2901
|
-
latestIndex: -1,
|
|
2902
|
-
latestTemperature: -1,
|
|
2903
|
-
latestUncertainties: -1
|
|
2904
|
-
}
|
|
2905
|
-
};
|
|
2906
|
-
const queue = [];
|
|
2907
|
-
let waiter = null;
|
|
2908
|
-
const onSampleDone = (sample) => {
|
|
2909
|
-
if (waiter) {
|
|
2910
|
-
const w = waiter;
|
|
2911
|
-
waiter = null;
|
|
2912
|
-
w(sample);
|
|
2913
|
-
} else {
|
|
2914
|
-
queue.push(sample);
|
|
2915
|
-
}
|
|
2916
|
-
};
|
|
2917
|
-
const callModel = this.modelForCurrentCall();
|
|
2918
|
-
const branchPromise = runBranches(
|
|
2919
|
-
this.client,
|
|
2920
|
-
{
|
|
2921
|
-
model: callModel,
|
|
2922
|
-
messages,
|
|
2923
|
-
tools: toolSpecs.length ? toolSpecs : void 0,
|
|
2924
|
-
signal,
|
|
2925
|
-
thinking: thinkingModeForModel(callModel),
|
|
2926
|
-
reasoningEffort: this.reasoningEffort
|
|
2927
|
-
},
|
|
2928
|
-
{
|
|
2929
|
-
...this.branchOptions,
|
|
2930
|
-
harvestOptions: this.harvestOptions,
|
|
2931
|
-
onSampleDone
|
|
2932
|
-
}
|
|
2933
|
-
);
|
|
2934
|
-
for (let k = 0; k < budget; k++) {
|
|
2935
|
-
const sample = queue.shift() ?? await new Promise((resolve10) => {
|
|
2936
|
-
waiter = resolve10;
|
|
2937
|
-
});
|
|
2938
|
-
yield {
|
|
2939
|
-
turn: this._turn,
|
|
2940
|
-
role: "branch_progress",
|
|
2941
|
-
content: "",
|
|
2942
|
-
branchProgress: {
|
|
2943
|
-
completed: k + 1,
|
|
2944
|
-
total: budget,
|
|
2945
|
-
latestIndex: sample.index,
|
|
2946
|
-
latestTemperature: sample.temperature,
|
|
2947
|
-
latestUncertainties: sample.planState.uncertainties.length
|
|
2948
|
-
}
|
|
2949
|
-
};
|
|
2950
|
-
}
|
|
2951
|
-
const result = await branchPromise;
|
|
2952
|
-
assistantContent = result.chosen.response.content;
|
|
2953
|
-
reasoningContent = result.chosen.response.reasoningContent ?? "";
|
|
2954
|
-
toolCalls = result.chosen.response.toolCalls;
|
|
2955
|
-
const agg = aggregateBranchUsage(result.samples);
|
|
2956
|
-
usage = new Usage(
|
|
2957
|
-
agg.promptTokens,
|
|
2958
|
-
agg.completionTokens,
|
|
2959
|
-
agg.totalTokens,
|
|
2960
|
-
agg.promptCacheHitTokens,
|
|
2961
|
-
agg.promptCacheMissTokens
|
|
2962
|
-
);
|
|
2963
|
-
preHarvestedPlanState = result.chosen.planState;
|
|
2964
|
-
branchSummary = summarizeBranch(result.chosen, result.samples);
|
|
2965
|
-
yield {
|
|
2966
|
-
turn: this._turn,
|
|
2967
|
-
role: "branch_done",
|
|
2968
|
-
content: "",
|
|
2969
|
-
branch: branchSummary
|
|
2970
|
-
};
|
|
2971
|
-
} else if (this.stream) {
|
|
4306
|
+
if (this.stream) {
|
|
2972
4307
|
const callBuf = /* @__PURE__ */ new Map();
|
|
2973
4308
|
const readyIndices = /* @__PURE__ */ new Set();
|
|
2974
4309
|
const callModel = this.modelForCurrentCall();
|
|
@@ -3076,11 +4411,12 @@ ${reason}`
|
|
|
3076
4411
|
this._turnAbort = new AbortController();
|
|
3077
4412
|
return;
|
|
3078
4413
|
}
|
|
4414
|
+
const probe = is5xxError(err) ? await probeDeepSeekReachable(this.client) : void 0;
|
|
3079
4415
|
yield {
|
|
3080
4416
|
turn: this._turn,
|
|
3081
4417
|
role: "error",
|
|
3082
4418
|
content: "",
|
|
3083
|
-
error: formatLoopError(err)
|
|
4419
|
+
error: formatLoopError(err, probe)
|
|
3084
4420
|
};
|
|
3085
4421
|
return;
|
|
3086
4422
|
}
|
|
@@ -3091,14 +4427,12 @@ ${reason}`
|
|
|
3091
4427
|
yield {
|
|
3092
4428
|
turn: this._turn,
|
|
3093
4429
|
role: "warning",
|
|
3094
|
-
content:
|
|
4430
|
+
content: t("loop.flashEscalation", { model: ESCALATION_MODEL, reasonSuffix })
|
|
3095
4431
|
};
|
|
3096
4432
|
assistantContent = "";
|
|
3097
4433
|
reasoningContent = "";
|
|
3098
4434
|
toolCalls = [];
|
|
3099
4435
|
usage = null;
|
|
3100
|
-
branchSummary = void 0;
|
|
3101
|
-
preHarvestedPlanState = void 0;
|
|
3102
4436
|
iter--;
|
|
3103
4437
|
continue;
|
|
3104
4438
|
}
|
|
@@ -3112,14 +4446,6 @@ ${reason}`
|
|
|
3112
4446
|
pendingUser = null;
|
|
3113
4447
|
}
|
|
3114
4448
|
this.scratch.reasoning = reasoningContent || null;
|
|
3115
|
-
if (!preHarvestedPlanState && this.harvestEnabled && (reasoningContent?.trim().length ?? 0) >= 40) {
|
|
3116
|
-
yield {
|
|
3117
|
-
turn: this._turn,
|
|
3118
|
-
role: "status",
|
|
3119
|
-
content: "extracting plan state from reasoning\u2026"
|
|
3120
|
-
};
|
|
3121
|
-
}
|
|
3122
|
-
const planState = preHarvestedPlanState ? preHarvestedPlanState : this.harvestEnabled ? await harvest(reasoningContent || null, this.client, this.harvestOptions, signal) : emptyPlanState();
|
|
3123
4449
|
const { calls: repairedCalls, report } = this.repair.process(
|
|
3124
4450
|
toolCalls,
|
|
3125
4451
|
reasoningContent || null,
|
|
@@ -3138,15 +4464,17 @@ ${reason}`
|
|
|
3138
4464
|
role: "assistant_final",
|
|
3139
4465
|
content: assistantContent,
|
|
3140
4466
|
stats: turnStats,
|
|
3141
|
-
|
|
3142
|
-
repair: report,
|
|
3143
|
-
branch: branchSummary
|
|
4467
|
+
repair: report
|
|
3144
4468
|
};
|
|
3145
4469
|
if (this.noteToolFailureSignal("", report)) {
|
|
3146
4470
|
yield {
|
|
3147
4471
|
turn: this._turn,
|
|
3148
4472
|
role: "warning",
|
|
3149
|
-
content:
|
|
4473
|
+
content: t("loop.autoEscalation", {
|
|
4474
|
+
model: ESCALATION_MODEL,
|
|
4475
|
+
breakdown: this._turnFailures.formatBreakdown(),
|
|
4476
|
+
fallback: this.model
|
|
4477
|
+
})
|
|
3150
4478
|
};
|
|
3151
4479
|
}
|
|
3152
4480
|
const allSuppressed = report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;
|
|
@@ -3171,13 +4499,13 @@ ${reason}`
|
|
|
3171
4499
|
yield {
|
|
3172
4500
|
turn: this._turn,
|
|
3173
4501
|
role: "warning",
|
|
3174
|
-
content: "
|
|
4502
|
+
content: t("loop.repeatToolCallWarning")
|
|
3175
4503
|
};
|
|
3176
4504
|
continue;
|
|
3177
4505
|
}
|
|
3178
4506
|
if (report.stormsBroken > 0) {
|
|
3179
4507
|
const noteTail = report.notes.length ? ` \u2014 ${report.notes[report.notes.length - 1]}` : "";
|
|
3180
|
-
const phrase = allSuppressed ? "
|
|
4508
|
+
const phrase = allSuppressed ? t("loop.stormStuck") : t("loop.stormSuppressed", { count: report.stormsBroken });
|
|
3181
4509
|
yield {
|
|
3182
4510
|
turn: this._turn,
|
|
3183
4511
|
role: "warning",
|
|
@@ -3197,20 +4525,28 @@ ${reason}`
|
|
|
3197
4525
|
this._foldedThisTurn = true;
|
|
3198
4526
|
const before = decision.promptTokens;
|
|
3199
4527
|
const ctxMax = decision.ctxMax;
|
|
3200
|
-
const aggressiveTag = decision.aggressive ? "
|
|
4528
|
+
const aggressiveTag = decision.aggressive ? t("loop.aggressiveTag") : "";
|
|
3201
4529
|
yield {
|
|
3202
4530
|
turn: this._turn,
|
|
3203
4531
|
role: "status",
|
|
3204
|
-
content:
|
|
4532
|
+
content: t("loop.compactingHistoryStatus", { aggressiveTag })
|
|
3205
4533
|
};
|
|
3206
4534
|
const result = await this.compactHistory({ keepRecentTokens: decision.tailBudget });
|
|
3207
4535
|
if (result.folded) {
|
|
3208
4536
|
yield {
|
|
3209
4537
|
turn: this._turn,
|
|
3210
4538
|
role: "warning",
|
|
3211
|
-
content:
|
|
3212
|
-
|
|
3213
|
-
|
|
4539
|
+
content: t(
|
|
4540
|
+
decision.aggressive ? "loop.aggressivelyFoldedHistory" : "loop.foldedHistory",
|
|
4541
|
+
{
|
|
4542
|
+
before: before.toLocaleString(),
|
|
4543
|
+
ctxMax: ctxMax.toLocaleString(),
|
|
4544
|
+
pct: Math.round(before / ctxMax * 100),
|
|
4545
|
+
beforeMessages: result.beforeMessages,
|
|
4546
|
+
afterMessages: result.afterMessages,
|
|
4547
|
+
summaryChars: result.summaryChars
|
|
4548
|
+
}
|
|
4549
|
+
)
|
|
3214
4550
|
};
|
|
3215
4551
|
}
|
|
3216
4552
|
} else if (decision.kind === "exit-with-summary") {
|
|
@@ -3219,9 +4555,11 @@ ${reason}`
|
|
|
3219
4555
|
yield {
|
|
3220
4556
|
turn: this._turn,
|
|
3221
4557
|
role: "warning",
|
|
3222
|
-
content:
|
|
3223
|
-
before
|
|
3224
|
-
|
|
4558
|
+
content: t("loop.forcingSummary", {
|
|
4559
|
+
before: before.toLocaleString(),
|
|
4560
|
+
ctxMax: ctxMax.toLocaleString(),
|
|
4561
|
+
pct: Math.round(before / ctxMax * 100)
|
|
4562
|
+
})
|
|
3225
4563
|
};
|
|
3226
4564
|
this.context.trimTrailingToolCalls();
|
|
3227
4565
|
yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: "context-guard" });
|
|
@@ -3279,7 +4617,11 @@ ${reason}`
|
|
|
3279
4617
|
yield {
|
|
3280
4618
|
turn: this._turn,
|
|
3281
4619
|
role: "warning",
|
|
3282
|
-
content:
|
|
4620
|
+
content: t("loop.autoEscalation", {
|
|
4621
|
+
model: ESCALATION_MODEL,
|
|
4622
|
+
breakdown: this._turnFailures.formatBreakdown(),
|
|
4623
|
+
fallback: this.model
|
|
4624
|
+
})
|
|
3283
4625
|
};
|
|
3284
4626
|
}
|
|
3285
4627
|
yield {
|
|
@@ -3322,12 +4664,12 @@ function parsePositiveIntEnv(raw) {
|
|
|
3322
4664
|
}
|
|
3323
4665
|
|
|
3324
4666
|
// src/at-mentions.ts
|
|
3325
|
-
import { existsSync as existsSync4, readFileSync as
|
|
4667
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
3326
4668
|
import { readdir, stat } from "fs/promises";
|
|
3327
|
-
import { isAbsolute, join as
|
|
4669
|
+
import { isAbsolute, join as join5, relative, resolve } from "path";
|
|
3328
4670
|
|
|
3329
4671
|
// src/gitignore.ts
|
|
3330
|
-
import { readFileSync as
|
|
4672
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3331
4673
|
import { readFile } from "fs/promises";
|
|
3332
4674
|
import path from "path";
|
|
3333
4675
|
import ignore from "ignore";
|
|
@@ -3340,7 +4682,7 @@ async function loadGitignoreAt(dirAbs) {
|
|
|
3340
4682
|
}
|
|
3341
4683
|
function loadGitignoreAtSync(dirAbs) {
|
|
3342
4684
|
try {
|
|
3343
|
-
return ignore().add(
|
|
4685
|
+
return ignore().add(readFileSync5(path.join(dirAbs, ".gitignore"), "utf8"));
|
|
3344
4686
|
} catch {
|
|
3345
4687
|
return null;
|
|
3346
4688
|
}
|
|
@@ -3356,6 +4698,7 @@ function ignoredByLayers(layers, abs, isDir) {
|
|
|
3356
4698
|
|
|
3357
4699
|
// src/at-mentions.ts
|
|
3358
4700
|
var DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;
|
|
4701
|
+
var DEFAULT_AT_DIR_MAX_ENTRIES = 200;
|
|
3359
4702
|
var DEFAULT_PICKER_IGNORE_DIRS = [
|
|
3360
4703
|
"node_modules",
|
|
3361
4704
|
".git",
|
|
@@ -3398,7 +4741,7 @@ function listFilesWithStatsSync(root, opts = {}) {
|
|
|
3398
4741
|
for (const ent of entries) {
|
|
3399
4742
|
if (out.length >= maxResults) return;
|
|
3400
4743
|
const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
|
|
3401
|
-
const absPath =
|
|
4744
|
+
const absPath = join5(dirAbs, ent.name);
|
|
3402
4745
|
if (ent.isDirectory()) {
|
|
3403
4746
|
if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
|
|
3404
4747
|
if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
|
|
@@ -3411,6 +4754,16 @@ function listFilesWithStatsSync(root, opts = {}) {
|
|
|
3411
4754
|
} catch {
|
|
3412
4755
|
}
|
|
3413
4756
|
out.push({ path: relPath, mtimeMs });
|
|
4757
|
+
} else if (ent.isSymbolicLink()) {
|
|
4758
|
+
let target = null;
|
|
4759
|
+
try {
|
|
4760
|
+
target = statSync2(absPath);
|
|
4761
|
+
} catch {
|
|
4762
|
+
continue;
|
|
4763
|
+
}
|
|
4764
|
+
if (!target.isFile()) continue;
|
|
4765
|
+
if (ignoredByLayers(effectiveLayers, absPath, false)) continue;
|
|
4766
|
+
out.push({ path: relPath, mtimeMs: target.mtimeMs });
|
|
3414
4767
|
}
|
|
3415
4768
|
}
|
|
3416
4769
|
};
|
|
@@ -3441,7 +4794,7 @@ async function listFilesWithStatsAsync(root, opts = {}) {
|
|
|
3441
4794
|
for (const ent of entries) {
|
|
3442
4795
|
if (out.length >= maxResults) break;
|
|
3443
4796
|
const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
|
|
3444
|
-
const absPath =
|
|
4797
|
+
const absPath = join5(dirAbs, ent.name);
|
|
3445
4798
|
if (ent.isDirectory()) {
|
|
3446
4799
|
if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
|
|
3447
4800
|
if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
|
|
@@ -3451,7 +4804,7 @@ async function listFilesWithStatsAsync(root, opts = {}) {
|
|
|
3451
4804
|
if (out.length >= maxResults) return;
|
|
3452
4805
|
}
|
|
3453
4806
|
await walk2(absPath, relPath, effectiveLayers);
|
|
3454
|
-
} else if (ent.isFile()) {
|
|
4807
|
+
} else if (ent.isFile() || ent.isSymbolicLink()) {
|
|
3455
4808
|
fileEnts.push(ent);
|
|
3456
4809
|
}
|
|
3457
4810
|
}
|
|
@@ -3466,19 +4819,23 @@ async function statBatch(ents, dirAbs, dirRel, out, maxResults, layers) {
|
|
|
3466
4819
|
const accepted = [];
|
|
3467
4820
|
for (const e of ents) {
|
|
3468
4821
|
if (out.length + accepted.length >= maxResults) break;
|
|
3469
|
-
if (ignoredByLayers(layers,
|
|
4822
|
+
if (ignoredByLayers(layers, join5(dirAbs, e.name), false)) continue;
|
|
3470
4823
|
accepted.push(e);
|
|
3471
4824
|
}
|
|
3472
4825
|
const stats = await Promise.all(
|
|
3473
4826
|
accepted.map(
|
|
3474
|
-
(e) => stat(
|
|
4827
|
+
(e) => stat(join5(dirAbs, e.name)).then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() })).catch(() => null)
|
|
3475
4828
|
)
|
|
3476
4829
|
);
|
|
3477
4830
|
for (let i = 0; i < accepted.length; i++) {
|
|
3478
4831
|
const ent = accepted[i];
|
|
4832
|
+
const s = stats[i];
|
|
4833
|
+
if (ent.isSymbolicLink()) {
|
|
4834
|
+
if (!s || !s.isFile) continue;
|
|
4835
|
+
}
|
|
3479
4836
|
out.push({
|
|
3480
4837
|
path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
|
|
3481
|
-
mtimeMs:
|
|
4838
|
+
mtimeMs: s?.mtimeMs ?? 0
|
|
3482
4839
|
});
|
|
3483
4840
|
}
|
|
3484
4841
|
}
|
|
@@ -3539,25 +4896,37 @@ function rankPickerCandidates(files, query, limitOrOpts) {
|
|
|
3539
4896
|
var AT_MENTION_PATTERN = /(?<=^|\s)@([a-zA-Z0-9_./\\-]+)/g;
|
|
3540
4897
|
function expandAtMentions(text, rootDir, opts = {}) {
|
|
3541
4898
|
const maxBytes = opts.maxBytes ?? DEFAULT_AT_MENTION_MAX_BYTES;
|
|
4899
|
+
const maxDirEntries = Math.max(1, opts.maxDirEntries ?? DEFAULT_AT_DIR_MAX_ENTRIES);
|
|
3542
4900
|
const fs4 = opts.fs ?? defaultFs;
|
|
3543
4901
|
const root = resolve(rootDir);
|
|
3544
4902
|
const seen = /* @__PURE__ */ new Map();
|
|
3545
4903
|
const expansions = [];
|
|
4904
|
+
const dirListings = /* @__PURE__ */ new Map();
|
|
3546
4905
|
for (const match of text.matchAll(AT_MENTION_PATTERN)) {
|
|
3547
4906
|
const rawPath = match[1] ?? "";
|
|
3548
4907
|
let cleaned = rawPath;
|
|
3549
4908
|
while (cleaned.endsWith(".")) cleaned = cleaned.slice(0, -1);
|
|
4909
|
+
if (cleaned.endsWith("/") || cleaned.endsWith("\\")) cleaned = cleaned.slice(0, -1);
|
|
3550
4910
|
if (!cleaned) continue;
|
|
3551
4911
|
const token = `@${cleaned}`;
|
|
3552
4912
|
if (seen.has(token)) continue;
|
|
3553
|
-
const expansion = resolveMention(cleaned, root, maxBytes, fs4);
|
|
4913
|
+
const expansion = resolveMention(cleaned, root, maxBytes, maxDirEntries, fs4, dirListings);
|
|
3554
4914
|
seen.set(token, expansion);
|
|
3555
4915
|
expansions.push(expansion);
|
|
3556
4916
|
}
|
|
3557
4917
|
if (expansions.length === 0) return { text, expansions };
|
|
3558
4918
|
const blocks = [];
|
|
3559
4919
|
for (const ex of expansions) {
|
|
3560
|
-
if (ex.ok) {
|
|
4920
|
+
if (ex.ok && ex.isDirectory) {
|
|
4921
|
+
const files = dirListings.get(ex.path) ?? [];
|
|
4922
|
+
const truncAttr = ex.truncated ? ' truncated="true"' : "";
|
|
4923
|
+
const body = files.length > 0 ? `
|
|
4924
|
+
${files.join("\n")}
|
|
4925
|
+
` : "\n";
|
|
4926
|
+
blocks.push(
|
|
4927
|
+
`<directory path="${ex.path}" entries="${ex.entries ?? files.length}"${truncAttr}>${body}</directory>`
|
|
4928
|
+
);
|
|
4929
|
+
} else if (ex.ok) {
|
|
3561
4930
|
const content = readSafe(root, ex.path, fs4);
|
|
3562
4931
|
blocks.push(`<file path="${ex.path}">
|
|
3563
4932
|
${content}
|
|
@@ -3572,7 +4941,7 @@ ${content}
|
|
|
3572
4941
|
${blocks.join("\n\n")}`;
|
|
3573
4942
|
return { text: augmented, expansions };
|
|
3574
4943
|
}
|
|
3575
|
-
function resolveMention(rawPath, root, maxBytes, fs4) {
|
|
4944
|
+
function resolveMention(rawPath, root, maxBytes, maxDirEntries, fs4, dirListings) {
|
|
3576
4945
|
if (isAbsolute(rawPath)) {
|
|
3577
4946
|
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
|
|
3578
4947
|
}
|
|
@@ -3584,14 +4953,26 @@ function resolveMention(rawPath, root, maxBytes, fs4) {
|
|
|
3584
4953
|
if (!fs4.exists(resolved)) {
|
|
3585
4954
|
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "missing" };
|
|
3586
4955
|
}
|
|
3587
|
-
if (
|
|
3588
|
-
|
|
4956
|
+
if (fs4.isFile(resolved)) {
|
|
4957
|
+
const size = fs4.size(resolved);
|
|
4958
|
+
if (size > maxBytes) {
|
|
4959
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "too-large", bytes: size };
|
|
4960
|
+
}
|
|
4961
|
+
return { token: `@${rawPath}`, path: rawPath, ok: true, bytes: size };
|
|
3589
4962
|
}
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
4963
|
+
if (fs4.isDir?.(resolved) && fs4.listDir) {
|
|
4964
|
+
const { files, truncated } = fs4.listDir(resolved, root, maxDirEntries);
|
|
4965
|
+
dirListings.set(rawPath, files);
|
|
4966
|
+
return {
|
|
4967
|
+
token: `@${rawPath}`,
|
|
4968
|
+
path: rawPath,
|
|
4969
|
+
ok: true,
|
|
4970
|
+
isDirectory: true,
|
|
4971
|
+
entries: files.length,
|
|
4972
|
+
truncated
|
|
4973
|
+
};
|
|
3593
4974
|
}
|
|
3594
|
-
return { token: `@${rawPath}`, path: rawPath, ok:
|
|
4975
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "not-file" };
|
|
3595
4976
|
}
|
|
3596
4977
|
function readSafe(root, rawPath, fs4) {
|
|
3597
4978
|
const resolved = resolve(root, rawPath);
|
|
@@ -3610,6 +4991,24 @@ var defaultFs = {
|
|
|
3610
4991
|
return false;
|
|
3611
4992
|
}
|
|
3612
4993
|
},
|
|
4994
|
+
isDir: (p) => {
|
|
4995
|
+
try {
|
|
4996
|
+
return statSync2(p).isDirectory();
|
|
4997
|
+
} catch {
|
|
4998
|
+
return false;
|
|
4999
|
+
}
|
|
5000
|
+
},
|
|
5001
|
+
listDir: (dirAbs, root, max) => {
|
|
5002
|
+
const dirRel = relative(root, dirAbs).split(/[\\/]/).join("/");
|
|
5003
|
+
const walkCap = Math.max(max * 4, 5e3);
|
|
5004
|
+
const all = listFilesSync(root, { maxResults: walkCap });
|
|
5005
|
+
const prefix = dirRel ? `${dirRel}/` : "";
|
|
5006
|
+
const filtered = dirRel ? all.filter((f) => f === dirRel || f.startsWith(prefix)) : all;
|
|
5007
|
+
return {
|
|
5008
|
+
files: filtered.slice(0, max),
|
|
5009
|
+
truncated: filtered.length > max
|
|
5010
|
+
};
|
|
5011
|
+
},
|
|
3613
5012
|
size: (p) => {
|
|
3614
5013
|
try {
|
|
3615
5014
|
return statSync2(p).size;
|
|
@@ -3617,20 +5016,20 @@ var defaultFs = {
|
|
|
3617
5016
|
return 0;
|
|
3618
5017
|
}
|
|
3619
5018
|
},
|
|
3620
|
-
read: (p) =>
|
|
5019
|
+
read: (p) => readFileSync6(p, "utf8")
|
|
3621
5020
|
};
|
|
3622
5021
|
|
|
3623
5022
|
// src/memory/project.ts
|
|
3624
|
-
import { existsSync as existsSync5, readFileSync as
|
|
3625
|
-
import { join as
|
|
5023
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
|
|
5024
|
+
import { join as join6 } from "path";
|
|
3626
5025
|
var PROJECT_MEMORY_FILE = "REASONIX.md";
|
|
3627
5026
|
var PROJECT_MEMORY_MAX_CHARS = 8e3;
|
|
3628
5027
|
function readProjectMemory(rootDir) {
|
|
3629
|
-
const path2 =
|
|
5028
|
+
const path2 = join6(rootDir, PROJECT_MEMORY_FILE);
|
|
3630
5029
|
if (!existsSync5(path2)) return null;
|
|
3631
5030
|
let raw;
|
|
3632
5031
|
try {
|
|
3633
|
-
raw =
|
|
5032
|
+
raw = readFileSync7(path2, "utf8");
|
|
3634
5033
|
} catch {
|
|
3635
5034
|
return null;
|
|
3636
5035
|
}
|
|
@@ -3667,19 +5066,19 @@ ${mem.content}
|
|
|
3667
5066
|
import { createHash as createHash2 } from "crypto";
|
|
3668
5067
|
import {
|
|
3669
5068
|
existsSync as existsSync7,
|
|
3670
|
-
mkdirSync as
|
|
3671
|
-
readFileSync as
|
|
5069
|
+
mkdirSync as mkdirSync4,
|
|
5070
|
+
readFileSync as readFileSync9,
|
|
3672
5071
|
readdirSync as readdirSync4,
|
|
3673
5072
|
unlinkSync as unlinkSync2,
|
|
3674
|
-
writeFileSync as
|
|
5073
|
+
writeFileSync as writeFileSync4
|
|
3675
5074
|
} from "fs";
|
|
3676
|
-
import { homedir as
|
|
3677
|
-
import { join as
|
|
5075
|
+
import { homedir as homedir5 } from "os";
|
|
5076
|
+
import { join as join8, resolve as resolve3 } from "path";
|
|
3678
5077
|
|
|
3679
5078
|
// src/skills.ts
|
|
3680
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
3681
|
-
import { homedir as
|
|
3682
|
-
import { dirname as
|
|
5079
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
5080
|
+
import { homedir as homedir4 } from "os";
|
|
5081
|
+
import { dirname as dirname4, join as join7, resolve as resolve2 } from "path";
|
|
3683
5082
|
|
|
3684
5083
|
// src/prompt-fragments.ts
|
|
3685
5084
|
var TUI_FORMATTING_RULES = `Formatting (rendered in a TUI with a real markdown renderer):
|
|
@@ -3740,7 +5139,7 @@ var SkillStore = class {
|
|
|
3740
5139
|
projectRoot;
|
|
3741
5140
|
disableBuiltins;
|
|
3742
5141
|
constructor(opts = {}) {
|
|
3743
|
-
this.homeDir = opts.homeDir ??
|
|
5142
|
+
this.homeDir = opts.homeDir ?? homedir4();
|
|
3744
5143
|
this.projectRoot = opts.projectRoot ? resolve2(opts.projectRoot) : void 0;
|
|
3745
5144
|
this.disableBuiltins = opts.disableBuiltins === true;
|
|
3746
5145
|
}
|
|
@@ -3753,11 +5152,11 @@ var SkillStore = class {
|
|
|
3753
5152
|
const out = [];
|
|
3754
5153
|
if (this.projectRoot) {
|
|
3755
5154
|
out.push({
|
|
3756
|
-
dir:
|
|
5155
|
+
dir: join7(this.projectRoot, ".reasonix", SKILLS_DIRNAME),
|
|
3757
5156
|
scope: "project"
|
|
3758
5157
|
});
|
|
3759
5158
|
}
|
|
3760
|
-
out.push({ dir:
|
|
5159
|
+
out.push({ dir: join7(this.homeDir, ".reasonix", SKILLS_DIRNAME), scope: "global" });
|
|
3761
5160
|
return out;
|
|
3762
5161
|
}
|
|
3763
5162
|
/** Higher-priority root wins on collision (project > global > builtin); sorted for stable prefix hash. */
|
|
@@ -3792,15 +5191,15 @@ var SkillStore = class {
|
|
|
3792
5191
|
if (scope === "project" && !this.projectRoot) {
|
|
3793
5192
|
return { error: "project scope requires a workspace \u2014 run from `reasonix code`" };
|
|
3794
5193
|
}
|
|
3795
|
-
const root = scope === "project" ?
|
|
3796
|
-
const flat =
|
|
3797
|
-
const folder =
|
|
5194
|
+
const root = scope === "project" ? join7(this.projectRoot ?? "", ".reasonix", SKILLS_DIRNAME) : join7(this.homeDir, ".reasonix", SKILLS_DIRNAME);
|
|
5195
|
+
const flat = join7(root, `${name}.md`);
|
|
5196
|
+
const folder = join7(root, name, SKILL_FILE);
|
|
3798
5197
|
if (existsSync6(folder)) {
|
|
3799
5198
|
return { error: `skill "${name}" already exists at ${folder}` };
|
|
3800
5199
|
}
|
|
3801
|
-
|
|
5200
|
+
mkdirSync3(dirname4(flat), { recursive: true });
|
|
3802
5201
|
try {
|
|
3803
|
-
|
|
5202
|
+
writeFileSync3(flat, skillStubBody(name), { encoding: "utf8", flag: "wx" });
|
|
3804
5203
|
} catch (err) {
|
|
3805
5204
|
if (err.code === "EEXIST") {
|
|
3806
5205
|
return { error: `skill "${name}" already exists at ${flat}` };
|
|
@@ -3814,11 +5213,11 @@ var SkillStore = class {
|
|
|
3814
5213
|
if (!isValidSkillName(name)) return null;
|
|
3815
5214
|
for (const { dir, scope } of this.roots()) {
|
|
3816
5215
|
if (!existsSync6(dir)) continue;
|
|
3817
|
-
const dirCandidate =
|
|
5216
|
+
const dirCandidate = join7(dir, name, SKILL_FILE);
|
|
3818
5217
|
if (existsSync6(dirCandidate) && statSync3(dirCandidate).isFile()) {
|
|
3819
5218
|
return this.parse(dirCandidate, name, scope);
|
|
3820
5219
|
}
|
|
3821
|
-
const flatCandidate =
|
|
5220
|
+
const flatCandidate = join7(dir, `${name}.md`);
|
|
3822
5221
|
if (existsSync6(flatCandidate) && statSync3(flatCandidate).isFile()) {
|
|
3823
5222
|
return this.parse(flatCandidate, name, scope);
|
|
3824
5223
|
}
|
|
@@ -3833,21 +5232,21 @@ var SkillStore = class {
|
|
|
3833
5232
|
readEntry(dir, scope, entry) {
|
|
3834
5233
|
if (entry.isDirectory()) {
|
|
3835
5234
|
if (!isValidSkillName(entry.name)) return null;
|
|
3836
|
-
const file =
|
|
5235
|
+
const file = join7(dir, entry.name, SKILL_FILE);
|
|
3837
5236
|
if (!existsSync6(file)) return null;
|
|
3838
5237
|
return this.parse(file, entry.name, scope);
|
|
3839
5238
|
}
|
|
3840
5239
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
3841
5240
|
const stem = entry.name.slice(0, -3);
|
|
3842
5241
|
if (!isValidSkillName(stem)) return null;
|
|
3843
|
-
return this.parse(
|
|
5242
|
+
return this.parse(join7(dir, entry.name), stem, scope);
|
|
3844
5243
|
}
|
|
3845
5244
|
return null;
|
|
3846
5245
|
}
|
|
3847
5246
|
parse(path2, stem, scope) {
|
|
3848
5247
|
let raw;
|
|
3849
5248
|
try {
|
|
3850
|
-
raw =
|
|
5249
|
+
raw = readFileSync8(path2, "utf8");
|
|
3851
5250
|
} catch {
|
|
3852
5251
|
return null;
|
|
3853
5252
|
}
|
|
@@ -4118,15 +5517,15 @@ function projectHash(rootDir) {
|
|
|
4118
5517
|
}
|
|
4119
5518
|
function scopeDir(opts) {
|
|
4120
5519
|
if (opts.scope === "global") {
|
|
4121
|
-
return
|
|
5520
|
+
return join8(opts.homeDir, USER_MEMORY_DIR, "global");
|
|
4122
5521
|
}
|
|
4123
5522
|
if (!opts.projectRoot) {
|
|
4124
5523
|
throw new Error("scope=project requires a projectRoot on MemoryStore");
|
|
4125
5524
|
}
|
|
4126
|
-
return
|
|
5525
|
+
return join8(opts.homeDir, USER_MEMORY_DIR, projectHash(opts.projectRoot));
|
|
4127
5526
|
}
|
|
4128
5527
|
function ensureDir(p) {
|
|
4129
|
-
if (!existsSync7(p))
|
|
5528
|
+
if (!existsSync7(p)) mkdirSync4(p, { recursive: true });
|
|
4130
5529
|
}
|
|
4131
5530
|
function parseFrontmatter2(raw) {
|
|
4132
5531
|
const lines = raw.split(/\r?\n/);
|
|
@@ -4171,7 +5570,7 @@ var MemoryStore = class {
|
|
|
4171
5570
|
homeDir;
|
|
4172
5571
|
projectRoot;
|
|
4173
5572
|
constructor(opts = {}) {
|
|
4174
|
-
this.homeDir = opts.homeDir ??
|
|
5573
|
+
this.homeDir = opts.homeDir ?? join8(homedir5(), ".reasonix");
|
|
4175
5574
|
this.projectRoot = opts.projectRoot ? resolve3(opts.projectRoot) : void 0;
|
|
4176
5575
|
}
|
|
4177
5576
|
/** Directory this store writes `scope` files into, creating it if needed. */
|
|
@@ -4182,7 +5581,7 @@ var MemoryStore = class {
|
|
|
4182
5581
|
}
|
|
4183
5582
|
/** Absolute path to a memory file (no existence check). */
|
|
4184
5583
|
pathFor(scope, name) {
|
|
4185
|
-
return
|
|
5584
|
+
return join8(this.dir(scope), `${sanitizeMemoryName(name)}.md`);
|
|
4186
5585
|
}
|
|
4187
5586
|
/** True iff this store is configured with a project scope available. */
|
|
4188
5587
|
hasProjectScope() {
|
|
@@ -4190,14 +5589,14 @@ var MemoryStore = class {
|
|
|
4190
5589
|
}
|
|
4191
5590
|
loadIndex(scope) {
|
|
4192
5591
|
if (scope === "project" && !this.projectRoot) return null;
|
|
4193
|
-
const file =
|
|
5592
|
+
const file = join8(
|
|
4194
5593
|
scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot }),
|
|
4195
5594
|
MEMORY_INDEX_FILE
|
|
4196
5595
|
);
|
|
4197
5596
|
if (!existsSync7(file)) return null;
|
|
4198
5597
|
let raw;
|
|
4199
5598
|
try {
|
|
4200
|
-
raw =
|
|
5599
|
+
raw = readFileSync9(file, "utf8");
|
|
4201
5600
|
} catch {
|
|
4202
5601
|
return null;
|
|
4203
5602
|
}
|
|
@@ -4215,7 +5614,7 @@ var MemoryStore = class {
|
|
|
4215
5614
|
if (!existsSync7(file)) {
|
|
4216
5615
|
throw new Error(`memory not found: scope=${scope} name=${name}`);
|
|
4217
5616
|
}
|
|
4218
|
-
const raw =
|
|
5617
|
+
const raw = readFileSync9(file, "utf8");
|
|
4219
5618
|
const { data, body } = parseFrontmatter2(raw);
|
|
4220
5619
|
return {
|
|
4221
5620
|
name: data.name ?? name,
|
|
@@ -4268,10 +5667,10 @@ var MemoryStore = class {
|
|
|
4268
5667
|
createdAt: todayIso()
|
|
4269
5668
|
};
|
|
4270
5669
|
const dir = this.dir(input.scope);
|
|
4271
|
-
const file =
|
|
5670
|
+
const file = join8(dir, `${name}.md`);
|
|
4272
5671
|
const content = `${formatFrontmatter(entry)}${body}
|
|
4273
5672
|
`;
|
|
4274
|
-
|
|
5673
|
+
writeFileSync4(file, content, "utf8");
|
|
4275
5674
|
this.regenerateIndex(input.scope);
|
|
4276
5675
|
return file;
|
|
4277
5676
|
}
|
|
@@ -4297,7 +5696,7 @@ var MemoryStore = class {
|
|
|
4297
5696
|
return;
|
|
4298
5697
|
}
|
|
4299
5698
|
const mdFiles = files.filter((f) => f !== MEMORY_INDEX_FILE && f.endsWith(".md")).sort((a, b) => a.localeCompare(b));
|
|
4300
|
-
const indexPath =
|
|
5699
|
+
const indexPath = join8(dir, MEMORY_INDEX_FILE);
|
|
4301
5700
|
if (mdFiles.length === 0) {
|
|
4302
5701
|
if (existsSync7(indexPath)) unlinkSync2(indexPath);
|
|
4303
5702
|
return;
|
|
@@ -4312,16 +5711,16 @@ var MemoryStore = class {
|
|
|
4312
5711
|
lines.push(`- [${name}](${name}.md) \u2014 (malformed, check frontmatter)`);
|
|
4313
5712
|
}
|
|
4314
5713
|
}
|
|
4315
|
-
|
|
5714
|
+
writeFileSync4(indexPath, `${lines.join("\n")}
|
|
4316
5715
|
`, "utf8");
|
|
4317
5716
|
}
|
|
4318
5717
|
};
|
|
4319
|
-
function readGlobalReasonixMemory(homeDir =
|
|
4320
|
-
const path2 =
|
|
5718
|
+
function readGlobalReasonixMemory(homeDir = join8(homedir5(), ".reasonix")) {
|
|
5719
|
+
const path2 = join8(homeDir, "REASONIX.md");
|
|
4321
5720
|
if (!existsSync7(path2)) return null;
|
|
4322
5721
|
let raw;
|
|
4323
5722
|
try {
|
|
4324
|
-
raw =
|
|
5723
|
+
raw = readFileSync9(path2, "utf8");
|
|
4325
5724
|
} catch {
|
|
4326
5725
|
return null;
|
|
4327
5726
|
}
|
|
@@ -4335,7 +5734,7 @@ function readGlobalReasonixMemory(homeDir = join7(homedir4(), ".reasonix")) {
|
|
|
4335
5734
|
}
|
|
4336
5735
|
function applyGlobalReasonixMemory(basePrompt, homeDir) {
|
|
4337
5736
|
if (!memoryEnabled()) return basePrompt;
|
|
4338
|
-
const dir = homeDir ??
|
|
5737
|
+
const dir = homeDir ?? join8(homedir5(), ".reasonix");
|
|
4339
5738
|
const mem = readGlobalReasonixMemory(dir);
|
|
4340
5739
|
if (!mem) return basePrompt;
|
|
4341
5740
|
return [
|
|
@@ -4395,91 +5794,6 @@ import { promises as fs3 } from "fs";
|
|
|
4395
5794
|
import * as pathMod3 from "path";
|
|
4396
5795
|
import picomatch2 from "picomatch";
|
|
4397
5796
|
|
|
4398
|
-
// src/index/config.ts
|
|
4399
|
-
import picomatch from "picomatch";
|
|
4400
|
-
var DEFAULT_INDEX_EXCLUDES = {
|
|
4401
|
-
dirs: [
|
|
4402
|
-
"node_modules",
|
|
4403
|
-
".git",
|
|
4404
|
-
".hg",
|
|
4405
|
-
".svn",
|
|
4406
|
-
"dist",
|
|
4407
|
-
"build",
|
|
4408
|
-
"out",
|
|
4409
|
-
".next",
|
|
4410
|
-
".nuxt",
|
|
4411
|
-
"target",
|
|
4412
|
-
".venv",
|
|
4413
|
-
"venv",
|
|
4414
|
-
"__pycache__",
|
|
4415
|
-
".pytest_cache",
|
|
4416
|
-
".mypy_cache",
|
|
4417
|
-
".cache",
|
|
4418
|
-
"coverage",
|
|
4419
|
-
".turbo",
|
|
4420
|
-
".vercel",
|
|
4421
|
-
".reasonix"
|
|
4422
|
-
],
|
|
4423
|
-
files: [
|
|
4424
|
-
"package-lock.json",
|
|
4425
|
-
"yarn.lock",
|
|
4426
|
-
"pnpm-lock.yaml",
|
|
4427
|
-
"Cargo.lock",
|
|
4428
|
-
"poetry.lock",
|
|
4429
|
-
"Pipfile.lock",
|
|
4430
|
-
"go.sum",
|
|
4431
|
-
".DS_Store"
|
|
4432
|
-
],
|
|
4433
|
-
exts: [
|
|
4434
|
-
".png",
|
|
4435
|
-
".jpg",
|
|
4436
|
-
".jpeg",
|
|
4437
|
-
".gif",
|
|
4438
|
-
".webp",
|
|
4439
|
-
".bmp",
|
|
4440
|
-
".ico",
|
|
4441
|
-
".tiff",
|
|
4442
|
-
".woff",
|
|
4443
|
-
".woff2",
|
|
4444
|
-
".ttf",
|
|
4445
|
-
".otf",
|
|
4446
|
-
".eot",
|
|
4447
|
-
".zip",
|
|
4448
|
-
".tar",
|
|
4449
|
-
".gz",
|
|
4450
|
-
".bz2",
|
|
4451
|
-
".xz",
|
|
4452
|
-
".rar",
|
|
4453
|
-
".7z",
|
|
4454
|
-
".exe",
|
|
4455
|
-
".dll",
|
|
4456
|
-
".so",
|
|
4457
|
-
".dylib",
|
|
4458
|
-
".bin",
|
|
4459
|
-
".class",
|
|
4460
|
-
".jar",
|
|
4461
|
-
".war",
|
|
4462
|
-
".wasm",
|
|
4463
|
-
".o",
|
|
4464
|
-
".obj",
|
|
4465
|
-
".lib",
|
|
4466
|
-
".a",
|
|
4467
|
-
".pyc",
|
|
4468
|
-
".pyo",
|
|
4469
|
-
".mp3",
|
|
4470
|
-
".mp4",
|
|
4471
|
-
".wav",
|
|
4472
|
-
".ogg",
|
|
4473
|
-
".webm",
|
|
4474
|
-
".mov",
|
|
4475
|
-
".avi",
|
|
4476
|
-
".pdf",
|
|
4477
|
-
".sqlite",
|
|
4478
|
-
".db"
|
|
4479
|
-
]
|
|
4480
|
-
};
|
|
4481
|
-
var DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
4482
|
-
|
|
4483
5797
|
// src/tools/fs/edit.ts
|
|
4484
5798
|
import { promises as fs } from "fs";
|
|
4485
5799
|
import * as pathMod from "path";
|
|
@@ -4562,10 +5876,15 @@ function lineDiff(a, b) {
|
|
|
4562
5876
|
// src/tools/fs/search.ts
|
|
4563
5877
|
import { promises as fs2 } from "fs";
|
|
4564
5878
|
import * as pathMod2 from "path";
|
|
5879
|
+
function throwIfAborted(signal) {
|
|
5880
|
+
if (!signal?.aborted) return;
|
|
5881
|
+
throw new DOMException("search aborted by user", "AbortError");
|
|
5882
|
+
}
|
|
4565
5883
|
function displayRel2(rootDir, full) {
|
|
4566
5884
|
return pathMod2.relative(rootDir, full).replaceAll("\\", "/");
|
|
4567
5885
|
}
|
|
4568
5886
|
async function searchFiles(ctx, startAbs, args) {
|
|
5887
|
+
throwIfAborted(args.signal);
|
|
4569
5888
|
const needle = args.pattern.toLowerCase();
|
|
4570
5889
|
const includeDeps = args.include_deps === true;
|
|
4571
5890
|
let re = null;
|
|
@@ -4577,6 +5896,7 @@ async function searchFiles(ctx, startAbs, args) {
|
|
|
4577
5896
|
const matches = [];
|
|
4578
5897
|
let totalBytes = 0;
|
|
4579
5898
|
const walk2 = async (dir) => {
|
|
5899
|
+
throwIfAborted(args.signal);
|
|
4580
5900
|
let entries;
|
|
4581
5901
|
try {
|
|
4582
5902
|
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
@@ -4584,6 +5904,7 @@ async function searchFiles(ctx, startAbs, args) {
|
|
|
4584
5904
|
return;
|
|
4585
5905
|
}
|
|
4586
5906
|
for (const e of entries) {
|
|
5907
|
+
throwIfAborted(args.signal);
|
|
4587
5908
|
const full = pathMod2.join(dir, e.name);
|
|
4588
5909
|
const lower = e.name.toLowerCase();
|
|
4589
5910
|
const hit = re ? re.test(e.name) : lower.includes(needle);
|
|
@@ -4606,6 +5927,7 @@ async function searchFiles(ctx, startAbs, args) {
|
|
|
4606
5927
|
return matches.length === 0 ? "(no matches)" : matches.join("\n");
|
|
4607
5928
|
}
|
|
4608
5929
|
async function searchContent(ctx, startAbs, args) {
|
|
5930
|
+
throwIfAborted(args.signal);
|
|
4609
5931
|
const caseSensitive = args.case_sensitive === true;
|
|
4610
5932
|
const includeDeps = args.include_deps === true;
|
|
4611
5933
|
let re = null;
|
|
@@ -4621,6 +5943,7 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
4621
5943
|
let truncated = false;
|
|
4622
5944
|
const walk2 = async (dir) => {
|
|
4623
5945
|
if (truncated) return;
|
|
5946
|
+
throwIfAborted(args.signal);
|
|
4624
5947
|
let entries;
|
|
4625
5948
|
try {
|
|
4626
5949
|
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
@@ -4629,6 +5952,7 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
4629
5952
|
}
|
|
4630
5953
|
for (const e of entries) {
|
|
4631
5954
|
if (truncated) return;
|
|
5955
|
+
throwIfAborted(args.signal);
|
|
4632
5956
|
if (e.isDirectory()) {
|
|
4633
5957
|
if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;
|
|
4634
5958
|
await walk2(pathMod2.join(dir, e.name));
|
|
@@ -4646,6 +5970,7 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
4646
5970
|
}
|
|
4647
5971
|
let raw;
|
|
4648
5972
|
try {
|
|
5973
|
+
throwIfAborted(args.signal);
|
|
4649
5974
|
const st = await fh.stat();
|
|
4650
5975
|
if (st.size > 2 * 1024 * 1024) {
|
|
4651
5976
|
await fh.close();
|
|
@@ -4658,12 +5983,14 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
4658
5983
|
continue;
|
|
4659
5984
|
}
|
|
4660
5985
|
await fh.close();
|
|
5986
|
+
throwIfAborted(args.signal);
|
|
4661
5987
|
const firstNul = raw.indexOf(0);
|
|
4662
5988
|
if (firstNul !== -1 && firstNul < 8 * 1024) continue;
|
|
4663
5989
|
const text = raw.toString("utf8");
|
|
4664
5990
|
const rel = displayRel2(ctx.rootDir, full);
|
|
4665
5991
|
const lines = text.split(/\r?\n/);
|
|
4666
5992
|
for (let li = 0; li < lines.length; li++) {
|
|
5993
|
+
throwIfAborted(args.signal);
|
|
4667
5994
|
const line = lines[li];
|
|
4668
5995
|
const lineForCheck = caseSensitive ? line : line.toLowerCase();
|
|
4669
5996
|
const hit = re ? re.test(line) : lineForCheck.includes(needle);
|
|
@@ -4945,10 +6272,10 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
4945
6272
|
},
|
|
4946
6273
|
required: ["pattern"]
|
|
4947
6274
|
},
|
|
4948
|
-
fn: async (args) => searchFiles(
|
|
6275
|
+
fn: async (args, toolCtx) => searchFiles(
|
|
4949
6276
|
{ rootDir, maxListBytes, skipDirNames: SKIP_DIR_NAMES },
|
|
4950
6277
|
safePath(args.path ?? "."),
|
|
4951
|
-
args
|
|
6278
|
+
{ ...args, signal: toolCtx?.signal }
|
|
4952
6279
|
)
|
|
4953
6280
|
});
|
|
4954
6281
|
registry.register({
|
|
@@ -4982,7 +6309,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
4982
6309
|
},
|
|
4983
6310
|
required: ["pattern"]
|
|
4984
6311
|
},
|
|
4985
|
-
fn: async (args) => searchContent(
|
|
6312
|
+
fn: async (args, toolCtx) => searchContent(
|
|
4986
6313
|
{
|
|
4987
6314
|
rootDir,
|
|
4988
6315
|
maxListBytes,
|
|
@@ -4991,7 +6318,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
4991
6318
|
nameMatch: compileNameFilter(typeof args.glob === "string" ? args.glob : null)
|
|
4992
6319
|
},
|
|
4993
6320
|
safePath(args.path ?? "."),
|
|
4994
|
-
args
|
|
6321
|
+
{ ...args, signal: toolCtx?.signal }
|
|
4995
6322
|
)
|
|
4996
6323
|
});
|
|
4997
6324
|
registry.register({
|
|
@@ -5798,12 +7125,12 @@ async function spawnSubagent(opts) {
|
|
|
5798
7125
|
}
|
|
5799
7126
|
function aggregateChildUsage(loop) {
|
|
5800
7127
|
const agg = new Usage();
|
|
5801
|
-
for (const
|
|
5802
|
-
agg.promptTokens +=
|
|
5803
|
-
agg.completionTokens +=
|
|
5804
|
-
agg.totalTokens +=
|
|
5805
|
-
agg.promptCacheHitTokens +=
|
|
5806
|
-
agg.promptCacheMissTokens +=
|
|
7128
|
+
for (const t2 of loop.stats.turns) {
|
|
7129
|
+
agg.promptTokens += t2.usage.promptTokens;
|
|
7130
|
+
agg.completionTokens += t2.usage.completionTokens;
|
|
7131
|
+
agg.totalTokens += t2.usage.totalTokens;
|
|
7132
|
+
agg.promptCacheHitTokens += t2.usage.promptCacheHitTokens;
|
|
7133
|
+
agg.promptCacheMissTokens += t2.usage.promptCacheMissTokens;
|
|
5807
7134
|
}
|
|
5808
7135
|
return agg;
|
|
5809
7136
|
}
|
|
@@ -5930,70 +7257,6 @@ function forkRegistryWithAllowList(parent, allow, alsoExclude) {
|
|
|
5930
7257
|
// src/tools/shell.ts
|
|
5931
7258
|
import * as pathMod7 from "path";
|
|
5932
7259
|
|
|
5933
|
-
// src/config.ts
|
|
5934
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
5935
|
-
import { homedir as homedir5 } from "os";
|
|
5936
|
-
import { dirname as dirname5, join as join10 } from "path";
|
|
5937
|
-
function defaultConfigPath() {
|
|
5938
|
-
return join10(homedir5(), ".reasonix", "config.json");
|
|
5939
|
-
}
|
|
5940
|
-
function readConfig(path2 = defaultConfigPath()) {
|
|
5941
|
-
try {
|
|
5942
|
-
const raw = readFileSync9(path2, "utf8");
|
|
5943
|
-
const parsed = JSON.parse(raw);
|
|
5944
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
5945
|
-
} catch {
|
|
5946
|
-
}
|
|
5947
|
-
return {};
|
|
5948
|
-
}
|
|
5949
|
-
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
5950
|
-
mkdirSync4(dirname5(path2), { recursive: true });
|
|
5951
|
-
writeFileSync4(path2, JSON.stringify(cfg, null, 2), "utf8");
|
|
5952
|
-
try {
|
|
5953
|
-
chmodSync2(path2, 384);
|
|
5954
|
-
} catch {
|
|
5955
|
-
}
|
|
5956
|
-
}
|
|
5957
|
-
function loadApiKey(path2 = defaultConfigPath()) {
|
|
5958
|
-
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
5959
|
-
return readConfig(path2).apiKey;
|
|
5960
|
-
}
|
|
5961
|
-
function webSearchEngine(path2 = defaultConfigPath()) {
|
|
5962
|
-
const cfg = readConfig(path2).webSearchEngine;
|
|
5963
|
-
if (cfg === "searxng") return "searxng";
|
|
5964
|
-
return "mojeek";
|
|
5965
|
-
}
|
|
5966
|
-
function webSearchEndpoint(path2 = defaultConfigPath()) {
|
|
5967
|
-
const cfg = readConfig(path2).webSearchEndpoint;
|
|
5968
|
-
if (cfg && typeof cfg === "string") return cfg;
|
|
5969
|
-
return "http://localhost:8080";
|
|
5970
|
-
}
|
|
5971
|
-
function saveApiKey(key, path2 = defaultConfigPath()) {
|
|
5972
|
-
const cfg = readConfig(path2);
|
|
5973
|
-
cfg.apiKey = key.trim();
|
|
5974
|
-
writeConfig(cfg, path2);
|
|
5975
|
-
}
|
|
5976
|
-
function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
5977
|
-
const trimmed = prefix.trim();
|
|
5978
|
-
if (!trimmed) return;
|
|
5979
|
-
const cfg = readConfig(path2);
|
|
5980
|
-
if (!cfg.projects) cfg.projects = {};
|
|
5981
|
-
if (!cfg.projects[rootDir]) cfg.projects[rootDir] = {};
|
|
5982
|
-
const existing = cfg.projects[rootDir].shellAllowed ?? [];
|
|
5983
|
-
if (existing.includes(trimmed)) return;
|
|
5984
|
-
cfg.projects[rootDir].shellAllowed = [...existing, trimmed];
|
|
5985
|
-
writeConfig(cfg, path2);
|
|
5986
|
-
}
|
|
5987
|
-
function isPlausibleKey(key) {
|
|
5988
|
-
const trimmed = key.trim();
|
|
5989
|
-
return /^sk-[A-Za-z0-9_-]{16,}$/.test(trimmed);
|
|
5990
|
-
}
|
|
5991
|
-
function redactKey(key) {
|
|
5992
|
-
if (!key) return "";
|
|
5993
|
-
if (key.length <= 12) return "****";
|
|
5994
|
-
return `${key.slice(0, 6)}\u2026${key.slice(-4)}`;
|
|
5995
|
-
}
|
|
5996
|
-
|
|
5997
7260
|
// src/tools/jobs.ts
|
|
5998
7261
|
import { spawn as spawn2 } from "child_process";
|
|
5999
7262
|
import * as pathMod4 from "path";
|
|
@@ -7835,14 +9098,6 @@ function recordFromLoopEvent(ev, extra) {
|
|
|
7835
9098
|
if (ev.toolName !== void 0) rec.tool = ev.toolName;
|
|
7836
9099
|
if (ev.toolArgs !== void 0) rec.args = ev.toolArgs;
|
|
7837
9100
|
if (ev.error !== void 0) rec.error = ev.error;
|
|
7838
|
-
if (ev.planState && !isPlanStateEmptyShape(ev.planState)) {
|
|
7839
|
-
rec.planState = {
|
|
7840
|
-
subgoals: [...ev.planState.subgoals],
|
|
7841
|
-
hypotheses: [...ev.planState.hypotheses],
|
|
7842
|
-
uncertainties: [...ev.planState.uncertainties],
|
|
7843
|
-
rejectedPaths: [...ev.planState.rejectedPaths]
|
|
7844
|
-
};
|
|
7845
|
-
}
|
|
7846
9101
|
if (ev.stats) {
|
|
7847
9102
|
rec.usage = {
|
|
7848
9103
|
prompt_tokens: ev.stats.usage.promptTokens,
|
|
@@ -7878,9 +9133,6 @@ function readTranscript(path2) {
|
|
|
7878
9133
|
const raw = readFileSync11(path2, "utf8");
|
|
7879
9134
|
return parseTranscript(raw);
|
|
7880
9135
|
}
|
|
7881
|
-
function isPlanStateEmptyShape(s) {
|
|
7882
|
-
return s.subgoals.length === 0 && s.hypotheses.length === 0 && s.uncertainties.length === 0 && s.rejectedPaths.length === 0;
|
|
7883
|
-
}
|
|
7884
9136
|
function parseTranscript(raw) {
|
|
7885
9137
|
const out = { meta: null, records: [] };
|
|
7886
9138
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -7916,20 +9168,12 @@ function computeReplayStats(records) {
|
|
|
7916
9168
|
const prefixHashes = /* @__PURE__ */ new Set();
|
|
7917
9169
|
let userTurns = 0;
|
|
7918
9170
|
let toolCalls = 0;
|
|
7919
|
-
let harvestedTurns = 0;
|
|
7920
|
-
let totalUncertainties = 0;
|
|
7921
|
-
let totalSubgoals = 0;
|
|
7922
9171
|
for (const rec of records) {
|
|
7923
9172
|
if (rec.role === "user") userTurns++;
|
|
7924
9173
|
else if (rec.role === "tool") toolCalls++;
|
|
7925
9174
|
else if (rec.role === "assistant_final") {
|
|
7926
9175
|
if (rec.model) models.add(rec.model);
|
|
7927
9176
|
if (rec.prefixHash) prefixHashes.add(rec.prefixHash);
|
|
7928
|
-
if (rec.planState) {
|
|
7929
|
-
harvestedTurns++;
|
|
7930
|
-
totalUncertainties += rec.planState.uncertainties.length;
|
|
7931
|
-
totalSubgoals += rec.planState.subgoals.length;
|
|
7932
|
-
}
|
|
7933
9177
|
if (rec.usage && rec.model) {
|
|
7934
9178
|
const u = new Usage(
|
|
7935
9179
|
rec.usage.prompt_tokens ?? 0,
|
|
@@ -7957,22 +9201,19 @@ function computeReplayStats(records) {
|
|
|
7957
9201
|
prefixHashes: [...prefixHashes],
|
|
7958
9202
|
userTurns,
|
|
7959
9203
|
toolCalls,
|
|
7960
|
-
harvestedTurns,
|
|
7961
|
-
totalUncertainties,
|
|
7962
|
-
totalSubgoals,
|
|
7963
9204
|
...summarizeTurns(turns)
|
|
7964
9205
|
};
|
|
7965
9206
|
}
|
|
7966
9207
|
function summarizeTurns(turns) {
|
|
7967
|
-
const totalCost = turns.reduce((s,
|
|
7968
|
-
const totalInput = turns.reduce((s,
|
|
7969
|
-
const totalOutput = turns.reduce((s,
|
|
7970
|
-
const totalClaude = turns.reduce((s,
|
|
9208
|
+
const totalCost = turns.reduce((s, t2) => s + t2.cost, 0);
|
|
9209
|
+
const totalInput = turns.reduce((s, t2) => s + inputCostUsd(t2.model, t2.usage), 0);
|
|
9210
|
+
const totalOutput = turns.reduce((s, t2) => s + outputCostUsd(t2.model, t2.usage), 0);
|
|
9211
|
+
const totalClaude = turns.reduce((s, t2) => s + claudeEquivalentCost(t2.usage), 0);
|
|
7971
9212
|
let hit = 0;
|
|
7972
9213
|
let miss = 0;
|
|
7973
|
-
for (const
|
|
7974
|
-
hit +=
|
|
7975
|
-
miss +=
|
|
9214
|
+
for (const t2 of turns) {
|
|
9215
|
+
hit += t2.usage.promptCacheHitTokens;
|
|
9216
|
+
miss += t2.usage.promptCacheMissTokens;
|
|
7976
9217
|
}
|
|
7977
9218
|
const cacheHitRatio = hit + miss > 0 ? hit / (hit + miss) : 0;
|
|
7978
9219
|
const savingsVsClaude = totalClaude > 0 ? 1 - totalCost / totalClaude : 0;
|
|
@@ -8036,8 +9277,8 @@ function diffTranscripts(a, b) {
|
|
|
8036
9277
|
return { a: aSide, b: bSide, pairs, firstDivergenceTurn };
|
|
8037
9278
|
}
|
|
8038
9279
|
function classifyDivergence(a, b, aTools, bTools) {
|
|
8039
|
-
const aNames = aTools.map((
|
|
8040
|
-
const bNames = bTools.map((
|
|
9280
|
+
const aNames = aTools.map((t2) => t2.tool ?? "").sort();
|
|
9281
|
+
const bNames = bTools.map((t2) => t2.tool ?? "").sort();
|
|
8041
9282
|
if (aNames.join(",") !== bNames.join(",")) {
|
|
8042
9283
|
return `tool calls differ: A=[${aNames.join(",") || "\u2014"}] B=[${bNames.join(",") || "\u2014"}]`;
|
|
8043
9284
|
}
|
|
@@ -8067,7 +9308,7 @@ function tokenOverlap(a, b) {
|
|
|
8067
9308
|
const tb = new Set(b.toLowerCase().split(/\s+/).filter(Boolean));
|
|
8068
9309
|
if (ta.size === 0 && tb.size === 0) return 1;
|
|
8069
9310
|
let shared = 0;
|
|
8070
|
-
for (const
|
|
9311
|
+
for (const t2 of ta) if (tb.has(t2)) shared++;
|
|
8071
9312
|
return 2 * shared / (ta.size + tb.size);
|
|
8072
9313
|
}
|
|
8073
9314
|
function levenshtein(a, b) {
|
|
@@ -8137,41 +9378,6 @@ function renderSummaryTable(report, _opts = {}) {
|
|
|
8137
9378
|
)
|
|
8138
9379
|
);
|
|
8139
9380
|
lines.push(statRow("prefix hashes", a.stats.prefixHashes.length, b.stats.prefixHashes.length));
|
|
8140
|
-
if (a.stats.harvestedTurns > 0 || b.stats.harvestedTurns > 0) {
|
|
8141
|
-
lines.push(
|
|
8142
|
-
row(
|
|
8143
|
-
[
|
|
8144
|
-
"harvest turns",
|
|
8145
|
-
`${a.stats.harvestedTurns}`,
|
|
8146
|
-
`${b.stats.harvestedTurns}`,
|
|
8147
|
-
signed(b.stats.harvestedTurns - a.stats.harvestedTurns)
|
|
8148
|
-
],
|
|
8149
|
-
[20, 14, 14, 14]
|
|
8150
|
-
)
|
|
8151
|
-
);
|
|
8152
|
-
lines.push(
|
|
8153
|
-
row(
|
|
8154
|
-
[
|
|
8155
|
-
" subgoals",
|
|
8156
|
-
`${a.stats.totalSubgoals}`,
|
|
8157
|
-
`${b.stats.totalSubgoals}`,
|
|
8158
|
-
signed(b.stats.totalSubgoals - a.stats.totalSubgoals)
|
|
8159
|
-
],
|
|
8160
|
-
[20, 14, 14, 14]
|
|
8161
|
-
)
|
|
8162
|
-
);
|
|
8163
|
-
lines.push(
|
|
8164
|
-
row(
|
|
8165
|
-
[
|
|
8166
|
-
" uncertainties",
|
|
8167
|
-
`${a.stats.totalUncertainties}`,
|
|
8168
|
-
`${b.stats.totalUncertainties}`,
|
|
8169
|
-
signed(b.stats.totalUncertainties - a.stats.totalUncertainties)
|
|
8170
|
-
],
|
|
8171
|
-
[20, 14, 14, 14]
|
|
8172
|
-
)
|
|
8173
|
-
);
|
|
8174
|
-
}
|
|
8175
9381
|
lines.push("");
|
|
8176
9382
|
const aPrefixStable = a.stats.prefixHashes.length <= 1;
|
|
8177
9383
|
const bPrefixStable = b.stats.prefixHashes.length <= 1;
|
|
@@ -8243,25 +9449,14 @@ function renderMarkdown(report) {
|
|
|
8243
9449
|
out.push(
|
|
8244
9450
|
`| prefix hashes | ${a.stats.prefixHashes.length} | ${b.stats.prefixHashes.length} | \u2014 |`
|
|
8245
9451
|
);
|
|
8246
|
-
if (a.stats.harvestedTurns > 0 || b.stats.harvestedTurns > 0) {
|
|
8247
|
-
out.push(
|
|
8248
|
-
`| harvest turns | ${a.stats.harvestedTurns} | ${b.stats.harvestedTurns} | ${signed(b.stats.harvestedTurns - a.stats.harvestedTurns)} |`
|
|
8249
|
-
);
|
|
8250
|
-
out.push(
|
|
8251
|
-
`| harvest subgoals | ${a.stats.totalSubgoals} | ${b.stats.totalSubgoals} | ${signed(b.stats.totalSubgoals - a.stats.totalSubgoals)} |`
|
|
8252
|
-
);
|
|
8253
|
-
out.push(
|
|
8254
|
-
`| harvest uncertainties | ${a.stats.totalUncertainties} | ${b.stats.totalUncertainties} | ${signed(b.stats.totalUncertainties - a.stats.totalUncertainties)} |`
|
|
8255
|
-
);
|
|
8256
|
-
}
|
|
8257
9452
|
out.push("");
|
|
8258
9453
|
out.push("## Turn-by-turn");
|
|
8259
9454
|
out.push("");
|
|
8260
9455
|
out.push(`| turn | kind | ${a.label} tool calls | ${b.label} tool calls | note |`);
|
|
8261
9456
|
out.push("|---:|:---:|---|---|---|");
|
|
8262
9457
|
for (const p of report.pairs) {
|
|
8263
|
-
const aTools = p.aTools.map((
|
|
8264
|
-
const bTools = p.bTools.map((
|
|
9458
|
+
const aTools = p.aTools.map((t2) => t2.tool).filter(Boolean).join(", ") || "\u2014";
|
|
9459
|
+
const bTools = p.bTools.map((t2) => t2.tool).filter(Boolean).join(", ") || "\u2014";
|
|
8265
9460
|
out.push(`| ${p.turn} | ${p.kind} | ${aTools} | ${bTools} | ${p.divergenceNote ?? ""} |`);
|
|
8266
9461
|
}
|
|
8267
9462
|
out.push("");
|
|
@@ -9802,6 +10997,7 @@ export {
|
|
|
9802
10997
|
CODE_SYSTEM_PROMPT,
|
|
9803
10998
|
CacheFirstLoop,
|
|
9804
10999
|
ChoiceRequestedError,
|
|
11000
|
+
DEFAULT_AT_DIR_MAX_ENTRIES,
|
|
9805
11001
|
DEFAULT_AT_MENTION_MAX_BYTES,
|
|
9806
11002
|
DEFAULT_MAX_RESULT_CHARS,
|
|
9807
11003
|
DEFAULT_MAX_RESULT_TOKENS,
|
|
@@ -9834,7 +11030,6 @@ export {
|
|
|
9834
11030
|
Usage,
|
|
9835
11031
|
VERSION,
|
|
9836
11032
|
VolatileScratch,
|
|
9837
|
-
aggregateBranchUsage,
|
|
9838
11033
|
aggregateUsage,
|
|
9839
11034
|
analyzeSchema,
|
|
9840
11035
|
appendSessionMessage,
|
|
@@ -9854,13 +11049,11 @@ export {
|
|
|
9854
11049
|
costUsd,
|
|
9855
11050
|
decideOutcome,
|
|
9856
11051
|
defaultConfigPath,
|
|
9857
|
-
defaultSelector,
|
|
9858
11052
|
defaultUsageLogPath,
|
|
9859
11053
|
deleteSession,
|
|
9860
11054
|
detectAtPicker,
|
|
9861
11055
|
detectShellOperator,
|
|
9862
11056
|
diffTranscripts,
|
|
9863
|
-
emptyPlanState,
|
|
9864
11057
|
expandAtMentions,
|
|
9865
11058
|
fetchWithRetry,
|
|
9866
11059
|
fixToolCallPairing,
|
|
@@ -9874,7 +11067,6 @@ export {
|
|
|
9874
11067
|
formatSearchResults,
|
|
9875
11068
|
getLatestVersion,
|
|
9876
11069
|
globalSettingsPath,
|
|
9877
|
-
harvest,
|
|
9878
11070
|
healLoadedMessages,
|
|
9879
11071
|
healLoadedMessagesByTokens,
|
|
9880
11072
|
htmlToText,
|
|
@@ -9884,7 +11076,6 @@ export {
|
|
|
9884
11076
|
isAllowed,
|
|
9885
11077
|
isJsonRpcError,
|
|
9886
11078
|
isNpxInstall,
|
|
9887
|
-
isPlanStateEmpty,
|
|
9888
11079
|
isPlausibleKey,
|
|
9889
11080
|
listFilesSync,
|
|
9890
11081
|
listFilesWithStatsAsync,
|
|
@@ -9928,7 +11119,6 @@ export {
|
|
|
9928
11119
|
replayFromFile,
|
|
9929
11120
|
resolveExecutable,
|
|
9930
11121
|
restoreSnapshots,
|
|
9931
|
-
runBranches,
|
|
9932
11122
|
runCommand,
|
|
9933
11123
|
runHooks,
|
|
9934
11124
|
sanitizeMemoryName,
|