@julioborges/gantry 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,26 +16,40 @@
16
16
  const card = document.createElement("div");
17
17
  card.className = "card";
18
18
 
19
- const title = document.createElement("strong");
20
- title.textContent = issue.issue;
19
+ const title = document.createElement("div");
20
+ title.className = "card-title";
21
+ const titleText = document.createElement("span");
22
+ titleText.textContent = issue.issue;
23
+ title.appendChild(titleText);
24
+
25
+ if (issue.operatorWaiting) {
26
+ title.appendChild(badge("AWAITING OPERATOR", "waiting"));
27
+ }
21
28
  card.appendChild(title);
22
- card.appendChild(document.createElement("br"));
23
29
 
24
- if (issue.branch) card.appendChild(badge("branch: " + issue.branch));
25
- if (issue.worktree) card.appendChild(badge("worktree: " + issue.worktree));
30
+ const badgesContainer = document.createElement("div");
31
+ badgesContainer.className = "card-badges";
32
+
33
+ if (issue.branch) {
34
+ badgesContainer.appendChild(badge("br: " + issue.branch, "branch"));
35
+ }
36
+ if (issue.worktree) {
37
+ badgesContainer.appendChild(badge("wt: " + issue.worktree, "branch"));
38
+ }
26
39
  Object.keys(issue.models || {}).forEach(function (role) {
27
- card.appendChild(badge(role + ": " + issue.models[role]));
40
+ badgesContainer.appendChild(badge(role + ": " + issue.models[role], "model"));
28
41
  });
29
42
  if (issue.correctionBudget) {
30
- card.appendChild(
31
- badge("corrections: " + issue.correctionBudget.used + "/" + issue.correctionBudget.ceiling)
43
+ badgesContainer.appendChild(
44
+ badge("corrections: " + issue.correctionBudget.used + "/" + issue.correctionBudget.ceiling, "budget")
32
45
  );
33
46
  }
34
47
  if (typeof issue.elapsedPhaseSeconds === "number") {
35
- card.appendChild(badge("elapsed: " + issue.elapsedPhaseSeconds + "s"));
48
+ badgesContainer.appendChild(badge(issue.elapsedPhaseSeconds + "s", "elapsed"));
36
49
  }
37
- if (issue.operatorWaiting) {
38
- card.appendChild(badge("awaiting operator", "waiting"));
50
+
51
+ if (badgesContainer.children.length > 0) {
52
+ card.appendChild(badgesContainer);
39
53
  }
40
54
  return card;
41
55
  }
@@ -44,31 +58,68 @@
44
58
  const section = document.createElement("section");
45
59
  section.className = "swimlane" + (run.stale ? " stale" : "");
46
60
 
61
+ const header = document.createElement("div");
62
+ header.className = "swimlane-header";
63
+
64
+ const titleGroup = document.createElement("div");
65
+ titleGroup.className = "swimlane-title-group";
66
+
47
67
  const heading = document.createElement("h2");
48
- heading.textContent =
49
- run.repositoryRoot + " — " + run.run + (run.stale ? " (stale)" : "") + " [tier: " + run.tier + "]";
50
- section.appendChild(heading);
68
+ heading.textContent = run.repositoryRoot + " — " + run.run;
69
+ titleGroup.appendChild(heading);
70
+
71
+ const meta = document.createElement("div");
72
+ meta.className = "swimlane-meta";
73
+
74
+ meta.appendChild(badge("tier: " + run.tier, "tier"));
75
+
76
+ if (run.stale) {
77
+ meta.appendChild(badge("STALE", "stale"));
78
+ } else {
79
+ meta.appendChild(badge("LIVE", "live"));
80
+ }
51
81
 
52
82
  if (run.compactionAt) {
53
- section.appendChild(badge("compacted at " + run.compactionAt, "compaction"));
83
+ meta.appendChild(badge("compacted: " + run.compactionAt, "compaction"));
54
84
  }
55
85
 
86
+ header.appendChild(titleGroup);
87
+ header.appendChild(meta);
88
+ section.appendChild(header);
89
+
56
90
  const columnsEl = document.createElement("div");
57
91
  columnsEl.className = "columns";
58
92
 
59
93
  columns.forEach(function (columnName) {
60
94
  const columnEl = document.createElement("div");
61
95
  columnEl.className = "column";
62
- const title = document.createElement("h3");
96
+
97
+ const colHeader = document.createElement("div");
98
+ colHeader.className = "column-header";
99
+
100
+ const title = document.createElement("span");
101
+ title.className = "column-title";
63
102
  title.textContent = columnName;
64
- columnEl.appendChild(title);
65
- run.issues
66
- .filter(function (issue) {
67
- return issue.column === columnName;
68
- })
69
- .forEach(function (issue) {
70
- columnEl.appendChild(renderCard(issue));
71
- });
103
+ colHeader.appendChild(title);
104
+
105
+ const matchingIssues = run.issues.filter(function (issue) {
106
+ return issue.column === columnName;
107
+ });
108
+
109
+ const count = document.createElement("span");
110
+ count.className = "column-count";
111
+ count.textContent = matchingIssues.length;
112
+ colHeader.appendChild(count);
113
+
114
+ columnEl.appendChild(colHeader);
115
+
116
+ const cardsContainer = document.createElement("div");
117
+ cardsContainer.className = "column-cards";
118
+ matchingIssues.forEach(function (issue) {
119
+ cardsContainer.appendChild(renderCard(issue));
120
+ });
121
+ columnEl.appendChild(cardsContainer);
122
+
72
123
  columnsEl.appendChild(columnEl);
73
124
  });
74
125
 
@@ -79,6 +130,21 @@
79
130
  function render(state) {
80
131
  const root = document.getElementById("swimlanes");
81
132
  root.textContent = "";
133
+
134
+ if (!state.runs || state.runs.length === 0) {
135
+ const empty = document.createElement("div");
136
+ empty.className = "empty-state";
137
+ const title = document.createElement("div");
138
+ title.className = "empty-state-title";
139
+ title.textContent = "NO ACTIVE RUNS DETECTED";
140
+ empty.appendChild(title);
141
+ const desc = document.createElement("p");
142
+ desc.textContent = "No execution units found in ~/.gantry/state.";
143
+ empty.appendChild(desc);
144
+ root.appendChild(empty);
145
+ return;
146
+ }
147
+
82
148
  state.runs.forEach(function (run) {
83
149
  root.appendChild(renderRun(run, state.columns));
84
150
  });
@@ -1,16 +1,66 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="en" class="dark">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
- <title>Gantry Dashboard</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Gantry Dashboard // Telemetry & Run Log</title>
6
7
  <link rel="stylesheet" href="/style.css" />
7
8
  </head>
8
- <body>
9
- <header>
10
- <h1>Gantry Dashboard</h1>
11
- <p>Read-only view of every Run. Nothing on this page can change an Issue, a policy, a Run log, a branch or a worktree.</p>
9
+ <body class="industrial-grid">
10
+ <!-- Industrial Header / Rig Bar -->
11
+ <header class="rig-header">
12
+ <div class="header-inner">
13
+ <div class="brand-group">
14
+ <div class="brand-mark">
15
+ <span class="diamond"></span>
16
+ <span class="brand-title">GANTRY<span class="accent">::</span>DASHBOARD</span>
17
+ </div>
18
+ <span class="telemetry-badge">v1.0.4 [STANDBY]</span>
19
+ </div>
20
+ <div class="header-status">
21
+ <span class="status-indicator"></span>
22
+ <span class="status-text">LOOPBACK TELEMETRY // READ-ONLY</span>
23
+ </div>
24
+ </div>
12
25
  </header>
13
- <main id="swimlanes"></main>
26
+
27
+ <!-- Sub-header / System Status Strip -->
28
+ <div class="status-strip">
29
+ <div class="status-strip-inner">
30
+ <div class="status-strip-left">
31
+ <span class="signal-dot"></span>
32
+ <span class="strip-label">SYSTEM RIG // MULTI-RUN KANBAN ACTIVE</span>
33
+ </div>
34
+ <div class="status-strip-badges">
35
+ <span class="badge-tag">STATUS: RUNNING</span>
36
+ <span class="badge-tag">HARNESS-NEUTRAL</span>
37
+ <span class="badge-tag accent-tag">ZERO MUTATION</span>
38
+ </div>
39
+ </div>
40
+ </div>
41
+
42
+ <!-- Main Container -->
43
+ <main class="dashboard-main">
44
+ <div id="swimlanes"></div>
45
+ </main>
46
+
47
+ <!-- Telemetry Footer -->
48
+ <footer class="rig-footer">
49
+ <div class="footer-inner">
50
+ <div class="footer-left">
51
+ <span class="accent-diamond">◆</span>
52
+ <span>GANTRY AGENTIC SDLC</span>
53
+ <span>|</span>
54
+ <span>APACHE-2.0</span>
55
+ <span>|</span>
56
+ <span>DETERMINISTIC VERIFICATION</span>
57
+ </div>
58
+ <div class="footer-right">
59
+ <span>OPERATOR IN LOOP // READ-ONLY DISPATCH</span>
60
+ </div>
61
+ </div>
62
+ </footer>
63
+
14
64
  <script src="/app.js"></script>
15
65
  </body>
16
66
  </html>
@@ -1,74 +1,507 @@
1
+ /* Gantry Dashboard Industrial Dark Theme — Matching Gantry Site Visual Language */
2
+
1
3
  :root {
2
- color-scheme: light dark;
3
- --border: #8888;
4
+ --obsidian-950: #05070a;
5
+ --obsidian-900: #0a0d14;
6
+ --obsidian-850: #0f141f;
7
+ --obsidian-800: #141a29;
8
+ --obsidian-700: #1f293d;
9
+ --obsidian-600: #2a3752;
10
+
11
+ --amber-glow: #ff9900;
12
+ --amber-accent: #f59e0b;
13
+ --amber-dim: rgba(255, 153, 0, 0.15);
14
+
15
+ --slate-100: #f1f5f9;
16
+ --slate-200: #e2e8f0;
17
+ --slate-300: #cbd5e1;
18
+ --slate-400: #94a3b8;
19
+ --slate-500: #64748b;
20
+
21
+ --red-alert: #ef4444;
22
+ --red-dim: rgba(239, 68, 68, 0.15);
23
+ --green-ok: #10b981;
24
+ --green-dim: rgba(16, 185, 129, 0.15);
25
+ --purple-accent: #a855f7;
26
+ --purple-dim: rgba(168, 85, 247, 0.15);
27
+
28
+ --font-mono: "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
29
+ }
30
+
31
+ * {
32
+ box-sizing: border-box;
33
+ margin: 0;
34
+ padding: 0;
4
35
  }
5
36
 
6
37
  body {
7
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
8
- margin: 1.5rem;
38
+ background-color: var(--obsidian-900);
39
+ color: var(--slate-300);
40
+ font-family: var(--font-mono);
41
+ font-size: 13px;
42
+ line-height: 1.5;
43
+ min-height: 100vh;
44
+ display: flex;
45
+ flex-col: column;
46
+ flex-direction: column;
47
+ overflow-x: hidden;
48
+ -webkit-font-smoothing: antialiased;
49
+ }
50
+
51
+ /* Technical Grid Pattern */
52
+ .industrial-grid {
53
+ background-size: 32px 32px;
54
+ background-image:
55
+ linear-gradient(to right, rgba(255, 153, 0, 0.04) 1px, transparent 1px),
56
+ linear-gradient(to bottom, rgba(255, 153, 0, 0.04) 1px, transparent 1px);
57
+ }
58
+
59
+ /* Scrollbars */
60
+ ::-webkit-scrollbar {
61
+ width: 8px;
62
+ height: 8px;
63
+ }
64
+ ::-webkit-scrollbar-track {
65
+ background: var(--obsidian-900);
66
+ border-left: 1px solid var(--obsidian-700);
67
+ }
68
+ ::-webkit-scrollbar-thumb {
69
+ background: var(--obsidian-700);
70
+ border-radius: 2px;
71
+ }
72
+ ::-webkit-scrollbar-thumb:hover {
73
+ background: var(--amber-accent);
74
+ }
75
+
76
+ /* Header */
77
+ .rig-header {
78
+ position: sticky;
79
+ top: 0;
80
+ z-index: 50;
81
+ border-bottom: 1px solid var(--obsidian-700);
82
+ background-color: rgba(10, 13, 20, 0.95);
83
+ backdrop-filter: blur(8px);
84
+ }
85
+
86
+ .header-inner {
87
+ max-width: 1600px;
88
+ margin: 0 auto;
89
+ padding: 0 1.5rem;
90
+ height: 3.5rem;
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: space-between;
94
+ gap: 1rem;
95
+ }
96
+
97
+ .brand-group {
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 0.75rem;
101
+ }
102
+
103
+ .brand-mark {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 0.5rem;
107
+ font-weight: 700;
108
+ font-size: 1rem;
109
+ letter-spacing: 0.05em;
110
+ color: var(--slate-100);
111
+ }
112
+
113
+ .diamond {
114
+ display: inline-block;
115
+ width: 10px;
116
+ height: 10px;
117
+ background-color: var(--amber-glow);
118
+ transform: rotate(45deg);
119
+ box-shadow: 0 0 8px rgba(255, 153, 0, 0.6);
120
+ }
121
+
122
+ .accent {
123
+ color: var(--amber-accent);
124
+ }
125
+
126
+ .telemetry-badge {
127
+ display: inline-flex;
128
+ align-items: center;
129
+ padding: 0.15rem 0.5rem;
130
+ font-size: 11px;
131
+ background-color: var(--obsidian-800);
132
+ color: var(--amber-accent);
133
+ border: 1px solid var(--obsidian-700);
134
+ font-weight: 600;
135
+ }
136
+
137
+ .header-status {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 0.5rem;
141
+ font-size: 11px;
142
+ color: var(--slate-400);
143
+ }
144
+
145
+ .status-indicator {
146
+ width: 8px;
147
+ height: 8px;
148
+ background-color: var(--amber-glow);
149
+ border-radius: 50%;
150
+ box-shadow: 0 0 8px var(--amber-glow);
151
+ animation: pulse-glow 2s infinite ease-in-out;
152
+ }
153
+
154
+ @keyframes pulse-glow {
155
+ 0%, 100% { opacity: 1; transform: scale(1); }
156
+ 50% { opacity: 0.4; transform: scale(0.85); }
157
+ }
158
+
159
+ /* Status Sub-strip */
160
+ .status-strip {
161
+ border-bottom: 1px solid var(--obsidian-700);
162
+ background-color: rgba(5, 7, 10, 0.8);
163
+ font-size: 11px;
164
+ }
165
+
166
+ .status-strip-inner {
167
+ max-width: 1600px;
168
+ margin: 0 auto;
169
+ padding: 0.5rem 1.5rem;
170
+ display: flex;
171
+ flex-wrap: wrap;
172
+ align-items: center;
173
+ justify-content: space-between;
174
+ gap: 0.75rem;
175
+ }
176
+
177
+ .status-strip-left {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 0.5rem;
181
+ color: var(--amber-accent);
182
+ font-weight: 600;
9
183
  }
10
184
 
11
- header p {
12
- opacity: 0.75;
185
+ .signal-dot {
186
+ width: 6px;
187
+ height: 6px;
188
+ background-color: var(--amber-glow);
13
189
  }
14
190
 
191
+ .status-strip-badges {
192
+ display: flex;
193
+ flex-wrap: wrap;
194
+ align-items: center;
195
+ gap: 0.5rem;
196
+ }
197
+
198
+ .badge-tag {
199
+ padding: 0.15rem 0.5rem;
200
+ background-color: var(--obsidian-900);
201
+ border: 1px solid var(--obsidian-700);
202
+ color: var(--slate-400);
203
+ font-size: 10px;
204
+ text-transform: uppercase;
205
+ }
206
+
207
+ .badge-tag.accent-tag {
208
+ border-color: rgba(245, 158, 11, 0.5);
209
+ color: var(--amber-glow);
210
+ }
211
+
212
+ /* Main Workspace */
213
+ .dashboard-main {
214
+ flex: 1;
215
+ max-width: 1600px;
216
+ width: 100%;
217
+ margin: 0 auto;
218
+ padding: 1.5rem;
219
+ }
220
+
221
+ /* Empty state */
222
+ .empty-state {
223
+ border: 1px dashed var(--obsidian-700);
224
+ background-color: var(--obsidian-950);
225
+ padding: 3rem 2rem;
226
+ text-align: center;
227
+ color: var(--slate-500);
228
+ }
229
+ .empty-state-title {
230
+ color: var(--amber-accent);
231
+ font-size: 14px;
232
+ font-weight: 700;
233
+ margin-bottom: 0.5rem;
234
+ }
235
+
236
+ /* Swimlane / Execution Rig */
15
237
  .swimlane {
16
- border: 1px solid var(--border);
17
- border-radius: 0.5rem;
18
- margin-bottom: 1.5rem;
19
- padding: 0.75rem;
238
+ border: 1px solid var(--obsidian-700);
239
+ background-color: rgba(15, 20, 31, 0.85);
240
+ margin-bottom: 2rem;
241
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
242
+ position: relative;
243
+ transition: border-color 0.2s ease;
20
244
  }
21
245
 
22
246
  .swimlane.stale {
23
- border-color: #c0392b;
247
+ border-color: var(--red-alert);
248
+ }
249
+
250
+ .swimlane.stale .swimlane-header {
251
+ border-bottom-color: var(--red-alert);
252
+ }
253
+
254
+ /* Corner brackets on swimlane (matching site rig style) */
255
+ .swimlane::before,
256
+ .swimlane::after {
257
+ content: "";
258
+ position: absolute;
259
+ width: 8px;
260
+ height: 8px;
261
+ pointer-events: none;
262
+ }
263
+ .swimlane::before {
264
+ top: -1px;
265
+ left: -1px;
266
+ border-top: 2px solid var(--amber-glow);
267
+ border-left: 2px solid var(--amber-glow);
268
+ }
269
+ .swimlane::after {
270
+ bottom: -1px;
271
+ right: -1px;
272
+ border-bottom: 2px solid var(--amber-glow);
273
+ border-right: 2px solid var(--amber-glow);
274
+ }
275
+
276
+ .swimlane-header {
277
+ padding: 0.75rem 1rem;
278
+ border-bottom: 1px solid var(--obsidian-700);
279
+ background-color: var(--obsidian-950);
280
+ display: flex;
281
+ flex-wrap: wrap;
282
+ align-items: center;
283
+ justify-content: space-between;
284
+ gap: 0.75rem;
285
+ }
286
+
287
+ .swimlane-title-group {
288
+ display: flex;
289
+ flex-wrap: wrap;
290
+ align-items: center;
291
+ gap: 0.5rem;
24
292
  }
25
293
 
26
294
  .swimlane h2 {
27
- margin: 0 0 0.5rem 0;
28
- font-size: 1rem;
295
+ font-size: 12px;
296
+ font-weight: 700;
297
+ color: var(--slate-100);
298
+ letter-spacing: 0.03em;
299
+ text-transform: uppercase;
29
300
  }
30
301
 
302
+ .swimlane-meta {
303
+ display: flex;
304
+ flex-wrap: wrap;
305
+ align-items: center;
306
+ gap: 0.5rem;
307
+ }
308
+
309
+ /* Kanban Columns Grid */
31
310
  .columns {
32
311
  display: grid;
33
- grid-template-columns: repeat(8, minmax(8rem, 1fr));
34
- gap: 0.5rem;
312
+ grid-template-columns: repeat(8, minmax(130px, 1fr));
313
+ gap: 1px;
314
+ background-color: var(--obsidian-700);
315
+ overflow-x: auto;
35
316
  }
36
317
 
37
318
  .column {
38
- border: 1px dashed var(--border);
39
- border-radius: 0.375rem;
40
- padding: 0.5rem;
41
- min-height: 4rem;
319
+ background-color: var(--obsidian-900);
320
+ min-height: 180px;
321
+ padding: 0.65rem 0.5rem;
322
+ display: flex;
323
+ flex-direction: column;
42
324
  }
43
325
 
44
- .column h3 {
45
- margin: 0 0 0.5rem 0;
46
- font-size: 0.8rem;
326
+ .column-header {
327
+ display: flex;
328
+ align-items: center;
329
+ justify-content: space-between;
330
+ margin-bottom: 0.65rem;
331
+ padding-bottom: 0.4rem;
332
+ border-bottom: 1px solid var(--obsidian-800);
333
+ }
334
+
335
+ .column-title {
336
+ font-size: 10px;
337
+ font-weight: 700;
47
338
  text-transform: uppercase;
48
- opacity: 0.7;
339
+ letter-spacing: 0.08em;
340
+ color: var(--slate-400);
341
+ }
342
+
343
+ .column-count {
344
+ font-size: 9px;
345
+ padding: 0.1rem 0.35rem;
346
+ background-color: var(--obsidian-800);
347
+ border: 1px solid var(--obsidian-700);
348
+ color: var(--slate-500);
349
+ font-weight: 600;
49
350
  }
50
351
 
352
+ .column-cards {
353
+ display: flex;
354
+ flex-direction: column;
355
+ gap: 0.5rem;
356
+ flex: 1;
357
+ }
358
+
359
+ /* Card Rig */
51
360
  .card {
52
- border: 1px solid var(--border);
53
- border-radius: 0.25rem;
54
- padding: 0.35rem;
55
- margin-bottom: 0.35rem;
56
- font-size: 0.8rem;
361
+ border: 1px solid var(--obsidian-700);
362
+ background-color: var(--obsidian-950);
363
+ padding: 0.6rem;
364
+ transition: all 0.15s ease;
365
+ position: relative;
57
366
  }
58
367
 
368
+ .card:hover {
369
+ border-color: var(--amber-accent);
370
+ transform: translateY(-1px);
371
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
372
+ }
373
+
374
+ .card-title {
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: space-between;
378
+ font-size: 11px;
379
+ font-weight: 700;
380
+ color: var(--slate-100);
381
+ margin-bottom: 0.45rem;
382
+ }
383
+
384
+ .card-badges {
385
+ display: flex;
386
+ flex-wrap: wrap;
387
+ gap: 0.25rem;
388
+ }
389
+
390
+ /* Badges */
59
391
  .badge {
60
- display: inline-block;
61
- border-radius: 0.25rem;
62
- padding: 0 0.25rem;
63
- margin-right: 0.25rem;
64
- font-size: 0.7rem;
65
- border: 1px solid var(--border);
392
+ display: inline-flex;
393
+ align-items: center;
394
+ font-size: 9px;
395
+ font-weight: 500;
396
+ padding: 0.15rem 0.35rem;
397
+ border: 1px solid var(--obsidian-700);
398
+ background-color: var(--obsidian-850);
399
+ color: var(--slate-300);
400
+ text-transform: uppercase;
401
+ letter-spacing: 0.02em;
402
+ line-height: 1;
403
+ white-space: nowrap;
404
+ }
405
+
406
+ .badge.tier {
407
+ border-color: var(--amber-glow);
408
+ color: var(--amber-glow);
409
+ background-color: var(--amber-dim);
410
+ font-weight: 700;
411
+ }
412
+
413
+ .badge.stale {
414
+ border-color: var(--red-alert);
415
+ color: var(--red-alert);
416
+ background-color: var(--red-dim);
417
+ font-weight: 700;
418
+ }
419
+
420
+ .badge.live {
421
+ border-color: var(--green-ok);
422
+ color: var(--green-ok);
423
+ background-color: var(--green-dim);
66
424
  }
67
425
 
68
426
  .badge.waiting {
69
- border-color: #e67e22;
427
+ border-color: var(--amber-glow);
428
+ color: var(--amber-glow);
429
+ background-color: var(--amber-dim);
430
+ animation: pulse-glow 1.5s infinite;
431
+ font-weight: 700;
70
432
  }
71
433
 
72
434
  .badge.compaction {
73
- border-color: #8e44ad;
435
+ border-color: var(--purple-accent);
436
+ color: var(--purple-accent);
437
+ background-color: var(--purple-dim);
438
+ }
439
+
440
+ .badge.budget {
441
+ border-color: var(--obsidian-600);
442
+ color: var(--amber-accent);
443
+ }
444
+
445
+ .badge.elapsed {
446
+ border-color: var(--obsidian-600);
447
+ color: var(--slate-400);
448
+ }
449
+
450
+ .badge.model {
451
+ border-color: var(--obsidian-600);
452
+ color: var(--slate-300);
453
+ }
454
+
455
+ .badge.branch {
456
+ border-color: var(--obsidian-700);
457
+ color: var(--slate-400);
458
+ }
459
+
460
+ /* Footer */
461
+ .rig-footer {
462
+ border-top: 1px solid var(--obsidian-700);
463
+ background-color: var(--obsidian-950);
464
+ font-size: 11px;
465
+ padding: 1.25rem 0;
466
+ margin-top: auto;
467
+ }
468
+
469
+ .footer-inner {
470
+ max-width: 1600px;
471
+ margin: 0 auto;
472
+ padding: 0 1.5rem;
473
+ display: flex;
474
+ flex-wrap: wrap;
475
+ align-items: center;
476
+ justify-content: space-between;
477
+ gap: 0.75rem;
478
+ color: var(--slate-500);
479
+ }
480
+
481
+ .footer-left {
482
+ display: flex;
483
+ flex-wrap: wrap;
484
+ align-items: center;
485
+ gap: 0.5rem;
486
+ }
487
+
488
+ .accent-diamond {
489
+ color: var(--amber-glow);
490
+ }
491
+
492
+ .footer-right {
493
+ font-size: 10px;
494
+ letter-spacing: 0.05em;
495
+ }
496
+
497
+ @media (max-width: 1200px) {
498
+ .columns {
499
+ grid-template-columns: repeat(4, minmax(140px, 1fr));
500
+ }
501
+ }
502
+
503
+ @media (max-width: 768px) {
504
+ .columns {
505
+ grid-template-columns: repeat(2, minmax(130px, 1fr));
506
+ }
74
507
  }
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "python3 \".agents/skills/gantry/scripts/guard.py\" PreToolUse --json"
9
+ "command": "python3 \"skills/gantry/scripts/guard.py\" PreToolUse --json"
10
10
  }
11
11
  ]
12
12
  }
@@ -17,7 +17,7 @@
17
17
  "hooks": [
18
18
  {
19
19
  "type": "command",
20
- "command": "python3 \".agents/skills/gantry/scripts/guard.py\" PostToolUse --json"
20
+ "command": "python3 \"skills/gantry/scripts/guard.py\" PostToolUse --json"
21
21
  }
22
22
  ]
23
23
  }
@@ -569,8 +569,14 @@ def main() -> int:
569
569
 
570
570
  payload = read_payload()
571
571
  cwd = Path(args.cwd).resolve()
572
+ name = tool_name(payload) if payload else ""
573
+ normalized_name = re.sub(r"[^a-z]", "", name)
572
574
 
573
575
  def allow() -> int:
576
+ if args.event == "PostToolUse" and (not args.run_id or normalized_name != "invokesubagent"):
577
+ if args.json:
578
+ print("{}")
579
+ return 0
574
580
  if args.json:
575
581
  print(json.dumps({"decision": "allow"}, separators=(",", ":")))
576
582
  else:
@@ -119,7 +119,7 @@ def main() -> None:
119
119
  if ag_hook_frag_path.exists():
120
120
  ag_hook_frag = json.loads(ag_hook_frag_path.read_text(encoding="utf-8"))
121
121
  else:
122
- guard_cmd = 'python3 ".agents/skills/gantry/scripts/guard.py" PreToolUse --json'
122
+ guard_cmd = 'python3 "skills/gantry/scripts/guard.py" PreToolUse --json'
123
123
  ag_hook_frag = {
124
124
  "hooks": {
125
125
  "PreToolUse": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@julioborges/gantry",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Install the Gantry harness-neutral agentic SDLC skill pack.",
5
5
  "type": "commonjs",
6
6
  "license": "Apache-2.0",