@phren/cli 0.0.20 → 0.0.22
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/mcp/dist/generated/memory-ui-graph.browser.js +22 -22
- package/mcp/dist/hooks.js +51 -14
- package/mcp/dist/mcp-data.js +4 -5
- package/mcp/dist/memory-ui-assets.js +2 -2
- package/mcp/dist/memory-ui-data.js +1 -1
- package/mcp/dist/memory-ui-graph.runtime.js +22 -22
- package/mcp/dist/memory-ui-page.js +71 -44
- package/mcp/dist/memory-ui-scripts.js +37 -555
- package/mcp/dist/memory-ui-server.js +0 -34
- package/mcp/dist/memory-ui-styles.js +137 -136
- package/mcp/dist/profile-store.js +1 -13
- package/mcp/dist/shared-ollama.js +1 -12
- package/mcp/dist/shell-state-store.js +2 -14
- package/package.json +1 -1
|
@@ -16,7 +16,6 @@ import { getWorkflowPolicy, updateWorkflowPolicy, mergeConfig, getRetentionPolic
|
|
|
16
16
|
import { readProjectConfig, updateProjectConfigOverrides } from "./project-config.js";
|
|
17
17
|
import { findSkill } from "./skill-registry.js";
|
|
18
18
|
import { setSkillEnabledAndSync } from "./skill-files.js";
|
|
19
|
-
import { listAllSessions, getSessionArtifacts } from "./mcp-session.js";
|
|
20
19
|
import { repairPreexistingInstall } from "./init-setup.js";
|
|
21
20
|
const CSRF_TOKEN_TTL_MS = 15 * 60 * 1000;
|
|
22
21
|
const MAX_FORM_BODY_BYTES = 1_048_576;
|
|
@@ -982,39 +981,6 @@ export function createWebUiHttpServer(phrenPath, renderPage, profile, opts) {
|
|
|
982
981
|
});
|
|
983
982
|
return;
|
|
984
983
|
}
|
|
985
|
-
if (req.method === "GET" && pathname === "/api/sessions") {
|
|
986
|
-
if (!requireGetAuth(req, res, url, authToken, true))
|
|
987
|
-
return;
|
|
988
|
-
try {
|
|
989
|
-
const qs = url.includes("?") ? querystring.parse(url.slice(url.indexOf("?") + 1)) : {};
|
|
990
|
-
const sessionId = typeof qs.sessionId === "string" ? qs.sessionId : undefined;
|
|
991
|
-
const project = typeof qs.project === "string" ? qs.project : undefined;
|
|
992
|
-
const limit = parseInt(typeof qs.limit === "string" ? qs.limit : "50", 10) || 50;
|
|
993
|
-
if (sessionId) {
|
|
994
|
-
const sessions = listAllSessions(phrenPath, 200);
|
|
995
|
-
const session = sessions.find(s => s.sessionId === sessionId || s.sessionId.startsWith(sessionId));
|
|
996
|
-
if (!session) {
|
|
997
|
-
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
998
|
-
res.end(JSON.stringify({ ok: false, error: "Session not found" }));
|
|
999
|
-
return;
|
|
1000
|
-
}
|
|
1001
|
-
const artifacts = getSessionArtifacts(phrenPath, session.sessionId, project);
|
|
1002
|
-
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
1003
|
-
res.end(JSON.stringify({ ok: true, session, ...artifacts }));
|
|
1004
|
-
}
|
|
1005
|
-
else {
|
|
1006
|
-
const sessions = listAllSessions(phrenPath, limit);
|
|
1007
|
-
const filtered = project ? sessions.filter(s => s.project === project) : sessions;
|
|
1008
|
-
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
1009
|
-
res.end(JSON.stringify({ ok: true, sessions: filtered }));
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
catch (err) {
|
|
1013
|
-
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
1014
|
-
res.end(JSON.stringify({ ok: false, error: errorMessage(err), sessions: [] }));
|
|
1015
|
-
}
|
|
1016
|
-
return;
|
|
1017
|
-
}
|
|
1018
984
|
if (req.method === "GET" && pathname === "/api/settings") {
|
|
1019
985
|
if (!requireGetAuth(req, res, url, authToken, true))
|
|
1020
986
|
return;
|
|
@@ -359,112 +359,113 @@ export const TASK_UI_STYLES = `
|
|
|
359
359
|
.task-add-btn:hover { background: var(--accent-hover); }
|
|
360
360
|
.task-add-btn:active { transform: scale(0.97); }
|
|
361
361
|
|
|
362
|
-
.task-
|
|
362
|
+
.task-section-group { margin-bottom: 24px; }
|
|
363
363
|
.task-section-header {
|
|
364
364
|
display: flex;
|
|
365
365
|
align-items: center;
|
|
366
366
|
gap: 8px;
|
|
367
|
-
font-size: var(--text-
|
|
368
|
-
font-weight:
|
|
369
|
-
color: var(--
|
|
367
|
+
font-size: var(--text-xs);
|
|
368
|
+
font-weight: 700;
|
|
369
|
+
color: var(--muted);
|
|
370
370
|
text-transform: uppercase;
|
|
371
|
-
letter-spacing: 0.
|
|
372
|
-
margin-bottom:
|
|
373
|
-
padding
|
|
374
|
-
border-bottom: 1px solid var(--border-light);
|
|
371
|
+
letter-spacing: 0.06em;
|
|
372
|
+
margin-bottom: 8px;
|
|
373
|
+
padding: 0 4px;
|
|
375
374
|
}
|
|
376
|
-
.task-section-icon { display: flex; align-items: center; }
|
|
377
375
|
.task-section-count {
|
|
378
|
-
font-weight:
|
|
376
|
+
font-weight: 500;
|
|
379
377
|
color: var(--muted);
|
|
380
378
|
font-size: var(--text-xs);
|
|
381
|
-
|
|
379
|
+
opacity: 0.7;
|
|
382
380
|
}
|
|
383
381
|
|
|
384
|
-
.task-
|
|
382
|
+
.task-list {
|
|
385
383
|
display: flex;
|
|
386
384
|
flex-direction: column;
|
|
387
|
-
gap:
|
|
388
|
-
}
|
|
389
|
-
.task-card {
|
|
385
|
+
gap: 2px;
|
|
390
386
|
border: 1px solid var(--border);
|
|
391
387
|
border-radius: var(--radius);
|
|
392
388
|
background: var(--surface);
|
|
393
|
-
|
|
394
|
-
transition: box-shadow 0.15s, border-color 0.15s, transform 0.1s;
|
|
395
|
-
cursor: default;
|
|
396
|
-
border-left: 3px solid transparent;
|
|
389
|
+
overflow: hidden;
|
|
397
390
|
}
|
|
398
|
-
.task-card:hover {
|
|
399
|
-
box-shadow: var(--shadow);
|
|
400
|
-
border-color: var(--accent-dim);
|
|
401
|
-
transform: translateY(-1px);
|
|
402
|
-
}
|
|
403
|
-
.task-card-high { border-left-color: #ef4444; }
|
|
404
|
-
.task-card-medium { border-left-color: #f59e0b; }
|
|
405
|
-
.task-card-low { border-left-color: #6b7280; }
|
|
406
|
-
.task-card-done {
|
|
407
|
-
opacity: 0.55;
|
|
408
|
-
border-left-color: var(--success) !important;
|
|
409
|
-
}
|
|
410
|
-
.task-card-done:hover { opacity: 0.75; }
|
|
411
|
-
.task-card-done .task-card-text { text-decoration: line-through; }
|
|
412
391
|
|
|
413
|
-
.task-
|
|
392
|
+
.task-row {
|
|
414
393
|
display: flex;
|
|
415
394
|
align-items: center;
|
|
416
|
-
gap:
|
|
417
|
-
|
|
418
|
-
|
|
395
|
+
gap: 10px;
|
|
396
|
+
padding: 10px 14px;
|
|
397
|
+
border-bottom: 1px solid var(--border-light);
|
|
398
|
+
transition: background 0.1s;
|
|
399
|
+
cursor: default;
|
|
400
|
+
position: relative;
|
|
419
401
|
}
|
|
402
|
+
.task-row:last-child { border-bottom: none; }
|
|
403
|
+
.task-row:hover { background: var(--surface-raised); }
|
|
404
|
+
.task-row:hover .task-row-actions { opacity: 1; pointer-events: auto; }
|
|
420
405
|
|
|
421
|
-
.task-
|
|
422
|
-
|
|
406
|
+
.task-row-priority {
|
|
407
|
+
width: 4px;
|
|
408
|
+
height: 24px;
|
|
409
|
+
border-radius: 2px;
|
|
410
|
+
flex-shrink: 0;
|
|
411
|
+
}
|
|
412
|
+
.task-row-priority-high { background: #ef4444; }
|
|
413
|
+
.task-row-priority-medium { background: #f59e0b; }
|
|
414
|
+
.task-row-priority-low { background: #9ca3af; }
|
|
415
|
+
.task-row-priority-none { background: transparent; }
|
|
416
|
+
|
|
417
|
+
.task-row-content {
|
|
418
|
+
flex: 1;
|
|
419
|
+
min-width: 0;
|
|
420
|
+
display: flex;
|
|
423
421
|
align-items: center;
|
|
424
|
-
gap:
|
|
425
|
-
padding: 2px 8px;
|
|
426
|
-
border-radius: 999px;
|
|
427
|
-
font-size: 11px;
|
|
428
|
-
font-weight: 600;
|
|
429
|
-
letter-spacing: 0.02em;
|
|
422
|
+
gap: 8px;
|
|
430
423
|
}
|
|
431
|
-
.task-
|
|
432
|
-
|
|
433
|
-
color: var(--
|
|
434
|
-
|
|
424
|
+
.task-row-text {
|
|
425
|
+
font-size: var(--text-base);
|
|
426
|
+
color: var(--ink);
|
|
427
|
+
line-height: 1.4;
|
|
428
|
+
word-break: break-word;
|
|
435
429
|
}
|
|
436
|
-
.task-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
border: 1px solid transparent;
|
|
430
|
+
.task-row-done .task-row-text {
|
|
431
|
+
text-decoration: line-through;
|
|
432
|
+
opacity: 0.5;
|
|
440
433
|
}
|
|
441
|
-
.task-
|
|
442
|
-
|
|
443
|
-
color: var(--
|
|
444
|
-
|
|
434
|
+
.task-row-context {
|
|
435
|
+
font-size: var(--text-xs);
|
|
436
|
+
color: var(--muted);
|
|
437
|
+
font-style: italic;
|
|
438
|
+
margin-top: 2px;
|
|
445
439
|
}
|
|
446
440
|
|
|
447
|
-
.task-
|
|
448
|
-
display:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
font-weight: 500;
|
|
453
|
-
background: var(--accent-dim);
|
|
454
|
-
color: var(--accent);
|
|
441
|
+
.task-row-meta {
|
|
442
|
+
display: flex;
|
|
443
|
+
align-items: center;
|
|
444
|
+
gap: 6px;
|
|
445
|
+
flex-shrink: 0;
|
|
455
446
|
}
|
|
456
447
|
|
|
457
448
|
.task-priority-badge {
|
|
458
449
|
display: inline-block;
|
|
459
450
|
padding: 1px 7px;
|
|
460
451
|
border-radius: 999px;
|
|
461
|
-
font-size:
|
|
452
|
+
font-size: 10px;
|
|
462
453
|
font-weight: 600;
|
|
463
454
|
text-transform: capitalize;
|
|
464
455
|
}
|
|
465
|
-
.task-priority-high { background: #
|
|
466
|
-
.task-priority-medium { background: #
|
|
467
|
-
.task-priority-low { background: #
|
|
456
|
+
.task-priority-high { background: #ef444418; color: #ef4444; }
|
|
457
|
+
.task-priority-medium { background: #f59e0b18; color: #f59e0b; }
|
|
458
|
+
.task-priority-low { background: #6b728018; color: #6b7280; }
|
|
459
|
+
|
|
460
|
+
.task-project-badge {
|
|
461
|
+
display: inline-block;
|
|
462
|
+
padding: 2px 8px;
|
|
463
|
+
border-radius: var(--radius-sm);
|
|
464
|
+
font-size: 10px;
|
|
465
|
+
font-weight: 500;
|
|
466
|
+
background: var(--accent-dim);
|
|
467
|
+
color: var(--accent);
|
|
468
|
+
}
|
|
468
469
|
|
|
469
470
|
.task-pin-indicator {
|
|
470
471
|
color: var(--accent);
|
|
@@ -478,7 +479,7 @@ export const TASK_UI_STYLES = `
|
|
|
478
479
|
gap: 4px;
|
|
479
480
|
padding: 2px 8px;
|
|
480
481
|
border-radius: var(--radius-sm);
|
|
481
|
-
font-size:
|
|
482
|
+
font-size: 10px;
|
|
482
483
|
font-weight: 500;
|
|
483
484
|
background: var(--surface-sunken);
|
|
484
485
|
color: var(--ink-secondary);
|
|
@@ -487,33 +488,21 @@ export const TASK_UI_STYLES = `
|
|
|
487
488
|
}
|
|
488
489
|
.task-github-badge:hover { background: var(--accent-dim); color: var(--accent); }
|
|
489
490
|
|
|
490
|
-
.task-
|
|
491
|
+
.task-row-actions {
|
|
491
492
|
display: flex;
|
|
492
|
-
|
|
493
|
+
align-items: center;
|
|
493
494
|
gap: 4px;
|
|
495
|
+
opacity: 0;
|
|
496
|
+
pointer-events: none;
|
|
497
|
+
transition: opacity 0.15s;
|
|
498
|
+
flex-shrink: 0;
|
|
494
499
|
}
|
|
495
|
-
.task-
|
|
496
|
-
font-size: var(--text-base);
|
|
497
|
-
color: var(--ink);
|
|
498
|
-
line-height: 1.5;
|
|
499
|
-
word-break: break-word;
|
|
500
|
-
}
|
|
501
|
-
.task-card-context {
|
|
502
|
-
font-size: var(--text-sm);
|
|
503
|
-
color: var(--muted);
|
|
504
|
-
font-style: italic;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
.task-card-actions {
|
|
508
|
-
margin-top: 10px;
|
|
509
|
-
display: flex;
|
|
510
|
-
justify-content: flex-end;
|
|
511
|
-
}
|
|
512
|
-
.task-done-btn {
|
|
500
|
+
.task-action-btn {
|
|
513
501
|
display: inline-flex;
|
|
514
502
|
align-items: center;
|
|
515
|
-
|
|
516
|
-
|
|
503
|
+
justify-content: center;
|
|
504
|
+
width: 28px;
|
|
505
|
+
height: 28px;
|
|
517
506
|
border: 1px solid var(--border);
|
|
518
507
|
border-radius: var(--radius-sm);
|
|
519
508
|
background: var(--surface);
|
|
@@ -522,28 +511,39 @@ export const TASK_UI_STYLES = `
|
|
|
522
511
|
font-size: 12px;
|
|
523
512
|
font-family: var(--font);
|
|
524
513
|
transition: all 0.15s;
|
|
514
|
+
padding: 0;
|
|
515
|
+
}
|
|
516
|
+
.task-action-btn:hover {
|
|
517
|
+
border-color: var(--accent);
|
|
518
|
+
color: var(--accent);
|
|
519
|
+
background: var(--accent-dim);
|
|
525
520
|
}
|
|
526
|
-
.task-
|
|
521
|
+
.task-action-btn.task-action-complete:hover {
|
|
527
522
|
border-color: var(--success);
|
|
528
523
|
color: var(--success);
|
|
529
524
|
background: var(--success-dim);
|
|
530
525
|
}
|
|
526
|
+
.task-action-btn.task-action-delete:hover {
|
|
527
|
+
border-color: var(--danger);
|
|
528
|
+
color: var(--danger);
|
|
529
|
+
background: var(--danger-dim);
|
|
530
|
+
}
|
|
531
531
|
|
|
532
532
|
.task-done-section { margin-top: 24px; }
|
|
533
533
|
.task-done-toggle {
|
|
534
534
|
display: flex;
|
|
535
535
|
align-items: center;
|
|
536
536
|
gap: 8px;
|
|
537
|
-
padding: 10px
|
|
537
|
+
padding: 10px 4px;
|
|
538
538
|
background: none;
|
|
539
539
|
border: none;
|
|
540
540
|
color: var(--muted);
|
|
541
|
-
font-size: var(--text-
|
|
542
|
-
font-weight:
|
|
541
|
+
font-size: var(--text-xs);
|
|
542
|
+
font-weight: 700;
|
|
543
543
|
font-family: var(--font);
|
|
544
544
|
cursor: pointer;
|
|
545
545
|
text-transform: uppercase;
|
|
546
|
-
letter-spacing: 0.
|
|
546
|
+
letter-spacing: 0.06em;
|
|
547
547
|
transition: color 0.15s;
|
|
548
548
|
}
|
|
549
549
|
.task-done-toggle:hover { color: var(--ink-secondary); }
|
|
@@ -551,7 +551,7 @@ export const TASK_UI_STYLES = `
|
|
|
551
551
|
font-size: 10px;
|
|
552
552
|
transition: transform 0.2s;
|
|
553
553
|
}
|
|
554
|
-
.task-done-list { padding-top:
|
|
554
|
+
.task-done-list { padding-top: 4px; }
|
|
555
555
|
|
|
556
556
|
/* ── Task Summary Bar ──────────────────────────── */
|
|
557
557
|
.task-summary-bar {
|
|
@@ -606,52 +606,53 @@ export const TASK_UI_STYLES = `
|
|
|
606
606
|
color: var(--muted);
|
|
607
607
|
border: 1px solid var(--border);
|
|
608
608
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
609
|
+
`;
|
|
610
|
+
export const REVIEW_UI_STYLES = `
|
|
611
|
+
/* ── Review Toolbar ──────────────────────────── */
|
|
612
|
+
.review-toolbar {
|
|
612
613
|
display: flex;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
614
|
+
align-items: center;
|
|
615
|
+
gap: 10px;
|
|
616
|
+
margin-bottom: 16px;
|
|
617
|
+
flex-wrap: wrap;
|
|
617
618
|
}
|
|
618
|
-
.
|
|
619
|
-
display: flex;
|
|
619
|
+
.review-flagged-toggle {
|
|
620
|
+
display: inline-flex;
|
|
620
621
|
align-items: center;
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
border: none;
|
|
626
|
-
color: var(--muted);
|
|
622
|
+
gap: 6px;
|
|
623
|
+
font-size: var(--text-sm);
|
|
624
|
+
font-weight: 500;
|
|
625
|
+
color: var(--ink-secondary);
|
|
627
626
|
cursor: pointer;
|
|
628
|
-
|
|
627
|
+
user-select: none;
|
|
629
628
|
}
|
|
630
|
-
.
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
629
|
+
.review-flagged-toggle input[type="checkbox"] {
|
|
630
|
+
width: 14px;
|
|
631
|
+
height: 14px;
|
|
632
|
+
cursor: pointer;
|
|
633
|
+
accent-color: var(--accent);
|
|
634
634
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
635
|
+
.review-sync-dot {
|
|
636
|
+
display: inline-flex;
|
|
637
|
+
align-items: center;
|
|
638
|
+
justify-content: center;
|
|
639
|
+
width: 22px;
|
|
640
|
+
height: 22px;
|
|
641
|
+
flex-shrink: 0;
|
|
641
642
|
}
|
|
642
|
-
.
|
|
643
|
-
|
|
643
|
+
.review-sync-indicator {
|
|
644
|
+
width: 8px;
|
|
645
|
+
height: 8px;
|
|
646
|
+
border-radius: 50%;
|
|
647
|
+
background: var(--success);
|
|
648
|
+
display: inline-block;
|
|
649
|
+
transition: background 0.3s;
|
|
644
650
|
}
|
|
645
|
-
.
|
|
646
|
-
|
|
651
|
+
.review-sync-indicator.syncing {
|
|
652
|
+
background: var(--warning);
|
|
653
|
+
animation: ledPulse 1.2s infinite;
|
|
647
654
|
}
|
|
648
|
-
.
|
|
649
|
-
|
|
650
|
-
display: -webkit-box;
|
|
651
|
-
-webkit-line-clamp: 2;
|
|
652
|
-
-webkit-box-orient: vertical;
|
|
653
|
-
overflow: hidden;
|
|
655
|
+
.review-sync-indicator.error {
|
|
656
|
+
background: var(--danger);
|
|
654
657
|
}
|
|
655
|
-
.task-card-grid-compact .task-card-context { display: none; }
|
|
656
|
-
.task-card-grid-compact .task-card-actions { margin-top: 6px; }
|
|
657
658
|
`;
|
|
@@ -4,21 +4,9 @@ import * as crypto from "crypto";
|
|
|
4
4
|
import * as yaml from "js-yaml";
|
|
5
5
|
import { phrenErr, PhrenError, phrenOk, forwardErr, getProjectDirs, readRootManifest, } from "./shared.js";
|
|
6
6
|
import { defaultMachineName, getMachineName } from "./machine-identity.js";
|
|
7
|
-
import { withFileLock as withFileLockRaw } from "./shared-governance.js";
|
|
8
7
|
import { errorMessage, isValidProjectName } from "./utils.js";
|
|
9
8
|
import { TASK_FILE_ALIASES } from "./data-tasks.js";
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
return withFileLockRaw(filePath, fn);
|
|
13
|
-
}
|
|
14
|
-
catch (err) {
|
|
15
|
-
const msg = errorMessage(err);
|
|
16
|
-
if (msg.includes("could not acquire lock")) {
|
|
17
|
-
return phrenErr(`Could not acquire write lock for "${path.basename(filePath)}". Another write may be in progress; please retry.`, PhrenError.LOCK_TIMEOUT);
|
|
18
|
-
}
|
|
19
|
-
throw err;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
9
|
+
import { withSafeLock } from "./shared-data-utils.js";
|
|
22
10
|
export function resolveActiveProfile(phrenPath, requestedProfile) {
|
|
23
11
|
const manifest = readRootManifest(phrenPath);
|
|
24
12
|
if (manifest?.installMode === "project-local") {
|
|
@@ -178,15 +178,4 @@ export async function generateText(prompt, model, url) {
|
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
export
|
|
182
|
-
if (a.length !== b.length || a.length === 0)
|
|
183
|
-
return 0;
|
|
184
|
-
let dot = 0, normA = 0, normB = 0;
|
|
185
|
-
for (let i = 0; i < a.length; i++) {
|
|
186
|
-
dot += a[i] * b[i];
|
|
187
|
-
normA += a[i] * a[i];
|
|
188
|
-
normB += b[i] * b[i];
|
|
189
|
-
}
|
|
190
|
-
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
191
|
-
return denom === 0 ? 0 : dot / denom;
|
|
192
|
-
}
|
|
181
|
+
export { cosineSimilarity } from "./embedding.js";
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { phrenOk, shellStateFile } from "./shared.js";
|
|
4
|
+
import { withSafeLock } from "./shared-data-utils.js";
|
|
5
5
|
import { errorMessage } from "./utils.js";
|
|
6
|
-
function withSafeLock(filePath, fn) {
|
|
7
|
-
try {
|
|
8
|
-
return withFileLockRaw(filePath, fn);
|
|
9
|
-
}
|
|
10
|
-
catch (err) {
|
|
11
|
-
const msg = errorMessage(err);
|
|
12
|
-
if (msg.includes("could not acquire lock")) {
|
|
13
|
-
return phrenErr(`Could not acquire write lock for "${path.basename(filePath)}". Another write may be in progress; please retry.`, PhrenError.LOCK_TIMEOUT);
|
|
14
|
-
}
|
|
15
|
-
throw err;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
6
|
const SHELL_STATE_VERSION = 3;
|
|
19
7
|
const VALID_VIEWS = new Set(["Projects", "Tasks", "Findings", "Review Queue", "Skills", "Hooks", "Machines/Profiles", "Health"]);
|
|
20
8
|
export function loadShellState(phrenPath) {
|