@hanzlaa/rcode 4.6.0 → 4.7.1
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 +47 -14
- package/package.json +1 -1
- package/rcode/bin/rcode-tools.cjs +1 -0
- package/rcode/references/agent-shared-rules.md +6 -0
- package/rcode/references/executor-playbook.md +1 -1
- package/rcode/references/planner-playbook.md +1 -0
- package/rcode/workflows/init.md +13 -0
- package/rcode/workflows/new-project-create-roadmap.md +18 -28
- package/server/lib/html/client/components/App.js +15 -1
- package/server/lib/html/client/components/PhaseGraph.js +20 -12
- package/server/lib/html/client/components/Sidebar.js +1 -0
- package/server/lib/html/client/components/Topbar.js +3 -3
- package/server/lib/html/client/components/dashboard/Blockers.js +3 -3
- package/server/lib/html/client/components/dashboard/CompletedTasks.js +4 -2
- package/server/lib/html/client/components/dashboard/InProgress.js +4 -3
- package/server/lib/html/client/components/dashboard/ProgressTimeline.js +11 -6
- package/server/lib/html/client/components/dashboard/RecentDecisions.js +4 -3
- package/server/lib/html/client/components/shared.js +111 -2
- package/server/lib/html/client/store.js +47 -0
- package/server/lib/html/client/util.js +1 -22
- package/server/lib/html/client/views/BacklogView.js +44 -0
- package/server/lib/html/client/views/DecisionsView.js +4 -3
- package/server/lib/html/client/views/FilesView.js +47 -34
- package/server/lib/html/client/views/KanbanView.js +8 -0
- package/server/lib/html/client/views/PhasesView.js +8 -3
- package/server/lib/html/client/views/SprintsView.js +9 -0
- package/server/lib/html/client.js +1 -0
- package/server/lib/html/css.js +96 -19
- package/server/lib/scanner.js +21 -6
package/server/lib/html/css.js
CHANGED
|
@@ -997,6 +997,7 @@ section .body {
|
|
|
997
997
|
.inline-file-entry {
|
|
998
998
|
display: flex;
|
|
999
999
|
align-items: center;
|
|
1000
|
+
gap: var(--space-2);
|
|
1000
1001
|
padding: var(--space-2) var(--space-4);
|
|
1001
1002
|
font-family: var(--font-mono);
|
|
1002
1003
|
font-size: var(--text-xs);
|
|
@@ -1009,8 +1010,49 @@ section .body {
|
|
|
1009
1010
|
.inline-file-entry:last-child { border-bottom: none; }
|
|
1010
1011
|
.inline-file-entry:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
1011
1012
|
.inline-file-entry.selected { background: rgba(34,182,207,0.1); color: var(--accent-primary); }
|
|
1013
|
+
.inline-file-entry svg { flex-shrink: 0; opacity: 0.7; }
|
|
1014
|
+
.inline-file-label {
|
|
1015
|
+
overflow: hidden;
|
|
1016
|
+
text-overflow: ellipsis;
|
|
1017
|
+
white-space: nowrap;
|
|
1018
|
+
}
|
|
1012
1019
|
.inline-file-group { margin-bottom: var(--space-3); }
|
|
1013
|
-
.inline-
|
|
1020
|
+
.inline-file-group-title {
|
|
1021
|
+
display: flex;
|
|
1022
|
+
align-items: center;
|
|
1023
|
+
gap: var(--space-2);
|
|
1024
|
+
font-size: var(--text-xs);
|
|
1025
|
+
font-weight: 600;
|
|
1026
|
+
color: var(--text-muted);
|
|
1027
|
+
text-transform: uppercase;
|
|
1028
|
+
letter-spacing: 0.07em;
|
|
1029
|
+
padding: var(--space-1) var(--space-3);
|
|
1030
|
+
}
|
|
1031
|
+
.inline-file-group-count {
|
|
1032
|
+
font-size: var(--text-2xs);
|
|
1033
|
+
font-weight: 500;
|
|
1034
|
+
color: var(--text-muted);
|
|
1035
|
+
text-transform: none;
|
|
1036
|
+
letter-spacing: 0;
|
|
1037
|
+
}
|
|
1038
|
+
.inline-subgroup { padding-left: var(--space-4); margin-bottom: var(--space-1); }
|
|
1039
|
+
.inline-subgroup-summary {
|
|
1040
|
+
display: flex;
|
|
1041
|
+
align-items: center;
|
|
1042
|
+
gap: var(--space-2);
|
|
1043
|
+
font-size: var(--text-xs);
|
|
1044
|
+
font-weight: 500;
|
|
1045
|
+
color: var(--text-secondary);
|
|
1046
|
+
cursor: pointer;
|
|
1047
|
+
padding: var(--space-1) var(--space-3);
|
|
1048
|
+
user-select: none;
|
|
1049
|
+
}
|
|
1050
|
+
.inline-file-total {
|
|
1051
|
+
margin-left: var(--space-2);
|
|
1052
|
+
font-size: var(--text-sm);
|
|
1053
|
+
font-weight: 400;
|
|
1054
|
+
color: var(--text-muted);
|
|
1055
|
+
}
|
|
1014
1056
|
.file-path-header {
|
|
1015
1057
|
font-size: var(--text-2xs);
|
|
1016
1058
|
color: var(--text-muted);
|
|
@@ -2254,6 +2296,27 @@ footer {
|
|
|
2254
2296
|
color var(--t-fast) var(--ease),
|
|
2255
2297
|
border-color var(--t-fast) var(--ease);
|
|
2256
2298
|
}
|
|
2299
|
+
.card-file-btn {
|
|
2300
|
+
float: right;
|
|
2301
|
+
margin-left: var(--space-3);
|
|
2302
|
+
height: 20px;
|
|
2303
|
+
padding: 0 var(--space-3);
|
|
2304
|
+
background: transparent;
|
|
2305
|
+
border: 1px solid var(--border-default);
|
|
2306
|
+
border-radius: var(--radius-2);
|
|
2307
|
+
color: var(--text-tertiary);
|
|
2308
|
+
font-size: 10px;
|
|
2309
|
+
font-weight: 500;
|
|
2310
|
+
cursor: pointer;
|
|
2311
|
+
transition: background var(--t-fast) var(--ease),
|
|
2312
|
+
color var(--t-fast) var(--ease),
|
|
2313
|
+
border-color var(--t-fast) var(--ease);
|
|
2314
|
+
}
|
|
2315
|
+
.card-file-btn:hover {
|
|
2316
|
+
background: var(--accent-blue);
|
|
2317
|
+
border-color: var(--accent-blue);
|
|
2318
|
+
color: #fff;
|
|
2319
|
+
}
|
|
2257
2320
|
.card-run-btn:hover {
|
|
2258
2321
|
background: var(--accent-green);
|
|
2259
2322
|
border-color: var(--accent-green);
|
|
@@ -2879,7 +2942,6 @@ footer {
|
|
|
2879
2942
|
}
|
|
2880
2943
|
.tb-btn--primary:hover { filter: brightness(1.05); border-color: transparent; }
|
|
2881
2944
|
.tb-btn--icon { padding: 8px 11px; }
|
|
2882
|
-
.tb-kebab { font-size: 16px; line-height: 1; letter-spacing: 1px; }
|
|
2883
2945
|
|
|
2884
2946
|
@media (max-width: 760px) {
|
|
2885
2947
|
.tb-sub { display: none; }
|
|
@@ -3718,7 +3780,6 @@ footer {
|
|
|
3718
3780
|
}
|
|
3719
3781
|
.tb-btn--primary:hover { filter: brightness(1.05); border-color: transparent; }
|
|
3720
3782
|
.tb-btn--icon { padding: 8px 11px; }
|
|
3721
|
-
.tb-kebab { font-size: 16px; line-height: 1; letter-spacing: 1px; }
|
|
3722
3783
|
|
|
3723
3784
|
@media (max-width: 760px) {
|
|
3724
3785
|
.tb-sub { display: none; }
|
|
@@ -5436,21 +5497,32 @@ summary:focus-visible,
|
|
|
5436
5497
|
.pg-tip text { font-size: var(--text-2xs); fill: var(--text-secondary); pointer-events: none; }
|
|
5437
5498
|
.pg-tip .pg-tip-title { fill: var(--text-primary); font-weight: 600; }
|
|
5438
5499
|
|
|
5439
|
-
/*
|
|
5440
|
-
.
|
|
5500
|
+
/* Sequence mode — no cross-phase dependencies: a connected chain of small
|
|
5501
|
+
pills in roadmap order (arrows imply order, not a graph edge). */
|
|
5502
|
+
.pg-hint-top {
|
|
5503
|
+
padding-top: var(--space-2);
|
|
5504
|
+
padding-bottom: var(--space-3);
|
|
5505
|
+
}
|
|
5506
|
+
.pg-seq {
|
|
5441
5507
|
display: flex;
|
|
5442
5508
|
flex-wrap: wrap;
|
|
5443
|
-
|
|
5444
|
-
|
|
5509
|
+
align-items: center;
|
|
5510
|
+
row-gap: var(--space-2);
|
|
5511
|
+
padding: 0 var(--space-5) var(--space-4);
|
|
5445
5512
|
}
|
|
5446
|
-
.pg-
|
|
5513
|
+
.pg-seq-item {
|
|
5447
5514
|
display: inline-flex;
|
|
5448
5515
|
align-items: center;
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5516
|
+
}
|
|
5517
|
+
.pg-seq-chip {
|
|
5518
|
+
display: inline-flex;
|
|
5519
|
+
align-items: center;
|
|
5520
|
+
gap: 6px;
|
|
5521
|
+
max-width: 190px;
|
|
5522
|
+
padding: 3px var(--space-3);
|
|
5523
|
+
border: 1px solid var(--border-default);
|
|
5524
|
+
border-left: 3px solid var(--pg-todo);
|
|
5525
|
+
border-radius: var(--radius-full, 999px);
|
|
5454
5526
|
background: var(--bg-elev-2);
|
|
5455
5527
|
color: var(--text-secondary);
|
|
5456
5528
|
font-size: var(--text-2xs);
|
|
@@ -5458,20 +5530,25 @@ summary:focus-visible,
|
|
|
5458
5530
|
cursor: pointer;
|
|
5459
5531
|
transition: background var(--t-fast) var(--ease);
|
|
5460
5532
|
}
|
|
5461
|
-
.pg-chip:hover { background: var(--bg-hover); }
|
|
5462
|
-
.pg-chip.pg-done { border-color: var(--pg-done); }
|
|
5463
|
-
.pg-chip.pg-active { border-color: var(--pg-active); animation: pg-chip-pulse 2s var(--ease) infinite; }
|
|
5464
|
-
.pg-chip.pg-blocked { border-color: var(--pg-blocked); }
|
|
5533
|
+
.pg-seq-chip:hover { background: var(--bg-hover); }
|
|
5534
|
+
.pg-seq-chip.pg-done { border-left-color: var(--pg-done); }
|
|
5535
|
+
.pg-seq-chip.pg-active { border-left-color: var(--pg-active); animation: pg-chip-pulse 2s var(--ease) infinite; }
|
|
5536
|
+
.pg-seq-chip.pg-blocked { border-left-color: var(--pg-blocked); }
|
|
5465
5537
|
@keyframes pg-chip-pulse {
|
|
5466
5538
|
0%, 100% { box-shadow: 0 0 0 0 transparent; }
|
|
5467
5539
|
50% { box-shadow: 0 0 6px 0 var(--pg-active); }
|
|
5468
5540
|
}
|
|
5469
|
-
.pg-
|
|
5470
|
-
.pg-
|
|
5541
|
+
.pg-seq-id { font-weight: 600; color: var(--text-primary); }
|
|
5542
|
+
.pg-seq-name {
|
|
5471
5543
|
overflow: hidden;
|
|
5472
5544
|
text-overflow: ellipsis;
|
|
5473
5545
|
white-space: nowrap;
|
|
5474
5546
|
}
|
|
5547
|
+
.pg-seq-arrow {
|
|
5548
|
+
padding: 0 var(--space-2);
|
|
5549
|
+
color: var(--text-muted);
|
|
5550
|
+
font-size: var(--text-sm);
|
|
5551
|
+
}
|
|
5475
5552
|
.pg-hint, .pg-empty {
|
|
5476
5553
|
padding: 0 var(--space-5) var(--space-3);
|
|
5477
5554
|
font-size: var(--text-2xs);
|
package/server/lib/scanner.js
CHANGED
|
@@ -195,7 +195,13 @@ function buildPhaseTree(projectDir, rawPhases, listCached, overrides) {
|
|
|
195
195
|
: (p.status === 'active' || p.status === 'in_progress') ? 'in_progress'
|
|
196
196
|
: 'planned';
|
|
197
197
|
|
|
198
|
-
|
|
198
|
+
// Project-relative path to this sprint's SPRINT.md — same shape as the
|
|
199
|
+
// sprintFile computed below for roadmap cards, so /api/file?path=...
|
|
200
|
+
// resolves it identically. Lets task rows (which point at a specific
|
|
201
|
+
// sprint, not just "latest file in the phase") open the right file.
|
|
202
|
+
const file = path.join('.planning', 'phases', dir.name, f).split(path.sep).join('/');
|
|
203
|
+
|
|
204
|
+
return { id: sid, number: num, goal: goal || `Sprint ${num}`, status, stories, dependsOn, file };
|
|
199
205
|
});
|
|
200
206
|
|
|
201
207
|
// Derive phase-level depends_on by aggregating sprint-level depends_on entries.
|
|
@@ -366,11 +372,15 @@ function buildDashboard(state) {
|
|
|
366
372
|
const stories = Array.isArray(s.stories) ? s.stories : [];
|
|
367
373
|
for (const st of stories) {
|
|
368
374
|
if (/done|complete/i.test(st.status || '')) {
|
|
369
|
-
completedTasks.push({
|
|
375
|
+
completedTasks.push({
|
|
376
|
+
title: st.title || st.id,
|
|
377
|
+
date: fmtISODate(p.completed || s.completed_at || p.created),
|
|
378
|
+
file: s.file || null,
|
|
379
|
+
});
|
|
370
380
|
} else if (p.state === 'active') {
|
|
371
381
|
// No per-task progress tracking exists — pct stays null and the UI
|
|
372
382
|
// omits the percent pill rather than inventing a number.
|
|
373
|
-
inProgressTasks.push({ title: st.title || st.id, pct: null });
|
|
383
|
+
inProgressTasks.push({ title: st.title || st.id, pct: null, file: s.file || null });
|
|
374
384
|
}
|
|
375
385
|
}
|
|
376
386
|
}
|
|
@@ -378,10 +388,10 @@ function buildDashboard(state) {
|
|
|
378
388
|
// Fallbacks so the cards are never empty when stories are unregistered in state.json.
|
|
379
389
|
if (!completedTasks.length) {
|
|
380
390
|
phases.filter(p => p.state === 'done').slice(-6).forEach(p =>
|
|
381
|
-
completedTasks.push({ title: p.name, date: fmtISODate(p.completed || p.created) }));
|
|
391
|
+
completedTasks.push({ title: p.name, date: fmtISODate(p.completed || p.created), file: null }));
|
|
382
392
|
}
|
|
383
393
|
if (!inProgressTasks.length && activePhase && activePhase.state === 'active') {
|
|
384
|
-
inProgressTasks.push({ title: activePhase.name, pct: null });
|
|
394
|
+
inProgressTasks.push({ title: activePhase.name, pct: null, file: null });
|
|
385
395
|
}
|
|
386
396
|
const tasks = {
|
|
387
397
|
completed: completedTasks.slice(-8).reverse(),
|
|
@@ -419,6 +429,11 @@ function buildDashboard(state) {
|
|
|
419
429
|
.sort((a, b) => String(b.date).localeCompare(String(a.date)))
|
|
420
430
|
.slice(0, 8);
|
|
421
431
|
|
|
432
|
+
// ---- backlog (phases not yet started — real state.json data, no invented rows) ----
|
|
433
|
+
const backlog = phases
|
|
434
|
+
.filter(p => p.state === 'todo')
|
|
435
|
+
.map(p => ({ id: p.id, name: p.name, range: p.range }));
|
|
436
|
+
|
|
422
437
|
// ---- project (identity + current user) ----
|
|
423
438
|
// User comes from config, else the real OS account running the server;
|
|
424
439
|
// null when neither exists (UI greets generically, hides the profile row).
|
|
@@ -430,7 +445,7 @@ function buildDashboard(state) {
|
|
|
430
445
|
user: { name: userName, email: cfg.user_email || '' },
|
|
431
446
|
};
|
|
432
447
|
|
|
433
|
-
return { project, progress, currentPhase, timeline: { launchDate, onTrack: blockers.length === 0, points }, tasks, blockers, health, decisions, phases };
|
|
448
|
+
return { project, progress, currentPhase, timeline: { launchDate, onTrack: blockers.length === 0, points }, tasks, blockers, health, decisions, phases, backlog };
|
|
434
449
|
}
|
|
435
450
|
|
|
436
451
|
function scanStateUncached(rcodeDir) {
|