@faviovazquez/deliberate 0.1.0

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.
@@ -0,0 +1,339 @@
1
+ // deliberate visual companion -- client-side helper
2
+ // Handles selection, event recording, and interactive features
3
+
4
+ (function() {
5
+ 'use strict';
6
+
7
+ var SERVER_URL = window.location.origin;
8
+ var selections = {};
9
+ var selectionCount = 0;
10
+
11
+ // Selection handling
12
+ window.toggleSelect = function(el) {
13
+ var container = el.closest('.options, .cards');
14
+ var isMultiSelect = container && container.hasAttribute('data-multiselect');
15
+ var choice = el.getAttribute('data-choice');
16
+
17
+ if (!isMultiSelect) {
18
+ // Single select: deselect all others in this container
19
+ var siblings = container ? container.querySelectorAll('.option, .card') : [];
20
+ for (var i = 0; i < siblings.length; i++) {
21
+ if (siblings[i] !== el) {
22
+ siblings[i].classList.remove('selected');
23
+ var sibChoice = siblings[i].getAttribute('data-choice');
24
+ if (sibChoice && selections[sibChoice]) {
25
+ delete selections[sibChoice];
26
+ selectionCount--;
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ // Toggle this element
33
+ var isSelected = el.classList.toggle('selected');
34
+
35
+ if (isSelected) {
36
+ selections[choice] = true;
37
+ selectionCount++;
38
+ } else {
39
+ delete selections[choice];
40
+ selectionCount--;
41
+ }
42
+
43
+ // Update selection bar
44
+ updateSelectionBar();
45
+
46
+ // Record event
47
+ recordEvent({
48
+ type: 'click',
49
+ choice: choice,
50
+ text: extractText(el),
51
+ selected: isSelected,
52
+ timestamp: Math.floor(Date.now() / 1000)
53
+ });
54
+ };
55
+
56
+ function extractText(el) {
57
+ var h3 = el.querySelector('h3');
58
+ var p = el.querySelector('p');
59
+ var parts = [];
60
+ if (h3) parts.push(h3.textContent.trim());
61
+ if (p) parts.push(p.textContent.trim());
62
+ return parts.join(' - ') || el.textContent.trim().substring(0, 100);
63
+ }
64
+
65
+ function updateSelectionBar() {
66
+ var bar = document.getElementById('selection-bar');
67
+ var text = document.getElementById('selection-text');
68
+ if (!bar || !text) return;
69
+
70
+ if (selectionCount > 0) {
71
+ bar.classList.add('visible');
72
+ text.textContent = selectionCount + ' selected';
73
+ } else {
74
+ bar.classList.remove('visible');
75
+ }
76
+ }
77
+
78
+ function recordEvent(event) {
79
+ try {
80
+ var xhr = new XMLHttpRequest();
81
+ xhr.open('POST', SERVER_URL + '/events', true);
82
+ xhr.setRequestHeader('Content-Type', 'application/json');
83
+ xhr.send(JSON.stringify(event));
84
+ } catch (e) {
85
+ // Silent fail -- events are supplementary
86
+ }
87
+ }
88
+
89
+ // Tooltip system for Canvas visualizations
90
+ var tooltip = document.getElementById('tooltip');
91
+
92
+ window.showTooltip = function(x, y, content) {
93
+ if (!tooltip) return;
94
+ tooltip.innerHTML = content;
95
+ tooltip.style.left = (x + 12) + 'px';
96
+ tooltip.style.top = (y + 12) + 'px';
97
+ tooltip.classList.add('visible');
98
+ };
99
+
100
+ window.hideTooltip = function() {
101
+ if (!tooltip) return;
102
+ tooltip.classList.remove('visible');
103
+ };
104
+
105
+ // Auto-refresh: poll for new content every 2 seconds
106
+ var currentFile = '{{FILENAME}}';
107
+ var refreshInterval = setInterval(function() {
108
+ try {
109
+ var xhr = new XMLHttpRequest();
110
+ xhr.open('GET', SERVER_URL + '/status', true);
111
+ xhr.timeout = 2000;
112
+ xhr.onload = function() {
113
+ if (xhr.status === 200) {
114
+ var data = JSON.parse(xhr.responseText);
115
+ var statusEl = document.getElementById('status-text');
116
+ if (statusEl) statusEl.textContent = 'connected';
117
+
118
+ if (data.newest && data.newest !== currentFile) {
119
+ // New content available, reload
120
+ window.location.reload();
121
+ }
122
+ }
123
+ };
124
+ xhr.onerror = function() {
125
+ var statusEl = document.getElementById('status-text');
126
+ if (statusEl) statusEl.textContent = 'disconnected';
127
+ };
128
+ xhr.ontimeout = function() {
129
+ var statusEl = document.getElementById('status-text');
130
+ if (statusEl) statusEl.textContent = 'reconnecting...';
131
+ };
132
+ xhr.send();
133
+ } catch (e) {
134
+ // Silent fail
135
+ }
136
+ }, 2000);
137
+
138
+ // Utility: agent color lookup
139
+ window.AGENT_COLORS = {
140
+ 'assumption-breaker': '#ff6b6b',
141
+ 'first-principles': '#ff9f43',
142
+ 'classifier': '#feca57',
143
+ 'formal-verifier': '#48dbfb',
144
+ 'bias-detector': '#a55eea',
145
+ 'systems-thinker': '#0abde3',
146
+ 'resilience-anchor': '#c8d6e5',
147
+ 'adversarial-strategist': '#ee5a24',
148
+ 'emergence-reader': '#686de0',
149
+ 'incentive-mapper': '#b33939',
150
+ 'pragmatic-builder': '#f6e58d',
151
+ 'reframer': '#be2edd',
152
+ 'risk-analyst': '#535c68',
153
+ 'inverter': '#f9ca24',
154
+ 'ml-intuition': '#6ab04c',
155
+ 'safety-frontier': '#7ed6df',
156
+ 'design-lens': '#dfe6e9'
157
+ };
158
+
159
+ window.getAgentColor = function(agentName) {
160
+ return window.AGENT_COLORS[agentName] || '#8b949e';
161
+ };
162
+
163
+ // Force-directed graph utilities for idea maps and agent position maps
164
+ window.ForceGraph = function(canvas, options) {
165
+ var ctx = canvas.getContext('2d');
166
+ var nodes = options.nodes || [];
167
+ var edges = options.edges || [];
168
+ var onHover = options.onHover || null;
169
+ var onClick = options.onClick || null;
170
+ var width = canvas.width;
171
+ var height = canvas.height;
172
+ var dpr = window.devicePixelRatio || 1;
173
+
174
+ // Set canvas for high DPI
175
+ canvas.width = width * dpr;
176
+ canvas.height = height * dpr;
177
+ canvas.style.width = width + 'px';
178
+ canvas.style.height = height + 'px';
179
+ ctx.scale(dpr, dpr);
180
+
181
+ // Initialize positions
182
+ for (var i = 0; i < nodes.length; i++) {
183
+ if (nodes[i].x === undefined) nodes[i].x = width / 2 + (Math.random() - 0.5) * 200;
184
+ if (nodes[i].y === undefined) nodes[i].y = height / 2 + (Math.random() - 0.5) * 200;
185
+ nodes[i].vx = 0;
186
+ nodes[i].vy = 0;
187
+ }
188
+
189
+ function simulate() {
190
+ var k = 0.01; // spring constant
191
+ var repulsion = 5000;
192
+ var damping = 0.85;
193
+ var centerForce = 0.005;
194
+
195
+ // Repulsion between all nodes
196
+ for (var i = 0; i < nodes.length; i++) {
197
+ for (var j = i + 1; j < nodes.length; j++) {
198
+ var dx = nodes[j].x - nodes[i].x;
199
+ var dy = nodes[j].y - nodes[i].y;
200
+ var dist = Math.sqrt(dx * dx + dy * dy) || 1;
201
+ var force = repulsion / (dist * dist);
202
+ var fx = (dx / dist) * force;
203
+ var fy = (dy / dist) * force;
204
+ nodes[i].vx -= fx;
205
+ nodes[i].vy -= fy;
206
+ nodes[j].vx += fx;
207
+ nodes[j].vy += fy;
208
+ }
209
+ }
210
+
211
+ // Attraction along edges
212
+ for (var e = 0; e < edges.length; e++) {
213
+ var src = edges[e].source;
214
+ var tgt = edges[e].target;
215
+ var dx = tgt.x - src.x;
216
+ var dy = tgt.y - src.y;
217
+ var dist = Math.sqrt(dx * dx + dy * dy) || 1;
218
+ var force = k * (dist - 120);
219
+ var fx = (dx / dist) * force;
220
+ var fy = (dy / dist) * force;
221
+ src.vx += fx;
222
+ src.vy += fy;
223
+ tgt.vx -= fx;
224
+ tgt.vy -= fy;
225
+ }
226
+
227
+ // Center gravity
228
+ for (var i = 0; i < nodes.length; i++) {
229
+ nodes[i].vx += (width / 2 - nodes[i].x) * centerForce;
230
+ nodes[i].vy += (height / 2 - nodes[i].y) * centerForce;
231
+ nodes[i].vx *= damping;
232
+ nodes[i].vy *= damping;
233
+ nodes[i].x += nodes[i].vx;
234
+ nodes[i].y += nodes[i].vy;
235
+
236
+ // Bounds
237
+ var r = nodes[i].radius || 20;
238
+ nodes[i].x = Math.max(r, Math.min(width - r, nodes[i].x));
239
+ nodes[i].y = Math.max(r, Math.min(height - r, nodes[i].y));
240
+ }
241
+ }
242
+
243
+ function draw() {
244
+ ctx.clearRect(0, 0, width, height);
245
+
246
+ // Draw edges
247
+ for (var e = 0; e < edges.length; e++) {
248
+ var edge = edges[e];
249
+ ctx.beginPath();
250
+ ctx.moveTo(edge.source.x, edge.source.y);
251
+ ctx.lineTo(edge.target.x, edge.target.y);
252
+ ctx.strokeStyle = edge.color || 'rgba(139,148,158,0.3)';
253
+ ctx.lineWidth = edge.width || 1;
254
+ if (edge.dashed) ctx.setLineDash([6, 4]);
255
+ else ctx.setLineDash([]);
256
+ ctx.stroke();
257
+ ctx.setLineDash([]);
258
+ }
259
+
260
+ // Draw nodes
261
+ for (var i = 0; i < nodes.length; i++) {
262
+ var node = nodes[i];
263
+ var r = node.radius || 20;
264
+
265
+ ctx.beginPath();
266
+ ctx.arc(node.x, node.y, r, 0, Math.PI * 2);
267
+ ctx.fillStyle = node.color || '#8b949e';
268
+ ctx.globalAlpha = node.opacity || 0.9;
269
+ ctx.fill();
270
+ ctx.globalAlpha = 1;
271
+
272
+ if (node.selected) {
273
+ ctx.strokeStyle = '#58a6ff';
274
+ ctx.lineWidth = 3;
275
+ ctx.stroke();
276
+ }
277
+
278
+ // Label
279
+ if (node.label) {
280
+ ctx.fillStyle = '#e6edf3';
281
+ ctx.font = (node.fontSize || 11) + 'px -apple-system, system-ui, sans-serif';
282
+ ctx.textAlign = 'center';
283
+ ctx.textBaseline = 'middle';
284
+ ctx.fillText(node.label, node.x, node.y + r + 14);
285
+ }
286
+ }
287
+ }
288
+
289
+ // Mouse interaction
290
+ var hoveredNode = null;
291
+
292
+ canvas.addEventListener('mousemove', function(e) {
293
+ var rect = canvas.getBoundingClientRect();
294
+ var mx = e.clientX - rect.left;
295
+ var my = e.clientY - rect.top;
296
+ hoveredNode = null;
297
+
298
+ for (var i = 0; i < nodes.length; i++) {
299
+ var dx = mx - nodes[i].x;
300
+ var dy = my - nodes[i].y;
301
+ var r = nodes[i].radius || 20;
302
+ if (dx * dx + dy * dy < r * r) {
303
+ hoveredNode = nodes[i];
304
+ canvas.style.cursor = 'pointer';
305
+ if (onHover) onHover(nodes[i], e.clientX, e.clientY);
306
+ return;
307
+ }
308
+ }
309
+ canvas.style.cursor = 'default';
310
+ hideTooltip();
311
+ });
312
+
313
+ canvas.addEventListener('click', function(e) {
314
+ if (hoveredNode && onClick) {
315
+ onClick(hoveredNode);
316
+ recordEvent({
317
+ type: 'node-click',
318
+ nodeId: hoveredNode.id,
319
+ nodeLabel: hoveredNode.label,
320
+ timestamp: Math.floor(Date.now() / 1000)
321
+ });
322
+ }
323
+ });
324
+
325
+ // Run simulation
326
+ var iterations = 100;
327
+ for (var step = 0; step < iterations; step++) {
328
+ simulate();
329
+ }
330
+ draw();
331
+
332
+ return {
333
+ nodes: nodes,
334
+ edges: edges,
335
+ redraw: draw,
336
+ simulate: simulate
337
+ };
338
+ };
339
+ })();
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # deliberate -- Release script
5
+ # Usage: ./scripts/release.sh [patch|minor|major]
6
+ #
7
+ # This script:
8
+ # 1. Bumps the version in package.json
9
+ # 2. Updates CHANGELOG.md with the new version header
10
+ # 3. Commits the version bump
11
+ # 4. Creates a git tag
12
+ # 5. Pushes to origin with tags
13
+ # 6. Creates a GitHub release
14
+ # 7. Publishes to npm
15
+
16
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
17
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
18
+ BUMP_TYPE="${1:-patch}"
19
+
20
+ if [[ "$BUMP_TYPE" != "patch" && "$BUMP_TYPE" != "minor" && "$BUMP_TYPE" != "major" ]]; then
21
+ echo "Usage: release.sh [patch|minor|major]"
22
+ echo " patch: 0.1.0 -> 0.1.1 (bug fixes)"
23
+ echo " minor: 0.1.0 -> 0.2.0 (new features, new agents)"
24
+ echo " major: 0.1.0 -> 1.0.0 (breaking changes)"
25
+ exit 1
26
+ fi
27
+
28
+ cd "$ROOT_DIR"
29
+
30
+ # Pre-flight checks
31
+ echo "=== Pre-flight checks ==="
32
+
33
+ if [[ -n "$(git status --porcelain)" ]]; then
34
+ echo "ERROR: Working directory is not clean. Commit or stash changes first."
35
+ git status --short
36
+ exit 1
37
+ fi
38
+
39
+ if ! command -v gh >/dev/null 2>&1; then
40
+ echo "ERROR: GitHub CLI (gh) is required. Install: https://cli.github.com"
41
+ exit 1
42
+ fi
43
+
44
+ if ! command -v npm >/dev/null 2>&1; then
45
+ echo "ERROR: npm is required."
46
+ exit 1
47
+ fi
48
+
49
+ CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
50
+ if [[ "$CURRENT_BRANCH" != "main" ]]; then
51
+ echo "WARNING: You are on branch '$CURRENT_BRANCH', not 'main'."
52
+ read -p "Continue? [y/N] " confirm
53
+ [[ "$confirm" == "y" || "$confirm" == "Y" ]] || exit 1
54
+ fi
55
+
56
+ # Get current and new version
57
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
58
+ echo "Current version: $CURRENT_VERSION"
59
+
60
+ # Bump version (npm version updates package.json and creates git tag)
61
+ NEW_VERSION=$(npm version "$BUMP_TYPE" --no-git-tag-version | sed 's/^v//')
62
+ echo "New version: $NEW_VERSION"
63
+
64
+ # Update CHANGELOG.md
65
+ DATE=$(date +%Y-%m-%d)
66
+ CHANGELOG_ENTRY="## [$NEW_VERSION] - $DATE"
67
+
68
+ if grep -q "## \[Unreleased\]" CHANGELOG.md 2>/dev/null; then
69
+ # Replace [Unreleased] with the new version
70
+ sed -i "s/## \[Unreleased\]/$CHANGELOG_ENTRY/" CHANGELOG.md
71
+ else
72
+ # Insert new version header after the first ## line
73
+ sed -i "/^## \[/i\\
74
+ \\n$CHANGELOG_ENTRY\\n" CHANGELOG.md
75
+ fi
76
+
77
+ echo ""
78
+ echo "=== CHANGELOG.md updated ==="
79
+ echo "Please review and add release notes to CHANGELOG.md before continuing."
80
+ echo "The new version header has been added. Add your changes under it."
81
+ echo ""
82
+ read -p "Open CHANGELOG.md for editing? [Y/n] " edit_changelog
83
+ if [[ "$edit_changelog" != "n" && "$edit_changelog" != "N" ]]; then
84
+ ${EDITOR:-vi} CHANGELOG.md
85
+ fi
86
+
87
+ # Commit and tag
88
+ echo ""
89
+ echo "=== Committing and tagging ==="
90
+ git add package.json CHANGELOG.md
91
+ git commit -m "release: v$NEW_VERSION"
92
+ git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION"
93
+
94
+ # Push
95
+ echo ""
96
+ echo "=== Pushing to origin ==="
97
+ git push origin "$CURRENT_BRANCH"
98
+ git push origin "v$NEW_VERSION"
99
+
100
+ # Create GitHub release
101
+ echo ""
102
+ echo "=== Creating GitHub release ==="
103
+
104
+ # Extract changelog for this version
105
+ RELEASE_NOTES=$(awk "/^## \[$NEW_VERSION\]/{found=1; next} /^## \[/{if(found) exit} found{print}" CHANGELOG.md)
106
+
107
+ if [[ -z "$RELEASE_NOTES" ]]; then
108
+ RELEASE_NOTES="Release v$NEW_VERSION"
109
+ fi
110
+
111
+ gh release create "v$NEW_VERSION" \
112
+ --title "v$NEW_VERSION" \
113
+ --notes "$RELEASE_NOTES"
114
+
115
+ # Publish to npm
116
+ echo ""
117
+ echo "=== Publishing to npm ==="
118
+ read -p "Publish to npm? [Y/n] " publish
119
+ if [[ "$publish" != "n" && "$publish" != "N" ]]; then
120
+ npm publish
121
+ echo ""
122
+ echo "Published! Users can now run: npx deliberate"
123
+ else
124
+ echo "Skipped npm publish. Run 'npm publish' manually when ready."
125
+ fi
126
+
127
+ echo ""
128
+ echo "=== Release v$NEW_VERSION complete ==="
129
+ echo " Git tag: v$NEW_VERSION"
130
+ echo " GitHub: https://github.com/FavioVazquez/deliberate/releases/tag/v$NEW_VERSION"
131
+ echo " npm: https://www.npmjs.com/package/deliberate"