opencastle 0.28.0 → 0.29.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.
- package/README.md +12 -3
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +0 -9
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +1 -0
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/cli/convoy/export.d.ts +1 -3
- package/dist/cli/convoy/export.d.ts.map +1 -1
- package/dist/cli/convoy/export.js +9 -88
- package/dist/cli/convoy/export.js.map +1 -1
- package/dist/cli/convoy/export.test.js +7 -186
- package/dist/cli/convoy/export.test.js.map +1 -1
- package/dist/cli/convoy/pipeline.d.ts.map +1 -1
- package/dist/cli/convoy/pipeline.js +0 -21
- package/dist/cli/convoy/pipeline.js.map +1 -1
- package/dist/cli/convoy/pipeline.test.js +0 -21
- package/dist/cli/convoy/pipeline.test.js.map +1 -1
- package/dist/cli/dashboard.d.ts.map +1 -1
- package/dist/cli/dashboard.js +32 -8
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/destroy.d.ts.map +1 -1
- package/dist/cli/destroy.js +13 -0
- package/dist/cli/destroy.js.map +1 -1
- package/dist/cli/dispute.d.ts +3 -0
- package/dist/cli/dispute.d.ts.map +1 -0
- package/dist/cli/dispute.js +25 -0
- package/dist/cli/dispute.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +14 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/eject.d.ts.map +1 -1
- package/dist/cli/eject.js +14 -0
- package/dist/cli/eject.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +14 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/log.d.ts +0 -11
- package/dist/cli/log.d.ts.map +1 -1
- package/dist/cli/log.js +2 -114
- package/dist/cli/log.js.map +1 -1
- package/dist/cli/pipeline.js +21 -5
- package/dist/cli/pipeline.js.map +1 -1
- package/dist/cli/run.js +2 -2
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +16 -0
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/watch.d.ts.map +1 -1
- package/dist/cli/watch.js +1 -3
- package/dist/cli/watch.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.test.ts +1 -0
- package/src/cli/convoy/engine.ts +0 -3
- package/src/cli/convoy/export.test.ts +7 -224
- package/src/cli/convoy/export.ts +10 -106
- package/src/cli/convoy/pipeline.test.ts +0 -25
- package/src/cli/convoy/pipeline.ts +0 -19
- package/src/cli/dashboard.ts +33 -8
- package/src/cli/destroy.ts +15 -0
- package/src/cli/dispute.ts +28 -0
- package/src/cli/doctor.ts +16 -1
- package/src/cli/eject.ts +16 -0
- package/src/cli/init.ts +16 -0
- package/src/cli/log.ts +2 -120
- package/src/cli/pipeline.ts +24 -5
- package/src/cli/run.ts +2 -2
- package/src/cli/update.ts +18 -0
- package/src/cli/watch.ts +1 -3
- package/src/dashboard/dist/_astro/index.Je1YjU_y.css +1 -0
- package/src/dashboard/dist/index.html +141 -1391
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/scripts/etl.test.ts +4 -62
- package/src/dashboard/scripts/etl.ts +13 -33
- package/src/dashboard/src/pages/index.astro +204 -1630
- package/src/dashboard/src/styles/dashboard.css +473 -7
- package/src/orchestrator/prompts/brainstorm.prompt.md +1 -0
- package/src/orchestrator/prompts/generate-convoy.prompt.md +7 -0
- package/src/orchestrator/prompts/generate-prd.prompt.md +6 -0
- package/dist/cli/convoy/log-merge.test.d.ts +0 -2
- package/dist/cli/convoy/log-merge.test.d.ts.map +0 -1
- package/dist/cli/convoy/log-merge.test.js +0 -147
- package/dist/cli/convoy/log-merge.test.js.map +0 -1
- package/src/cli/convoy/log-merge.test.ts +0 -179
- package/src/dashboard/dist/_astro/index.6L3_HsPT.css +0 -1
|
@@ -275,7 +275,6 @@ body {
|
|
|
275
275
|
background: var(--bg-secondary);
|
|
276
276
|
border: 1px solid var(--border-color);
|
|
277
277
|
border-radius: 12px;
|
|
278
|
-
overflow: hidden;
|
|
279
278
|
transition: border-color var(--transition-fast);
|
|
280
279
|
}
|
|
281
280
|
|
|
@@ -1880,15 +1879,18 @@ body {
|
|
|
1880
1879
|
bottom: 100%;
|
|
1881
1880
|
left: 50%;
|
|
1882
1881
|
transform: translateX(-50%);
|
|
1883
|
-
padding: 0.
|
|
1882
|
+
padding: 0.5rem 0.875rem;
|
|
1884
1883
|
background: var(--bg-primary);
|
|
1885
1884
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
1886
1885
|
border-radius: 6px;
|
|
1887
|
-
font-size: 0.
|
|
1886
|
+
font-size: 0.8125rem;
|
|
1888
1887
|
color: var(--text-primary);
|
|
1889
|
-
max-width:
|
|
1888
|
+
max-width: 420px;
|
|
1889
|
+
min-width: 180px;
|
|
1890
1890
|
white-space: normal;
|
|
1891
1891
|
text-align: left;
|
|
1892
|
+
line-height: 1.5;
|
|
1893
|
+
word-break: break-word;
|
|
1892
1894
|
z-index: 100;
|
|
1893
1895
|
pointer-events: none;
|
|
1894
1896
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
@@ -1906,15 +1908,18 @@ body {
|
|
|
1906
1908
|
bottom: 100%;
|
|
1907
1909
|
left: 50%;
|
|
1908
1910
|
transform: translateX(-50%);
|
|
1909
|
-
padding: 0.
|
|
1911
|
+
padding: 0.5rem 0.875rem;
|
|
1910
1912
|
background: var(--bg-primary);
|
|
1911
1913
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
1912
1914
|
border-radius: 6px;
|
|
1913
|
-
font-size: 0.
|
|
1915
|
+
font-size: 0.8125rem;
|
|
1914
1916
|
color: var(--text-primary);
|
|
1915
|
-
max-width:
|
|
1917
|
+
max-width: 420px;
|
|
1918
|
+
min-width: 180px;
|
|
1916
1919
|
white-space: normal;
|
|
1917
1920
|
text-align: left;
|
|
1921
|
+
line-height: 1.5;
|
|
1922
|
+
word-break: break-word;
|
|
1918
1923
|
z-index: 100;
|
|
1919
1924
|
pointer-events: none;
|
|
1920
1925
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
@@ -2269,3 +2274,464 @@ body {
|
|
|
2269
2274
|
margin-bottom: 0.5rem;
|
|
2270
2275
|
font-style: italic;
|
|
2271
2276
|
}
|
|
2277
|
+
|
|
2278
|
+
/* ── View Management ───────────────────────────────────── */
|
|
2279
|
+
.view-home,
|
|
2280
|
+
.view-convoy-detail {
|
|
2281
|
+
display: flex;
|
|
2282
|
+
flex-direction: column;
|
|
2283
|
+
gap: 20px;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
[data-view-hidden] {
|
|
2287
|
+
display: none !important;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
/* ── Breadcrumbs ───────────────────────────────────────── */
|
|
2291
|
+
.breadcrumbs {
|
|
2292
|
+
display: flex;
|
|
2293
|
+
align-items: center;
|
|
2294
|
+
gap: 6px;
|
|
2295
|
+
font-size: 0.8125rem;
|
|
2296
|
+
color: var(--text-tertiary);
|
|
2297
|
+
flex-wrap: wrap;
|
|
2298
|
+
padding: 0;
|
|
2299
|
+
margin-bottom: -8px;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
.breadcrumbs__link {
|
|
2303
|
+
color: var(--text-secondary);
|
|
2304
|
+
text-decoration: none;
|
|
2305
|
+
transition: color var(--transition-fast);
|
|
2306
|
+
border-bottom: 1px solid transparent;
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
.breadcrumbs__link:hover {
|
|
2310
|
+
color: var(--text-accent);
|
|
2311
|
+
border-bottom-color: rgba(167, 139, 250, 0.4);
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
.breadcrumbs__link:focus-visible {
|
|
2315
|
+
outline: 2px solid var(--accent-blue);
|
|
2316
|
+
outline-offset: 2px;
|
|
2317
|
+
border-radius: 2px;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
.breadcrumbs__separator {
|
|
2321
|
+
color: var(--text-tertiary);
|
|
2322
|
+
opacity: 0.5;
|
|
2323
|
+
user-select: none;
|
|
2324
|
+
font-size: 0.75rem;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
.breadcrumbs__current {
|
|
2328
|
+
color: var(--text-accent);
|
|
2329
|
+
font-weight: 500;
|
|
2330
|
+
max-width: 320px;
|
|
2331
|
+
overflow: hidden;
|
|
2332
|
+
text-overflow: ellipsis;
|
|
2333
|
+
white-space: nowrap;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
/* ── Convoy List Section ───────────────────────────────── */
|
|
2337
|
+
.convoy-list-section {
|
|
2338
|
+
background: var(--bg-secondary);
|
|
2339
|
+
border: 1px solid var(--border-color);
|
|
2340
|
+
border-radius: 12px;
|
|
2341
|
+
transition: border-color var(--transition-fast);
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
.convoy-list-section:hover {
|
|
2345
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
.convoy-list-section__header {
|
|
2349
|
+
padding: 20px 24px 12px;
|
|
2350
|
+
border-bottom: 1px solid var(--border-color);
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
.convoy-list-section__header h2,
|
|
2354
|
+
.convoy-list-section__header .convoy-list-section__title {
|
|
2355
|
+
font-size: 0.9375rem;
|
|
2356
|
+
font-weight: 600;
|
|
2357
|
+
color: var(--text-primary);
|
|
2358
|
+
margin: 0;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
.convoy-list-section__desc {
|
|
2362
|
+
font-size: 0.75rem;
|
|
2363
|
+
color: var(--text-tertiary);
|
|
2364
|
+
margin-top: 2px;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
/* ── Convoy List Filters ─────────────────────────────── */
|
|
2368
|
+
.convoy-list-filters {
|
|
2369
|
+
display: flex;
|
|
2370
|
+
flex-wrap: wrap;
|
|
2371
|
+
gap: 10px;
|
|
2372
|
+
align-items: flex-end;
|
|
2373
|
+
padding: 14px 24px;
|
|
2374
|
+
background: rgba(255, 255, 255, 0.01);
|
|
2375
|
+
border-bottom: 1px solid var(--border-color);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
.convoy-list-filters__group {
|
|
2379
|
+
display: flex;
|
|
2380
|
+
flex-direction: column;
|
|
2381
|
+
gap: 4px;
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
.convoy-list-filters__group label {
|
|
2385
|
+
font-size: 0.6875rem;
|
|
2386
|
+
font-weight: 500;
|
|
2387
|
+
color: var(--text-tertiary);
|
|
2388
|
+
text-transform: uppercase;
|
|
2389
|
+
letter-spacing: 0.05em;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
.convoy-list-filters__input,
|
|
2393
|
+
.convoy-list-filters__select,
|
|
2394
|
+
.convoy-list-filters__date {
|
|
2395
|
+
height: 34px;
|
|
2396
|
+
padding: 0 10px;
|
|
2397
|
+
font-size: 0.8125rem;
|
|
2398
|
+
color: var(--text-primary);
|
|
2399
|
+
background: var(--bg-tertiary);
|
|
2400
|
+
border: 1px solid var(--border-color);
|
|
2401
|
+
border-radius: 8px;
|
|
2402
|
+
outline: none;
|
|
2403
|
+
font-family: inherit;
|
|
2404
|
+
transition: border-color var(--transition-fast);
|
|
2405
|
+
color-scheme: dark;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
.convoy-list-filters__input:focus,
|
|
2409
|
+
.convoy-list-filters__select:focus,
|
|
2410
|
+
.convoy-list-filters__date:focus {
|
|
2411
|
+
border-color: var(--border-accent);
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
.convoy-list-filters__input {
|
|
2415
|
+
width: 180px;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
.convoy-list-filters__date {
|
|
2419
|
+
width: 150px;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
.convoy-list-filters__select {
|
|
2423
|
+
min-width: 140px;
|
|
2424
|
+
cursor: pointer;
|
|
2425
|
+
appearance: none;
|
|
2426
|
+
-webkit-appearance: none;
|
|
2427
|
+
background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235a5a6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
2428
|
+
background-repeat: no-repeat;
|
|
2429
|
+
background-position: right 10px center;
|
|
2430
|
+
padding-right: 28px;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
.convoy-list-filters__reset {
|
|
2434
|
+
height: 34px;
|
|
2435
|
+
padding: 0 14px;
|
|
2436
|
+
font-size: 0.75rem;
|
|
2437
|
+
font-weight: 500;
|
|
2438
|
+
font-family: inherit;
|
|
2439
|
+
color: var(--text-secondary);
|
|
2440
|
+
background: rgba(255, 255, 255, 0.06);
|
|
2441
|
+
border: none;
|
|
2442
|
+
border-radius: 8px;
|
|
2443
|
+
cursor: pointer;
|
|
2444
|
+
transition: background var(--transition-fast), color var(--transition-fast);
|
|
2445
|
+
align-self: flex-end;
|
|
2446
|
+
white-space: nowrap;
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
.convoy-list-filters__reset:hover {
|
|
2450
|
+
background: rgba(255, 255, 255, 0.10);
|
|
2451
|
+
color: var(--text-primary);
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
.convoy-list-filters__reset:focus-visible {
|
|
2455
|
+
outline: 2px solid var(--accent-blue);
|
|
2456
|
+
outline-offset: 2px;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
/* ── Convoy List Table ───────────────────────────────── */
|
|
2460
|
+
.convoy-list-table {
|
|
2461
|
+
width: 100%;
|
|
2462
|
+
border-collapse: collapse;
|
|
2463
|
+
font-size: 0.8125rem;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
.convoy-list-table thead {
|
|
2467
|
+
position: sticky;
|
|
2468
|
+
top: 0;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
.convoy-list-table th {
|
|
2472
|
+
padding: 10px 16px;
|
|
2473
|
+
font-size: 0.6875rem;
|
|
2474
|
+
font-weight: 600;
|
|
2475
|
+
color: var(--text-tertiary);
|
|
2476
|
+
text-align: left;
|
|
2477
|
+
text-transform: uppercase;
|
|
2478
|
+
letter-spacing: 0.06em;
|
|
2479
|
+
background: var(--bg-tertiary);
|
|
2480
|
+
border-bottom: 1px solid var(--border-color);
|
|
2481
|
+
white-space: nowrap;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
.convoy-list-table td {
|
|
2485
|
+
padding: 10px 16px;
|
|
2486
|
+
color: var(--text-secondary);
|
|
2487
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
|
2488
|
+
white-space: nowrap;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
.convoy-list-table tr {
|
|
2492
|
+
cursor: pointer;
|
|
2493
|
+
transition: background var(--transition-fast);
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
.convoy-list-table tbody tr:hover td {
|
|
2497
|
+
background: rgba(167, 139, 250, 0.05);
|
|
2498
|
+
color: var(--text-primary);
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
.convoy-list-table tbody tr:hover td:first-child {
|
|
2502
|
+
color: var(--text-accent);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
.convoy-list-table .td-convoy-name {
|
|
2506
|
+
font-weight: 600;
|
|
2507
|
+
color: var(--text-primary);
|
|
2508
|
+
max-width: 240px;
|
|
2509
|
+
overflow: hidden;
|
|
2510
|
+
text-overflow: ellipsis;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
/* ── Convoy List Empty ───────────────────────────────── */
|
|
2514
|
+
.convoy-list-empty {
|
|
2515
|
+
display: flex;
|
|
2516
|
+
flex-direction: column;
|
|
2517
|
+
align-items: center;
|
|
2518
|
+
justify-content: center;
|
|
2519
|
+
gap: 10px;
|
|
2520
|
+
padding: 48px 24px;
|
|
2521
|
+
text-align: center;
|
|
2522
|
+
color: var(--text-tertiary);
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
.convoy-list-empty__icon {
|
|
2526
|
+
font-size: 2rem;
|
|
2527
|
+
opacity: 0.35;
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
.convoy-list-empty__text {
|
|
2531
|
+
font-size: 0.875rem;
|
|
2532
|
+
color: var(--text-tertiary);
|
|
2533
|
+
max-width: 300px;
|
|
2534
|
+
line-height: 1.5;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
/* ── Convoy Detail Hero ──────────────────────────────── */
|
|
2538
|
+
.convoy-detail-hero {
|
|
2539
|
+
display: flex;
|
|
2540
|
+
flex-direction: column;
|
|
2541
|
+
gap: 12px;
|
|
2542
|
+
padding: 24px;
|
|
2543
|
+
background: var(--bg-secondary);
|
|
2544
|
+
border: 1px solid var(--border-color);
|
|
2545
|
+
border-radius: 12px;
|
|
2546
|
+
position: relative;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
.convoy-detail-hero::before {
|
|
2550
|
+
content: '';
|
|
2551
|
+
position: absolute;
|
|
2552
|
+
top: 0;
|
|
2553
|
+
left: 0;
|
|
2554
|
+
right: 0;
|
|
2555
|
+
height: 2px;
|
|
2556
|
+
background: var(--gradient-accent);
|
|
2557
|
+
opacity: 0.6;
|
|
2558
|
+
pointer-events: none;
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
.convoy-detail-hero__top {
|
|
2562
|
+
display: flex;
|
|
2563
|
+
align-items: center;
|
|
2564
|
+
gap: 14px;
|
|
2565
|
+
flex-wrap: wrap;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
.convoy-detail-hero__title {
|
|
2569
|
+
font-size: 1.5rem;
|
|
2570
|
+
font-weight: 700;
|
|
2571
|
+
color: var(--text-primary);
|
|
2572
|
+
letter-spacing: -0.02em;
|
|
2573
|
+
line-height: 1.25;
|
|
2574
|
+
margin: 0;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
.convoy-detail-hero__status {
|
|
2578
|
+
flex-shrink: 0;
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
.convoy-detail-hero__status .status-badge {
|
|
2582
|
+
padding: 0.25rem 0.875rem;
|
|
2583
|
+
font-size: 0.75rem;
|
|
2584
|
+
border-radius: 8px;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
.convoy-detail-hero__meta {
|
|
2588
|
+
display: flex;
|
|
2589
|
+
flex-wrap: wrap;
|
|
2590
|
+
gap: 20px;
|
|
2591
|
+
padding-top: 4px;
|
|
2592
|
+
border-top: 1px solid var(--border-color);
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
.convoy-detail-hero__meta-item {
|
|
2596
|
+
display: flex;
|
|
2597
|
+
flex-direction: column;
|
|
2598
|
+
gap: 2px;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
.convoy-detail-hero__meta-label {
|
|
2602
|
+
font-size: 0.6875rem;
|
|
2603
|
+
font-weight: 500;
|
|
2604
|
+
color: var(--text-tertiary);
|
|
2605
|
+
text-transform: uppercase;
|
|
2606
|
+
letter-spacing: 0.05em;
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
.convoy-detail-hero__meta-value {
|
|
2610
|
+
font-size: 0.875rem;
|
|
2611
|
+
color: var(--text-secondary);
|
|
2612
|
+
font-variant-numeric: tabular-nums;
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
/* ── Task Row Enhancement ────────────────────────────── */
|
|
2616
|
+
.task-row--clickable {
|
|
2617
|
+
cursor: pointer;
|
|
2618
|
+
transition: background var(--transition-fast);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
.task-row--clickable:hover td {
|
|
2622
|
+
background: rgba(167, 139, 250, 0.04);
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
.task-row--clickable td:first-child {
|
|
2626
|
+
position: relative;
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
.task-row--clickable td:first-child::before {
|
|
2630
|
+
content: '';
|
|
2631
|
+
position: absolute;
|
|
2632
|
+
left: 0;
|
|
2633
|
+
top: 0;
|
|
2634
|
+
bottom: 0;
|
|
2635
|
+
width: 2px;
|
|
2636
|
+
background: transparent;
|
|
2637
|
+
transition: background var(--transition-fast);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
.task-row--clickable:hover td:first-child::before {
|
|
2641
|
+
background: var(--border-accent);
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
.task-detail-expand {
|
|
2645
|
+
background: var(--bg-tertiary);
|
|
2646
|
+
border-bottom: 1px solid var(--border-color);
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
.task-detail-expand__inner {
|
|
2650
|
+
padding: 16px 20px;
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
.task-detail-expand__grid {
|
|
2654
|
+
display: grid;
|
|
2655
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
2656
|
+
gap: 12px 24px;
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
.task-detail-expand__field {
|
|
2660
|
+
display: flex;
|
|
2661
|
+
flex-direction: column;
|
|
2662
|
+
gap: 3px;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
.task-detail-expand__label {
|
|
2666
|
+
font-size: 0.6875rem;
|
|
2667
|
+
font-weight: 600;
|
|
2668
|
+
text-transform: uppercase;
|
|
2669
|
+
letter-spacing: 0.05em;
|
|
2670
|
+
color: var(--text-tertiary);
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
.task-detail-expand__value {
|
|
2674
|
+
font-size: 0.8125rem;
|
|
2675
|
+
color: var(--text-secondary);
|
|
2676
|
+
word-break: break-word;
|
|
2677
|
+
font-variant-numeric: tabular-nums;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
.task-detail-expand__value code {
|
|
2681
|
+
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
|
|
2682
|
+
font-size: 0.75rem;
|
|
2683
|
+
background: rgba(255, 255, 255, 0.06);
|
|
2684
|
+
padding: 1px 6px;
|
|
2685
|
+
border-radius: 4px;
|
|
2686
|
+
color: var(--text-accent);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
/* ── Responsive: Convoy List Filters ────────────────── */
|
|
2690
|
+
@media (max-width: 768px) {
|
|
2691
|
+
.convoy-list-filters {
|
|
2692
|
+
padding: 12px 16px;
|
|
2693
|
+
gap: 8px;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
.convoy-list-filters__input,
|
|
2697
|
+
.convoy-list-filters__select,
|
|
2698
|
+
.convoy-list-filters__date {
|
|
2699
|
+
width: 100%;
|
|
2700
|
+
min-width: unset;
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
.convoy-list-filters__group {
|
|
2704
|
+
flex: 1 1 calc(50% - 4px);
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
.convoy-list-filters__reset {
|
|
2708
|
+
flex: 1 1 100%;
|
|
2709
|
+
width: 100%;
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
.convoy-list-section__header {
|
|
2713
|
+
padding: 16px 16px 12px;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
@media (max-width: 480px) {
|
|
2718
|
+
.convoy-list-filters__group {
|
|
2719
|
+
flex: 1 1 100%;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
.convoy-detail-hero__title {
|
|
2723
|
+
font-size: 1.25rem;
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
.convoy-detail-hero {
|
|
2727
|
+
padding: 16px;
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
.task-detail-expand__grid {
|
|
2731
|
+
grid-template-columns: 1fr;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
.breadcrumbs__current {
|
|
2735
|
+
max-width: 180px;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
@@ -38,6 +38,7 @@ Research before proposing. Gather data, don't guess:
|
|
|
38
38
|
2. **Check documentation** — read `.opencastle/project.instructions.md`, `.opencastle/project/decisions.md`, `.opencastle/KNOWN-ISSUES.md` for constraints
|
|
39
39
|
3. **Check lessons learned** — read `.opencastle/LESSONS-LEARNED.md` for pitfalls in this area
|
|
40
40
|
4. **Identify affected layers** — which apps, libs, data stores, and third-party services are involved?
|
|
41
|
+
5. **Research unknown topics** — if the request involves a real-world person, place, organization, or topic you don't have confident knowledge about, **search the internet** using any available web search or fetch tools (e.g. `fetch_webpage`, web search MCP, or similar) to gather accurate facts. Never fabricate content about real-world subjects.
|
|
41
42
|
|
|
42
43
|
### 3. Generate Alternatives
|
|
43
44
|
|
|
@@ -151,6 +151,13 @@ Enables continuous re-execution triggered by file changes, cron, or git push.
|
|
|
151
151
|
| `cron` | `schedule` (5-field cron) | Re-run on cron schedule |
|
|
152
152
|
| `git-push` | `branch` | Re-run when new commits are pushed |
|
|
153
153
|
|
|
154
|
+
### Content Research Rule
|
|
155
|
+
|
|
156
|
+
When writing task `prompt` fields that involve creating content about real-world people, places, organizations, or topics — **include an explicit instruction in the prompt** telling the agent to search the internet first using any available web search or fetch tools (e.g. `fetch_webpage`, web search MCP). Agents must never fabricate bios, descriptions, histories, statistics, or any factual claims. If web search is unavailable, the prompt should instruct the agent to use placeholder text clearly marked as `[NEEDS RESEARCH]` rather than inventing content.
|
|
157
|
+
|
|
158
|
+
Example prompt suffix to include when content research is needed:
|
|
159
|
+
> "Before writing any content about [topic], search the internet for accurate information. Do not make up facts, descriptions, or biographical details. Use verified sources only."
|
|
160
|
+
|
|
154
161
|
### Task Fields
|
|
155
162
|
|
|
156
163
|
| Field | Type | Required | Default | Description |
|
|
@@ -20,6 +20,12 @@ You are the Team Lead. Convert the feature request below into a structured Produ
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## Research Before Writing
|
|
24
|
+
|
|
25
|
+
If the feature request involves a specific person, place, organization, topic, or any real-world subject you are not confident you have accurate knowledge about — **you MUST search the internet first** using any available web search or fetch tools (e.g. `fetch_webpage`, web search MCP, or similar). Use the search results to gather accurate facts, names, dates, descriptions, and other details.
|
|
26
|
+
|
|
27
|
+
**Never fabricate or hallucinate content** about real-world subjects. If you cannot verify a claim through web search, state what is unknown rather than inventing plausible-sounding text. This applies to all content: bios, descriptions, histories, statistics, quotes, and any factual claims.
|
|
28
|
+
|
|
23
29
|
## Required PRD Structure
|
|
24
30
|
|
|
25
31
|
Produce the PRD in Markdown using **exactly** the sections below. Do not skip or merge sections. Do not wrap the output in a code fence — output raw Markdown starting directly with the `#` heading.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"log-merge.test.d.ts","sourceRoot":"","sources":["../../../src/cli/convoy/log-merge.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync, realpathSync, mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
-
import { mergeConvoyLogs } from '../log.js';
|
|
6
|
-
const CONVOYS_REL = '.opencastle/logs/convoys';
|
|
7
|
-
const OUTPUT_REL = '.opencastle/logs/convoy-events.ndjson';
|
|
8
|
-
function makeBase() {
|
|
9
|
-
const dir = realpathSync(mkdtempSync(join(tmpdir(), 'log-merge-test-')));
|
|
10
|
-
mkdirSync(join(dir, CONVOYS_REL), { recursive: true });
|
|
11
|
-
return dir;
|
|
12
|
-
}
|
|
13
|
-
function writeConvoyFile(base, convoyId, records) {
|
|
14
|
-
const path = join(base, CONVOYS_REL, `${convoyId}.ndjson`);
|
|
15
|
-
writeFileSync(path, records.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf8');
|
|
16
|
-
}
|
|
17
|
-
let tmpDir;
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
tmpDir = makeBase();
|
|
20
|
-
});
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
describe('mergeConvoyLogs', () => {
|
|
25
|
-
it('returns zeros when convoys directory is missing', async () => {
|
|
26
|
-
rmSync(join(tmpDir, '.opencastle'), { recursive: true, force: true });
|
|
27
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir });
|
|
28
|
-
expect(result).toEqual({ merged: 0, deduplicated: 0, written: 0 });
|
|
29
|
-
});
|
|
30
|
-
it('returns zeros when convoys directory is empty', async () => {
|
|
31
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir });
|
|
32
|
-
expect(result).toEqual({ merged: 0, deduplicated: 0, written: 0 });
|
|
33
|
-
});
|
|
34
|
-
it('merges records from 3 convoy files', async () => {
|
|
35
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
36
|
-
{ _event_id: 1, timestamp: '2026-01-01T10:00:00.000Z', type: 'task_started' },
|
|
37
|
-
]);
|
|
38
|
-
writeConvoyFile(tmpDir, 'convoy-b', [
|
|
39
|
-
{ _event_id: 2, timestamp: '2026-01-02T10:00:00.000Z', type: 'task_done' },
|
|
40
|
-
]);
|
|
41
|
-
writeConvoyFile(tmpDir, 'convoy-c', [
|
|
42
|
-
{ _event_id: 3, timestamp: '2026-01-03T10:00:00.000Z', type: 'session' },
|
|
43
|
-
]);
|
|
44
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir });
|
|
45
|
-
expect(result.merged).toBe(3);
|
|
46
|
-
expect(result.written).toBe(3);
|
|
47
|
-
});
|
|
48
|
-
it('output is sorted by timestamp ascending', async () => {
|
|
49
|
-
writeConvoyFile(tmpDir, 'convoy-z', [
|
|
50
|
-
{ _event_id: 10, timestamp: '2026-03-01T00:00:00.000Z', type: 'task_done' },
|
|
51
|
-
{ _event_id: 11, timestamp: '2026-01-01T00:00:00.000Z', type: 'task_started' },
|
|
52
|
-
]);
|
|
53
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
54
|
-
{ _event_id: 12, timestamp: '2026-02-01T00:00:00.000Z', type: 'session' },
|
|
55
|
-
]);
|
|
56
|
-
const outputPath = join(tmpDir, 'merged.ndjson');
|
|
57
|
-
await mergeConvoyLogs({ basePath: tmpDir, output: outputPath });
|
|
58
|
-
const lines = readFileSync(outputPath, 'utf8').split('\n').filter(l => l.trim());
|
|
59
|
-
const timestamps = lines.map(l => JSON.parse(l).timestamp);
|
|
60
|
-
expect(timestamps).toEqual([
|
|
61
|
-
'2026-01-01T00:00:00.000Z',
|
|
62
|
-
'2026-02-01T00:00:00.000Z',
|
|
63
|
-
'2026-03-01T00:00:00.000Z',
|
|
64
|
-
]);
|
|
65
|
-
});
|
|
66
|
-
it('deduplicates records by _event_id (keeps first occurrence)', async () => {
|
|
67
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
68
|
-
{ _event_id: 5, timestamp: '2026-01-01T00:00:00.000Z', type: 'task_started', note: 'first' },
|
|
69
|
-
]);
|
|
70
|
-
writeConvoyFile(tmpDir, 'convoy-b', [
|
|
71
|
-
{ _event_id: 5, timestamp: '2026-01-01T00:00:00.000Z', type: 'task_started', note: 'duplicate' },
|
|
72
|
-
{ _event_id: 6, timestamp: '2026-01-02T00:00:00.000Z', type: 'task_done' },
|
|
73
|
-
]);
|
|
74
|
-
const outputPath = join(tmpDir, 'merged.ndjson');
|
|
75
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir, output: outputPath });
|
|
76
|
-
expect(result.merged).toBe(3);
|
|
77
|
-
expect(result.deduplicated).toBe(1);
|
|
78
|
-
expect(result.written).toBe(2);
|
|
79
|
-
const lines = readFileSync(outputPath, 'utf8').split('\n').filter(l => l.trim());
|
|
80
|
-
expect(lines).toHaveLength(2);
|
|
81
|
-
const first = JSON.parse(lines[0]);
|
|
82
|
-
expect(first.note).toBe('first');
|
|
83
|
-
});
|
|
84
|
-
it('filters by --since (inclusive)', async () => {
|
|
85
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
86
|
-
{ _event_id: 1, timestamp: '2026-01-01T00:00:00.000Z', type: 'session' },
|
|
87
|
-
{ _event_id: 2, timestamp: '2026-02-01T00:00:00.000Z', type: 'session' },
|
|
88
|
-
{ _event_id: 3, timestamp: '2026-03-01T00:00:00.000Z', type: 'session' },
|
|
89
|
-
]);
|
|
90
|
-
const outputPath = join(tmpDir, 'merged.ndjson');
|
|
91
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir, since: '2026-02-01T00:00:00.000Z', output: outputPath });
|
|
92
|
-
expect(result.written).toBe(2);
|
|
93
|
-
const lines = readFileSync(outputPath, 'utf8').split('\n').filter(l => l.trim());
|
|
94
|
-
expect(lines).toHaveLength(2);
|
|
95
|
-
});
|
|
96
|
-
it('filters by --until (inclusive)', async () => {
|
|
97
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
98
|
-
{ _event_id: 1, timestamp: '2026-01-01T00:00:00.000Z', type: 'session' },
|
|
99
|
-
{ _event_id: 2, timestamp: '2026-02-01T00:00:00.000Z', type: 'session' },
|
|
100
|
-
{ _event_id: 3, timestamp: '2026-03-01T00:00:00.000Z', type: 'session' },
|
|
101
|
-
]);
|
|
102
|
-
const outputPath = join(tmpDir, 'merged.ndjson');
|
|
103
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir, until: '2026-02-01T00:00:00.000Z', output: outputPath });
|
|
104
|
-
expect(result.written).toBe(2);
|
|
105
|
-
});
|
|
106
|
-
it('filters by --since and --until together', async () => {
|
|
107
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
108
|
-
{ _event_id: 1, timestamp: '2026-01-01T00:00:00.000Z', type: 'session' },
|
|
109
|
-
{ _event_id: 2, timestamp: '2026-02-15T00:00:00.000Z', type: 'session' },
|
|
110
|
-
{ _event_id: 3, timestamp: '2026-03-01T00:00:00.000Z', type: 'session' },
|
|
111
|
-
]);
|
|
112
|
-
const outputPath = join(tmpDir, 'merged.ndjson');
|
|
113
|
-
const result = await mergeConvoyLogs({
|
|
114
|
-
basePath: tmpDir,
|
|
115
|
-
since: '2026-02-01T00:00:00.000Z',
|
|
116
|
-
until: '2026-02-28T23:59:59.999Z',
|
|
117
|
-
output: outputPath,
|
|
118
|
-
});
|
|
119
|
-
expect(result.written).toBe(1);
|
|
120
|
-
const lines = readFileSync(outputPath, 'utf8').split('\n').filter(l => l.trim());
|
|
121
|
-
const record = JSON.parse(lines[0]);
|
|
122
|
-
expect(record.timestamp).toBe('2026-02-15T00:00:00.000Z');
|
|
123
|
-
});
|
|
124
|
-
it('writes to default output path when --output not specified', async () => {
|
|
125
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
126
|
-
{ _event_id: 1, timestamp: '2026-01-01T00:00:00.000Z', type: 'session' },
|
|
127
|
-
]);
|
|
128
|
-
await mergeConvoyLogs({ basePath: tmpDir });
|
|
129
|
-
const defaultPath = join(tmpDir, '.opencastle', 'logs', 'convoy-events.ndjson');
|
|
130
|
-
expect(existsSync(defaultPath)).toBe(true);
|
|
131
|
-
});
|
|
132
|
-
it('returns written: 0 when all records filtered out', async () => {
|
|
133
|
-
writeConvoyFile(tmpDir, 'convoy-a', [
|
|
134
|
-
{ _event_id: 1, timestamp: '2026-01-01T00:00:00.000Z', type: 'session' },
|
|
135
|
-
]);
|
|
136
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir, since: '2027-01-01T00:00:00.000Z' });
|
|
137
|
-
expect(result.written).toBe(0);
|
|
138
|
-
expect(result.merged).toBe(1);
|
|
139
|
-
});
|
|
140
|
-
it('skips malformed JSON lines gracefully', async () => {
|
|
141
|
-
const path = join(tmpDir, CONVOYS_REL, 'convoy-bad.ndjson');
|
|
142
|
-
writeFileSync(path, '{"_event_id":1,"timestamp":"2026-01-01T00:00:00.000Z","type":"session"}\nnot-valid-json\n{"_event_id":2,"timestamp":"2026-01-02T00:00:00.000Z","type":"task_done"}\n', 'utf8');
|
|
143
|
-
const result = await mergeConvoyLogs({ basePath: tmpDir });
|
|
144
|
-
expect(result.written).toBe(2);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
//# sourceMappingURL=log-merge.test.js.map
|