@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.
@@ -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-priority-section { margin-bottom: 24px; }
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-sm);
368
- font-weight: 600;
369
- color: var(--ink-secondary);
367
+ font-size: var(--text-xs);
368
+ font-weight: 700;
369
+ color: var(--muted);
370
370
  text-transform: uppercase;
371
- letter-spacing: 0.04em;
372
- margin-bottom: 12px;
373
- padding-bottom: 8px;
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: 400;
376
+ font-weight: 500;
379
377
  color: var(--muted);
380
378
  font-size: var(--text-xs);
381
- margin-left: 4px;
379
+ opacity: 0.7;
382
380
  }
383
381
 
384
- .task-card-grid {
382
+ .task-list {
385
383
  display: flex;
386
384
  flex-direction: column;
387
- gap: 8px;
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
- padding: 14px 16px;
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-card-top {
392
+ .task-row {
414
393
  display: flex;
415
394
  align-items: center;
416
- gap: 8px;
417
- margin-bottom: 8px;
418
- flex-wrap: wrap;
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-status-chip {
422
- display: inline-flex;
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: 4px;
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-status-pending {
432
- border: 1px solid var(--border);
433
- color: var(--muted);
434
- background: transparent;
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-status-active {
437
- background: var(--accent);
438
- color: #fff;
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-status-done {
442
- background: var(--success-dim);
443
- color: var(--success);
444
- border: 1px solid transparent;
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-project-badge {
448
- display: inline-block;
449
- padding: 2px 8px;
450
- border-radius: var(--radius-sm);
451
- font-size: 11px;
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: 11px;
452
+ font-size: 10px;
462
453
  font-weight: 600;
463
454
  text-transform: capitalize;
464
455
  }
465
- .task-priority-high { background: #ef444422; color: #ef4444; }
466
- .task-priority-medium { background: #f59e0b22; color: #f59e0b; }
467
- .task-priority-low { background: #6b728022; color: #6b7280; }
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: 11px;
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-card-body {
491
+ .task-row-actions {
491
492
  display: flex;
492
- flex-direction: column;
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-card-text {
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
- gap: 4px;
516
- padding: 4px 12px;
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-done-btn:hover {
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 0;
537
+ padding: 10px 4px;
538
538
  background: none;
539
539
  border: none;
540
540
  color: var(--muted);
541
- font-size: var(--text-sm);
542
- font-weight: 600;
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.04em;
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: 8px; }
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
- /* ── Task View Toggle ──────────────────────────── */
611
- .task-view-toggle {
609
+ `;
610
+ export const REVIEW_UI_STYLES = `
611
+ /* ── Review Toolbar ──────────────────────────── */
612
+ .review-toolbar {
612
613
  display: flex;
613
- gap: 2px;
614
- border: 1px solid var(--border);
615
- border-radius: var(--radius-sm);
616
- overflow: hidden;
614
+ align-items: center;
615
+ gap: 10px;
616
+ margin-bottom: 16px;
617
+ flex-wrap: wrap;
617
618
  }
618
- .task-view-btn {
619
- display: flex;
619
+ .review-flagged-toggle {
620
+ display: inline-flex;
620
621
  align-items: center;
621
- justify-content: center;
622
- width: 30px;
623
- height: 28px;
624
- background: var(--surface);
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
- transition: background 0.15s, color 0.15s;
627
+ user-select: none;
629
628
  }
630
- .task-view-btn:hover { color: var(--ink); }
631
- .task-view-btn.active {
632
- background: var(--accent-dim);
633
- color: var(--accent);
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
- /* ── Task Compact Grid ──────────────────────────── */
637
- .task-card-grid-compact {
638
- display: grid !important;
639
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
640
- gap: 8px;
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
- .task-card-grid-compact .task-card {
643
- padding: 10px 12px;
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
- .task-card-grid-compact .task-card-body {
646
- gap: 2px;
651
+ .review-sync-indicator.syncing {
652
+ background: var(--warning);
653
+ animation: ledPulse 1.2s infinite;
647
654
  }
648
- .task-card-grid-compact .task-card-text {
649
- font-size: var(--text-sm);
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
- function withSafeLock(filePath, fn) {
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 function cosineSimilarity(a, b) {
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 { phrenErr, PhrenError, phrenOk, shellStateFile } from "./shared.js";
4
- import { withFileLock as withFileLockRaw } from "./shared-governance.js";
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phren/cli",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "Knowledge layer for AI agents. Phren learns and recalls.",
5
5
  "type": "module",
6
6
  "bin": {