@phren/cli 0.0.10 → 0.0.12
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 +11 -17
- package/mcp/dist/capabilities/cli.js +1 -1
- package/mcp/dist/capabilities/mcp.js +1 -1
- package/mcp/dist/capabilities/vscode.js +1 -1
- package/mcp/dist/capabilities/web-ui.js +1 -1
- package/mcp/dist/cli-actions.js +58 -71
- package/mcp/dist/cli-config.js +337 -131
- package/mcp/dist/cli-extract.js +3 -2
- package/mcp/dist/cli-govern.js +35 -63
- package/mcp/dist/cli-graph.js +19 -4
- package/mcp/dist/cli-hooks-globs.js +2 -1
- package/mcp/dist/cli-hooks-output.js +4 -4
- package/mcp/dist/cli-hooks-session.js +1 -1
- package/mcp/dist/cli-hooks.js +44 -35
- package/mcp/dist/cli-namespaces.js +15 -5
- package/mcp/dist/cli-search.js +2 -2
- package/mcp/dist/cli.js +1 -1
- package/mcp/dist/content-archive.js +23 -14
- package/mcp/dist/content-citation.js +13 -2
- package/mcp/dist/content-dedup.js +9 -9
- package/mcp/dist/content-learning.js +6 -4
- package/mcp/dist/content-metadata.js +10 -0
- package/mcp/dist/core-finding.js +1 -1
- package/mcp/dist/data-access.js +10 -31
- package/mcp/dist/data-tasks.js +5 -26
- package/mcp/dist/embedding.js +7 -8
- package/mcp/dist/entrypoint.js +133 -102
- package/mcp/dist/finding-impact.js +1 -32
- package/mcp/dist/finding-journal.js +1 -1
- package/mcp/dist/finding-lifecycle.js +2 -7
- package/mcp/dist/governance-locks.js +12 -5
- package/mcp/dist/governance-policy.js +156 -9
- package/mcp/dist/governance-scores.js +4 -10
- package/mcp/dist/hooks.js +62 -18
- package/mcp/dist/index.js +4 -4
- package/mcp/dist/init-config.js +4 -25
- package/mcp/dist/init-preferences.js +1 -1
- package/mcp/dist/init-setup.js +6 -55
- package/mcp/dist/init-shared.js +53 -1
- package/mcp/dist/init.js +191 -29
- package/mcp/dist/link-checksums.js +3 -2
- package/mcp/dist/link-context.js +2 -2
- package/mcp/dist/link-doctor.js +14 -57
- package/mcp/dist/link-skills.js +98 -12
- package/mcp/dist/link.js +16 -75
- 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 +12 -7
- package/mcp/dist/mcp-extract.js +2 -2
- package/mcp/dist/mcp-finding.js +16 -20
- package/mcp/dist/mcp-graph.js +12 -12
- package/mcp/dist/mcp-hooks.js +1 -1
- package/mcp/dist/mcp-ops.js +18 -18
- package/mcp/dist/mcp-search.js +11 -16
- package/mcp/dist/mcp-session.js +12 -2
- package/mcp/dist/memory-ui-assets.js +1 -36
- package/mcp/dist/memory-ui-graph.js +152 -50
- package/mcp/dist/memory-ui-page.js +30 -5
- package/mcp/dist/memory-ui-scripts.js +252 -63
- package/mcp/dist/memory-ui-server.js +115 -3
- package/mcp/dist/phren-core.js +2 -0
- package/mcp/dist/phren-paths.js +8 -9
- package/mcp/dist/proactivity.js +5 -5
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/project-config.js +64 -17
- package/mcp/dist/provider-adapters.js +1 -1
- package/mcp/dist/query-correlation.js +22 -19
- package/mcp/dist/session-checkpoints.js +14 -14
- package/mcp/dist/session-utils.js +3 -2
- package/mcp/dist/shared-data-utils.js +28 -0
- package/mcp/dist/shared-fragment-graph.js +22 -21
- package/mcp/dist/shared-governance.js +1 -1
- package/mcp/dist/shared-index.js +144 -105
- package/mcp/dist/shared-retrieval.js +21 -23
- package/mcp/dist/shared-search-fallback.js +15 -25
- package/mcp/dist/shared-sqljs.js +3 -2
- package/mcp/dist/shared.js +5 -6
- package/mcp/dist/shell-entry.js +1 -1
- package/mcp/dist/shell-input.js +63 -53
- package/mcp/dist/shell-palette.js +6 -1
- package/mcp/dist/shell-render.js +9 -5
- package/mcp/dist/shell-state-store.js +2 -5
- package/mcp/dist/shell-view.js +7 -6
- package/mcp/dist/shell.js +5 -55
- package/mcp/dist/skill-files.js +4 -10
- package/mcp/dist/skill-registry.js +3 -0
- package/mcp/dist/status.js +43 -21
- 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 +4 -4
- package/package.json +2 -3
- package/skills/docs.md +11 -11
- package/starter/README.md +1 -1
- package/starter/global/CLAUDE.md +2 -2
- package/starter/global/skills/audit.md +106 -0
- package/mcp/dist/cli-hooks-retrieval.js +0 -2
- package/mcp/dist/impact-scoring.js +0 -22
- package/mcp/dist/shared-paths.js +0 -1
|
@@ -27,11 +27,20 @@ export function renderGraphScript() {
|
|
|
27
27
|
/* ── colour & size maps ─────────────────────────────────────────────── */
|
|
28
28
|
/* Fixed colors for non-finding node types — phren purple/cyan palette */
|
|
29
29
|
var COLORS = {
|
|
30
|
-
project: '#
|
|
31
|
-
'task-active': '#10b981', 'task-queue': '#
|
|
32
|
-
entity: '#
|
|
30
|
+
project: '#9B7FD4',
|
|
31
|
+
'task-active': '#10b981', 'task-queue': '#7B68AE',
|
|
32
|
+
entity: '#D4892E', reference: '#6b8e7a',
|
|
33
33
|
other: '#B8A0D6'
|
|
34
34
|
};
|
|
35
|
+
|
|
36
|
+
/* Finding topic slug -> semantic color for visual hierarchy */
|
|
37
|
+
var FINDING_TOPIC_COLORS = {
|
|
38
|
+
architecture: '#3B82F6', debugging: '#EF4444', security: '#EF4444',
|
|
39
|
+
performance: '#F59E0B', testing: '#10B981', devops: '#10B981',
|
|
40
|
+
tooling: '#3B82F6', api: '#3B82F6', database: '#3B82F6',
|
|
41
|
+
frontend: '#8B5CF6', auth: '#EF4444', data: '#6B7280',
|
|
42
|
+
mobile: '#06B6D4', ai_ml: '#8B5CF6', general: '#6B7280'
|
|
43
|
+
};
|
|
35
44
|
var RADII = {
|
|
36
45
|
project: 18,
|
|
37
46
|
'task-active': 7, 'task-queue': 7,
|
|
@@ -53,7 +62,8 @@ export function renderGraphScript() {
|
|
|
53
62
|
waypointIdx: 0, /* current waypoint index */
|
|
54
63
|
tripDist: 0, /* total distance of current trip (for ease-in-out) */
|
|
55
64
|
tripProgress: 0, /* distance traveled so far this trip */
|
|
56
|
-
targetNodeId: null
|
|
65
|
+
targetNodeId: null, /* id of destination node (set in phrenMoveTo) */
|
|
66
|
+
targetNodeRef: null /* live reference to destination node object */
|
|
57
67
|
};
|
|
58
68
|
|
|
59
69
|
/* ── phren sprite image ─────────────────────────────────────────────── */
|
|
@@ -92,6 +102,14 @@ export function renderGraphScript() {
|
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
/* find shortest edge path from nearest node to target node via BFS */
|
|
105
|
+
/* look up a visible node by id — used to resolve live positions */
|
|
106
|
+
function phrenNodeById(id) {
|
|
107
|
+
for (var i = 0; i < visibleNodes.length; i++) {
|
|
108
|
+
if (visibleNodes[i].id === id) return visibleNodes[i];
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
95
113
|
function phrenFindEdgePath(targetNode) {
|
|
96
114
|
if (!targetNode || visibleNodes.length === 0) return [];
|
|
97
115
|
/* find nearest visible node to phren's current position */
|
|
@@ -134,14 +152,11 @@ export function renderGraphScript() {
|
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
154
|
if (!found) return [];
|
|
137
|
-
/* reconstruct path */
|
|
155
|
+
/* reconstruct path — store node IDs so positions are resolved live */
|
|
138
156
|
var path = [];
|
|
139
|
-
var nodeById = {};
|
|
140
|
-
for (var i = 0; i < visibleNodes.length; i++) nodeById[visibleNodes[i].id] = visibleNodes[i];
|
|
141
157
|
var cur = targetId;
|
|
142
158
|
while (cur !== undefined && cur !== startId) {
|
|
143
|
-
|
|
144
|
-
if (nd) path.unshift({ x: nd.x, y: nd.y });
|
|
159
|
+
path.unshift(cur);
|
|
145
160
|
cur = prev[cur];
|
|
146
161
|
}
|
|
147
162
|
/* cap path length to avoid very long walks */
|
|
@@ -160,9 +175,10 @@ export function renderGraphScript() {
|
|
|
160
175
|
phren.tripDist = Math.sqrt(tdx * tdx + tdy * tdy);
|
|
161
176
|
phren.tripProgress = 0;
|
|
162
177
|
/* try edge-walking if a target node is given */
|
|
163
|
-
phren.waypoints = targetNode ? phrenFindEdgePath(targetNode) : [];
|
|
178
|
+
phren.waypoints = targetNode ? phrenFindEdgePath(targetNode) : []; /* array of node IDs */
|
|
164
179
|
phren.waypointIdx = 0;
|
|
165
180
|
phren.targetNodeId = targetNode ? targetNode.id : null;
|
|
181
|
+
phren.targetNodeRef = targetNode || null; /* live reference for position tracking */
|
|
166
182
|
/* ensure animation loop is running so phren movement renders */
|
|
167
183
|
if (!animFrame) animFrame = requestAnimationFrame(tick);
|
|
168
184
|
}
|
|
@@ -183,11 +199,18 @@ export function renderGraphScript() {
|
|
|
183
199
|
function phrenUpdate(dt) {
|
|
184
200
|
phren.idlePhase += dt;
|
|
185
201
|
if (phren.moving) {
|
|
202
|
+
/* keep target coordinates synced with live node position (force layout moves nodes) */
|
|
203
|
+
if (phren.targetNodeRef && typeof phren.targetNodeRef.x === 'number') {
|
|
204
|
+
phren.targetX = phren.targetNodeRef.x;
|
|
205
|
+
phren.targetY = phren.targetNodeRef.y;
|
|
206
|
+
}
|
|
186
207
|
/* determine current move target: next waypoint or final destination */
|
|
187
208
|
var wx, wy;
|
|
188
209
|
if (phren.waypoints.length > 0 && phren.waypointIdx < phren.waypoints.length) {
|
|
189
|
-
|
|
190
|
-
|
|
210
|
+
/* waypoints are node IDs — resolve to live positions */
|
|
211
|
+
var wpNode = phrenNodeById(phren.waypoints[phren.waypointIdx]);
|
|
212
|
+
if (wpNode) { wx = wpNode.x; wy = wpNode.y; }
|
|
213
|
+
else { wx = phren.targetX; wy = phren.targetY; phren.waypointIdx = phren.waypoints.length; }
|
|
191
214
|
} else {
|
|
192
215
|
wx = phren.targetX;
|
|
193
216
|
wy = phren.targetY;
|
|
@@ -333,9 +356,11 @@ export function renderGraphScript() {
|
|
|
333
356
|
}
|
|
334
357
|
|
|
335
358
|
function topicGroupColor(group) {
|
|
336
|
-
/* group is 'topic:<slug>' for findings */
|
|
359
|
+
/* group is 'topic:<slug>' for findings — use semantic color if available */
|
|
337
360
|
if (typeof group === 'string' && group.indexOf('topic:') === 0) {
|
|
338
|
-
|
|
361
|
+
var slug = group.slice(6);
|
|
362
|
+
if (FINDING_TOPIC_COLORS[slug]) return FINDING_TOPIC_COLORS[slug];
|
|
363
|
+
return topicSlugToColor(slug);
|
|
339
364
|
}
|
|
340
365
|
return COLORS[group] || COLORS.other;
|
|
341
366
|
}
|
|
@@ -371,9 +396,13 @@ export function renderGraphScript() {
|
|
|
371
396
|
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
|
|
372
397
|
|
|
373
398
|
function nodeRadius(n) {
|
|
374
|
-
|
|
375
|
-
if (
|
|
376
|
-
|
|
399
|
+
var base;
|
|
400
|
+
if (n.group === 'entity') base = Math.min(6 + (n.refCount || 0), 16);
|
|
401
|
+
else if (typeof n.group === 'string' && n.group.indexOf('topic:') === 0) base = RADII.other;
|
|
402
|
+
else base = RADII[n.group] || RADII.other;
|
|
403
|
+
/* size boost by connection count */
|
|
404
|
+
var conns = n._connectionCount || 0;
|
|
405
|
+
return base + Math.min(conns * 0.5, 6);
|
|
377
406
|
}
|
|
378
407
|
|
|
379
408
|
function nodeColor(n) { return topicGroupColor(n.group); }
|
|
@@ -865,6 +894,14 @@ export function renderGraphScript() {
|
|
|
865
894
|
}
|
|
866
895
|
}
|
|
867
896
|
|
|
897
|
+
/* compute connection count per node for size scaling */
|
|
898
|
+
for (var i = 0; i < visibleNodes.length; i++) visibleNodes[i]._connectionCount = 0;
|
|
899
|
+
for (var i = 0; i < visibleLinks.length; i++) {
|
|
900
|
+
var lk = visibleLinks[i];
|
|
901
|
+
if (lk._source) lk._source._connectionCount = (lk._source._connectionCount || 0) + 1;
|
|
902
|
+
if (lk._target) lk._target._connectionCount = (lk._target._connectionCount || 0) + 1;
|
|
903
|
+
}
|
|
904
|
+
|
|
868
905
|
updateLegend();
|
|
869
906
|
/* only restart simulation if node count changed significantly */
|
|
870
907
|
var oldCount = _prevVisibleCount || 0;
|
|
@@ -940,14 +977,26 @@ export function renderGraphScript() {
|
|
|
940
977
|
var sMatch = ((lk._source.label || '').toLowerCase().indexOf(_sq) !== -1 || (lk._source.fullLabel || '').toLowerCase().indexOf(_sq) !== -1);
|
|
941
978
|
var tMatch = ((lk._target.label || '').toLowerCase().indexOf(_sq) !== -1 || (lk._target.fullLabel || '').toLowerCase().indexOf(_sq) !== -1);
|
|
942
979
|
ctx.beginPath();
|
|
943
|
-
ctx.strokeStyle = (sMatch || tMatch) ? 'rgba(
|
|
980
|
+
ctx.strokeStyle = (sMatch || tMatch) ? 'rgba(255,255,255,0.20)' : 'rgba(255,255,255,0.04)';
|
|
981
|
+
ctx.moveTo(lk._source.x, lk._source.y);
|
|
982
|
+
ctx.lineTo(lk._target.x, lk._target.y);
|
|
983
|
+
ctx.stroke();
|
|
984
|
+
}
|
|
985
|
+
} else if (selectedNode) {
|
|
986
|
+
/* When a node is selected, highlight connected edges */
|
|
987
|
+
for (var i = 0; i < links.length; i++) {
|
|
988
|
+
var lk = links[i];
|
|
989
|
+
if (!lk._source || !lk._target) continue;
|
|
990
|
+
var isConnected = (lk._source === selectedNode || lk._target === selectedNode);
|
|
991
|
+
ctx.beginPath();
|
|
992
|
+
ctx.strokeStyle = isConnected ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.08)';
|
|
944
993
|
ctx.moveTo(lk._source.x, lk._source.y);
|
|
945
994
|
ctx.lineTo(lk._target.x, lk._target.y);
|
|
946
995
|
ctx.stroke();
|
|
947
996
|
}
|
|
948
997
|
} else {
|
|
949
998
|
ctx.beginPath();
|
|
950
|
-
ctx.strokeStyle = 'rgba(
|
|
999
|
+
ctx.strokeStyle = 'rgba(255,255,255,0.08)';
|
|
951
1000
|
for (var i = 0; i < links.length; i++) {
|
|
952
1001
|
var lk = links[i];
|
|
953
1002
|
if (!lk._source || !lk._target) continue;
|
|
@@ -1052,35 +1101,69 @@ export function renderGraphScript() {
|
|
|
1052
1101
|
ctx.setLineDash([]);
|
|
1053
1102
|
}
|
|
1054
1103
|
|
|
1055
|
-
/* 3. nodes
|
|
1056
|
-
|
|
1104
|
+
/* 3. nodes with glow, selection dimming, and visual hierarchy */
|
|
1105
|
+
/* Build set of nodes connected to selected node for dimming */
|
|
1106
|
+
var _connectedToSelected = null;
|
|
1107
|
+
if (selectedNode) {
|
|
1108
|
+
_connectedToSelected = {};
|
|
1109
|
+
_connectedToSelected[selectedNode.id] = true;
|
|
1110
|
+
for (var i = 0; i < links.length; i++) {
|
|
1111
|
+
var lk = links[i];
|
|
1112
|
+
if (!lk._source || !lk._target) continue;
|
|
1113
|
+
if (lk._source === selectedNode) _connectedToSelected[lk._target.id] = true;
|
|
1114
|
+
if (lk._target === selectedNode) _connectedToSelected[lk._source.id] = true;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1057
1118
|
for (var i = 0; i < nodes.length; i++) {
|
|
1058
|
-
var
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
var
|
|
1068
|
-
var
|
|
1069
|
-
var
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1119
|
+
var nd = nodes[i];
|
|
1120
|
+
var col = nodeColor(nd);
|
|
1121
|
+
var r = nodeRadius(nd);
|
|
1122
|
+
var mult = qualityMultiplier(nd);
|
|
1123
|
+
var opacity = 0.3 + (mult - 0.2) * (0.7 / 1.3);
|
|
1124
|
+
opacity = clamp(opacity, 0.3, 1.0);
|
|
1125
|
+
|
|
1126
|
+
/* dim non-matching nodes when search is active */
|
|
1127
|
+
if (searchQuery) {
|
|
1128
|
+
var sq = searchQuery.toLowerCase();
|
|
1129
|
+
var sl = (nd.label || '').toLowerCase();
|
|
1130
|
+
var sf = (nd.fullLabel || '').toLowerCase();
|
|
1131
|
+
if (sl.indexOf(sq) === -1 && sf.indexOf(sq) === -1) opacity = 0.1;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/* dim non-connected nodes when a node is selected */
|
|
1135
|
+
if (_connectedToSelected && !_connectedToSelected[nd.id]) {
|
|
1136
|
+
opacity *= 0.4;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
ctx.globalAlpha = opacity;
|
|
1140
|
+
|
|
1141
|
+
/* node glow based on type */
|
|
1142
|
+
var glowBlur = 0;
|
|
1143
|
+
if (nd === selectedNode) {
|
|
1144
|
+
glowBlur = 30;
|
|
1145
|
+
ctx.shadowColor = 'rgba(255,255,255,0.6)';
|
|
1146
|
+
} else if (nd.group === 'project') {
|
|
1147
|
+
glowBlur = 20;
|
|
1148
|
+
ctx.shadowColor = colorWithAlpha(col, 0.5);
|
|
1149
|
+
} else if (nd.group === 'entity') {
|
|
1150
|
+
glowBlur = 12;
|
|
1151
|
+
ctx.shadowColor = 'rgba(212,137,46,0.4)';
|
|
1152
|
+
} else if (typeof nd.group === 'string' && nd.group.indexOf('topic:') === 0) {
|
|
1153
|
+
glowBlur = 6;
|
|
1154
|
+
ctx.shadowColor = colorWithAlpha(col, 0.25);
|
|
1083
1155
|
}
|
|
1156
|
+
if (glowBlur > 0) {
|
|
1157
|
+
ctx.shadowBlur = glowBlur / scale;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
ctx.beginPath();
|
|
1161
|
+
ctx.arc(nd.x, nd.y, r, 0, Math.PI * 2);
|
|
1162
|
+
ctx.fillStyle = col;
|
|
1163
|
+
ctx.fill();
|
|
1164
|
+
|
|
1165
|
+
ctx.shadowBlur = 0;
|
|
1166
|
+
ctx.shadowColor = 'transparent';
|
|
1084
1167
|
}
|
|
1085
1168
|
ctx.globalAlpha = 1.0;
|
|
1086
1169
|
|
|
@@ -1155,13 +1238,17 @@ export function renderGraphScript() {
|
|
|
1155
1238
|
}
|
|
1156
1239
|
}
|
|
1157
1240
|
|
|
1158
|
-
/* 6. selection highlight */
|
|
1241
|
+
/* 6. selection highlight with bloom */
|
|
1159
1242
|
if (selectedNode) {
|
|
1243
|
+
ctx.save();
|
|
1244
|
+
ctx.shadowColor = 'rgba(255,255,255,0.5)';
|
|
1245
|
+
ctx.shadowBlur = 30 / scale;
|
|
1160
1246
|
ctx.beginPath();
|
|
1161
1247
|
ctx.arc(selectedNode.x, selectedNode.y, nodeRadius(selectedNode) + 5, 0, Math.PI * 2);
|
|
1162
|
-
ctx.strokeStyle = '
|
|
1248
|
+
ctx.strokeStyle = 'rgba(255,255,255,0.7)';
|
|
1163
1249
|
ctx.lineWidth = 2.5 / scale;
|
|
1164
1250
|
ctx.stroke();
|
|
1251
|
+
ctx.restore();
|
|
1165
1252
|
}
|
|
1166
1253
|
|
|
1167
1254
|
/* 6c. phren character */
|
|
@@ -1407,17 +1494,22 @@ export function renderGraphScript() {
|
|
|
1407
1494
|
|
|
1408
1495
|
/* ── interaction ────────────────────────────────────────────────────── */
|
|
1409
1496
|
function setupInteraction() {
|
|
1497
|
+
var _mouseDownX = 0, _mouseDownY = 0, _mouseDownHit = null;
|
|
1498
|
+
var CLICK_THRESHOLD = 5; /* px — distinguish click from drag */
|
|
1499
|
+
|
|
1410
1500
|
canvas.addEventListener('mousedown', function(e) {
|
|
1411
1501
|
var rect = canvas.getBoundingClientRect();
|
|
1412
1502
|
var mx = e.clientX - rect.left;
|
|
1413
1503
|
var my = e.clientY - rect.top;
|
|
1504
|
+
_mouseDownX = mx;
|
|
1505
|
+
_mouseDownY = my;
|
|
1414
1506
|
var hit = hitTest(mx, my);
|
|
1507
|
+
_mouseDownHit = hit;
|
|
1415
1508
|
if (hit) {
|
|
1416
1509
|
dragging = hit;
|
|
1417
1510
|
dragPullDepths = collectConnectedDepths(hit, 2);
|
|
1418
1511
|
dragOffX = (mx - panX) / scale - hit.x;
|
|
1419
1512
|
dragOffY = (my - panY) / scale - hit.y;
|
|
1420
|
-
renderGraphDetails(hit);
|
|
1421
1513
|
} else {
|
|
1422
1514
|
panning = true;
|
|
1423
1515
|
panStartX = mx - panX;
|
|
@@ -1465,14 +1557,24 @@ export function renderGraphScript() {
|
|
|
1465
1557
|
}
|
|
1466
1558
|
});
|
|
1467
1559
|
|
|
1468
|
-
canvas.addEventListener('mouseup', function() {
|
|
1560
|
+
canvas.addEventListener('mouseup', function(e) {
|
|
1561
|
+
var rect = canvas.getBoundingClientRect();
|
|
1562
|
+
var mx = e.clientX - rect.left;
|
|
1563
|
+
var my = e.clientY - rect.top;
|
|
1564
|
+
var dragDist = Math.sqrt((_mouseDownX - mx) * (_mouseDownX - mx) + (_mouseDownY - my) * (_mouseDownY - my));
|
|
1469
1565
|
if (dragging) {
|
|
1566
|
+
var clickedNode = dragging;
|
|
1470
1567
|
dragging = null;
|
|
1471
1568
|
dragPullDepths = null;
|
|
1472
1569
|
/* restart simulation so gravity pulls nodes back into place */
|
|
1473
1570
|
alpha = Math.max(alpha, 0.3);
|
|
1571
|
+
/* only select + walk phren if this was a click, not a drag */
|
|
1572
|
+
if (dragDist < CLICK_THRESHOLD && _mouseDownHit && clickedNode === _mouseDownHit) {
|
|
1573
|
+
renderGraphDetails(_mouseDownHit);
|
|
1574
|
+
}
|
|
1474
1575
|
}
|
|
1475
1576
|
panning = false;
|
|
1577
|
+
_mouseDownHit = null;
|
|
1476
1578
|
});
|
|
1477
1579
|
|
|
1478
1580
|
canvas.addEventListener('mouseleave', function() {
|
|
@@ -2,7 +2,7 @@ import { WEB_UI_STYLES, renderWebUiScript } from "./memory-ui-assets.js";
|
|
|
2
2
|
import { renderGraphScript } from "./memory-ui-graph.js";
|
|
3
3
|
import { readSyncSnapshot } from "./memory-ui-data.js";
|
|
4
4
|
import { PROJECT_REFERENCE_UI_STYLES, SETTINGS_TAB_UI_STYLES, TASK_UI_STYLES } from "./memory-ui-styles.js";
|
|
5
|
-
import { renderSkillUiEnhancementScript, renderProjectReferenceEnhancementScript, renderReviewQueueEditSyncScript, renderTasksAndSettingsScript, renderSearchScript, renderEventWiringScript, } from "./memory-ui-scripts.js";
|
|
5
|
+
import { renderSharedWebUiHelpers, renderSkillUiEnhancementScript, renderProjectReferenceEnhancementScript, renderReviewQueueEditSyncScript, renderTasksAndSettingsScript, renderSearchScript, renderEventWiringScript, } from "./memory-ui-scripts.js";
|
|
6
6
|
function h(s) {
|
|
7
7
|
return s
|
|
8
8
|
.replace(/&/g, "&")
|
|
@@ -21,7 +21,6 @@ export function renderWebUiPage(phrenPath, authToken, nonce) {
|
|
|
21
21
|
<link rel="preconnect" href="https://fonts.bunny.net" />
|
|
22
22
|
<link href="https://fonts.bunny.net/css?family=inter:400,500,600,700&display=swap" rel="stylesheet" />
|
|
23
23
|
<title>phren</title>
|
|
24
|
-
<script${nonceAttr} src="https://cdn.jsdelivr.net/npm/marked@12/marked.min.js"></script>
|
|
25
24
|
<style>
|
|
26
25
|
${WEB_UI_STYLES}
|
|
27
26
|
${PROJECT_REFERENCE_UI_STYLES}
|
|
@@ -104,9 +103,9 @@ ${TASK_UI_STYLES}
|
|
|
104
103
|
<dt>What is the Review Queue?</dt>
|
|
105
104
|
<dd>Fragments flagged by governance for human review. Items accumulate here when <code>phren maintain govern</code> is run.</dd>
|
|
106
105
|
<dt>Can I approve, reject, or edit items here?</dt>
|
|
107
|
-
<dd>
|
|
106
|
+
<dd>Yes. Each review card has <strong>Approve</strong>, <strong>Reject</strong>, and <strong>Edit</strong> buttons. Approve accepts the fragment, Reject removes it, and Edit lets you revise the text before accepting. You can also use batch actions to approve or reject multiple items at once.</dd>
|
|
108
107
|
<dt>How do I clear items?</dt>
|
|
109
|
-
<dd>
|
|
108
|
+
<dd>Approve or reject items directly in the UI, or use maintenance flows such as <code>phren maintain prune</code>.</dd>
|
|
110
109
|
<dt>Is this automatic?</dt>
|
|
111
110
|
<dd>No. Agents do not auto-accept review-queue items.</dd>
|
|
112
111
|
<dt>How do items get here?</dt>
|
|
@@ -116,7 +115,7 @@ ${TASK_UI_STYLES}
|
|
|
116
115
|
</dl>
|
|
117
116
|
</details>
|
|
118
117
|
|
|
119
|
-
<p style="font-size:var(--text-sm);color:var(--muted);margin-bottom:12px;letter-spacing:-0.01em">Fragments flagged for review.
|
|
118
|
+
<p style="font-size:var(--text-sm);color:var(--muted);margin-bottom:12px;letter-spacing:-0.01em">Fragments flagged for review. Approve, reject, or edit items directly from this tab.</p>
|
|
120
119
|
|
|
121
120
|
<div id="review-summary-banner" style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px;align-items:center"></div>
|
|
122
121
|
|
|
@@ -267,6 +266,17 @@ ${TASK_UI_STYLES}
|
|
|
267
266
|
<div id="tab-settings" class="tab-content">
|
|
268
267
|
<div class="settings-shell">
|
|
269
268
|
<div id="settings-status-inline" class="settings-status-inline" aria-live="polite"></div>
|
|
269
|
+
<section class="settings-section" style="border-top:3px solid color-mix(in srgb, var(--cyan) 45%, var(--border))">
|
|
270
|
+
<div class="settings-section-header" style="display:flex;align-items:center;justify-content:space-between;gap:16px">
|
|
271
|
+
<span>Scope</span>
|
|
272
|
+
<select id="settings-project-select" style="border:1px solid var(--border);border-radius:var(--radius-sm);padding:6px 10px;background:var(--surface);color:var(--ink);font-size:var(--text-sm);font-family:var(--font)">
|
|
273
|
+
<option value="">Global (all projects)</option>
|
|
274
|
+
</select>
|
|
275
|
+
</div>
|
|
276
|
+
<div class="settings-section-body" style="padding:12px 18px">
|
|
277
|
+
<div id="settings-scope-note" style="font-size:var(--text-sm);color:var(--muted)">Showing global settings. Select a project to view and edit per-project overrides.</div>
|
|
278
|
+
</div>
|
|
279
|
+
</section>
|
|
270
280
|
<section class="settings-section settings-section-findings">
|
|
271
281
|
<div class="settings-section-header">Findings</div>
|
|
272
282
|
<div class="settings-section-body">
|
|
@@ -279,6 +289,18 @@ ${TASK_UI_STYLES}
|
|
|
279
289
|
<div id="settings-behavior" style="color:var(--muted)">Loading...</div>
|
|
280
290
|
</div>
|
|
281
291
|
</section>
|
|
292
|
+
<section class="settings-section" style="border-top:3px solid color-mix(in srgb, var(--warning) 45%, var(--border))">
|
|
293
|
+
<div class="settings-section-header">Retention Policy</div>
|
|
294
|
+
<div class="settings-section-body">
|
|
295
|
+
<div id="settings-retention" style="color:var(--muted)">Loading...</div>
|
|
296
|
+
</div>
|
|
297
|
+
</section>
|
|
298
|
+
<section class="settings-section" style="border-top:3px solid color-mix(in srgb, var(--success) 45%, var(--border))">
|
|
299
|
+
<div class="settings-section-header">Workflow Policy</div>
|
|
300
|
+
<div class="settings-section-body">
|
|
301
|
+
<div id="settings-workflow" style="color:var(--muted)">Loading...</div>
|
|
302
|
+
</div>
|
|
303
|
+
</section>
|
|
282
304
|
<section class="settings-section settings-section-integrations">
|
|
283
305
|
<div class="settings-section-header">Integrations</div>
|
|
284
306
|
<div class="settings-section-body">
|
|
@@ -308,6 +330,9 @@ ${renderGraphScript()}
|
|
|
308
330
|
${renderReviewQueueEditSyncScript()}
|
|
309
331
|
</script>
|
|
310
332
|
<script${nonceAttr}>
|
|
333
|
+
${renderSharedWebUiHelpers(h(authToken || ""))}
|
|
334
|
+
</script>
|
|
335
|
+
<script${nonceAttr}>
|
|
311
336
|
${renderSkillUiEnhancementScript(h(authToken || ""))}
|
|
312
337
|
</script>
|
|
313
338
|
<script${nonceAttr}>
|