@warpgogol/forge 5.2.0 → 5.2.3
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/AGENTS.md +2 -1
- package/dist/os/adr/adr.module.js +1 -1
- package/dist/os/adr/adr.module.js.map +1 -1
- package/dist/os/compass/compass.module.js +2 -2
- package/dist/os/compass/compass.module.js.map +1 -1
- package/dist/os/compass/handlers/compass-audit-handler.d.ts +3 -0
- package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-audit-handler.js +87 -8
- package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -1
- package/dist/os/core/core.module.js +1 -1
- package/dist/os/core/core.module.js.map +1 -1
- package/dist/os/mission/handlers/archive.d.ts.map +1 -1
- package/dist/os/mission/handlers/archive.js +22 -6
- package/dist/os/mission/handlers/archive.js.map +1 -1
- package/dist/os/program/lease.js +1 -1
- package/dist/os/program/lease.js.map +1 -1
- package/dist/os/queue/queue.module.js +1 -1
- package/dist/os/queue/queue.module.js.map +1 -1
- package/dist/os/rfc/handlers/list-create.d.ts.map +1 -1
- package/dist/os/rfc/handlers/list-create.js +79 -32
- package/dist/os/rfc/handlers/list-create.js.map +1 -1
- package/dist/os/rfc/rfc.module.js +1 -1
- package/dist/os/rfc/rfc.module.js.map +1 -1
- package/dist/os/spec/spec.module.d.ts.map +1 -1
- package/dist/os/spec/spec.module.js +2 -0
- package/dist/os/spec/spec.module.js.map +1 -1
- package/dist/src/knowledge/index.d.ts +1 -0
- package/dist/src/knowledge/index.d.ts.map +1 -1
- package/dist/src/knowledge/index.js +1 -0
- package/dist/src/knowledge/index.js.map +1 -1
- package/dist/src/knowledge/sync.d.ts +24 -0
- package/dist/src/knowledge/sync.d.ts.map +1 -0
- package/dist/src/knowledge/sync.js +127 -0
- package/dist/src/knowledge/sync.js.map +1 -0
- package/dist/src/onboarding/doctor.d.ts.map +1 -1
- package/dist/src/onboarding/doctor.js +7 -6
- package/dist/src/onboarding/doctor.js.map +1 -1
- package/dist/src/onboarding/init.d.ts.map +1 -1
- package/dist/src/onboarding/init.js +15 -8
- package/dist/src/onboarding/init.js.map +1 -1
- package/dist/src/onboarding/upgrade.d.ts.map +1 -1
- package/dist/src/onboarding/upgrade.js +42 -13
- package/dist/src/onboarding/upgrade.js.map +1 -1
- package/dist/src/types.d.ts +1 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +1 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/fs-atomic.js +1 -1
- package/dist/src/utils/fs-atomic.js.map +1 -1
- package/os/adr/adr.module.ts +1 -1
- package/os/compass/compass.module.ts +2 -2
- package/os/compass/handlers/compass-audit-handler.ts +116 -8
- package/os/compass/handlers/tests/compass-audit-plan.test.ts +98 -0
- package/os/compass/handlers/tests/compass-audit-record.test.ts +105 -0
- package/os/compass/handlers/tests/compass-audit-validate.test.ts +133 -0
- package/os/compass/handlers/tests/compass-ledger-scope.test.ts +124 -0
- package/os/core/core.module.ts +1 -1
- package/os/mission/handlers/archive.test.ts +49 -0
- package/os/mission/handlers/archive.ts +21 -6
- package/os/program/lease.ts +1 -1
- package/os/queue/queue.module.ts +1 -1
- package/os/rfc/handlers/list-create.ts +83 -35
- package/os/rfc/handlers/validate-rules.test.ts +50 -0
- package/os/rfc/rfc-create-concurrent.test.ts +118 -0
- package/os/rfc/rfc-create-hint.test.ts +90 -0
- package/os/rfc/rfc-read-only-no-side-effects.test.ts +83 -0
- package/os/rfc/rfc.module.ts +1 -1
- package/os/spec/spec.module.ts +2 -0
- package/package.json +1 -1
- package/src/knowledge/__tests__/sync.test.ts +227 -0
- package/src/knowledge/index.ts +7 -0
- package/src/knowledge/sync.ts +161 -0
- package/src/onboarding/doctor.ts +7 -6
- package/src/onboarding/init.ts +15 -8
- package/src/onboarding/upgrade.ts +47 -16
- package/src/tests/upgrade.test.ts +53 -0
- package/src/types.ts +2 -2
- package/src/utils/fs-atomic.ts +1 -1
package/src/onboarding/init.ts
CHANGED
|
@@ -24,6 +24,7 @@ import fs from "node:fs";
|
|
|
24
24
|
import path from "node:path";
|
|
25
25
|
import { stringify as stringifyYaml, parse as parseYaml } from "yaml";
|
|
26
26
|
import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
|
|
27
|
+
import { syncKnowledgeFile } from "../knowledge/index.ts";
|
|
27
28
|
import { defaultForgeConfig, loadForgeConfig, resolveForgeRoot, type ForgeConfig } from "../config/forge-config.ts";
|
|
28
29
|
import { listStackProfiles, detectStack } from "../profiles/stack-profile.ts";
|
|
29
30
|
|
|
@@ -292,9 +293,11 @@ export function runInit(
|
|
|
292
293
|
const kfSrcPath = path.join(skillSrcDir, kf);
|
|
293
294
|
const kfDestPath = path.join(destDir, kf);
|
|
294
295
|
if (fs.existsSync(kfSrcPath)) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
// Append-only sync — never overwrite local knowledge entries
|
|
297
|
+
const syncResult = syncKnowledgeFile(kfSrcPath, kfDestPath);
|
|
298
|
+
if (syncResult.action === "copied" || syncResult.action === "merged") {
|
|
299
|
+
created.push(`${config.paths.skillsDir}/${skillName}/${kf}`);
|
|
300
|
+
}
|
|
298
301
|
} else {
|
|
299
302
|
errors.push(`Knowledge file '${kf}' declared in ${skillName} but not found in source`);
|
|
300
303
|
}
|
|
@@ -362,9 +365,11 @@ export function runInit(
|
|
|
362
365
|
const kfSrcPath = path.join(skillSrcDir, kf);
|
|
363
366
|
const kfDestPath = path.join(destDir, kf);
|
|
364
367
|
if (fs.existsSync(kfSrcPath)) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
+
// Append-only sync — never overwrite local knowledge entries
|
|
369
|
+
const syncResult = syncKnowledgeFile(kfSrcPath, kfDestPath);
|
|
370
|
+
if (syncResult.action === "copied" || syncResult.action === "merged") {
|
|
371
|
+
created.push(`${config.paths.skillsDir}/${skillName}/${kf}`);
|
|
372
|
+
}
|
|
368
373
|
} else {
|
|
369
374
|
errors.push(`Knowledge file '${kf}' declared in ${skillName} but not found in source`);
|
|
370
375
|
}
|
|
@@ -382,8 +387,10 @@ export function runInit(
|
|
|
382
387
|
const sharedKnowledgeDestDir = path.join(agentsSkillsDir, "shared-knowledge");
|
|
383
388
|
fs.mkdirSync(sharedKnowledgeDestDir, { recursive: true });
|
|
384
389
|
const sharedKnowledgeDest = path.join(sharedKnowledgeDestDir, "learned-principles.md");
|
|
385
|
-
|
|
386
|
-
|
|
390
|
+
const syncResult = syncKnowledgeFile(sharedKnowledgeSrc, sharedKnowledgeDest);
|
|
391
|
+
if (syncResult.action === "copied" || syncResult.action === "merged") {
|
|
392
|
+
created.push(`${config.paths.skillsDir}/shared-knowledge/learned-principles.md`);
|
|
393
|
+
}
|
|
387
394
|
}
|
|
388
395
|
|
|
389
396
|
// 4. Create docs directories from config paths
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
type ForgeBindings,
|
|
40
40
|
} from "../config/forge-config.ts";
|
|
41
41
|
import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
|
|
42
|
+
import { syncKnowledgeFile } from "../knowledge/index.ts";
|
|
42
43
|
import { generateNestedAgentsMd } from "./nested-agents-generate.ts";
|
|
43
44
|
import { scaffoldMemoryLayer } from "./memory-scaffold.ts";
|
|
44
45
|
import type { SkippedSkill } from "./init.ts";
|
|
@@ -143,8 +144,9 @@ function syncForgeSkills(
|
|
|
143
144
|
forgeRoot: string,
|
|
144
145
|
skillsDir: string,
|
|
145
146
|
dryRun: boolean,
|
|
146
|
-
): string[] {
|
|
147
|
+
): { updated: string[]; knowledgeConflicts: string[] } {
|
|
147
148
|
const updated: string[] = [];
|
|
149
|
+
const knowledgeConflicts: string[] = [];
|
|
148
150
|
const agentsSkillsDir = path.join(workspaceRoot, skillsDir);
|
|
149
151
|
|
|
150
152
|
for (const skill of FORGE_SKILLS) {
|
|
@@ -160,7 +162,7 @@ function syncForgeSkills(
|
|
|
160
162
|
const content = fs.readFileSync(srcPath, "utf8");
|
|
161
163
|
fs.writeFileSync(destPath, content, "utf8");
|
|
162
164
|
|
|
163
|
-
// Sync knowledge files
|
|
165
|
+
// Sync knowledge files (append-only — never overwrite local entries)
|
|
164
166
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
165
167
|
if (fmMatch) {
|
|
166
168
|
try {
|
|
@@ -173,7 +175,10 @@ function syncForgeSkills(
|
|
|
173
175
|
const kfSrcPath = path.join(skillSrcDir, kf);
|
|
174
176
|
const kfDestPath = path.join(destDir, kf);
|
|
175
177
|
if (fs.existsSync(kfSrcPath)) {
|
|
176
|
-
|
|
178
|
+
const syncResult = syncKnowledgeFile(kfSrcPath, kfDestPath);
|
|
179
|
+
for (const id of syncResult.conflicts) {
|
|
180
|
+
knowledgeConflicts.push(`${skillName}/${kf}#${id}`);
|
|
181
|
+
}
|
|
177
182
|
}
|
|
178
183
|
}
|
|
179
184
|
}
|
|
@@ -185,7 +190,7 @@ function syncForgeSkills(
|
|
|
185
190
|
updated.push(skillName);
|
|
186
191
|
}
|
|
187
192
|
|
|
188
|
-
return updated;
|
|
193
|
+
return { updated, knowledgeConflicts };
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
function syncSharedKnowledge(
|
|
@@ -193,20 +198,24 @@ function syncSharedKnowledge(
|
|
|
193
198
|
forgeRoot: string,
|
|
194
199
|
skillsDir: string,
|
|
195
200
|
dryRun: boolean,
|
|
196
|
-
): string[] {
|
|
201
|
+
): { updated: string[]; knowledgeConflicts: string[] } {
|
|
197
202
|
const updated: string[] = [];
|
|
203
|
+
const knowledgeConflicts: string[] = [];
|
|
198
204
|
const srcPath = path.join(forgeRoot, "skills", "shared", "knowledge", "learned-principles.md");
|
|
199
|
-
if (!fs.existsSync(srcPath)) return updated;
|
|
205
|
+
if (!fs.existsSync(srcPath)) return { updated, knowledgeConflicts };
|
|
200
206
|
|
|
201
207
|
const destDir = path.join(workspaceRoot, skillsDir, "shared-knowledge");
|
|
202
208
|
const destPath = path.join(destDir, "learned-principles.md");
|
|
203
209
|
|
|
204
210
|
if (!dryRun) {
|
|
205
211
|
fs.mkdirSync(destDir, { recursive: true });
|
|
206
|
-
|
|
212
|
+
const syncResult = syncKnowledgeFile(srcPath, destPath);
|
|
213
|
+
for (const id of syncResult.conflicts) {
|
|
214
|
+
knowledgeConflicts.push(`shared-knowledge/learned-principles.md#${id}`);
|
|
215
|
+
}
|
|
207
216
|
}
|
|
208
217
|
updated.push("shared-knowledge");
|
|
209
|
-
return updated;
|
|
218
|
+
return { updated, knowledgeConflicts };
|
|
210
219
|
}
|
|
211
220
|
|
|
212
221
|
function syncPackSkills(
|
|
@@ -214,9 +223,10 @@ function syncPackSkills(
|
|
|
214
223
|
config: ForgeConfig,
|
|
215
224
|
skillsDir: string,
|
|
216
225
|
dryRun: boolean,
|
|
217
|
-
): { updated: string[]; skipped: SkippedSkill[] } {
|
|
226
|
+
): { updated: string[]; skipped: SkippedSkill[]; knowledgeConflicts: string[] } {
|
|
218
227
|
const updated: string[] = [];
|
|
219
228
|
const skipped: SkippedSkill[] = [];
|
|
229
|
+
const knowledgeConflicts: string[] = [];
|
|
220
230
|
const agentsSkillsDir = path.join(workspaceRoot, skillsDir);
|
|
221
231
|
let packSkills: ReturnType<typeof discoverPackSkills> = [];
|
|
222
232
|
try {
|
|
@@ -245,7 +255,7 @@ function syncPackSkills(
|
|
|
245
255
|
const content = fs.readFileSync(srcPath, "utf8");
|
|
246
256
|
fs.writeFileSync(destPath, content, "utf8");
|
|
247
257
|
|
|
248
|
-
// Sync knowledge files
|
|
258
|
+
// Sync knowledge files (append-only — never overwrite local entries)
|
|
249
259
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
250
260
|
if (fmMatch) {
|
|
251
261
|
try {
|
|
@@ -258,7 +268,10 @@ function syncPackSkills(
|
|
|
258
268
|
const kfSrcPath = path.join(skillSrcDir, kf);
|
|
259
269
|
const kfDestPath = path.join(destDir, kf);
|
|
260
270
|
if (fs.existsSync(kfSrcPath)) {
|
|
261
|
-
|
|
271
|
+
const syncResult = syncKnowledgeFile(kfSrcPath, kfDestPath);
|
|
272
|
+
for (const id of syncResult.conflicts) {
|
|
273
|
+
knowledgeConflicts.push(`${skillName}/${kf}#${id}`);
|
|
274
|
+
}
|
|
262
275
|
}
|
|
263
276
|
}
|
|
264
277
|
}
|
|
@@ -270,7 +283,7 @@ function syncPackSkills(
|
|
|
270
283
|
updated.push(skillName);
|
|
271
284
|
}
|
|
272
285
|
|
|
273
|
-
return { updated, skipped };
|
|
286
|
+
return { updated, skipped, knowledgeConflicts };
|
|
274
287
|
}
|
|
275
288
|
|
|
276
289
|
function addMissingBindingDefaults(
|
|
@@ -467,7 +480,7 @@ export async function runUpgrade(
|
|
|
467
480
|
}
|
|
468
481
|
|
|
469
482
|
// Step 3: Sync forge skills
|
|
470
|
-
const
|
|
483
|
+
const forgeSkillsResult = syncForgeSkills(
|
|
471
484
|
workspaceRoot,
|
|
472
485
|
forgeRoot,
|
|
473
486
|
config.paths.skillsDir,
|
|
@@ -483,14 +496,29 @@ export async function runUpgrade(
|
|
|
483
496
|
);
|
|
484
497
|
|
|
485
498
|
// Step 3c: Sync shared knowledge layer (RFC-0663)
|
|
486
|
-
const
|
|
499
|
+
const sharedKnowledgeResult = syncSharedKnowledge(
|
|
487
500
|
workspaceRoot,
|
|
488
501
|
forgeRoot,
|
|
489
502
|
config.paths.skillsDir,
|
|
490
503
|
isDryRun,
|
|
491
504
|
);
|
|
492
505
|
|
|
493
|
-
const skillsUpdated = [
|
|
506
|
+
const skillsUpdated = [
|
|
507
|
+
...forgeSkillsResult.updated,
|
|
508
|
+
...packResult.updated,
|
|
509
|
+
...sharedKnowledgeResult.updated,
|
|
510
|
+
];
|
|
511
|
+
const knowledgeConflicts = [
|
|
512
|
+
...forgeSkillsResult.knowledgeConflicts,
|
|
513
|
+
...packResult.knowledgeConflicts,
|
|
514
|
+
...sharedKnowledgeResult.knowledgeConflicts,
|
|
515
|
+
];
|
|
516
|
+
if (knowledgeConflicts.length > 0) {
|
|
517
|
+
context.logger.warn(
|
|
518
|
+
`forge.upgrade: ${knowledgeConflicts.length} knowledge entr${knowledgeConflicts.length === 1 ? "y" : "ies"} ` +
|
|
519
|
+
`diverge between package and project — local versions kept: ${knowledgeConflicts.join(", ")}`,
|
|
520
|
+
);
|
|
521
|
+
}
|
|
494
522
|
|
|
495
523
|
// Step 3d: Scaffold memory layer (RFC-0664)
|
|
496
524
|
const memoryScaffold = isDryRun ? { created: [], gitignoreUpdated: false, skipped: [] } : scaffoldMemoryLayer(workspaceRoot);
|
|
@@ -565,6 +593,9 @@ export async function runUpgrade(
|
|
|
565
593
|
? `[dry-run] forge.upgrade: would sync ${skillsUpdated.length} skill(s), add ${bindingsAdded.length} binding(s), update syncedVersion to ${toVersion}`
|
|
566
594
|
: npmCheck.warning
|
|
567
595
|
? `[forge.upgrade] OK — ${skillsUpdated.length} skill(s) synced, ${bindingsAdded.length} binding(s) added, syncedVersion → ${toVersion} — ⚠ ${npmCheck.warning}`
|
|
568
|
-
: `[forge.upgrade] OK — ${skillsUpdated.length} skill(s) synced, ${bindingsAdded.length} binding(s) added, syncedVersion → ${toVersion}
|
|
596
|
+
: `[forge.upgrade] OK — ${skillsUpdated.length} skill(s) synced, ${bindingsAdded.length} binding(s) added, syncedVersion → ${toVersion}` +
|
|
597
|
+
(knowledgeConflicts.length > 0
|
|
598
|
+
? ` — ${knowledgeConflicts.length} knowledge conflict(s), local kept`
|
|
599
|
+
: ""),
|
|
569
600
|
};
|
|
570
601
|
}
|
|
@@ -431,3 +431,56 @@ test("forge.upgrade without --update-npm does not attempt npm install", async ()
|
|
|
431
431
|
expect(result.data?.npmUpdated).toBe(false);
|
|
432
432
|
expect(result.data?.npmUpdateSkipped).toBe(null);
|
|
433
433
|
});
|
|
434
|
+
|
|
435
|
+
function qaLog(entries: { id: string; title: string; body: string }[]): string {
|
|
436
|
+
const blocks = entries.map(
|
|
437
|
+
(e) =>
|
|
438
|
+
`### ${e.id}: ${e.title}\n\n\`\`\`knowledge-entry\nid: ${e.id}\nlayer: L0\ncreated: 2026-08-03\nstatus: active\n\`\`\`\n\n${e.body}\n`,
|
|
439
|
+
);
|
|
440
|
+
return `<!-- knowledge-layer: L0 -->\n\n# Q&A Log (L0)\n\n${blocks.join("\n")}`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
test("forge.upgrade preserves project-accumulated knowledge entries (append-only merge)", async () => {
|
|
444
|
+
await setupForgeSource(tempDir, "0.2.0");
|
|
445
|
+
// Declare a knowledge file on the fixture skill
|
|
446
|
+
await writeFile(
|
|
447
|
+
join(tempDir, "packages", "forge", "skills", "fo", "fo-idea", "SKILL.md"),
|
|
448
|
+
"---\nname: fo-idea\nknowledge: [qa-log.md]\n---\n# fo-idea\n",
|
|
449
|
+
"utf8",
|
|
450
|
+
);
|
|
451
|
+
// Package ships K-0001 + new K-0003
|
|
452
|
+
await writeFile(
|
|
453
|
+
join(tempDir, "packages", "forge", "skills", "fo", "fo-idea", "qa-log.md"),
|
|
454
|
+
qaLog([
|
|
455
|
+
{ id: "K-0001", title: "First", body: "Package body." },
|
|
456
|
+
{ id: "K-0003", title: "Third", body: "New package entry." },
|
|
457
|
+
]),
|
|
458
|
+
"utf8",
|
|
459
|
+
);
|
|
460
|
+
await setupConsumerForgeYaml(tempDir, null);
|
|
461
|
+
|
|
462
|
+
// Local copy already accumulated: edited K-0001 + project-only K-0002
|
|
463
|
+
await mkdir(join(tempDir, ".agents", "skills", "fo-idea"), { recursive: true });
|
|
464
|
+
await writeFile(
|
|
465
|
+
join(tempDir, ".agents", "skills", "fo-idea", "qa-log.md"),
|
|
466
|
+
qaLog([
|
|
467
|
+
{ id: "K-0001", title: "First", body: "Locally edited body." },
|
|
468
|
+
{ id: "K-0002", title: "Local record", body: "Project-accumulated entry." },
|
|
469
|
+
]),
|
|
470
|
+
"utf8",
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const result = await runUpgrade({ argv: [], flags: {} }, makeContext(tempDir, false));
|
|
474
|
+
|
|
475
|
+
expect(result.data?.status).toBe("pass");
|
|
476
|
+
const merged = await readFile(join(tempDir, ".agents", "skills", "fo-idea", "qa-log.md"), "utf8");
|
|
477
|
+
// Local entries preserved — the data-loss bug would have wiped K-0002
|
|
478
|
+
expect(merged).toContain("K-0002");
|
|
479
|
+
expect(merged).toContain("Project-accumulated entry.");
|
|
480
|
+
// Local version of the conflicting entry wins
|
|
481
|
+
expect(merged).toContain("Locally edited body.");
|
|
482
|
+
expect(merged).not.toContain("Package body.");
|
|
483
|
+
// New package entry appended
|
|
484
|
+
expect(merged).toContain("K-0003");
|
|
485
|
+
expect(merged).toContain("New package entry.");
|
|
486
|
+
});
|
package/src/types.ts
CHANGED
|
@@ -16,6 +16,7 @@ Mechanical v1 to v2 header migration across the workspace: 942 files rewritten
|
|
|
16
16
|
<item>RFC-1097: sweep — werkstatt-engine clean
|
|
17
17
|
|
|
18
18
|
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.</item>
|
|
19
|
+
<item>RFC-1147: ForgeRegisteredCommandInfo.provider narrowed to "workspace" and siteName field removed — forge never enumerated sites; mirrors the KernelRegisteredCommandInfo cleanup.</item>
|
|
19
20
|
</CHANGE_SUMMARY>
|
|
20
21
|
*/
|
|
21
22
|
|
|
@@ -112,8 +113,7 @@ export interface GateMetadata {
|
|
|
112
113
|
|
|
113
114
|
export interface ForgeRegisteredCommandInfo extends ForgeCommandMetadata {
|
|
114
115
|
name: string;
|
|
115
|
-
provider: "workspace"
|
|
116
|
-
siteName?: string;
|
|
116
|
+
provider: "workspace";
|
|
117
117
|
module?: string;
|
|
118
118
|
flags?: Record<string, ForgeFlagSpec>;
|
|
119
119
|
reads?: string[];
|
package/src/utils/fs-atomic.ts
CHANGED
|
@@ -56,7 +56,7 @@ function sleep(ms: number): Promise<void> {
|
|
|
56
56
|
function makeTempPath(filePath: string): string {
|
|
57
57
|
const dir = dirname(filePath);
|
|
58
58
|
const name = basename(filePath);
|
|
59
|
-
const random = randomBytes(6).toString("
|
|
59
|
+
const random = Array.from(randomBytes(6), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
60
60
|
return join(dir, `${name}.${random}.tmp`);
|
|
61
61
|
}
|
|
62
62
|
|