agentpack-cli 0.1.7 → 0.1.8
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 +21 -10
- package/dist/src/cli/index.js +27 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/operations.d.ts +2 -0
- package/dist/src/operations.js +56 -1
- package/dist/src/operations.js.map +1 -1
- package/docs/CLI.md +9 -0
- package/docs/ROADMAP.md +4 -2
- package/docs/VISION.md +43 -0
- package/docs/agentpack-architecture.html +1330 -0
- package/docs/agentpack-architecture.json +838 -0
- package/package.json +1 -1
- package/docs/agentpack-flow.md +0 -63
|
@@ -0,0 +1,1330 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>agentpack — Architecture & Flows</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root,
|
|
9
|
+
[data-theme="dark"] {
|
|
10
|
+
--bg: #07090f;
|
|
11
|
+
--bg-grid: rgba(255,255,255,0.025);
|
|
12
|
+
--panel: #0d1320;
|
|
13
|
+
--panel-2: #11192a;
|
|
14
|
+
--panel-hover: #141e33;
|
|
15
|
+
--border: rgba(255,255,255,0.09);
|
|
16
|
+
--border-strong: rgba(255,255,255,0.18);
|
|
17
|
+
--fg-1: #e7ecf3;
|
|
18
|
+
--fg-2: #aab3c5;
|
|
19
|
+
--fg-3: #6c7689;
|
|
20
|
+
--fg-4: #4a5366;
|
|
21
|
+
--code-bg: rgba(255,255,255,0.05);
|
|
22
|
+
--edge: rgba(180,188,220,0.18);
|
|
23
|
+
--edge-strong: rgba(220,226,245,0.52);
|
|
24
|
+
--arrow: rgba(220,226,245,0.40);
|
|
25
|
+
--topbar-bg: linear-gradient(180deg, rgba(8,11,18,0.92), rgba(8,11,18,0.7));
|
|
26
|
+
--sidebar-bg: rgba(10,13,21,0.6);
|
|
27
|
+
--dashed: rgba(255,255,255,0.07);
|
|
28
|
+
--hl-bg: rgba(148,148,255,0.07);
|
|
29
|
+
|
|
30
|
+
--actor: #5fa3ff;
|
|
31
|
+
--entry: #c197ff;
|
|
32
|
+
--tool: #6ee39a;
|
|
33
|
+
--operation: #f4a55f;
|
|
34
|
+
--core: #76d6e6;
|
|
35
|
+
--state: #ff8dc1;
|
|
36
|
+
--output: #e87cd9;
|
|
37
|
+
--quality: #d4e36b;
|
|
38
|
+
|
|
39
|
+
--highlight: #9494ff;
|
|
40
|
+
--highlight-glow: rgba(148,148,255,0.32);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-theme="light"] {
|
|
44
|
+
--bg: #f5f7fa;
|
|
45
|
+
--bg-grid: rgba(20,30,55,0.04);
|
|
46
|
+
--panel: #ffffff;
|
|
47
|
+
--panel-2: #f8fafc;
|
|
48
|
+
--panel-hover: #f1f4f9;
|
|
49
|
+
--border: rgba(15,23,42,0.10);
|
|
50
|
+
--border-strong: rgba(15,23,42,0.22);
|
|
51
|
+
--fg-1: #0f172a;
|
|
52
|
+
--fg-2: #334155;
|
|
53
|
+
--fg-3: #64748b;
|
|
54
|
+
--fg-4: #94a3b8;
|
|
55
|
+
--code-bg: rgba(15,23,42,0.06);
|
|
56
|
+
--edge: rgba(40,60,110,0.16);
|
|
57
|
+
--edge-strong: rgba(40,60,110,0.45);
|
|
58
|
+
--arrow: rgba(40,60,110,0.40);
|
|
59
|
+
--topbar-bg: linear-gradient(180deg, rgba(245,247,250,0.95), rgba(245,247,250,0.78));
|
|
60
|
+
--sidebar-bg: rgba(248,250,252,0.7);
|
|
61
|
+
--dashed: rgba(15,23,42,0.10);
|
|
62
|
+
--hl-bg: rgba(96,71,255,0.08);
|
|
63
|
+
|
|
64
|
+
--actor: #2563eb;
|
|
65
|
+
--entry: #7c3aed;
|
|
66
|
+
--tool: #0f9d58;
|
|
67
|
+
--operation: #d97706;
|
|
68
|
+
--core: #0891b2;
|
|
69
|
+
--state: #db2777;
|
|
70
|
+
--output: #a21caf;
|
|
71
|
+
--quality: #65a30d;
|
|
72
|
+
|
|
73
|
+
--highlight: #6047ff;
|
|
74
|
+
--highlight-glow: rgba(96,71,255,0.32);
|
|
75
|
+
}
|
|
76
|
+
* { box-sizing: border-box; }
|
|
77
|
+
html, body {
|
|
78
|
+
margin: 0; padding: 0;
|
|
79
|
+
background: var(--bg);
|
|
80
|
+
color: var(--fg-1);
|
|
81
|
+
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
82
|
+
font-size: 13px;
|
|
83
|
+
line-height: 1.45;
|
|
84
|
+
-webkit-font-smoothing: antialiased;
|
|
85
|
+
text-rendering: optimizeLegibility;
|
|
86
|
+
}
|
|
87
|
+
body {
|
|
88
|
+
background-image:
|
|
89
|
+
linear-gradient(var(--bg-grid) 1px, transparent 1px),
|
|
90
|
+
linear-gradient(90deg, var(--bg-grid) 1px, transparent 1px);
|
|
91
|
+
background-size: 32px 32px;
|
|
92
|
+
background-position: -1px -1px;
|
|
93
|
+
min-width: 1920px;
|
|
94
|
+
}
|
|
95
|
+
button {
|
|
96
|
+
font: inherit; color: inherit; cursor: pointer;
|
|
97
|
+
background: transparent; border: none; padding: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ------------- top bar ------------- */
|
|
101
|
+
.topbar {
|
|
102
|
+
display: grid;
|
|
103
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
104
|
+
gap: 32px;
|
|
105
|
+
padding: 22px 28px 18px;
|
|
106
|
+
border-bottom: 1px solid var(--border);
|
|
107
|
+
background: var(--topbar-bg);
|
|
108
|
+
backdrop-filter: blur(8px);
|
|
109
|
+
}
|
|
110
|
+
.title h1 {
|
|
111
|
+
margin: 0 0 6px;
|
|
112
|
+
font-size: 16px;
|
|
113
|
+
font-weight: 700;
|
|
114
|
+
letter-spacing: 0;
|
|
115
|
+
color: var(--fg-1);
|
|
116
|
+
}
|
|
117
|
+
.title p {
|
|
118
|
+
margin: 0;
|
|
119
|
+
max-width: 720px;
|
|
120
|
+
color: var(--fg-3);
|
|
121
|
+
font-size: 12px;
|
|
122
|
+
line-height: 1.5;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.legend {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-wrap: wrap;
|
|
128
|
+
gap: 14px 18px;
|
|
129
|
+
align-items: center;
|
|
130
|
+
justify-content: flex-end;
|
|
131
|
+
max-width: 980px;
|
|
132
|
+
}
|
|
133
|
+
.topbar-right {
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: flex-start;
|
|
136
|
+
gap: 18px;
|
|
137
|
+
}
|
|
138
|
+
.theme-toggle {
|
|
139
|
+
display: inline-flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: 6px;
|
|
142
|
+
padding: 6px 10px;
|
|
143
|
+
font-size: 10.5px;
|
|
144
|
+
letter-spacing: 0.06em;
|
|
145
|
+
color: var(--fg-2);
|
|
146
|
+
border: 1px solid var(--border);
|
|
147
|
+
border-radius: 4px;
|
|
148
|
+
background: var(--panel);
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
flex-shrink: 0;
|
|
152
|
+
transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
|
|
153
|
+
}
|
|
154
|
+
.theme-toggle:hover {
|
|
155
|
+
color: var(--fg-1);
|
|
156
|
+
border-color: var(--border-strong);
|
|
157
|
+
background: var(--panel-hover);
|
|
158
|
+
}
|
|
159
|
+
.theme-toggle .glyph {
|
|
160
|
+
width: 12px; height: 12px;
|
|
161
|
+
display: inline-block;
|
|
162
|
+
border-radius: 50%;
|
|
163
|
+
background: var(--fg-2);
|
|
164
|
+
box-shadow: inset -3px -3px 0 0 var(--panel);
|
|
165
|
+
}
|
|
166
|
+
.legend-item {
|
|
167
|
+
display: inline-flex; align-items: center; gap: 7px;
|
|
168
|
+
color: var(--fg-2);
|
|
169
|
+
font-size: 11px;
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
}
|
|
172
|
+
.legend-dot {
|
|
173
|
+
width: 9px; height: 9px; border-radius: 2px;
|
|
174
|
+
background: var(--c, var(--fg-3));
|
|
175
|
+
box-shadow: 0 0 6px rgba(0,0,0,0.4);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* ------------- flowbar (top nav) ------------- */
|
|
179
|
+
.flowbar {
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
gap: 10px;
|
|
183
|
+
padding: 10px 28px 11px;
|
|
184
|
+
border-bottom: 1px solid var(--border);
|
|
185
|
+
background: var(--panel-2);
|
|
186
|
+
flex-wrap: wrap;
|
|
187
|
+
}
|
|
188
|
+
.flowbar-label {
|
|
189
|
+
font-size: 10.5px;
|
|
190
|
+
letter-spacing: 0.14em;
|
|
191
|
+
text-transform: uppercase;
|
|
192
|
+
color: var(--fg-3);
|
|
193
|
+
margin-right: 4px;
|
|
194
|
+
}
|
|
195
|
+
.flow-chip {
|
|
196
|
+
border: 1px solid var(--border);
|
|
197
|
+
background: transparent;
|
|
198
|
+
color: var(--fg-2);
|
|
199
|
+
padding: 5px 10px;
|
|
200
|
+
border-radius: 4px;
|
|
201
|
+
font-size: 11.5px;
|
|
202
|
+
cursor: pointer;
|
|
203
|
+
display: inline-flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: 7px;
|
|
206
|
+
transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
|
|
207
|
+
}
|
|
208
|
+
.flow-chip:hover {
|
|
209
|
+
border-color: var(--border-strong);
|
|
210
|
+
color: var(--fg-1);
|
|
211
|
+
background: var(--panel-hover);
|
|
212
|
+
}
|
|
213
|
+
.flow-chip.active {
|
|
214
|
+
border-color: var(--highlight);
|
|
215
|
+
background: var(--hl-bg);
|
|
216
|
+
color: var(--fg-1);
|
|
217
|
+
}
|
|
218
|
+
.flow-chip .n {
|
|
219
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
220
|
+
width: 16px; height: 16px;
|
|
221
|
+
border-radius: 50%;
|
|
222
|
+
border: 1px solid var(--border-strong);
|
|
223
|
+
color: var(--fg-3);
|
|
224
|
+
font-size: 9.5px;
|
|
225
|
+
font-weight: 600;
|
|
226
|
+
}
|
|
227
|
+
.flow-chip.active .n {
|
|
228
|
+
border-color: var(--highlight);
|
|
229
|
+
color: var(--highlight);
|
|
230
|
+
background: transparent;
|
|
231
|
+
}
|
|
232
|
+
.flow-clear {
|
|
233
|
+
border: 1px solid var(--border);
|
|
234
|
+
background: transparent;
|
|
235
|
+
color: var(--fg-3);
|
|
236
|
+
padding: 5px 10px;
|
|
237
|
+
font-size: 10.5px;
|
|
238
|
+
border-radius: 4px;
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
transition: color 120ms ease, border-color 120ms ease;
|
|
241
|
+
}
|
|
242
|
+
.flow-clear:hover { color: var(--fg-1); border-color: var(--border-strong); }
|
|
243
|
+
.flow-clear[hidden] { display: none; }
|
|
244
|
+
.canvas-wrap {
|
|
245
|
+
display: grid;
|
|
246
|
+
grid-template-columns: 1fr 320px;
|
|
247
|
+
align-items: start;
|
|
248
|
+
}
|
|
249
|
+
.canvas {
|
|
250
|
+
position: relative;
|
|
251
|
+
padding: 22px 24px 60px;
|
|
252
|
+
}
|
|
253
|
+
.col-headers {
|
|
254
|
+
display: grid;
|
|
255
|
+
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
256
|
+
column-gap: 14px;
|
|
257
|
+
margin-bottom: 14px;
|
|
258
|
+
}
|
|
259
|
+
.col-headers div {
|
|
260
|
+
font-size: 10.5px;
|
|
261
|
+
letter-spacing: 0.12em;
|
|
262
|
+
color: var(--fg-3);
|
|
263
|
+
text-transform: uppercase;
|
|
264
|
+
padding: 0 2px;
|
|
265
|
+
}
|
|
266
|
+
.cols {
|
|
267
|
+
display: grid;
|
|
268
|
+
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
269
|
+
column-gap: 14px;
|
|
270
|
+
align-items: start;
|
|
271
|
+
position: relative;
|
|
272
|
+
}
|
|
273
|
+
.col {
|
|
274
|
+
display: flex;
|
|
275
|
+
flex-direction: column;
|
|
276
|
+
gap: 14px;
|
|
277
|
+
min-width: 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Storage column header is enough; no extra bracket */
|
|
281
|
+
|
|
282
|
+
/* ------------- nodes ------------- */
|
|
283
|
+
.node {
|
|
284
|
+
position: relative;
|
|
285
|
+
background: var(--panel);
|
|
286
|
+
border: 1px solid var(--border);
|
|
287
|
+
border-left: 2px solid var(--c, var(--fg-3));
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
padding: 9px 11px 10px;
|
|
290
|
+
color: var(--fg-1);
|
|
291
|
+
transition: background 120ms ease, border-color 120ms ease, opacity 160ms ease;
|
|
292
|
+
z-index: 2;
|
|
293
|
+
cursor: pointer;
|
|
294
|
+
}
|
|
295
|
+
.node:hover {
|
|
296
|
+
background: var(--panel-hover);
|
|
297
|
+
}
|
|
298
|
+
.node-head {
|
|
299
|
+
display: flex; align-items: flex-start; justify-content: space-between;
|
|
300
|
+
gap: 6px;
|
|
301
|
+
margin-bottom: 3px;
|
|
302
|
+
}
|
|
303
|
+
.node-title {
|
|
304
|
+
font-size: 11.5px;
|
|
305
|
+
font-weight: 600;
|
|
306
|
+
color: var(--fg-1);
|
|
307
|
+
letter-spacing: -0.1px;
|
|
308
|
+
line-height: 1.3;
|
|
309
|
+
overflow-wrap: anywhere;
|
|
310
|
+
flex: 1 1 auto;
|
|
311
|
+
min-width: 0;
|
|
312
|
+
}
|
|
313
|
+
.node-sub {
|
|
314
|
+
font-size: 9.5px;
|
|
315
|
+
color: var(--fg-4);
|
|
316
|
+
font-weight: 500;
|
|
317
|
+
white-space: nowrap;
|
|
318
|
+
padding: 1px 5px;
|
|
319
|
+
border: 1px solid var(--border);
|
|
320
|
+
border-radius: 3px;
|
|
321
|
+
flex-shrink: 0;
|
|
322
|
+
margin-top: 1px;
|
|
323
|
+
}
|
|
324
|
+
.node-desc {
|
|
325
|
+
font-size: 10.5px;
|
|
326
|
+
line-height: 1.5;
|
|
327
|
+
color: var(--fg-3);
|
|
328
|
+
margin: 4px 0 0;
|
|
329
|
+
overflow-wrap: anywhere;
|
|
330
|
+
}
|
|
331
|
+
.node-files {
|
|
332
|
+
margin-top: 6px;
|
|
333
|
+
font-size: 10px;
|
|
334
|
+
color: var(--fg-4);
|
|
335
|
+
line-height: 1.4;
|
|
336
|
+
word-break: break-all;
|
|
337
|
+
display: none;
|
|
338
|
+
}
|
|
339
|
+
.node[data-type="actor"] { --c: var(--actor); }
|
|
340
|
+
.node[data-type="entry"] { --c: var(--entry); }
|
|
341
|
+
.node[data-type="tool"] { --c: var(--tool); }
|
|
342
|
+
.node[data-type="operation"] { --c: var(--operation); }
|
|
343
|
+
.node[data-type="core"] { --c: var(--core); }
|
|
344
|
+
.node[data-type="state"] { --c: var(--state); }
|
|
345
|
+
.node[data-type="output"] { --c: var(--output); }
|
|
346
|
+
.node[data-type="quality"] { --c: var(--quality); }
|
|
347
|
+
|
|
348
|
+
/* Highlighting states */
|
|
349
|
+
body[data-mode="flow"] .node:not(.in-flow),
|
|
350
|
+
body[data-mode="node"] .node:not(.is-selected) {
|
|
351
|
+
opacity: 0.3;
|
|
352
|
+
}
|
|
353
|
+
body[data-mode="flow"] .node:not(.in-flow) .node-desc,
|
|
354
|
+
body[data-mode="node"] .node:not(.is-selected) .node-desc {
|
|
355
|
+
color: var(--fg-4);
|
|
356
|
+
}
|
|
357
|
+
body[data-mode="flow"] .node.in-flow {
|
|
358
|
+
border-color: var(--highlight);
|
|
359
|
+
background: var(--hl-bg);
|
|
360
|
+
box-shadow: 0 0 0 1px var(--highlight), 0 0 16px var(--highlight-glow);
|
|
361
|
+
}
|
|
362
|
+
body[data-mode="flow"] .node.in-flow .node-title { color: #fff8de; }
|
|
363
|
+
body[data-mode="node"] .node.is-selected {
|
|
364
|
+
border-color: var(--highlight);
|
|
365
|
+
box-shadow: 0 0 0 1px var(--highlight), 0 0 14px var(--highlight-glow);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* ------------- SVG edges ------------- */
|
|
369
|
+
svg.edges {
|
|
370
|
+
position: absolute;
|
|
371
|
+
left: 0; top: 0;
|
|
372
|
+
width: 100%; height: 100%;
|
|
373
|
+
pointer-events: none;
|
|
374
|
+
z-index: 1;
|
|
375
|
+
overflow: visible;
|
|
376
|
+
}
|
|
377
|
+
svg.edges path {
|
|
378
|
+
fill: none;
|
|
379
|
+
stroke: var(--edge);
|
|
380
|
+
stroke-width: 1.1;
|
|
381
|
+
transition: stroke 200ms ease, stroke-width 200ms ease, opacity 200ms ease;
|
|
382
|
+
}
|
|
383
|
+
/* arrow markers use CSS so they theme */
|
|
384
|
+
svg.edges marker#arrow-default > path { fill: var(--arrow); }
|
|
385
|
+
svg.edges marker#arrow-flow > path { fill: var(--highlight); }
|
|
386
|
+
body[data-mode="flow"] svg.edges path:not(.in-flow) { opacity: 0.18; }
|
|
387
|
+
svg.edges path.in-flow {
|
|
388
|
+
stroke: var(--highlight);
|
|
389
|
+
stroke-width: 1.7;
|
|
390
|
+
opacity: 0.95;
|
|
391
|
+
filter: drop-shadow(0 0 4px var(--highlight-glow));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* ------------- sidebar ------------- */
|
|
395
|
+
.sidebar {
|
|
396
|
+
border-left: 1px solid var(--border);
|
|
397
|
+
background: var(--sidebar-bg);
|
|
398
|
+
min-height: 100vh;
|
|
399
|
+
padding: 22px 22px 40px;
|
|
400
|
+
}
|
|
401
|
+
.sidebar h2 {
|
|
402
|
+
margin: 0 0 12px;
|
|
403
|
+
font-size: 10.5px;
|
|
404
|
+
font-weight: 600;
|
|
405
|
+
letter-spacing: 0.14em;
|
|
406
|
+
color: var(--fg-3);
|
|
407
|
+
text-transform: uppercase;
|
|
408
|
+
}
|
|
409
|
+
.sidebar h2:not(:first-child) { margin-top: 26px; }
|
|
410
|
+
.flow-list { display: flex; flex-direction: column; gap: 8px; }
|
|
411
|
+
.flow-card {
|
|
412
|
+
text-align: left;
|
|
413
|
+
width: 100%;
|
|
414
|
+
background: var(--panel);
|
|
415
|
+
border: 1px solid var(--border);
|
|
416
|
+
border-radius: 4px;
|
|
417
|
+
padding: 9px 11px 10px;
|
|
418
|
+
transition: background 120ms ease, border-color 120ms ease;
|
|
419
|
+
}
|
|
420
|
+
.flow-card:hover { background: var(--panel-hover); border-color: var(--border-strong); }
|
|
421
|
+
.flow-card.active {
|
|
422
|
+
background: var(--hl-bg);
|
|
423
|
+
border-color: var(--highlight);
|
|
424
|
+
box-shadow: 0 0 0 1px var(--highlight), 0 0 14px var(--highlight-glow);
|
|
425
|
+
}
|
|
426
|
+
.flow-card.active .flow-card-label { color: #fff8de; }
|
|
427
|
+
.flow-card-head {
|
|
428
|
+
display: flex; align-items: center; gap: 8px;
|
|
429
|
+
font-size: 12px;
|
|
430
|
+
font-weight: 600;
|
|
431
|
+
color: var(--fg-1);
|
|
432
|
+
}
|
|
433
|
+
.flow-card-label {
|
|
434
|
+
overflow-wrap: anywhere;
|
|
435
|
+
line-height: 1.3;
|
|
436
|
+
}
|
|
437
|
+
.flow-num {
|
|
438
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
439
|
+
width: 18px; height: 18px;
|
|
440
|
+
border: 1px solid var(--border-strong);
|
|
441
|
+
border-radius: 50%;
|
|
442
|
+
font-size: 10px;
|
|
443
|
+
color: var(--fg-2);
|
|
444
|
+
flex-shrink: 0;
|
|
445
|
+
}
|
|
446
|
+
.flow-card.active .flow-num {
|
|
447
|
+
border-color: var(--highlight);
|
|
448
|
+
color: var(--highlight);
|
|
449
|
+
background: var(--hl-bg);
|
|
450
|
+
}
|
|
451
|
+
.flow-card-label { color: var(--fg-1); }
|
|
452
|
+
.flow-card-sum {
|
|
453
|
+
margin: 5px 0 0 26px;
|
|
454
|
+
font-size: 10.5px;
|
|
455
|
+
color: var(--fg-3);
|
|
456
|
+
line-height: 1.5;
|
|
457
|
+
overflow-wrap: anywhere;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.clear-btn {
|
|
461
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
462
|
+
margin-top: 12px;
|
|
463
|
+
padding: 6px 10px;
|
|
464
|
+
font-size: 10.5px;
|
|
465
|
+
color: var(--fg-3);
|
|
466
|
+
border: 1px solid var(--border);
|
|
467
|
+
border-radius: 3px;
|
|
468
|
+
background: transparent;
|
|
469
|
+
}
|
|
470
|
+
.clear-btn:hover { color: var(--fg-1); border-color: var(--border-strong); }
|
|
471
|
+
.clear-btn[hidden] { display: none; }
|
|
472
|
+
|
|
473
|
+
/* Steps + node detail */
|
|
474
|
+
.steps-empty {
|
|
475
|
+
font-size: 11px;
|
|
476
|
+
color: var(--fg-4);
|
|
477
|
+
line-height: 1.6;
|
|
478
|
+
}
|
|
479
|
+
.steps-empty .arrow { color: var(--highlight); }
|
|
480
|
+
.step {
|
|
481
|
+
display: grid;
|
|
482
|
+
grid-template-columns: 22px 1fr;
|
|
483
|
+
column-gap: 9px;
|
|
484
|
+
padding: 9px 0;
|
|
485
|
+
border-top: 1px dashed var(--dashed);
|
|
486
|
+
align-items: start;
|
|
487
|
+
}
|
|
488
|
+
.step:first-of-type { border-top: none; }
|
|
489
|
+
.step-num {
|
|
490
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
491
|
+
width: 18px; height: 18px;
|
|
492
|
+
border-radius: 50%;
|
|
493
|
+
background: var(--hl-bg);
|
|
494
|
+
color: var(--highlight);
|
|
495
|
+
border: 1px solid var(--highlight);
|
|
496
|
+
font-size: 10px;
|
|
497
|
+
font-weight: 600;
|
|
498
|
+
}
|
|
499
|
+
.step-body {
|
|
500
|
+
font-size: 10.5px;
|
|
501
|
+
color: var(--fg-2);
|
|
502
|
+
line-height: 1.55;
|
|
503
|
+
overflow-wrap: anywhere;
|
|
504
|
+
}
|
|
505
|
+
.step-body code {
|
|
506
|
+
background: var(--code-bg);
|
|
507
|
+
padding: 1px 4px;
|
|
508
|
+
border-radius: 2px;
|
|
509
|
+
color: var(--fg-1);
|
|
510
|
+
font-size: 10.5px;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.node-detail {
|
|
514
|
+
background: var(--panel);
|
|
515
|
+
border: 1px solid var(--border);
|
|
516
|
+
border-left: 2px solid var(--c, var(--fg-3));
|
|
517
|
+
border-radius: 4px;
|
|
518
|
+
padding: 11px 13px 12px;
|
|
519
|
+
margin-bottom: 14px;
|
|
520
|
+
}
|
|
521
|
+
.node-detail-type {
|
|
522
|
+
font-size: 10px;
|
|
523
|
+
letter-spacing: 0.12em;
|
|
524
|
+
text-transform: uppercase;
|
|
525
|
+
color: var(--c, var(--fg-3));
|
|
526
|
+
margin-bottom: 4px;
|
|
527
|
+
}
|
|
528
|
+
.node-detail-title {
|
|
529
|
+
font-size: 13px;
|
|
530
|
+
font-weight: 600;
|
|
531
|
+
color: var(--fg-1);
|
|
532
|
+
margin-bottom: 6px;
|
|
533
|
+
}
|
|
534
|
+
.node-detail-desc {
|
|
535
|
+
font-size: 11.5px;
|
|
536
|
+
color: var(--fg-2);
|
|
537
|
+
line-height: 1.5;
|
|
538
|
+
}
|
|
539
|
+
.node-detail-files {
|
|
540
|
+
margin-top: 8px;
|
|
541
|
+
padding-top: 8px;
|
|
542
|
+
border-top: 1px dashed var(--dashed);
|
|
543
|
+
font-size: 10.5px;
|
|
544
|
+
color: var(--fg-3);
|
|
545
|
+
line-height: 1.55;
|
|
546
|
+
word-break: break-all;
|
|
547
|
+
}
|
|
548
|
+
.node-detail-files .label {
|
|
549
|
+
color: var(--fg-4);
|
|
550
|
+
letter-spacing: 0.1em;
|
|
551
|
+
text-transform: uppercase;
|
|
552
|
+
font-size: 9.5px;
|
|
553
|
+
display: block;
|
|
554
|
+
margin-bottom: 3px;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* small footer/meta */
|
|
558
|
+
.meta {
|
|
559
|
+
margin-top: 30px;
|
|
560
|
+
padding-top: 14px;
|
|
561
|
+
border-top: 1px dashed var(--dashed);
|
|
562
|
+
font-size: 10px;
|
|
563
|
+
color: var(--fg-4);
|
|
564
|
+
line-height: 1.6;
|
|
565
|
+
}
|
|
566
|
+
.meta code { color: var(--fg-2); background: var(--code-bg); padding: 1px 4px; border-radius: 2px; }
|
|
567
|
+
</style>
|
|
568
|
+
</head>
|
|
569
|
+
<body>
|
|
570
|
+
<header class="topbar">
|
|
571
|
+
<div class="title">
|
|
572
|
+
<h1 id="title">agentpack — Architecture & Flows</h1>
|
|
573
|
+
<p id="subtitle">Every module and data flow in the agentpack CLI + MCP server. Pick a flow on the right to highlight the path through CLI dispatch, core builders, durable state, and generated handoff context.</p>
|
|
574
|
+
</div>
|
|
575
|
+
<div class="topbar-right">
|
|
576
|
+
<div class="legend" id="legend"></div>
|
|
577
|
+
<button class="theme-toggle" id="theme-toggle" title="Toggle light / dark theme">
|
|
578
|
+
<span class="glyph" aria-hidden="true"></span>
|
|
579
|
+
<span id="theme-label">dark</span>
|
|
580
|
+
</button>
|
|
581
|
+
</div>
|
|
582
|
+
</header>
|
|
583
|
+
|
|
584
|
+
<nav class="flowbar" id="flowbar">
|
|
585
|
+
<span class="flowbar-label">Flows</span>
|
|
586
|
+
<div id="flow-chips" style="display:flex; gap:6px; flex-wrap:wrap;"></div>
|
|
587
|
+
<button class="flow-clear" id="flow-clear" hidden>× clear selection</button>
|
|
588
|
+
</nav>
|
|
589
|
+
|
|
590
|
+
<main class="canvas-wrap">
|
|
591
|
+
<section class="canvas" id="canvas">
|
|
592
|
+
<div class="col-headers" id="col-headers"></div>
|
|
593
|
+
<div class="cols" id="cols"></div>
|
|
594
|
+
<svg class="edges" id="edges">
|
|
595
|
+
<defs>
|
|
596
|
+
<marker id="arrow-default" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto">
|
|
597
|
+
<path d="M0,0 L8,4 L0,8 z"></path>
|
|
598
|
+
</marker>
|
|
599
|
+
<marker id="arrow-flow" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto">
|
|
600
|
+
<path d="M0,0 L8,4 L0,8 z"></path>
|
|
601
|
+
</marker>
|
|
602
|
+
</defs>
|
|
603
|
+
</svg>
|
|
604
|
+
</section>
|
|
605
|
+
|
|
606
|
+
<aside class="sidebar">
|
|
607
|
+
<h2>Steps</h2>
|
|
608
|
+
<div id="detail"></div>
|
|
609
|
+
|
|
610
|
+
<div class="meta" id="meta"></div>
|
|
611
|
+
</aside>
|
|
612
|
+
</main>
|
|
613
|
+
|
|
614
|
+
<!-- Architecture data (same shape as agentpack-architecture.json) -->
|
|
615
|
+
<script type="application/json" id="arch-data">
|
|
616
|
+
{
|
|
617
|
+
"schemaVersion": 1,
|
|
618
|
+
"title": "agentpack — Architecture & Flows",
|
|
619
|
+
"description": "Local task-state ledger for AI coding agents. Pick a flow on the right to highlight the path through CLI, MCP tools, durable state, and generated handoff context.",
|
|
620
|
+
"repository": "agentpack-cli",
|
|
621
|
+
"package": "agentpack-cli",
|
|
622
|
+
"binary": "agentpack",
|
|
623
|
+
"engines": { "node": ">=20" },
|
|
624
|
+
"columns": [
|
|
625
|
+
{ "id": "actors", "label": "Actors" },
|
|
626
|
+
{ "id": "entry", "label": "CLI / MCP entry" },
|
|
627
|
+
{ "id": "dispatch", "label": "Dispatch & tools" },
|
|
628
|
+
{ "id": "operations","label": "Operations" },
|
|
629
|
+
{ "id": "core", "label": "Core builders" },
|
|
630
|
+
{ "id": "storage", "label": ".agentpack/ state" },
|
|
631
|
+
{ "id": "outputs", "label": "Outputs" },
|
|
632
|
+
{ "id": "quality", "label": "Tests & release" }
|
|
633
|
+
],
|
|
634
|
+
"legend": [
|
|
635
|
+
{ "type": "actor", "label": "Actor" },
|
|
636
|
+
{ "type": "entry", "label": "CLI / MCP entry" },
|
|
637
|
+
{ "type": "tool", "label": "Dispatch / tool" },
|
|
638
|
+
{ "type": "operation", "label": "Operation" },
|
|
639
|
+
{ "type": "core", "label": "Core builder" },
|
|
640
|
+
{ "type": "state", "label": ".agentpack state" },
|
|
641
|
+
{ "type": "output", "label": "Output artifact" },
|
|
642
|
+
{ "type": "quality", "label": "Tests / CI" }
|
|
643
|
+
],
|
|
644
|
+
"nodes": [
|
|
645
|
+
{
|
|
646
|
+
"id": "human",
|
|
647
|
+
"column": "actors",
|
|
648
|
+
"type": "actor",
|
|
649
|
+
"title": "Developer",
|
|
650
|
+
"summary": "Runs setup commands, reviews handoffs, steers coding-agent work.",
|
|
651
|
+
"files": ["README.md", "docs/CLI.md"]
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
"id": "agent",
|
|
655
|
+
"column": "actors",
|
|
656
|
+
"type": "actor",
|
|
657
|
+
"title": "Coding agent",
|
|
658
|
+
"summary": "Codex / Claude Code / Cursor / any MCP client. Reads instructions, calls MCP tools.",
|
|
659
|
+
"files": ["AGENTS.md", "CLAUDE.md", ".agentpack/instructions/"]
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
"id": "ci",
|
|
663
|
+
"column": "actors",
|
|
664
|
+
"type": "actor",
|
|
665
|
+
"title": "CI / release",
|
|
666
|
+
"summary": "GitHub Actions builds, tests, smokes MCP, packs, publishes provenance releases.",
|
|
667
|
+
"files": [".github/workflows/ci.yml", ".github/workflows/publish.yml"]
|
|
668
|
+
},
|
|
669
|
+
|
|
670
|
+
{
|
|
671
|
+
"id": "agentpack-bin",
|
|
672
|
+
"column": "entry",
|
|
673
|
+
"type": "entry",
|
|
674
|
+
"title": "agentpack",
|
|
675
|
+
"subtitle": "package bin",
|
|
676
|
+
"summary": "package.json#bin → dist/src/agentpack.js after TypeScript build.",
|
|
677
|
+
"files": ["package.json", "src/agentpack.ts"]
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
"id": "cli",
|
|
681
|
+
"column": "entry",
|
|
682
|
+
"type": "entry",
|
|
683
|
+
"title": "src/cli/index.ts",
|
|
684
|
+
"subtitle": "495 lines",
|
|
685
|
+
"summary": "Parses commands and flags, resolves --root / AGENTPACK_ROOT / cwd, dispatches CLI operations, can start MCP mode.",
|
|
686
|
+
"files": ["src/cli/index.ts"]
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
"id": "mcp-start",
|
|
690
|
+
"column": "entry",
|
|
691
|
+
"type": "entry",
|
|
692
|
+
"title": "agentpack mcp",
|
|
693
|
+
"subtitle": "stdio launcher",
|
|
694
|
+
"summary": "Launches JSON-RPC over stdio. Root resolution: --root → AGENTPACK_ROOT → cwd.",
|
|
695
|
+
"files": ["src/cli/index.ts", "src/mcp/server.ts"]
|
|
696
|
+
},
|
|
697
|
+
|
|
698
|
+
{
|
|
699
|
+
"id": "mcp-server",
|
|
700
|
+
"column": "dispatch",
|
|
701
|
+
"type": "tool",
|
|
702
|
+
"title": "src/mcp/server.ts",
|
|
703
|
+
"subtitle": "397 lines",
|
|
704
|
+
"summary": "MCP tools: load_context, source_status, record_source, record_decision, record_dead_end, attach_evidence, checkpoint, diff, replay, resume.",
|
|
705
|
+
"files": ["src/mcp/server.ts", "docs/MCP.md"]
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
"id": "commands",
|
|
709
|
+
"column": "dispatch",
|
|
710
|
+
"type": "tool",
|
|
711
|
+
"title": "CLI commands",
|
|
712
|
+
"subtitle": "human surface",
|
|
713
|
+
"summary": "init · set · record · note · source · evidence · run · checkpoint · resume · export · diff · replay · doctor.",
|
|
714
|
+
"files": ["src/cli/index.ts", "docs/CLI.md"]
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
"id": "install",
|
|
718
|
+
"column": "dispatch",
|
|
719
|
+
"type": "tool",
|
|
720
|
+
"title": "installIntegration()",
|
|
721
|
+
"subtitle": "client installer",
|
|
722
|
+
"summary": "Generates Codex / Claude / Claude Desktop / Cursor instructions + MCP config. Dry-run unless --write.",
|
|
723
|
+
"files": ["src/integrations/install.ts", "docs/INTEGRATIONS.md"]
|
|
724
|
+
},
|
|
725
|
+
|
|
726
|
+
{
|
|
727
|
+
"id": "operations",
|
|
728
|
+
"column": "operations",
|
|
729
|
+
"type": "operation",
|
|
730
|
+
"title": "src/operations.ts",
|
|
731
|
+
"subtitle": "268 lines",
|
|
732
|
+
"summary": "Source records, evidence files, source status, replay output, git-status labels, redaction at the operation boundary.",
|
|
733
|
+
"files": ["src/operations.ts"]
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
"id": "doctor",
|
|
737
|
+
"column": "operations",
|
|
738
|
+
"type": "operation",
|
|
739
|
+
"title": "core/doctor.ts",
|
|
740
|
+
"summary": "Pack health, ignore gaps, project MCP names, stale roots, Desktop config, launcher risks.",
|
|
741
|
+
"files": ["src/core/doctor.ts"]
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
"id": "git",
|
|
745
|
+
"column": "operations",
|
|
746
|
+
"type": "operation",
|
|
747
|
+
"title": "core/git.ts",
|
|
748
|
+
"summary": "Branch, head commit, working-tree status, bounded diff context for resumes.",
|
|
749
|
+
"files": ["src/core/git.ts"]
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
"id": "hash",
|
|
753
|
+
"column": "operations",
|
|
754
|
+
"type": "operation",
|
|
755
|
+
"title": "core/hash.ts",
|
|
756
|
+
"summary": "sha256File + getFileRecord. Powers source-cache identity and stale detection.",
|
|
757
|
+
"files": ["src/core/hash.ts"]
|
|
758
|
+
},
|
|
759
|
+
|
|
760
|
+
{
|
|
761
|
+
"id": "store",
|
|
762
|
+
"column": "core",
|
|
763
|
+
"type": "core",
|
|
764
|
+
"title": "core/store.ts",
|
|
765
|
+
"subtitle": "286 lines",
|
|
766
|
+
"summary": "Finds pack roots, initPack(), appends ignore patterns, reads/writes state, appends events, serializes writes with .lock.",
|
|
767
|
+
"files": ["src/core/store.ts"]
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"id": "checkpoints",
|
|
771
|
+
"column": "core",
|
|
772
|
+
"type": "core",
|
|
773
|
+
"title": "core/checkpoints.ts",
|
|
774
|
+
"summary": "Creates checkpoint directories, manifests, resume snapshots, updates state pointer, diffs checkpoint pairs.",
|
|
775
|
+
"files": ["src/core/checkpoints.ts"]
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
"id": "resume",
|
|
779
|
+
"column": "core",
|
|
780
|
+
"type": "core",
|
|
781
|
+
"title": "core/resume.ts",
|
|
782
|
+
"summary": "Builds budgeted markdown context: state + git + source cache + decisions + dead ends + evidence + recent timeline.",
|
|
783
|
+
"files": ["src/core/resume.ts"]
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
"id": "budget",
|
|
787
|
+
"column": "core",
|
|
788
|
+
"type": "core",
|
|
789
|
+
"title": "core/budget.ts",
|
|
790
|
+
"summary": "Token estimator + section packer. Required sections kept, optionals truncated, omissions reported.",
|
|
791
|
+
"files": ["src/core/budget.ts", "src/core/presets.ts"]
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
"id": "redaction",
|
|
795
|
+
"column": "core",
|
|
796
|
+
"type": "core",
|
|
797
|
+
"title": "core/redaction.ts",
|
|
798
|
+
"summary": "Strips root paths and configured secret patterns before state leaves the local ledger.",
|
|
799
|
+
"files": ["src/core/redaction.ts"]
|
|
800
|
+
},
|
|
801
|
+
|
|
802
|
+
{
|
|
803
|
+
"id": "state",
|
|
804
|
+
"column": "storage",
|
|
805
|
+
"type": "state",
|
|
806
|
+
"title": "state.json",
|
|
807
|
+
"summary": "Goal · current status · next actions · current checkpoint pointer.",
|
|
808
|
+
"files": [".agentpack/state.json"]
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
"id": "events",
|
|
812
|
+
"column": "storage",
|
|
813
|
+
"type": "state",
|
|
814
|
+
"title": "events.jsonl",
|
|
815
|
+
"summary": "Append-only timeline: decisions, notes, evidence, commands, sources, checkpoints.",
|
|
816
|
+
"files": [".agentpack/events.jsonl"]
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
"id": "sources",
|
|
820
|
+
"column": "storage",
|
|
821
|
+
"type": "state",
|
|
822
|
+
"title": "sources.json",
|
|
823
|
+
"summary": "Source-cache summaries keyed by path + sha256 so future agents avoid stale assumptions.",
|
|
824
|
+
"files": [".agentpack/sources.json"]
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
"id": "evidence",
|
|
828
|
+
"column": "storage",
|
|
829
|
+
"type": "state",
|
|
830
|
+
"title": "evidence/",
|
|
831
|
+
"summary": "Local evidence payloads attached from command output, files, notes, links.",
|
|
832
|
+
"files": [".agentpack/evidence/"]
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
"id": "checkpoint-store",
|
|
836
|
+
"column": "storage",
|
|
837
|
+
"type": "state",
|
|
838
|
+
"title": "checkpoints/",
|
|
839
|
+
"summary": "Durable checkpoint manifests and resume snapshots for recovery & comparison.",
|
|
840
|
+
"files": [".agentpack/checkpoints/"]
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
"id": "instructions",
|
|
844
|
+
"column": "storage",
|
|
845
|
+
"type": "state",
|
|
846
|
+
"title": "instructions/",
|
|
847
|
+
"summary": "Generated client instructions for the Agentpack workflow.",
|
|
848
|
+
"files": [".agentpack/instructions/"]
|
|
849
|
+
},
|
|
850
|
+
|
|
851
|
+
{
|
|
852
|
+
"id": "resume-output",
|
|
853
|
+
"column": "outputs",
|
|
854
|
+
"type": "output",
|
|
855
|
+
"title": "resume / load_context",
|
|
856
|
+
"summary": "Markdown task context: pack root, state, git, source-cache guidance, decisions, evidence, next actions.",
|
|
857
|
+
"files": ["docs/MCP.md", "docs/CLI.md"]
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
"id": "handoff-export",
|
|
861
|
+
"column": "outputs",
|
|
862
|
+
"type": "output",
|
|
863
|
+
"title": "exports/",
|
|
864
|
+
"summary": "Markdown handoff fallback for clients that cannot read local MCP tools.",
|
|
865
|
+
"files": [".agentpack/exports/"]
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
"id": "client-config",
|
|
869
|
+
"column": "outputs",
|
|
870
|
+
"type": "output",
|
|
871
|
+
"title": "Client MCP config",
|
|
872
|
+
"summary": "Project-local Codex / Claude / Cursor. Claude Desktop gets a merge snippet for global config.",
|
|
873
|
+
"files": [".codex/config.toml", ".mcp.json", ".cursor/mcp.json"]
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
"id": "doctor-output",
|
|
877
|
+
"column": "outputs",
|
|
878
|
+
"type": "output",
|
|
879
|
+
"title": "doctor report",
|
|
880
|
+
"summary": "Ok / warn / fail checks for pack setup, ignore rules, MCP launcher safety.",
|
|
881
|
+
"files": ["src/core/doctor.ts"]
|
|
882
|
+
},
|
|
883
|
+
|
|
884
|
+
{
|
|
885
|
+
"id": "tests",
|
|
886
|
+
"column": "quality",
|
|
887
|
+
"type": "quality",
|
|
888
|
+
"title": "tests/agentpack.test.ts",
|
|
889
|
+
"summary": "node --test suite covering CLI, pack state, source status, resumes, MCP tools, doctor, installs, redaction, budget.",
|
|
890
|
+
"files": ["tests/agentpack.test.ts"]
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
"id": "smoke",
|
|
894
|
+
"column": "quality",
|
|
895
|
+
"type": "quality",
|
|
896
|
+
"title": "scripts/mcp-smoke.mjs",
|
|
897
|
+
"summary": "Builds CLI, starts MCP, sends initialize / tools/list / resume JSON-RPC, validates output.",
|
|
898
|
+
"files": ["scripts/mcp-smoke.mjs"]
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
"id": "release",
|
|
902
|
+
"column": "quality",
|
|
903
|
+
"type": "quality",
|
|
904
|
+
"title": "release workflow",
|
|
905
|
+
"summary": "ci.yml builds + tests + smokes + packs. publish.yml releases npm with provenance via OIDC Trusted Publisher.",
|
|
906
|
+
"files": [".github/workflows/ci.yml", ".github/workflows/publish.yml", "docs/RELEASING.md"]
|
|
907
|
+
}
|
|
908
|
+
],
|
|
909
|
+
"edges": [
|
|
910
|
+
{ "id": "e-human-bin", "from": "human", "to": "agentpack-bin" },
|
|
911
|
+
{ "id": "e-agent-mcp", "from": "agent", "to": "mcp-start" },
|
|
912
|
+
{ "id": "e-ci-bin", "from": "ci", "to": "agentpack-bin" },
|
|
913
|
+
|
|
914
|
+
{ "id": "e-bin-cli", "from": "agentpack-bin", "to": "cli" },
|
|
915
|
+
{ "id": "e-cli-mcp", "from": "cli", "to": "mcp-start" },
|
|
916
|
+
{ "id": "e-mcp-server", "from": "mcp-start", "to": "mcp-server" },
|
|
917
|
+
|
|
918
|
+
{ "id": "e-mcp-commands", "from": "mcp-server", "to": "commands" },
|
|
919
|
+
{ "id": "e-cli-commands", "from": "cli", "to": "commands" },
|
|
920
|
+
{ "id": "e-cli-install", "from": "cli", "to": "install" },
|
|
921
|
+
|
|
922
|
+
{ "id": "e-commands-operations","from": "commands", "to": "operations" },
|
|
923
|
+
{ "id": "e-commands-doctor", "from": "commands", "to": "doctor" },
|
|
924
|
+
{ "id": "e-mcp-operations", "from": "mcp-server", "to": "operations" },
|
|
925
|
+
|
|
926
|
+
{ "id": "e-operations-hash", "from": "operations", "to": "hash" },
|
|
927
|
+
{ "id": "e-operations-git", "from": "operations", "to": "git" },
|
|
928
|
+
{ "id": "e-commands-store", "from": "commands", "to": "store" },
|
|
929
|
+
{ "id": "e-install-store", "from": "install", "to": "store" },
|
|
930
|
+
|
|
931
|
+
{ "id": "e-commands-checkpoints","from": "commands", "to": "checkpoints" },
|
|
932
|
+
{ "id": "e-mcp-checkpoints", "from": "mcp-server", "to": "checkpoints" },
|
|
933
|
+
{ "id": "e-commands-resume", "from": "commands", "to": "resume" },
|
|
934
|
+
{ "id": "e-mcp-resume", "from": "mcp-server", "to": "resume" },
|
|
935
|
+
|
|
936
|
+
{ "id": "e-store-state", "from": "store", "to": "state" },
|
|
937
|
+
{ "id": "e-store-events", "from": "store", "to": "events" },
|
|
938
|
+
{ "id": "e-store-sources", "from": "store", "to": "sources" },
|
|
939
|
+
{ "id": "e-operations-sources", "from": "operations", "to": "sources" },
|
|
940
|
+
{ "id": "e-operations-evidence","from": "operations", "to": "evidence" },
|
|
941
|
+
{ "id": "e-operations-events", "from": "operations", "to": "events" },
|
|
942
|
+
{ "id": "e-checkpoints-store", "from": "checkpoints", "to": "checkpoint-store" },
|
|
943
|
+
{ "id": "e-checkpoints-state", "from": "checkpoints", "to": "state" },
|
|
944
|
+
{ "id": "e-checkpoints-events", "from": "checkpoints", "to": "events" },
|
|
945
|
+
{ "id": "e-install-instructions","from": "install", "to": "instructions" },
|
|
946
|
+
|
|
947
|
+
{ "id": "e-resume-state", "from": "resume", "to": "state" },
|
|
948
|
+
{ "id": "e-resume-sources", "from": "resume", "to": "sources" },
|
|
949
|
+
{ "id": "e-resume-events", "from": "resume", "to": "events" },
|
|
950
|
+
{ "id": "e-resume-git", "from": "resume", "to": "git" },
|
|
951
|
+
{ "id": "e-resume-budget", "from": "resume", "to": "budget" },
|
|
952
|
+
{ "id": "e-resume-redaction", "from": "resume", "to": "redaction" },
|
|
953
|
+
|
|
954
|
+
{ "id": "e-resume-out", "from": "resume", "to": "resume-output" },
|
|
955
|
+
{ "id": "e-resume-export", "from": "resume", "to": "handoff-export" },
|
|
956
|
+
{ "id": "e-install-config", "from": "install", "to": "client-config" },
|
|
957
|
+
{ "id": "e-doctor-output", "from": "doctor", "to": "doctor-output" },
|
|
958
|
+
|
|
959
|
+
{ "id": "e-tests-cli", "from": "tests", "to": "cli" },
|
|
960
|
+
{ "id": "e-tests-mcp", "from": "tests", "to": "mcp-server" },
|
|
961
|
+
{ "id": "e-smoke-mcp", "from": "smoke", "to": "mcp-server" },
|
|
962
|
+
{ "id": "e-release-tests", "from": "release", "to": "tests" },
|
|
963
|
+
{ "id": "e-release-smoke", "from": "release", "to": "smoke" }
|
|
964
|
+
],
|
|
965
|
+
"flows": [
|
|
966
|
+
{
|
|
967
|
+
"id": "init",
|
|
968
|
+
"label": "init workspace",
|
|
969
|
+
"summary": "Create local task-state ledger and keep it out of git.",
|
|
970
|
+
"nodes": ["human","agentpack-bin","cli","commands","store","state","events","sources","instructions"],
|
|
971
|
+
"edges": ["e-human-bin","e-bin-cli","e-cli-commands","e-commands-store","e-store-state","e-store-events","e-store-sources"],
|
|
972
|
+
"steps": [
|
|
973
|
+
"Developer runs `agentpack init` in a repo.",
|
|
974
|
+
"CLI resolves the workspace and calls `initPack()` in core/store.ts.",
|
|
975
|
+
"store.ts creates `.agentpack/` and default state.json, events.jsonl, sources.json.",
|
|
976
|
+
"`ensurePackIgnored()` appends local-only ignore patterns to .gitignore without replacing existing rules."
|
|
977
|
+
]
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
"id": "install",
|
|
981
|
+
"label": "install client",
|
|
982
|
+
"summary": "Generate client instructions + MCP launcher config for Codex / Claude / Cursor.",
|
|
983
|
+
"nodes": ["human","agentpack-bin","cli","install","store","instructions","client-config"],
|
|
984
|
+
"edges": ["e-human-bin","e-bin-cli","e-cli-install","e-install-store","e-install-instructions","e-install-config"],
|
|
985
|
+
"steps": [
|
|
986
|
+
"Developer runs `agentpack install codex|claude|claude-desktop|cursor --write`.",
|
|
987
|
+
"`installIntegration()` builds the per-client file plan (dry-run unless --write).",
|
|
988
|
+
"Generated workflow instructions are written under `.agentpack/instructions/`.",
|
|
989
|
+
"Project-local clients get config files (.codex, .mcp.json, .cursor/mcp.json); Claude Desktop gets a merge snippet."
|
|
990
|
+
]
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
"id": "source-cache",
|
|
994
|
+
"label": "source cache",
|
|
995
|
+
"summary": "Record source conclusions by hash and detect staleness on the next session.",
|
|
996
|
+
"nodes": ["agent","mcp-start","mcp-server","operations","hash","git","sources","events","resume-output"],
|
|
997
|
+
"edges": ["e-agent-mcp","e-mcp-server","e-mcp-operations","e-operations-hash","e-operations-git","e-operations-sources","e-operations-events"],
|
|
998
|
+
"steps": [
|
|
999
|
+
"Agent inspects an important source file.",
|
|
1000
|
+
"`record_source` (or `agentpack source add`) stores summary + snippet + path + sha256 via core/hash.ts.",
|
|
1001
|
+
"`source_status` later compares recorded hashes against the working tree and git status.",
|
|
1002
|
+
"Future resumes show unchanged sources as compact stubs and flag changed/missing files in full."
|
|
1003
|
+
]
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
"id": "checkpoint",
|
|
1007
|
+
"label": "checkpoint",
|
|
1008
|
+
"summary": "Persist meaningful progress into durable state and a recoverable snapshot.",
|
|
1009
|
+
"nodes": ["agent","mcp-start","mcp-server","checkpoints","store","state","events","checkpoint-store"],
|
|
1010
|
+
"edges": ["e-agent-mcp","e-mcp-server","e-mcp-checkpoints","e-checkpoints-store","e-checkpoints-state","e-checkpoints-events"],
|
|
1011
|
+
"steps": [
|
|
1012
|
+
"Agent reaches a coherent boundary and calls `checkpoint`.",
|
|
1013
|
+
"`checkpoints.ts` writes a manifest + resume snapshot under the Agentpack write lock.",
|
|
1014
|
+
"state.json is updated with status, next actions, and the current checkpoint id.",
|
|
1015
|
+
"events.jsonl gets a `checkpoint` event for the timeline."
|
|
1016
|
+
]
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
"id": "resume",
|
|
1020
|
+
"label": "resume context",
|
|
1021
|
+
"summary": "Pack current task state into budgeted markdown for the next agent.",
|
|
1022
|
+
"nodes": ["agent","mcp-start","mcp-server","resume","git","budget","redaction","state","sources","events","resume-output","handoff-export"],
|
|
1023
|
+
"edges": ["e-agent-mcp","e-mcp-server","e-mcp-resume","e-resume-state","e-resume-sources","e-resume-events","e-resume-git","e-resume-budget","e-resume-redaction","e-resume-out","e-resume-export"],
|
|
1024
|
+
"steps": [
|
|
1025
|
+
"Agent calls `load_context` or `resume` with a preset, budget, and optional query.",
|
|
1026
|
+
"core/resume.ts combines state + git + source cache + decisions + evidence + timeline.",
|
|
1027
|
+
"core/budget.ts keeps required sections and reports truncated/omitted ones.",
|
|
1028
|
+
"core/redaction.ts strips root paths + configured secrets before output leaves the ledger.",
|
|
1029
|
+
"Result is returned via MCP, or written to `.agentpack/exports/` as a markdown fallback."
|
|
1030
|
+
]
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
"id": "doctor-ci",
|
|
1034
|
+
"label": "doctor + CI",
|
|
1035
|
+
"summary": "Catch broken setup, stale MCP roots, and release regressions.",
|
|
1036
|
+
"nodes": ["human","ci","agentpack-bin","cli","commands","doctor","doctor-output","tests","smoke","release","mcp-server"],
|
|
1037
|
+
"edges": ["e-human-bin","e-ci-bin","e-bin-cli","e-cli-commands","e-commands-doctor","e-doctor-output","e-release-tests","e-release-smoke","e-tests-cli","e-tests-mcp","e-smoke-mcp"],
|
|
1038
|
+
"steps": [
|
|
1039
|
+
"`agentpack doctor` reports pack health, ignore rules, launcher names, and stale roots.",
|
|
1040
|
+
"Node tests cover CLI behavior, MCP tools, installs, source cache, redaction, budget packing.",
|
|
1041
|
+
"`scripts/mcp-smoke.mjs` runs a JSON-RPC flow against the built server.",
|
|
1042
|
+
"Release workflow publishes npm packages with provenance after build/test/smoke/pack checks."
|
|
1043
|
+
]
|
|
1044
|
+
}
|
|
1045
|
+
]
|
|
1046
|
+
}
|
|
1047
|
+
</script>
|
|
1048
|
+
|
|
1049
|
+
<script>
|
|
1050
|
+
(() => {
|
|
1051
|
+
// ---- Theme toggle --------------------------------------------------------
|
|
1052
|
+
const THEME_KEY = 'agentpack-arch-theme';
|
|
1053
|
+
const themeBtn = document.getElementById('theme-toggle');
|
|
1054
|
+
const themeLabel = document.getElementById('theme-label');
|
|
1055
|
+
function applyTheme(t) {
|
|
1056
|
+
document.documentElement.setAttribute('data-theme', t);
|
|
1057
|
+
themeLabel.textContent = t;
|
|
1058
|
+
}
|
|
1059
|
+
const saved = (() => { try { return localStorage.getItem(THEME_KEY); } catch (_) { return null; } })();
|
|
1060
|
+
const prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
|
|
1061
|
+
applyTheme(saved || (prefersLight ? 'light' : 'dark'));
|
|
1062
|
+
themeBtn.addEventListener('click', () => {
|
|
1063
|
+
const next = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
|
|
1064
|
+
applyTheme(next);
|
|
1065
|
+
try { localStorage.setItem(THEME_KEY, next); } catch (_) {}
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
// ---- Load embedded data --------------------------------------------------
|
|
1069
|
+
const data = JSON.parse(document.getElementById('arch-data').textContent);
|
|
1070
|
+
document.getElementById('title').textContent = data.title;
|
|
1071
|
+
document.getElementById('subtitle').textContent = data.description;
|
|
1072
|
+
|
|
1073
|
+
// ---- Legend --------------------------------------------------------------
|
|
1074
|
+
const legendEl = document.getElementById('legend');
|
|
1075
|
+
data.legend.forEach(l => {
|
|
1076
|
+
const span = document.createElement('span');
|
|
1077
|
+
span.className = 'legend-item';
|
|
1078
|
+
span.innerHTML = `<span class="legend-dot" style="--c: var(--${l.type})"></span>${l.label}`;
|
|
1079
|
+
legendEl.appendChild(span);
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// ---- Column headers + columns -------------------------------------------
|
|
1083
|
+
const headersEl = document.getElementById('col-headers');
|
|
1084
|
+
const colsEl = document.getElementById('cols');
|
|
1085
|
+
const colMap = {};
|
|
1086
|
+
data.columns.forEach(c => {
|
|
1087
|
+
const h = document.createElement('div');
|
|
1088
|
+
h.textContent = c.label;
|
|
1089
|
+
headersEl.appendChild(h);
|
|
1090
|
+
|
|
1091
|
+
const col = document.createElement('div');
|
|
1092
|
+
col.className = 'col col-' + c.id;
|
|
1093
|
+
col.dataset.col = c.id;
|
|
1094
|
+
colsEl.appendChild(col);
|
|
1095
|
+
colMap[c.id] = col;
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
// ---- Nodes ---------------------------------------------------------------
|
|
1099
|
+
const nodeEls = {};
|
|
1100
|
+
data.nodes.forEach(n => {
|
|
1101
|
+
const el = document.createElement('article');
|
|
1102
|
+
el.className = 'node';
|
|
1103
|
+
el.dataset.id = n.id;
|
|
1104
|
+
el.dataset.type = n.type;
|
|
1105
|
+
el.tabIndex = 0;
|
|
1106
|
+
el.innerHTML = `
|
|
1107
|
+
<div class="node-head">
|
|
1108
|
+
<div class="node-title">${softBreak(n.title)}</div>
|
|
1109
|
+
${n.subtitle ? `<span class="node-sub">${escapeHTML(n.subtitle)}</span>` : ''}
|
|
1110
|
+
</div>
|
|
1111
|
+
<p class="node-desc">${escapeHTML(n.summary)}</p>
|
|
1112
|
+
`;
|
|
1113
|
+
el.addEventListener('click', () => selectNode(n.id));
|
|
1114
|
+
colMap[n.column].appendChild(el);
|
|
1115
|
+
nodeEls[n.id] = el;
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
// ---- Flow chips (top bar) -----------------------------------------------
|
|
1119
|
+
const flowChipsEl = document.getElementById('flow-chips');
|
|
1120
|
+
const flowEls = {};
|
|
1121
|
+
data.flows.forEach((f, idx) => {
|
|
1122
|
+
const btn = document.createElement('button');
|
|
1123
|
+
btn.className = 'flow-chip';
|
|
1124
|
+
btn.dataset.id = f.id;
|
|
1125
|
+
btn.title = f.summary;
|
|
1126
|
+
btn.innerHTML = `<span class="n">${idx + 1}</span><span class="flow-chip-label">${softBreak(f.label)}</span>`;
|
|
1127
|
+
btn.addEventListener('click', () => selectFlow(f.id));
|
|
1128
|
+
flowChipsEl.appendChild(btn);
|
|
1129
|
+
flowEls[f.id] = btn;
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
document.getElementById('flow-clear').addEventListener('click', clearSelection);
|
|
1133
|
+
|
|
1134
|
+
// ---- Meta info -----------------------------------------------------------
|
|
1135
|
+
document.getElementById('meta').innerHTML = `
|
|
1136
|
+
<strong style="color:var(--fg-2)">${escapeHTML(data.repository || 'agentpack')}</strong>
|
|
1137
|
+
· bin <code>${escapeHTML(data.binary || 'agentpack')}</code>
|
|
1138
|
+
· node ${escapeHTML(data?.engines?.node || '>=20')}
|
|
1139
|
+
<br>
|
|
1140
|
+
<span style="color:var(--fg-4)">${data.nodes.length} modules · ${data.edges.length} edges · ${data.flows.length} flows</span>
|
|
1141
|
+
`;
|
|
1142
|
+
|
|
1143
|
+
// ---- SVG edges -----------------------------------------------------------
|
|
1144
|
+
const edgesSvg = document.getElementById('edges');
|
|
1145
|
+
const edgeEls = {};
|
|
1146
|
+
data.edges.forEach(e => {
|
|
1147
|
+
const p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1148
|
+
p.setAttribute('data-id', e.id);
|
|
1149
|
+
p.setAttribute('data-from', e.from);
|
|
1150
|
+
p.setAttribute('data-to', e.to);
|
|
1151
|
+
p.setAttribute('marker-end', 'url(#arrow-default)');
|
|
1152
|
+
edgesSvg.appendChild(p);
|
|
1153
|
+
edgeEls[e.id] = p;
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
// ---- Position edges ------------------------------------------------------
|
|
1157
|
+
const canvasEl = document.getElementById('canvas');
|
|
1158
|
+
function layoutEdges() {
|
|
1159
|
+
const cbox = canvasEl.getBoundingClientRect();
|
|
1160
|
+
edgesSvg.setAttribute('viewBox', `0 0 ${cbox.width} ${cbox.height}`);
|
|
1161
|
+
edgesSvg.setAttribute('width', cbox.width);
|
|
1162
|
+
edgesSvg.setAttribute('height', cbox.height);
|
|
1163
|
+
data.edges.forEach(e => {
|
|
1164
|
+
const a = nodeEls[e.from], b = nodeEls[e.to];
|
|
1165
|
+
if (!a || !b) return;
|
|
1166
|
+
const ar = a.getBoundingClientRect();
|
|
1167
|
+
const br = b.getBoundingClientRect();
|
|
1168
|
+
const x1 = ar.right - cbox.left;
|
|
1169
|
+
const y1 = ar.top + ar.height / 2 - cbox.top;
|
|
1170
|
+
const x2 = br.left - cbox.left;
|
|
1171
|
+
const y2 = br.top + br.height / 2 - cbox.top;
|
|
1172
|
+
// cubic bezier — keep control points horizontal
|
|
1173
|
+
const dx = Math.max(40, (x2 - x1) * 0.55);
|
|
1174
|
+
const c1x = x1 + dx;
|
|
1175
|
+
const c2x = x2 - dx;
|
|
1176
|
+
edgeEls[e.id].setAttribute('d',
|
|
1177
|
+
`M ${x1.toFixed(1)} ${y1.toFixed(1)} C ${c1x.toFixed(1)} ${y1.toFixed(1)}, ${c2x.toFixed(1)} ${y2.toFixed(1)}, ${x2.toFixed(1)} ${y2.toFixed(1)}`
|
|
1178
|
+
);
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
layoutEdges();
|
|
1182
|
+
window.addEventListener('resize', layoutEdges);
|
|
1183
|
+
// re-layout once fonts load
|
|
1184
|
+
if (document.fonts && document.fonts.ready) {
|
|
1185
|
+
document.fonts.ready.then(layoutEdges);
|
|
1186
|
+
}
|
|
1187
|
+
// observe canvas size changes
|
|
1188
|
+
if (window.ResizeObserver) {
|
|
1189
|
+
const ro = new ResizeObserver(layoutEdges);
|
|
1190
|
+
ro.observe(canvasEl);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// ---- Detail panel rendering ---------------------------------------------
|
|
1194
|
+
const detailEl = document.getElementById('detail');
|
|
1195
|
+
const clearBtn = document.getElementById('flow-clear');
|
|
1196
|
+
|
|
1197
|
+
function renderEmpty() {
|
|
1198
|
+
detailEl.innerHTML = `
|
|
1199
|
+
<div class="steps-empty">
|
|
1200
|
+
<span class="arrow">↑</span> pick a flow above to highlight the path through the system,
|
|
1201
|
+
or click any module on the canvas to see its details.
|
|
1202
|
+
</div>
|
|
1203
|
+
`;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function renderFlow(flow) {
|
|
1207
|
+
let html = `<div style="font-size:11px; color:var(--fg-3); line-height:1.5; margin-bottom:14px; padding:10px 12px; border:1px solid var(--border); border-left:2px solid var(--highlight); border-radius:4px; background:var(--hl-bg);">${escapeHTML(flow.summary)}</div>`;
|
|
1208
|
+
flow.steps.forEach((s, i) => {
|
|
1209
|
+
html += `
|
|
1210
|
+
<div class="step">
|
|
1211
|
+
<div class="step-num">${i + 1}</div>
|
|
1212
|
+
<div class="step-body">${formatStep(s)}</div>
|
|
1213
|
+
</div>
|
|
1214
|
+
`;
|
|
1215
|
+
});
|
|
1216
|
+
detailEl.innerHTML = html;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function renderNode(node) {
|
|
1220
|
+
const filesHtml = node.files && node.files.length
|
|
1221
|
+
? `<div class="node-detail-files">
|
|
1222
|
+
<span class="label">Files</span>
|
|
1223
|
+
${node.files.map(f => escapeHTML(f)).join('<br>')}
|
|
1224
|
+
</div>`
|
|
1225
|
+
: '';
|
|
1226
|
+
detailEl.innerHTML = `
|
|
1227
|
+
<div class="node-detail" style="--c: var(--${node.type})">
|
|
1228
|
+
<div class="node-detail-type">${escapeHTML(node.type)}</div>
|
|
1229
|
+
<div class="node-detail-title">${softBreak(node.title)}${node.subtitle ? ` <span style="font-weight:400;color:var(--fg-4);font-size:11px">· ${escapeHTML(node.subtitle)}</span>` : ''}</div>
|
|
1230
|
+
<div class="node-detail-desc">${escapeHTML(node.summary)}</div>
|
|
1231
|
+
${filesHtml}
|
|
1232
|
+
</div>
|
|
1233
|
+
<div class="steps-empty">
|
|
1234
|
+
Pick a flow to see how this module participates.
|
|
1235
|
+
</div>
|
|
1236
|
+
`;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
function formatStep(s) {
|
|
1240
|
+
// turn `code` into <code>code</code> and escape the rest
|
|
1241
|
+
const parts = [];
|
|
1242
|
+
let i = 0;
|
|
1243
|
+
s.replace(/`([^`]+)`/g, (m, code, idx) => {
|
|
1244
|
+
parts.push(escapeHTML(s.slice(i, idx)));
|
|
1245
|
+
parts.push('<code>' + escapeHTML(code) + '</code>');
|
|
1246
|
+
i = idx + m.length;
|
|
1247
|
+
return m;
|
|
1248
|
+
});
|
|
1249
|
+
parts.push(escapeHTML(s.slice(i)));
|
|
1250
|
+
return parts.join('');
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// ---- Selection state ----------------------------------------------------
|
|
1254
|
+
function clearSelection() {
|
|
1255
|
+
document.body.dataset.mode = '';
|
|
1256
|
+
Object.values(nodeEls).forEach(el => { el.classList.remove('in-flow'); el.classList.remove('is-selected'); });
|
|
1257
|
+
Object.values(edgeEls).forEach(el => { el.classList.remove('in-flow'); el.setAttribute('marker-end', 'url(#arrow-default)'); });
|
|
1258
|
+
Object.values(flowEls).forEach(el => el.classList.remove('active'));
|
|
1259
|
+
clearBtn.hidden = true;
|
|
1260
|
+
renderEmpty();
|
|
1261
|
+
}
|
|
1262
|
+
clearSelection();
|
|
1263
|
+
|
|
1264
|
+
function selectFlow(id) {
|
|
1265
|
+
// Toggle: clicking the active flow chip clears it.
|
|
1266
|
+
if (document.body.dataset.mode === 'flow' && flowEls[id]?.classList.contains('active')) {
|
|
1267
|
+
clearSelection();
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
const f = data.flows.find(x => x.id === id);
|
|
1271
|
+
if (!f) return;
|
|
1272
|
+
document.body.dataset.mode = 'flow';
|
|
1273
|
+
Object.values(nodeEls).forEach(el => { el.classList.remove('in-flow'); el.classList.remove('is-selected'); });
|
|
1274
|
+
Object.values(edgeEls).forEach(el => { el.classList.remove('in-flow'); el.setAttribute('marker-end', 'url(#arrow-default)'); });
|
|
1275
|
+
Object.values(flowEls).forEach(el => el.classList.remove('active'));
|
|
1276
|
+
f.nodes.forEach(nid => nodeEls[nid] && nodeEls[nid].classList.add('in-flow'));
|
|
1277
|
+
f.edges.forEach(eid => {
|
|
1278
|
+
const e = edgeEls[eid];
|
|
1279
|
+
if (e) { e.classList.add('in-flow'); e.setAttribute('marker-end', 'url(#arrow-flow)'); }
|
|
1280
|
+
});
|
|
1281
|
+
flowEls[id].classList.add('active');
|
|
1282
|
+
clearBtn.hidden = false;
|
|
1283
|
+
renderFlow(f);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
function selectNode(id) {
|
|
1287
|
+
// Toggle: clicking the same selected node clears the node selection.
|
|
1288
|
+
if (document.body.dataset.mode === 'node' && nodeEls[id]?.classList.contains('is-selected')) {
|
|
1289
|
+
clearSelection();
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const n = data.nodes.find(x => x.id === id);
|
|
1293
|
+
if (!n) return;
|
|
1294
|
+
document.body.dataset.mode = 'node';
|
|
1295
|
+
Object.values(nodeEls).forEach(el => { el.classList.remove('in-flow'); el.classList.remove('is-selected'); });
|
|
1296
|
+
Object.values(edgeEls).forEach(el => { el.classList.remove('in-flow'); el.setAttribute('marker-end', 'url(#arrow-default)'); });
|
|
1297
|
+
Object.values(flowEls).forEach(el => el.classList.remove('active'));
|
|
1298
|
+
nodeEls[id].classList.add('is-selected');
|
|
1299
|
+
clearBtn.hidden = false;
|
|
1300
|
+
renderNode(n);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Click on empty canvas area = clear selection
|
|
1304
|
+
document.getElementById('canvas').addEventListener('click', (e) => {
|
|
1305
|
+
if (e.target.closest('.node')) return;
|
|
1306
|
+
if (document.body.dataset.mode) clearSelection();
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
// ESC also clears selection
|
|
1310
|
+
document.addEventListener('keydown', (e) => {
|
|
1311
|
+
if (e.key === 'Escape' && document.body.dataset.mode) clearSelection();
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// ---- helpers ------------------------------------------------------------
|
|
1315
|
+
function escapeHTML(s) {
|
|
1316
|
+
return String(s).replace(/[&<>"']/g, c => ({
|
|
1317
|
+
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
|
1318
|
+
})[c]);
|
|
1319
|
+
}
|
|
1320
|
+
// Insert <wbr> soft hyphens at slashes, dots, and lowercase→uppercase
|
|
1321
|
+
// boundaries so monospace file paths break at natural points.
|
|
1322
|
+
function softBreak(s) {
|
|
1323
|
+
return escapeHTML(s)
|
|
1324
|
+
.replace(/([\/.\-_])/g, '$1<wbr>')
|
|
1325
|
+
.replace(/([a-z])([A-Z])/g, '$1<wbr>$2');
|
|
1326
|
+
}
|
|
1327
|
+
})();
|
|
1328
|
+
</script>
|
|
1329
|
+
</body>
|
|
1330
|
+
</html>
|