@phren/cli 0.0.9 → 0.0.11
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 +2 -8
- package/mcp/dist/cli-actions.js +5 -5
- package/mcp/dist/cli-config.js +334 -127
- package/mcp/dist/cli-govern.js +140 -3
- package/mcp/dist/cli-graph.js +3 -2
- package/mcp/dist/cli-hooks-globs.js +2 -1
- package/mcp/dist/cli-hooks-output.js +3 -3
- package/mcp/dist/cli-hooks.js +41 -34
- package/mcp/dist/cli-namespaces.js +15 -5
- package/mcp/dist/cli-search.js +2 -2
- package/mcp/dist/content-archive.js +2 -2
- package/mcp/dist/content-citation.js +12 -22
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/data-access.js +1 -1
- package/mcp/dist/data-tasks.js +23 -0
- package/mcp/dist/embedding.js +7 -7
- package/mcp/dist/entrypoint.js +129 -102
- package/mcp/dist/governance-locks.js +6 -5
- package/mcp/dist/governance-policy.js +155 -2
- package/mcp/dist/governance-scores.js +3 -3
- package/mcp/dist/hooks.js +39 -18
- package/mcp/dist/index.js +4 -4
- package/mcp/dist/init-config.js +3 -24
- package/mcp/dist/init-setup.js +5 -5
- package/mcp/dist/init.js +170 -23
- package/mcp/dist/link-checksums.js +3 -2
- package/mcp/dist/link-context.js +1 -1
- package/mcp/dist/link-doctor.js +3 -3
- package/mcp/dist/link-skills.js +98 -12
- package/mcp/dist/link.js +17 -27
- package/mcp/dist/machine-identity.js +1 -9
- package/mcp/dist/mcp-config.js +247 -42
- package/mcp/dist/mcp-data.js +9 -9
- package/mcp/dist/mcp-extract-facts.js +1 -1
- package/mcp/dist/mcp-extract.js +2 -2
- package/mcp/dist/mcp-finding.js +6 -6
- package/mcp/dist/mcp-graph.js +11 -11
- package/mcp/dist/mcp-ops.js +18 -18
- package/mcp/dist/mcp-search.js +8 -8
- package/mcp/dist/mcp-tasks.js +21 -1
- package/mcp/dist/memory-ui-page.js +23 -0
- package/mcp/dist/memory-ui-scripts.js +210 -27
- package/mcp/dist/memory-ui-server.js +115 -3
- package/mcp/dist/phren-paths.js +7 -7
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/project-config.js +63 -16
- package/mcp/dist/session-utils.js +3 -2
- package/mcp/dist/shared-fragment-graph.js +22 -21
- package/mcp/dist/shared-index.js +144 -105
- package/mcp/dist/shared-retrieval.js +22 -56
- package/mcp/dist/shared-search-fallback.js +13 -13
- package/mcp/dist/shared-sqljs.js +3 -2
- package/mcp/dist/shared.js +3 -3
- package/mcp/dist/shell-input.js +1 -1
- package/mcp/dist/shell-state-store.js +1 -1
- package/mcp/dist/shell-view.js +3 -2
- package/mcp/dist/shell.js +1 -1
- package/mcp/dist/skill-files.js +4 -10
- package/mcp/dist/skill-registry.js +3 -0
- package/mcp/dist/status.js +41 -13
- package/mcp/dist/task-hygiene.js +1 -1
- package/mcp/dist/telemetry.js +5 -4
- package/mcp/dist/update.js +1 -1
- package/mcp/dist/utils.js +3 -3
- package/package.json +2 -2
- package/starter/global/skills/audit.md +106 -0
- package/mcp/dist/shared-paths.js +0 -1
package/mcp/dist/link-skills.js
CHANGED
|
@@ -108,7 +108,75 @@ export function readSkillManifestHooks(phrenPath) {
|
|
|
108
108
|
}
|
|
109
109
|
return Object.keys(result).length > 0 ? result : null;
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Returns true if `destPath` is a symlink whose resolved target lives under
|
|
113
|
+
* `managedRoot`. Used to decide whether phren owns a symlink.
|
|
114
|
+
*/
|
|
115
|
+
export function isManagedSymlink(destPath, managedRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const stat = fs.lstatSync(destPath);
|
|
118
|
+
if (!stat.isSymbolicLink())
|
|
119
|
+
return false;
|
|
120
|
+
const target = fs.readlinkSync(destPath);
|
|
121
|
+
const resolvedTarget = path.resolve(path.dirname(destPath), target);
|
|
122
|
+
const managedPrefix = path.resolve(managedRoot) + path.sep;
|
|
123
|
+
return resolvedTarget.startsWith(managedPrefix);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Returns true if `destPath` exists and is NOT a symlink that points into
|
|
131
|
+
* managedRoot — i.e. it's a file/dir the user owns.
|
|
132
|
+
*/
|
|
133
|
+
function isUserOwnedFile(destPath, managedRoot) {
|
|
134
|
+
try {
|
|
135
|
+
fs.lstatSync(destPath);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false; // doesn't exist
|
|
139
|
+
}
|
|
140
|
+
return !isManagedSymlink(destPath, managedRoot);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Scan destDir for files that phren would want to link (based on srcDir) but
|
|
144
|
+
* can't because a user-owned file already occupies the destination slot.
|
|
145
|
+
*/
|
|
146
|
+
export function detectSkillCollisions(srcDir, destDir, managedRoot) {
|
|
147
|
+
if (!fs.existsSync(srcDir) || !fs.existsSync(destDir))
|
|
148
|
+
return [];
|
|
149
|
+
const collisions = [];
|
|
150
|
+
for (const entry of fs.readdirSync(srcDir)) {
|
|
151
|
+
const srcPath = path.join(srcDir, entry);
|
|
152
|
+
const stat = fs.statSync(srcPath);
|
|
153
|
+
if (stat.isFile() && entry.endsWith(".md")) {
|
|
154
|
+
const destPath = path.join(destDir, entry);
|
|
155
|
+
if (isUserOwnedFile(destPath, managedRoot)) {
|
|
156
|
+
const skillName = entry.replace(/\.md$/, "");
|
|
157
|
+
collisions.push({
|
|
158
|
+
skillName,
|
|
159
|
+
destPath,
|
|
160
|
+
message: `Skill '${skillName}' — user file already exists at ${destPath}. Rename or remove it to use phren's version.`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (stat.isDirectory()) {
|
|
165
|
+
const skillFile = path.join(srcPath, "SKILL.md");
|
|
166
|
+
if (fs.existsSync(skillFile)) {
|
|
167
|
+
const destPath = path.join(destDir, entry);
|
|
168
|
+
if (isUserOwnedFile(destPath, managedRoot)) {
|
|
169
|
+
collisions.push({
|
|
170
|
+
skillName: entry,
|
|
171
|
+
destPath,
|
|
172
|
+
message: `Skill '${entry}' — user directory already exists at ${destPath}. Rename or remove it to use phren's version.`,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return collisions;
|
|
179
|
+
}
|
|
112
180
|
function cleanupManagedSkillLinks(destDir, expectedNames, managedRoot) {
|
|
113
181
|
if (!fs.existsSync(destDir))
|
|
114
182
|
return;
|
|
@@ -117,27 +185,22 @@ function cleanupManagedSkillLinks(destDir, expectedNames, managedRoot) {
|
|
|
117
185
|
continue;
|
|
118
186
|
const destPath = path.join(destDir, entry);
|
|
119
187
|
try {
|
|
120
|
-
|
|
121
|
-
if (!stat.isSymbolicLink())
|
|
122
|
-
continue;
|
|
123
|
-
const target = fs.readlinkSync(destPath);
|
|
124
|
-
const resolvedTarget = path.resolve(path.dirname(destPath), target);
|
|
125
|
-
const managedPrefix = path.resolve(managedRoot) + path.sep;
|
|
126
|
-
if (!resolvedTarget.startsWith(managedPrefix))
|
|
188
|
+
if (!isManagedSymlink(destPath, managedRoot))
|
|
127
189
|
continue;
|
|
128
190
|
fs.unlinkSync(destPath);
|
|
129
191
|
}
|
|
130
192
|
catch (err) {
|
|
131
|
-
if ((process.env.PHREN_DEBUG
|
|
193
|
+
if ((process.env.PHREN_DEBUG))
|
|
132
194
|
process.stderr.write(`[phren] cleanupManagedSkillLinks: ${errorMessage(err)}\n`);
|
|
133
195
|
}
|
|
134
196
|
}
|
|
135
197
|
}
|
|
136
198
|
export function linkSkillsDir(srcDir, destDir, managedRoot, symlinkFile, opts) {
|
|
137
199
|
if (!fs.existsSync(srcDir))
|
|
138
|
-
return;
|
|
200
|
+
return [];
|
|
139
201
|
fs.mkdirSync(destDir, { recursive: true });
|
|
140
202
|
const expectedNames = new Set();
|
|
203
|
+
const collisions = [];
|
|
141
204
|
for (const entry of fs.readdirSync(srcDir)) {
|
|
142
205
|
const srcPath = path.join(srcDir, entry);
|
|
143
206
|
const stat = fs.statSync(srcPath);
|
|
@@ -146,20 +209,43 @@ export function linkSkillsDir(srcDir, destDir, managedRoot, symlinkFile, opts) {
|
|
|
146
209
|
continue;
|
|
147
210
|
}
|
|
148
211
|
if (stat.isFile() && entry.endsWith(".md")) {
|
|
212
|
+
const destPath = path.join(destDir, entry);
|
|
213
|
+
if (isUserOwnedFile(destPath, managedRoot)) {
|
|
214
|
+
const collision = {
|
|
215
|
+
skillName,
|
|
216
|
+
destPath,
|
|
217
|
+
message: `Skipping skill '${skillName}' — user skill already exists at ${destPath}. To use phren's version, rename or remove your skill first.`,
|
|
218
|
+
};
|
|
219
|
+
collisions.push(collision);
|
|
220
|
+
process.stderr.write(`[phren] ${collision.message}\n`);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
149
223
|
expectedNames.add(entry);
|
|
150
|
-
symlinkFile(srcPath,
|
|
224
|
+
symlinkFile(srcPath, destPath, managedRoot);
|
|
151
225
|
}
|
|
152
226
|
else if (stat.isDirectory()) {
|
|
153
227
|
const skillFile = path.join(srcPath, "SKILL.md");
|
|
154
228
|
if (fs.existsSync(skillFile)) {
|
|
229
|
+
const destPath = path.join(destDir, entry);
|
|
230
|
+
if (isUserOwnedFile(destPath, managedRoot)) {
|
|
231
|
+
const collision = {
|
|
232
|
+
skillName,
|
|
233
|
+
destPath,
|
|
234
|
+
message: `Skipping skill '${skillName}' — user skill already exists at ${destPath}. To use phren's version, rename or remove your skill first.`,
|
|
235
|
+
};
|
|
236
|
+
collisions.push(collision);
|
|
237
|
+
process.stderr.write(`[phren] ${collision.message}\n`);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
155
240
|
expectedNames.add(entry);
|
|
156
241
|
// Symlink the entire skill directory so bundled scripts and assets are accessible.
|
|
157
242
|
// Relative paths in the skill body remain valid because the directory structure is preserved.
|
|
158
|
-
symlinkFile(srcPath,
|
|
243
|
+
symlinkFile(srcPath, destPath, managedRoot);
|
|
159
244
|
}
|
|
160
245
|
}
|
|
161
246
|
}
|
|
162
247
|
cleanupManagedSkillLinks(destDir, expectedNames, managedRoot);
|
|
248
|
+
return collisions;
|
|
163
249
|
}
|
|
164
250
|
export function writeSkillMd(phrenPath) {
|
|
165
251
|
const lifecycle = buildSharedLifecycleCommands();
|
package/mcp/dist/link.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import * as crypto from "crypto";
|
|
4
3
|
import * as readline from "readline";
|
|
5
4
|
import * as yaml from "js-yaml";
|
|
6
5
|
import { execFileSync } from "child_process";
|
|
7
|
-
import {
|
|
6
|
+
import { ROOT } from "./package-metadata.js";
|
|
8
7
|
import { configureClaude, configureCodexMcp, configureCopilotMcp, configureCursorMcp, configureVSCode, ensureGovernanceFiles, getHooksEnabledPreference, getMcpEnabledPreference, isVersionNewer, logMcpTargetStatus, patchJsonFile, setMcpEnabledPreference, } from "./init.js";
|
|
9
8
|
import { configureAllHooks, detectInstalledTools } from "./hooks.js";
|
|
10
9
|
import { getMachineName, persistMachineName } from "./machine-identity.js";
|
|
11
|
-
import { debugLog, EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, isRecord, homePath, hookConfigPath, installPreferencesFile, } from "./shared.js";
|
|
10
|
+
import { debugLog, EXEC_TIMEOUT_MS, EXEC_TIMEOUT_QUICK_MS, isRecord, homePath, hookConfigPath, installPreferencesFile, atomicWriteText, } from "./shared.js";
|
|
12
11
|
import { errorMessage } from "./utils.js";
|
|
12
|
+
import { log } from "./init-shared.js";
|
|
13
13
|
import { listMachines as listMachinesShared, listProfiles as listProfilesShared, setMachineProfile, } from "./profile-store.js";
|
|
14
|
-
import { writeSkillMd } from "./link-skills.js";
|
|
14
|
+
import { writeSkillMd, isManagedSymlink } from "./link-skills.js";
|
|
15
15
|
import { syncScopeSkillsToDir } from "./skill-files.js";
|
|
16
16
|
import { renderSkillInstructionsSection } from "./skill-registry.js";
|
|
17
17
|
import { findProjectDir } from "./project-locator.js";
|
|
@@ -23,14 +23,6 @@ export { updateFileChecksums, verifyFileChecksums } from "./link-checksums.js";
|
|
|
23
23
|
export { findProjectDir } from "./project-locator.js";
|
|
24
24
|
export { parseSkillFrontmatter, validateSkillFrontmatter, validateSkillsDir, readSkillManifestHooks, } from "./link-skills.js";
|
|
25
25
|
// ── Helpers (exported for link-doctor) ──────────────────────────────────────
|
|
26
|
-
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
27
|
-
function log(msg) { process.stdout.write(msg + "\n"); }
|
|
28
|
-
function atomicWriteText(filePath, content) {
|
|
29
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
30
|
-
const tmpPath = `${filePath}.tmp-${crypto.randomUUID()}`;
|
|
31
|
-
fs.writeFileSync(tmpPath, content);
|
|
32
|
-
fs.renameSync(tmpPath, filePath);
|
|
33
|
-
}
|
|
34
26
|
export { getMachineName } from "./machine-identity.js";
|
|
35
27
|
export function lookupProfile(phrenPath, machine) {
|
|
36
28
|
const listed = listMachinesShared(phrenPath);
|
|
@@ -123,7 +115,7 @@ function setupSparseCheckout(phrenPath, projects) {
|
|
|
123
115
|
execFileSync("git", ["rev-parse", "--git-dir"], { cwd: phrenPath, stdio: "ignore", timeout: EXEC_TIMEOUT_QUICK_MS });
|
|
124
116
|
}
|
|
125
117
|
catch (err) {
|
|
126
|
-
if ((process.env.PHREN_DEBUG
|
|
118
|
+
if ((process.env.PHREN_DEBUG))
|
|
127
119
|
process.stderr.write(`[phren] setupSparseCheckout notAGitRepo: ${errorMessage(err)}\n`);
|
|
128
120
|
return;
|
|
129
121
|
}
|
|
@@ -184,10 +176,9 @@ function symlinkFile(src, dest, managedRoot) {
|
|
|
184
176
|
if (stat.isSymbolicLink()) {
|
|
185
177
|
const currentTarget = fs.readlinkSync(dest);
|
|
186
178
|
const resolvedTarget = path.resolve(path.dirname(dest), currentTarget);
|
|
187
|
-
const managedPrefix = path.resolve(managedRoot) + path.sep;
|
|
188
179
|
if (resolvedTarget === path.resolve(src))
|
|
189
180
|
return true;
|
|
190
|
-
if (!
|
|
181
|
+
if (!isManagedSymlink(dest, managedRoot)) {
|
|
191
182
|
log(` preserve existing symlink: ${dest}`);
|
|
192
183
|
return false;
|
|
193
184
|
}
|
|
@@ -238,8 +229,7 @@ function writeManagedAgentsFile(src, dest, content, managedRoot) {
|
|
|
238
229
|
if (stat.isSymbolicLink()) {
|
|
239
230
|
const currentTarget = fs.readlinkSync(dest);
|
|
240
231
|
const resolvedTarget = path.resolve(path.dirname(dest), currentTarget);
|
|
241
|
-
|
|
242
|
-
if (resolvedTarget === path.resolve(src) || resolvedTarget.startsWith(managedPrefix)) {
|
|
232
|
+
if (resolvedTarget === path.resolve(src) || isManagedSymlink(dest, managedRoot)) {
|
|
243
233
|
fs.unlinkSync(dest);
|
|
244
234
|
}
|
|
245
235
|
else {
|
|
@@ -278,7 +268,7 @@ function linkGlobal(phrenPath, tools) {
|
|
|
278
268
|
symlinkFile(globalClaude, path.join(copilotInstrDir, "copilot-instructions.md"), phrenPath);
|
|
279
269
|
}
|
|
280
270
|
catch (err) {
|
|
281
|
-
if ((process.env.PHREN_DEBUG
|
|
271
|
+
if ((process.env.PHREN_DEBUG))
|
|
282
272
|
process.stderr.write(`[phren] linkGlobal copilotInstructions: ${errorMessage(err)}\n`);
|
|
283
273
|
}
|
|
284
274
|
}
|
|
@@ -324,7 +314,7 @@ function linkProject(phrenPath, project, tools) {
|
|
|
324
314
|
symlinkFile(src, path.join(copilotDir, "copilot-instructions.md"), phrenPath);
|
|
325
315
|
}
|
|
326
316
|
catch (err) {
|
|
327
|
-
if ((process.env.PHREN_DEBUG
|
|
317
|
+
if ((process.env.PHREN_DEBUG))
|
|
328
318
|
process.stderr.write(`[phren] linkProject copilotInstructions: ${errorMessage(err)}\n`);
|
|
329
319
|
}
|
|
330
320
|
}
|
|
@@ -348,7 +338,7 @@ function linkProject(phrenPath, project, tools) {
|
|
|
348
338
|
addTokenAnnotation(claudeFile);
|
|
349
339
|
}
|
|
350
340
|
catch (err) {
|
|
351
|
-
if ((process.env.PHREN_DEBUG
|
|
341
|
+
if ((process.env.PHREN_DEBUG))
|
|
352
342
|
process.stderr.write(`[phren] linkProject tokenAnnotation: ${errorMessage(err)}\n`);
|
|
353
343
|
}
|
|
354
344
|
}
|
|
@@ -365,7 +355,7 @@ function linkProject(phrenPath, project, tools) {
|
|
|
365
355
|
excludeEntries.push("AGENTS.md");
|
|
366
356
|
}
|
|
367
357
|
catch (err) {
|
|
368
|
-
if ((process.env.PHREN_DEBUG
|
|
358
|
+
if ((process.env.PHREN_DEBUG))
|
|
369
359
|
process.stderr.write(`[phren] linkProject agentsMd: ${errorMessage(err)}\n`);
|
|
370
360
|
}
|
|
371
361
|
}
|
|
@@ -506,7 +496,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
506
496
|
mcpStatus = configureClaude(phrenPath, { mcpEnabled, hooksEnabled }) ?? "installed";
|
|
507
497
|
}
|
|
508
498
|
catch (err) {
|
|
509
|
-
if ((process.env.PHREN_DEBUG
|
|
499
|
+
if ((process.env.PHREN_DEBUG))
|
|
510
500
|
process.stderr.write(`[phren] link configureClaude: ${errorMessage(err)}\n`);
|
|
511
501
|
}
|
|
512
502
|
logMcpTargetStatus("Claude", mcpStatus);
|
|
@@ -515,7 +505,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
515
505
|
vsStatus = configureVSCode(phrenPath, { mcpEnabled }) ?? "no_vscode";
|
|
516
506
|
}
|
|
517
507
|
catch (err) {
|
|
518
|
-
if ((process.env.PHREN_DEBUG
|
|
508
|
+
if ((process.env.PHREN_DEBUG))
|
|
519
509
|
process.stderr.write(`[phren] link configureVSCode: ${errorMessage(err)}\n`);
|
|
520
510
|
}
|
|
521
511
|
logMcpTargetStatus("VS Code", vsStatus);
|
|
@@ -524,7 +514,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
524
514
|
cursorStatus = configureCursorMcp(phrenPath, { mcpEnabled }) ?? "no_cursor";
|
|
525
515
|
}
|
|
526
516
|
catch (err) {
|
|
527
|
-
if ((process.env.PHREN_DEBUG
|
|
517
|
+
if ((process.env.PHREN_DEBUG))
|
|
528
518
|
process.stderr.write(`[phren] link configureCursorMcp: ${errorMessage(err)}\n`);
|
|
529
519
|
}
|
|
530
520
|
logMcpTargetStatus("Cursor", cursorStatus);
|
|
@@ -533,7 +523,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
533
523
|
copilotStatus = configureCopilotMcp(phrenPath, { mcpEnabled }) ?? "no_copilot";
|
|
534
524
|
}
|
|
535
525
|
catch (err) {
|
|
536
|
-
if ((process.env.PHREN_DEBUG
|
|
526
|
+
if ((process.env.PHREN_DEBUG))
|
|
537
527
|
process.stderr.write(`[phren] link configureCopilotMcp: ${errorMessage(err)}\n`);
|
|
538
528
|
}
|
|
539
529
|
logMcpTargetStatus("Copilot CLI", copilotStatus);
|
|
@@ -542,7 +532,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
542
532
|
codexStatus = configureCodexMcp(phrenPath, { mcpEnabled }) ?? "no_codex";
|
|
543
533
|
}
|
|
544
534
|
catch (err) {
|
|
545
|
-
if ((process.env.PHREN_DEBUG
|
|
535
|
+
if ((process.env.PHREN_DEBUG))
|
|
546
536
|
process.stderr.write(`[phren] link configureCodexMcp: ${errorMessage(err)}\n`);
|
|
547
537
|
}
|
|
548
538
|
logMcpTargetStatus("Codex", codexStatus);
|
|
@@ -566,7 +556,7 @@ export async function runLink(phrenPath, opts = {}) {
|
|
|
566
556
|
log(` phren.SKILL.md written (agentskills-compatible tools)`);
|
|
567
557
|
}
|
|
568
558
|
catch (err) {
|
|
569
|
-
if ((process.env.PHREN_DEBUG
|
|
559
|
+
if ((process.env.PHREN_DEBUG))
|
|
570
560
|
process.stderr.write(`[phren] link writeSkillMd: ${errorMessage(err)}\n`);
|
|
571
561
|
}
|
|
572
562
|
log("");
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as os from "os";
|
|
3
|
-
import
|
|
4
|
-
import * as crypto from "crypto";
|
|
5
|
-
import { homePath } from "./shared.js";
|
|
3
|
+
import { homePath, atomicWriteText } from "./shared.js";
|
|
6
4
|
function phrenMachineFilePath() {
|
|
7
5
|
return homePath(".phren", ".machine-id");
|
|
8
6
|
}
|
|
9
|
-
function atomicWriteText(filePath, content) {
|
|
10
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
11
|
-
const tmpPath = `${filePath}.tmp-${crypto.randomUUID()}`;
|
|
12
|
-
fs.writeFileSync(tmpPath, content);
|
|
13
|
-
fs.renameSync(tmpPath, filePath);
|
|
14
|
-
}
|
|
15
7
|
export function machineFilePath() {
|
|
16
8
|
return phrenMachineFilePath();
|
|
17
9
|
}
|