@hanzlaa/rcode 3.4.33 → 3.5.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/AGENTS.md +6 -6
- package/CONTRIBUTING.md +2 -0
- package/LICENSE +21 -0
- package/README.md +66 -403
- package/cli/doctor.js +87 -1
- package/cli/install.js +122 -31
- package/cli/lib/schemas.cjs +318 -0
- package/cli/postinstall.js +19 -3
- package/dist/rcode.js +316 -23
- package/package.json +8 -4
- package/rihal/agents/rihal-cross-platform-auditor.md +1 -1
- package/rihal/agents/rihal-dep-auditor.md +1 -1
- package/rihal/agents/rihal-docs-auditor.md +3 -145
- package/rihal/agents/rihal-i18n-auditor.md +1 -1
- package/rihal/agents/rihal-nyquist-auditor.md +4 -156
- package/rihal/agents/rihal-observability-auditor.md +1 -1
- package/rihal/bin/rihal-hooks.cjs +394 -4
- package/rihal/bin/rihal-tools.cjs +891 -24
- package/rihal/commands/create-prd.md +18 -0
- package/rihal/commands/execute-milestone.md +18 -0
- package/rihal/commands/plan-milestone.md +18 -0
- package/rihal/commands/scaffold-milestone.md +18 -0
- package/rihal/commands/scaffold-skill.md +18 -0
- package/rihal/references/REFERENCES_INDEX.md +49 -7
- package/rihal/references/agent-contracts.md +10 -0
- package/rihal/references/design-tokens.md +98 -0
- package/rihal/references/docs-auditor-playbook.md +148 -0
- package/rihal/references/git-preflight.md +117 -0
- package/rihal/references/iterative-retrieval.md +85 -0
- package/rihal/references/nyquist-auditor-playbook.md +157 -0
- package/rihal/references/workstream-flag.md +2 -2
- package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +9 -0
- package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +9 -0
- package/rihal/skills/actions/4-implementation/rihal-ci/SKILL.md +4 -0
- package/rihal/skills/actions/4-implementation/rihal-code-review/steps/step-02-review.md +2 -2
- package/rihal/skills/actions/4-implementation/rihal-harden/SKILL.md +4 -0
- package/rihal/skills/actions/4-implementation/rihal-migrate/SKILL.md +4 -0
- package/rihal/skills/agents/haitham-frontend/SKILL.md +2 -0
- package/rihal/templates/settings-hooks.json +39 -0
- package/rihal/workflows/check-todos.md +4 -0
- package/rihal/workflows/code-review-fix.md +4 -3
- package/rihal/workflows/code-review.md +1 -1
- package/rihal/workflows/debug.md +1 -1
- package/rihal/workflows/dev-story.md +4 -0
- package/rihal/workflows/diff.md +2 -2
- package/rihal/workflows/do.md +16 -8
- package/rihal/workflows/docs-update.md +2 -2
- package/rihal/workflows/enable-hooks.md +6 -1
- package/rihal/workflows/execute-milestone.md +139 -0
- package/rihal/workflows/execute-regression-gates.md +1 -1
- package/rihal/workflows/execute-sprint.md +54 -2
- package/rihal/workflows/execute-verify-phase-goal.md +31 -4
- package/rihal/workflows/execute-waves.md +33 -5
- package/rihal/workflows/execute.md +40 -6
- package/rihal/workflows/help.md +1 -1
- package/rihal/workflows/import.md +1 -1
- package/rihal/workflows/lens-audit.md +39 -23
- package/rihal/workflows/list-workspaces.md +1 -1
- package/rihal/workflows/map-codebase.md +4 -4
- package/rihal/workflows/new-milestone.md +18 -1
- package/rihal/workflows/new-project-research.md +53 -1
- package/rihal/workflows/new-workspace.md +1 -1
- package/rihal/workflows/plan-milestone.md +105 -0
- package/rihal/workflows/plan-research-validation.md +1 -1
- package/rihal/workflows/plan-spawn-planner.md +1 -1
- package/rihal/workflows/plan.md +31 -3
- package/rihal/workflows/plant-seed.md +6 -0
- package/rihal/workflows/quick.md +11 -5
- package/rihal/workflows/research-phase.md +24 -0
- package/rihal/workflows/scaffold-milestone.md +60 -0
- package/rihal/workflows/scaffold-skill.md +137 -0
- package/rihal/workflows/scan.md +1 -1
- package/rihal/workflows/session-report.md +43 -3
- package/rihal/workflows/verify-work.md +3 -3
- package/server/dashboard.js +52 -5
- package/server/lib/html/client.js +723 -11
- package/server/lib/html/css.js +2046 -466
- package/server/lib/html/shell.js +227 -134
- package/server/lib/scanner.js +33 -0
- package/server/orchestrator.js +438 -0
package/server/lib/html/shell.js
CHANGED
|
@@ -6,7 +6,7 @@ const { renderClientJs } = require('./client');
|
|
|
6
6
|
|
|
7
7
|
function esc(s) { return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
|
|
8
8
|
|
|
9
|
-
function renderHtml(state) {
|
|
9
|
+
function renderHtml(state, orchToken) {
|
|
10
10
|
const projectName = state.projectName || 'No project initialized';
|
|
11
11
|
const currentPhase = state.currentPhase || '—';
|
|
12
12
|
const currentSprint = state.currentSprint || null;
|
|
@@ -15,34 +15,32 @@ function renderHtml(state) {
|
|
|
15
15
|
const artifactCount = state.planningFiles.length;
|
|
16
16
|
|
|
17
17
|
const agents = [
|
|
18
|
-
{ name: 'Sadiq Damani',
|
|
19
|
-
{ name: 'Waleed Al Harthi',
|
|
20
|
-
{ name: 'Ahmed Al Hassani',
|
|
21
|
-
{ name: 'Nasser',
|
|
22
|
-
{ name: 'Hussain',
|
|
23
|
-
{ name: 'Layla',
|
|
24
|
-
{ name: 'Zahra',
|
|
25
|
-
{ name: 'Omar',
|
|
26
|
-
{ name: 'Haitham Al Khamiyasi', arabic: 'هيثم',
|
|
27
|
-
{ name: 'Yousef',
|
|
28
|
-
{ name: 'Zayd',
|
|
29
|
-
{ name: 'Fatima',
|
|
30
|
-
{ name: 'Khalid',
|
|
31
|
-
{ name: 'Noor',
|
|
32
|
-
{ name: 'Mariam',
|
|
33
|
-
{ name: 'Raees',
|
|
34
|
-
{ name: 'Majlis',
|
|
35
|
-
{ name: 'Diwan',
|
|
18
|
+
{ name: 'Sadiq Damani', arabic: 'صادق', role: 'Director of Strategy', real: true, type: 'leadership' },
|
|
19
|
+
{ name: 'Waleed Al Harthi', arabic: 'وليد', role: 'CTO', real: true, type: 'leadership' },
|
|
20
|
+
{ name: 'Ahmed Al Hassani', arabic: 'أحمد الحسني', role: 'Technology & Development Director', real: true, type: 'leadership' },
|
|
21
|
+
{ name: 'Nasser', arabic: 'ناصر', role: 'Engineering Manager', real: true, type: 'leadership' },
|
|
22
|
+
{ name: 'Hussain', arabic: 'حسين', role: 'PM + Scrum Master', type: 'product' },
|
|
23
|
+
{ name: 'Layla', arabic: 'ليلى', role: 'Lead UX Designer', type: 'design' },
|
|
24
|
+
{ name: 'Zahra', arabic: 'زهرة', role: 'Branding & Creative Director', type: 'design' },
|
|
25
|
+
{ name: 'Omar', arabic: 'عمر', role: 'Full-Stack Engineer', type: 'engineering' },
|
|
26
|
+
{ name: 'Haitham Al Khamiyasi', arabic: 'هيثم', role: 'Senior Frontend', real: true, type: 'engineering' },
|
|
27
|
+
{ name: 'Yousef', arabic: 'يوسف', role: 'Senior Backend', type: 'engineering' },
|
|
28
|
+
{ name: 'Zayd', arabic: 'زيد', role: 'ML Engineer', type: 'engineering' },
|
|
29
|
+
{ name: 'Fatima', arabic: 'فاطمة', role: 'QA Lead', type: 'quality' },
|
|
30
|
+
{ name: 'Khalid', arabic: 'خالد', role: 'DevOps', type: 'engineering' },
|
|
31
|
+
{ name: 'Noor', arabic: 'نور', role: 'Scribe', type: 'support' },
|
|
32
|
+
{ name: 'Mariam', arabic: 'مريم', role: 'Marketing Lead', type: 'product' },
|
|
33
|
+
{ name: 'Raees', arabic: 'رئيس', role: 'Orchestration Director', type: 'system' },
|
|
34
|
+
{ name: 'Majlis', arabic: 'مجلس', role: 'Consulting Council', type: 'system' },
|
|
35
|
+
{ name: 'Diwan', arabic: 'ديوان', role: 'Dashboard Registry', type: 'system' },
|
|
36
36
|
];
|
|
37
37
|
|
|
38
|
-
// #305: separate real vs AI agents
|
|
39
38
|
const realAgents = agents.filter(a => a.real);
|
|
40
|
-
const aiAgents
|
|
39
|
+
const aiAgents = agents.filter(a => !a.real);
|
|
41
40
|
|
|
42
41
|
function agentCard(a) {
|
|
43
42
|
const filterText = (a.name + ' ' + a.role + ' ' + a.arabic + ' ' + a.type).toLowerCase();
|
|
44
|
-
|
|
45
|
-
const skillName = a.name.split(' ')[0].toLowerCase();
|
|
43
|
+
const skillName = a.name.split(' ')[0].toLowerCase();
|
|
46
44
|
return `<div class="agent-card" data-filter-text="${filterText}" onclick="viewAgentSkill('${skillName}')" style="cursor:pointer;">
|
|
47
45
|
<div class="name">${esc(a.name)}${a.real ? ' <span class="real-badge">real</span>' : ''} <span class="type-badge">${esc(a.type)}</span></div>
|
|
48
46
|
<div class="arabic">${a.arabic}</div>
|
|
@@ -57,37 +55,51 @@ function renderHtml(state) {
|
|
|
57
55
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
58
56
|
<title>Majlis — ${esc(projectName)}</title>
|
|
59
57
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
60
|
-
<link href="https://
|
|
58
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
|
|
61
59
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"><\/script>
|
|
60
|
+
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"><\/script>
|
|
61
|
+
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"><\/script>
|
|
62
|
+
<script>window.__ORCH_TOKEN__ = ${JSON.stringify(orchToken || '')};<\/script>
|
|
62
63
|
${renderCss()}
|
|
63
64
|
</head>
|
|
64
65
|
<body>
|
|
66
|
+
|
|
65
67
|
<div class="app-shell">
|
|
66
|
-
|
|
68
|
+
|
|
69
|
+
<!-- ── Sidebar ─────────────────────────────────────────────── -->
|
|
70
|
+
<aside class="sidebar" id="sidebar">
|
|
67
71
|
<div class="sidebar-project">
|
|
68
|
-
<div class="project-label">
|
|
69
|
-
|
|
72
|
+
<div class="project-label">Rihal</div>
|
|
73
|
+
<span>${esc(projectName)}</span>
|
|
70
74
|
</div>
|
|
71
75
|
<nav>
|
|
72
76
|
<div class="nav-section">Overview</div>
|
|
73
77
|
<button class="nav-link" data-view="overview">🏠 Overview</button>
|
|
74
78
|
<button class="nav-link" data-view="roadmap">🗺 Roadmap</button>
|
|
79
|
+
|
|
75
80
|
<div class="nav-section">Planning</div>
|
|
76
81
|
<button class="nav-link" data-view="milestones">🎯 Milestones</button>
|
|
77
82
|
<button class="nav-link" data-view="phases">📋 Phases</button>
|
|
78
83
|
<button class="nav-link" data-view="sprints">⚡ Sprints</button>
|
|
79
84
|
<button class="nav-link" data-view="tasks">✓ Tasks</button>
|
|
85
|
+
<button class="nav-link" data-view="kanban">🗂 Kanban</button>
|
|
86
|
+
|
|
80
87
|
<div class="nav-section">Workspace</div>
|
|
81
88
|
<button class="nav-link" data-view="files">📄 Files</button>
|
|
82
89
|
<button class="nav-link" data-view="agents">🤝 Agents</button>
|
|
83
90
|
<button class="nav-link" data-view="decisions">⚖ Decisions</button>
|
|
84
|
-
<button class="nav-link" data-view="memory">🧠 Memory
|
|
91
|
+
<button class="nav-link" data-view="memory">🧠 Memory</button>
|
|
85
92
|
</nav>
|
|
86
93
|
</aside>
|
|
94
|
+
|
|
87
95
|
<div id="sidebar-backdrop" onclick="closeSidebar()"></div>
|
|
96
|
+
|
|
97
|
+
<!-- ── Content area ────────────────────────────────────────── -->
|
|
88
98
|
<div class="content-area" id="main-content">
|
|
99
|
+
|
|
100
|
+
<!-- Topbar -->
|
|
89
101
|
<header>
|
|
90
|
-
<div style="display:flex;align-items:center;gap:
|
|
102
|
+
<div style="display:flex;align-items:center;gap:12px;">
|
|
91
103
|
<button class="hamburger-btn" id="hamburger-btn" onclick="toggleSidebar()" aria-label="Toggle menu">
|
|
92
104
|
<span></span><span></span><span></span>
|
|
93
105
|
</button>
|
|
@@ -100,138 +112,219 @@ ${renderCss()}
|
|
|
100
112
|
</div>
|
|
101
113
|
</div>
|
|
102
114
|
<div class="header-actions">
|
|
103
|
-
<span class="live" id="live-dot"></span>
|
|
104
|
-
<span id="updated-ago" style="font-size:
|
|
105
|
-
·
|
|
115
|
+
<span class="live" id="live-dot" title="Live"></span>
|
|
116
|
+
<span id="updated-ago" style="font-size:11px;color:var(--text-muted);">just now</span>
|
|
106
117
|
<button class="header-btn" id="refresh-btn" onclick="manualRefresh()">↺ Refresh</button>
|
|
107
|
-
<button class="header-btn" id="theme-btn" onclick="toggleTheme()" title="Toggle
|
|
108
|
-
<button class="header-btn" onclick="copyUrl()" title="Copy URL"
|
|
109
|
-
<button class="header-btn" onclick="exportSnapshot()" title="Export snapshot">📥</button>
|
|
118
|
+
<button class="header-btn" id="theme-btn" onclick="toggleTheme()" title="Toggle theme">◑</button>
|
|
119
|
+
<button class="header-btn" onclick="copyUrl()" title="Copy URL">⎘ Link</button>
|
|
110
120
|
</div>
|
|
111
121
|
</header>
|
|
112
122
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<div class="label">Current Phase</div>
|
|
133
|
-
<div class="value">${esc(currentPhase)}</div>
|
|
134
|
-
<div class="sub">${phaseCount} total phases${currentSprint ? ` · Sprint ${esc(currentSprint)}` : ''}</div>
|
|
135
|
-
</div>
|
|
136
|
-
<div class="stat">
|
|
137
|
-
<div class="label">Milestone</div>
|
|
138
|
-
<div class="value" style="font-size:16px;padding-top:6px;" id="stat-milestone">${esc(state.milestone || '—')}</div>
|
|
139
|
-
<div class="sub"> </div>
|
|
140
|
-
</div>
|
|
141
|
-
<div class="stat">
|
|
142
|
-
<div class="label">Decisions (ADRs)</div>
|
|
143
|
-
<div class="value">${decisionCount}</div>
|
|
144
|
-
<div class="sub">Architecture records</div>
|
|
123
|
+
<!-- Main scroll container -->
|
|
124
|
+
<div class="main-scroll" id="main-scroll">
|
|
125
|
+
|
|
126
|
+
${state.rawParseError ? `<div id="parse-warning">⚠ <strong>state.json parse error:</strong> ${esc(state.rawParseError)}</div>` : ''}
|
|
127
|
+
|
|
128
|
+
${state.blockers.length > 0 ? `
|
|
129
|
+
<div id="blocker-banner">
|
|
130
|
+
<span class="banner-title">🚧 ${state.blockers.length} blocker${state.blockers.length > 1 ? 's' : ''}</span>
|
|
131
|
+
<span class="banner-list">${state.blockers.map(b => esc(typeof b === 'string' ? b : (b.title || ''))).join(' · ')}</span>
|
|
132
|
+
<button class="banner-dismiss" onclick="dismissBlockers()">Dismiss</button>
|
|
133
|
+
</div>` : ''}
|
|
134
|
+
|
|
135
|
+
<!-- ── Overview ─────────────────────────────────────── -->
|
|
136
|
+
<div id="view-overview" class="view active">
|
|
137
|
+
${!state.exists ? `
|
|
138
|
+
<div class="empty" style="padding:80px;background:var(--bg-elev-2);border:1px solid var(--border-subtle);border-radius:var(--radius-4);">
|
|
139
|
+
<h2 style="color:var(--accent-primary);margin-bottom:12px;font-size:20px;letter-spacing:-0.017em;">No .rihal/ directory found</h2>
|
|
140
|
+
<p style="color:var(--text-tertiary);">Run the kickoff workflow to initialize a project.</p>
|
|
141
|
+
<div class="empty-action">npx rcode install</div>
|
|
145
142
|
</div>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<div class="
|
|
149
|
-
|
|
143
|
+
` : `
|
|
144
|
+
<div class="stats">
|
|
145
|
+
<div class="stat">
|
|
146
|
+
<div class="label">Current Phase</div>
|
|
147
|
+
<div class="value" style="font-size:22px;">${esc(currentPhase)}</div>
|
|
148
|
+
<div class="sub">${phaseCount} total${currentSprint ? ` · Sprint ${esc(currentSprint)}` : ''}</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="stat">
|
|
151
|
+
<div class="label">Milestone</div>
|
|
152
|
+
<div class="value" style="font-size:16px;padding-top:6px;" id="stat-milestone">${esc(state.milestone || '—')}</div>
|
|
153
|
+
<div class="sub"> </div>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="stat">
|
|
156
|
+
<div class="label">Decisions</div>
|
|
157
|
+
<div class="value">${decisionCount}</div>
|
|
158
|
+
<div class="sub">Architecture records</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="stat">
|
|
161
|
+
<div class="label">Planning Files</div>
|
|
162
|
+
<div class="value">${artifactCount}</div>
|
|
163
|
+
<div class="sub">SPRINT · CONTEXT · VERIFY</div>
|
|
164
|
+
</div>
|
|
165
|
+
${state.blockers.length > 0 ? `
|
|
166
|
+
<div class="stat" style="border-left-color:var(--red);">
|
|
167
|
+
<div class="label" style="color:var(--red);">Blockers</div>
|
|
168
|
+
<div class="value" style="color:var(--red);">${state.blockers.length}</div>
|
|
169
|
+
<div class="sub">Active</div>
|
|
170
|
+
</div>` : ''}
|
|
171
|
+
${state.councilSessions > 0 ? `
|
|
172
|
+
<div class="stat">
|
|
173
|
+
<div class="label">Council Sessions</div>
|
|
174
|
+
<div class="value">${state.councilSessions}</div>
|
|
175
|
+
<div class="sub">Recorded</div>
|
|
176
|
+
</div>` : ''}
|
|
150
177
|
</div>
|
|
151
|
-
${state.blockers.length > 0 ? `
|
|
152
|
-
<div class="stat" style="border-left-color:var(--accent-red);">
|
|
153
|
-
<div class="label" style="color:var(--accent-red);">Blockers</div>
|
|
154
|
-
<div class="value" style="color:var(--accent-red);">${state.blockers.length}</div>
|
|
155
|
-
<div class="sub">Active blockers</div>
|
|
156
|
-
</div>` : ''}
|
|
157
|
-
${state.councilSessions > 0 ? `
|
|
158
|
-
<div class="stat">
|
|
159
|
-
<div class="label">Council Sessions</div>
|
|
160
|
-
<div class="value">${state.councilSessions}</div>
|
|
161
|
-
<div class="sub">Recorded sessions</div>
|
|
162
|
-
</div>` : ''}
|
|
163
|
-
</div>
|
|
164
178
|
|
|
165
|
-
|
|
179
|
+
<div id="view-overview-dynamic"></div>
|
|
166
180
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
<section>
|
|
182
|
+
<h2>🎯 Active Context</h2>
|
|
183
|
+
<div class="body">
|
|
184
|
+
${state.context
|
|
185
|
+
? `<pre class="ctx-pre">${esc(state.context.replace(/^#[^\n]*\n?/, '').trim())}</pre>`
|
|
186
|
+
: `<div class="empty">No active context.<div class="empty-action">Run /rihal-init to populate</div></div>`}
|
|
187
|
+
</div>
|
|
188
|
+
</section>
|
|
189
|
+
`}
|
|
190
|
+
</div>
|
|
177
191
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
<!-- ── Dynamic views (rendered by JS) ─────────────────── -->
|
|
193
|
+
<div id="view-roadmap" class="view"></div>
|
|
194
|
+
<div id="view-milestones" class="view"></div>
|
|
195
|
+
<div id="view-phases" class="view"></div>
|
|
196
|
+
<div id="view-sprints" class="view"></div>
|
|
197
|
+
<div id="view-tasks" class="view"></div>
|
|
183
198
|
|
|
184
|
-
|
|
185
|
-
<div
|
|
186
|
-
<div id="file-list-inline"></div>
|
|
187
|
-
<div id="file-view"></div>
|
|
188
|
-
</div>
|
|
199
|
+
<!-- Kanban -->
|
|
200
|
+
<div id="view-kanban" class="view"></div>
|
|
189
201
|
|
|
190
|
-
|
|
191
|
-
<div
|
|
192
|
-
|
|
193
|
-
<
|
|
202
|
+
<!-- Files -->
|
|
203
|
+
<div id="view-files" class="view">
|
|
204
|
+
<div class="view-title">Files</div>
|
|
205
|
+
<div id="file-list-inline"></div>
|
|
206
|
+
<div id="file-view"></div>
|
|
194
207
|
</div>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
|
|
209
|
+
<!-- Agents -->
|
|
210
|
+
<div id="view-agents" class="view">
|
|
211
|
+
<div class="view-title">Agents</div>
|
|
212
|
+
<div class="body">
|
|
213
|
+
<div class="filter-bar">
|
|
214
|
+
<input class="filter-input" type="text" placeholder="Filter agents…" oninput="filterItems(this,'agents-list')">
|
|
215
|
+
</div>
|
|
216
|
+
<div id="agents-list" style="padding:12px;">
|
|
217
|
+
<div style="font-size:11px;font-weight:600;color:var(--amber);margin-bottom:10px;text-transform:uppercase;letter-spacing:0.06em;">Team</div>
|
|
218
|
+
<div class="agents" style="padding:0;margin-bottom:20px;">
|
|
219
|
+
${realAgents.map(agentCard).join('')}
|
|
220
|
+
</div>
|
|
221
|
+
<div style="font-size:11px;font-weight:600;color:var(--accent-primary);margin-bottom:10px;text-transform:uppercase;letter-spacing:0.06em;">AI Agents</div>
|
|
222
|
+
<div class="agents" style="padding:0;">
|
|
223
|
+
${aiAgents.map(agentCard).join('')}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
199
226
|
</div>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div id="view-decisions" class="view"></div>
|
|
230
|
+
|
|
231
|
+
<!-- Memory -->
|
|
232
|
+
<div id="view-memory" class="view">
|
|
233
|
+
<div id="view-memory-content">
|
|
234
|
+
<div class="empty" style="background:var(--bg-elev-2);border:1px solid var(--border-subtle);border-radius:var(--radius-4);">
|
|
235
|
+
<h2 style="color:var(--accent-primary);margin-bottom:12px;">Memory Bank</h2>
|
|
236
|
+
<p>Loading…</p>
|
|
237
|
+
</div>
|
|
203
238
|
</div>
|
|
204
239
|
</div>
|
|
240
|
+
|
|
241
|
+
</div><!-- /main-scroll -->
|
|
242
|
+
</div><!-- /content-area -->
|
|
243
|
+
</div><!-- /app-shell -->
|
|
244
|
+
|
|
245
|
+
<!-- ── Toast ──────────────────────────────────────────────── -->
|
|
246
|
+
<div class="toast" id="toast"></div>
|
|
247
|
+
|
|
248
|
+
<!-- ── Orchestrator side panel ─────────────────────────────── -->
|
|
249
|
+
<div id="orch-panel">
|
|
250
|
+
<div class="orch-panel-header">
|
|
251
|
+
<div class="orch-panel-title">
|
|
252
|
+
<span id="orch-panel-orch-dot" class="orch-status-dot"></span>
|
|
253
|
+
Agent Sessions
|
|
205
254
|
</div>
|
|
255
|
+
<button class="orch-panel-close" onclick="closeOrchPanel()" title="Close">✕</button>
|
|
256
|
+
</div>
|
|
206
257
|
|
|
207
|
-
|
|
258
|
+
<!-- Tab strip -->
|
|
259
|
+
<div class="orch-tabs" id="orch-tabs">
|
|
260
|
+
<div class="orch-term-empty" style="padding:6px 8px;font-size:11px;color:var(--text-muted);">No active sessions</div>
|
|
261
|
+
</div>
|
|
208
262
|
|
|
209
|
-
|
|
210
|
-
|
|
263
|
+
<!-- Terminal body -->
|
|
264
|
+
<div class="orch-terminal">
|
|
265
|
+
<div class="orch-term-body" id="orch-term-body">
|
|
266
|
+
<div class="orch-term-empty">
|
|
267
|
+
<div>Select a session or run a story card</div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="orch-files" id="orch-files" style="display:none;">
|
|
271
|
+
<div class="orch-files-head">File changes</div>
|
|
211
272
|
</div>
|
|
273
|
+
</div>
|
|
212
274
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
275
|
+
<!-- Footer controls -->
|
|
276
|
+
<div class="orch-panel-footer">
|
|
277
|
+
<button class="orch-footer-btn stop" id="orch-stop-btn" onclick="stopActiveSession()" style="display:none;">■ Stop</button>
|
|
278
|
+
<button class="orch-footer-btn" onclick="clearActiveTerminal()">Clear</button>
|
|
279
|
+
<button class="orch-footer-btn" onclick="openCleanSessions()">Clean sessions…</button>
|
|
280
|
+
<div style="flex:1;"></div>
|
|
281
|
+
<span id="orch-session-status" style="font-size:11px;color:var(--text-muted);"></span>
|
|
217
282
|
</div>
|
|
218
283
|
</div>
|
|
219
|
-
|
|
284
|
+
|
|
220
285
|
<script>
|
|
221
|
-
// #303: view agent skill file
|
|
222
286
|
function viewAgentSkill(name) {
|
|
223
|
-
// Try to find matching file in file tree
|
|
224
|
-
var items = document.querySelectorAll('.file-tree-item');
|
|
225
|
-
for (var i = 0; i < items.length; i++) {
|
|
226
|
-
if ((items[i].dataset.path || '').toLowerCase().includes(name)) {
|
|
227
|
-
items[i].click();
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
// Fallback
|
|
232
287
|
navTo('files');
|
|
288
|
+
// Wait for inline file tree to render (fetched async), then find the agent's skill file
|
|
289
|
+
setTimeout(function() {
|
|
290
|
+
var items = document.querySelectorAll('.inline-file-entry');
|
|
291
|
+
for (var i = 0; i < items.length; i++) {
|
|
292
|
+
if ((items[i].dataset.path || '').toLowerCase().includes(name)) {
|
|
293
|
+
items[i].click();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// No exact match — pre-fill the search so the user can see related files
|
|
298
|
+
var search = document.querySelector('#file-list-inline .filter-input');
|
|
299
|
+
if (search) {
|
|
300
|
+
search.value = name;
|
|
301
|
+
search.dispatchEvent(new Event('input'));
|
|
302
|
+
}
|
|
303
|
+
}, 400);
|
|
233
304
|
}
|
|
234
305
|
<\/script>
|
|
306
|
+
|
|
307
|
+
<!-- ── xterm Terminal Panel ───────────────────────────────────── -->
|
|
308
|
+
<div id="term-backdrop" class="term-backdrop"></div>
|
|
309
|
+
<div id="term-panel" class="term-panel">
|
|
310
|
+
<div class="term-header">
|
|
311
|
+
<div class="term-header-left">
|
|
312
|
+
<div class="term-status-dot" id="term-status-dot"></div>
|
|
313
|
+
<span class="term-title" id="term-title">Terminal</span>
|
|
314
|
+
</div>
|
|
315
|
+
<div class="term-header-right">
|
|
316
|
+
<button class="term-btn term-stop-btn" id="term-stop-btn" onclick="termStop()">■ Stop</button>
|
|
317
|
+
<button class="term-btn" onclick="closeTermPanel()">✕ Close</button>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
<div id="term-container"></div>
|
|
321
|
+
<div class="term-input-row">
|
|
322
|
+
<span class="term-prompt">❯</span>
|
|
323
|
+
<input type="text" id="term-input" class="term-input-field" placeholder="Send message to agent… (Enter)">
|
|
324
|
+
<button class="term-send-btn" onclick="termSend()">Send ↑</button>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
235
328
|
${renderClientJs(state)}
|
|
236
329
|
</body>
|
|
237
330
|
</html>`;
|
package/server/lib/scanner.js
CHANGED
|
@@ -167,6 +167,39 @@ function scanState(rihalDir) {
|
|
|
167
167
|
}
|
|
168
168
|
if (fs.existsSync(planningDir)) walkPlanning(planningDir, '');
|
|
169
169
|
|
|
170
|
+
// #12 — surface pending handoff (.rihal/HANDOFF.json) and active context
|
|
171
|
+
// (.rihal/context/active.md) for the dashboard banner + memory-bank summary.
|
|
172
|
+
// Both are no-op when the files don't exist. View-only — no writes.
|
|
173
|
+
const handoffPath = path.join(rihalDir, 'HANDOFF.json');
|
|
174
|
+
if (fs.existsSync(handoffPath)) {
|
|
175
|
+
const ho = safeReadJson(handoffPath);
|
|
176
|
+
if (ho && !ho.__parseError) {
|
|
177
|
+
state.pendingHandoff = {
|
|
178
|
+
path: '.rihal/HANDOFF.json',
|
|
179
|
+
ts: ho.ts || ho.timestamp || null,
|
|
180
|
+
summary: ho.summary || ho.note || ho.what_was_happening || null,
|
|
181
|
+
phase: ho.phase || ho.current_phase || null,
|
|
182
|
+
sprint: ho.sprint || ho.current_sprint || null,
|
|
183
|
+
resume_hint: ho.resume_hint || ho.next || null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const activeCtx = path.join(rihalDir, 'context', 'active.md');
|
|
189
|
+
if (fs.existsSync(activeCtx)) {
|
|
190
|
+
try {
|
|
191
|
+
const stat = fs.statSync(activeCtx);
|
|
192
|
+
const text = fs.readFileSync(activeCtx, 'utf8');
|
|
193
|
+
state.memoryBank = state.memoryBank || {};
|
|
194
|
+
state.memoryBank.active = {
|
|
195
|
+
path: '.rihal/context/active.md',
|
|
196
|
+
bytes: stat.size,
|
|
197
|
+
lines: text.split('\n').length,
|
|
198
|
+
updated: stat.mtime.toISOString(),
|
|
199
|
+
};
|
|
200
|
+
} catch { /* ignore */ }
|
|
201
|
+
}
|
|
202
|
+
|
|
170
203
|
return state;
|
|
171
204
|
}
|
|
172
205
|
|