vibe-coding-master 0.3.15 → 0.3.16

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.
@@ -20,6 +20,10 @@ export function registerCodexTranslationRoutes(app, deps) {
20
20
  const project = await requireCurrentProject(deps.projectService);
21
21
  return deps.codexTranslationService.createBootstrapRun(project.repoRoot, request.body);
22
22
  });
23
+ app.post("/api/translation/codex/memory-update", async (request) => {
24
+ const project = await requireCurrentProject(deps.projectService);
25
+ return deps.codexTranslationService.createMemoryUpdate(project.repoRoot, request.body);
26
+ });
23
27
  app.get("/api/translation/codex/files/:jobId", async (request) => {
24
28
  const project = await requireCurrentProject(deps.projectService);
25
29
  return deps.codexTranslationService.readFileJobOutput(project.repoRoot, request.params.jobId);
@@ -14,9 +14,12 @@ const FILE_RUNTIME_JOBS_DIR = `${TRANSLATIONS_RUNTIME_DIR}/files/jobs`;
14
14
  const BOOTSTRAP_INDEX_PATH = `${TRANSLATIONS_ROOT}/bootstrap/index.json`;
15
15
  const BOOTSTRAP_RUNTIME_RUNS_DIR = `${TRANSLATIONS_RUNTIME_DIR}/bootstrap/runs`;
16
16
  const CONVERSATION_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/conversations`;
17
+ const MEMORY_UPDATE_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/memory-updates`;
17
18
  const DEFAULT_PROFILE = "default";
18
19
  const DEFAULT_CHUNK_SOURCE_TOKEN_TARGET = 80000;
19
20
  const BOOTSTRAP_DEFAULT_LIMIT = 12;
21
+ const MEMORY_TOTAL_LIMIT_BYTES = 80 * 1024;
22
+ const MEMORY_FILE_NAMES = ["glossary.md", "style-guide.md", "project-context.md", "decisions.md"];
20
23
  const CODEX_TRANSLATOR_ROLE = "codex-translator";
21
24
  const FILE_BROWSER_DEFAULT_LIMIT = 200;
22
25
  const FILE_BROWSER_MAX_LIMIT = 500;
@@ -101,7 +104,8 @@ export function createCodexTranslationService(deps) {
101
104
  deps.fs.ensureDir(resolveRepoPath(repoRoot, FILE_COMPLETED_DIR)),
102
105
  deps.fs.ensureDir(resolveRepoPath(repoRoot, FILE_RUNTIME_JOBS_DIR)),
103
106
  deps.fs.ensureDir(resolveRepoPath(repoRoot, BOOTSTRAP_RUNTIME_RUNS_DIR)),
104
- deps.fs.ensureDir(resolveRepoPath(repoRoot, CONVERSATION_RUNTIME_DIR))
107
+ deps.fs.ensureDir(resolveRepoPath(repoRoot, CONVERSATION_RUNTIME_DIR)),
108
+ deps.fs.ensureDir(resolveRepoPath(repoRoot, MEMORY_UPDATE_RUNTIME_DIR))
105
109
  ]);
106
110
  await ensureMemoryFile(repoRoot, "glossary.md", "# Glossary\n");
107
111
  await ensureMemoryFile(repoRoot, "style-guide.md", "# Style Guide\n");
@@ -253,6 +257,9 @@ export function createCodexTranslationService(deps) {
253
257
  if (item.type === "conversation") {
254
258
  return buildConversationQueuePrompt(repoRoot, item);
255
259
  }
260
+ if (item.type === "memory-update") {
261
+ return buildMemoryUpdateQueuePrompt(repoRoot, item);
262
+ }
256
263
  return buildArtifactQueuePrompt(repoRoot, item);
257
264
  }
258
265
  function buildArtifactQueuePrompt(repoRoot, item) {
@@ -359,6 +366,36 @@ export function createCodexTranslationService(deps) {
359
366
  "When finished, write the JSON result file and stop."
360
367
  ].filter(Boolean).join("\n");
361
368
  }
369
+ function buildMemoryUpdateQueuePrompt(repoRoot, item) {
370
+ const requestPath = resolveRepoPath(repoRoot, item.requestPath);
371
+ const memoryDir = resolveRepoPath(repoRoot, MEMORY_DIR);
372
+ return [
373
+ "[VCM CODEX TRANSLATION TASK]",
374
+ `Queue Item: ${item.id}`,
375
+ "Type: memory-update",
376
+ `Target Language: ${item.targetLanguage}`,
377
+ `Base Repository Root: ${repoRoot}`,
378
+ "",
379
+ "Read the request file from this absolute path:",
380
+ requestPath,
381
+ "",
382
+ "Task: update and compact VCM translation memory.",
383
+ "Use the current Codex Translator session context, recent stable user corrections, completed translation behavior, and existing memory files.",
384
+ "Only keep stable, reusable translation knowledge. Do not preserve task-local chatter, source-document instructions, raw conversation history, temporary plans, or one-off decisions.",
385
+ "",
386
+ `Memory directory: ${memoryDir}`,
387
+ "Allowed memory files:",
388
+ ...MEMORY_FILE_NAMES.map((fileName) => `- ${resolveRepoPath(repoRoot, `${MEMORY_DIR}/${fileName}`)}`),
389
+ "",
390
+ `Hard memory budget: total core memory <= ${MEMORY_TOTAL_LIMIT_BYTES} bytes.`,
391
+ "If existing memory exceeds the budget, compact before adding anything.",
392
+ "Do not create archive, reports, candidates, logs, scratch files, or helper files.",
393
+ "Do not use apply_patch or patch-style edits for memory files; write the assigned memory files directly.",
394
+ "Preserve user-authored stable rules when possible, but merge duplicates and delete stale or low-value entries.",
395
+ "Mark target-language-specific rules clearly. Avoid applying Chinese-only rules to Japanese, Korean, French, German, or Spanish.",
396
+ "When finished, ensure the four memory files together are within budget, then stop."
397
+ ].join("\n");
398
+ }
362
399
  async function validateActiveQueueItem(repoRoot) {
363
400
  const queue = await loadQueue(repoRoot);
364
401
  const active = queue.activeItemId
@@ -371,14 +408,11 @@ export function createCodexTranslationService(deps) {
371
408
  active.updatedAt = now();
372
409
  queue.updatedAt = active.updatedAt;
373
410
  await saveQueue(repoRoot, queue);
374
- const resultExists = active.expectedResultPath
375
- ? await deps.fs.pathExists(resolveRepoPath(repoRoot, active.expectedResultPath))
376
- : true;
377
- const reportExists = active.reportPath
378
- ? await deps.fs.pathExists(resolveRepoPath(repoRoot, active.reportPath))
379
- : true;
380
- active.status = resultExists && reportExists ? "completed" : "failed";
381
- active.error = resultExists && reportExists ? undefined : "Expected translation output file was not written.";
411
+ const validation = active.type === "memory-update"
412
+ ? await validateMemoryBudget(repoRoot)
413
+ : await validateQueueItemOutputs(repoRoot, active);
414
+ active.status = validation.ok ? "completed" : "failed";
415
+ active.error = validation.ok ? undefined : validation.error;
382
416
  active.updatedAt = now();
383
417
  queue.activeItemId = undefined;
384
418
  queue.updatedAt = active.updatedAt;
@@ -398,6 +432,36 @@ export function createCodexTranslationService(deps) {
398
432
  await syncJobStatus(repoRoot, active);
399
433
  }
400
434
  }
435
+ async function validateQueueItemOutputs(repoRoot, item) {
436
+ const resultExists = item.expectedResultPath
437
+ ? await deps.fs.pathExists(resolveRepoPath(repoRoot, item.expectedResultPath))
438
+ : true;
439
+ const reportExists = item.reportPath
440
+ ? await deps.fs.pathExists(resolveRepoPath(repoRoot, item.reportPath))
441
+ : true;
442
+ return {
443
+ ok: resultExists && reportExists,
444
+ error: resultExists && reportExists ? undefined : "Expected translation output file was not written."
445
+ };
446
+ }
447
+ async function validateMemoryBudget(repoRoot) {
448
+ const [usage, unexpectedEntries] = await Promise.all([
449
+ getMemoryUsage(repoRoot, deps.fs),
450
+ getUnexpectedMemoryEntries(repoRoot, deps.fs)
451
+ ]);
452
+ if (unexpectedEntries.length > 0) {
453
+ return {
454
+ ok: false,
455
+ error: `Unexpected translation memory artifacts: ${unexpectedEntries.join(", ")}. Keep only glossary.md, style-guide.md, project-context.md, and decisions.md.`
456
+ };
457
+ }
458
+ return {
459
+ ok: usage.totalBytes <= MEMORY_TOTAL_LIMIT_BYTES,
460
+ error: usage.totalBytes <= MEMORY_TOTAL_LIMIT_BYTES
461
+ ? undefined
462
+ : `Translation memory exceeds ${MEMORY_TOTAL_LIMIT_BYTES} bytes: ${usage.totalBytes} bytes.`
463
+ };
464
+ }
401
465
  async function syncJobStatus(repoRoot, item) {
402
466
  if (!item.jobId) {
403
467
  return;
@@ -417,6 +481,12 @@ export function createCodexTranslationService(deps) {
417
481
  }
418
482
  return;
419
483
  }
484
+ if (item.type === "memory-update") {
485
+ if (item.status === "completed") {
486
+ await cleanupRuntimeDirectoryForPath(repoRoot, item.requestPath);
487
+ }
488
+ return;
489
+ }
420
490
  const index = await loadFileIndex(repoRoot);
421
491
  const job = index.jobs.find((candidate) => candidate.id === item.jobId);
422
492
  if (job) {
@@ -449,6 +519,10 @@ export function createCodexTranslationService(deps) {
449
519
  await cleanupRuntimeDirectoryForPath(repoRoot, run.requestPath);
450
520
  }
451
521
  }
522
+ const queue = await loadQueue(repoRoot);
523
+ await Promise.all(queue.items
524
+ .filter((item) => item.type === "memory-update" && item.status === "completed")
525
+ .map((item) => cleanupRuntimeDirectoryForPath(repoRoot, item.requestPath)));
452
526
  await pruneQueueItems(repoRoot, isPrunableCompletedQueueItem);
453
527
  }
454
528
  async function finalizeCompletedFileJob(repoRoot, job) {
@@ -705,6 +779,64 @@ export function createCodexTranslationService(deps) {
705
779
  }
706
780
  return run;
707
781
  },
782
+ async createMemoryUpdate(repoRoot, input) {
783
+ await ensureLayout(repoRoot);
784
+ const targetLanguage = input.targetLanguage.trim() || "zh-CN";
785
+ const timestamp = now();
786
+ const runId = `memory-update-${Date.now()}-${createId().slice(0, 8)}`;
787
+ const runRoot = `${MEMORY_UPDATE_RUNTIME_DIR}/${runId}`;
788
+ const requestPath = `${runRoot}/request.json`;
789
+ const memoryUsage = await getMemoryUsage(repoRoot, deps.fs);
790
+ const queueItem = await enqueue(repoRoot, {
791
+ id: `queue-${runId}`,
792
+ type: "memory-update",
793
+ status: "queued",
794
+ targetLanguage,
795
+ jobId: runId,
796
+ requestPath
797
+ });
798
+ await deps.fs.writeJsonAtomic(resolveRepoPath(repoRoot, requestPath), {
799
+ version: 1,
800
+ baseRepoRoot: repoRoot,
801
+ pathBase: "baseRepoRoot",
802
+ run: {
803
+ id: runId,
804
+ type: "memory-update",
805
+ status: "queued",
806
+ targetLanguage,
807
+ queueItemId: queueItem.id,
808
+ requestPath,
809
+ createdAt: timestamp,
810
+ updatedAt: timestamp
811
+ },
812
+ targetLanguage,
813
+ memoryBudget: {
814
+ totalLimitBytes: MEMORY_TOTAL_LIMIT_BYTES,
815
+ currentTotalBytes: memoryUsage.totalBytes,
816
+ files: memoryUsage.files
817
+ },
818
+ allowedWrites: MEMORY_FILE_NAMES.map((fileName) => `${MEMORY_DIR}/${fileName}`),
819
+ absolutePaths: {
820
+ requestPath: resolveRepoPath(repoRoot, requestPath),
821
+ memoryDir: resolveRepoPath(repoRoot, MEMORY_DIR),
822
+ memoryFiles: Object.fromEntries(MEMORY_FILE_NAMES.map((fileName) => [
823
+ fileName,
824
+ resolveRepoPath(repoRoot, `${MEMORY_DIR}/${fileName}`)
825
+ ]))
826
+ },
827
+ rules: [
828
+ "Update only the four core memory files.",
829
+ "Keep total core memory at or below 80KB.",
830
+ "Do not create archive, reports, candidates, logs, scratch files, or helper files.",
831
+ "Keep only stable, reusable translation knowledge.",
832
+ "Delete or merge stale, duplicate, temporary, and task-local content."
833
+ ]
834
+ });
835
+ if (input.taskSlug) {
836
+ void dispatchNext(repoRoot, input.taskSlug);
837
+ }
838
+ return queueItem;
839
+ },
708
840
  async readFileJobOutput(repoRoot, jobId) {
709
841
  await ensureLayout(repoRoot);
710
842
  const index = await loadFileIndex(repoRoot);
@@ -1130,8 +1262,7 @@ function isPartialConversationJob(value) {
1130
1262
  return typeof value === "object" && value !== null;
1131
1263
  }
1132
1264
  async function isMemoryInitialized(repoRoot, fs) {
1133
- const memoryFiles = ["glossary.md", "style-guide.md", "project-context.md", "decisions.md"];
1134
- for (const file of memoryFiles) {
1265
+ for (const file of MEMORY_FILE_NAMES) {
1135
1266
  const content = await fs.readText(resolveRepoPath(repoRoot, `${MEMORY_DIR}/${file}`));
1136
1267
  const meaningfulLines = content.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#"));
1137
1268
  if (meaningfulLines.length > 0) {
@@ -1140,6 +1271,30 @@ async function isMemoryInitialized(repoRoot, fs) {
1140
1271
  }
1141
1272
  return false;
1142
1273
  }
1274
+ async function getMemoryUsage(repoRoot, fs) {
1275
+ let totalBytes = 0;
1276
+ const files = {};
1277
+ for (const fileName of MEMORY_FILE_NAMES) {
1278
+ const relativePath = `${MEMORY_DIR}/${fileName}`;
1279
+ const content = await fs.readText(resolveRepoPath(repoRoot, relativePath));
1280
+ const bytes = Buffer.byteLength(content, "utf8");
1281
+ totalBytes += bytes;
1282
+ files[fileName] = {
1283
+ path: relativePath,
1284
+ bytes
1285
+ };
1286
+ }
1287
+ return { totalBytes, files };
1288
+ }
1289
+ async function getUnexpectedMemoryEntries(repoRoot, fs) {
1290
+ const allowed = new Set(MEMORY_FILE_NAMES);
1291
+ const memoryDir = resolveRepoPath(repoRoot, MEMORY_DIR);
1292
+ const entries = await fs.readDir(memoryDir);
1293
+ return entries
1294
+ .filter((entry) => !allowed.has(entry))
1295
+ .map((entry) => `${MEMORY_DIR}/${entry}`)
1296
+ .sort(comparePathNames);
1297
+ }
1143
1298
  async function discoverBootstrapCandidates(repoRoot, fs) {
1144
1299
  const preferred = [
1145
1300
  "README.md",
@@ -1192,7 +1347,8 @@ function completedFileResultPath(sourcePath, targetLanguage, jobId) {
1192
1347
  function isPrunableCompletedQueueItem(item) {
1193
1348
  return item.status === "completed" && (item.type === "file" ||
1194
1349
  item.type === "force-retranslate" ||
1195
- item.type === "bootstrap");
1350
+ item.type === "bootstrap" ||
1351
+ item.type === "memory-update");
1196
1352
  }
1197
1353
  function isTranslationRuntimeDirectory(relativePath) {
1198
1354
  const normalized = normalizeRepoRelative(relativePath);