@hanzlaa/rcode 3.6.14 → 3.6.16

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.
Files changed (127) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +3 -3
  4. package/cli/index.js +5 -1
  5. package/cli/lib/manifest.cjs +51 -43
  6. package/dist/rcode.js +32 -35
  7. package/package.json +3 -2
  8. package/rihal/bin/rihal-tools.cjs +20 -0
  9. package/rihal/commands/add-todo.md +1 -1
  10. package/rihal/commands/check-implementation-readiness.md +18 -0
  11. package/rihal/commands/create-architecture.md +18 -0
  12. package/rihal/commands/debug.md +1 -1
  13. package/rihal/commands/edit-prd.md +18 -0
  14. package/rihal/commands/execute-milestone.md +2 -2
  15. package/rihal/commands/memory-audit.md +1 -1
  16. package/rihal/commands/memory-distill.md +1 -1
  17. package/rihal/commands/memory-init.md +1 -1
  18. package/rihal/commands/memory-update.md +1 -1
  19. package/rihal/commands/plan-milestone.md +2 -2
  20. package/rihal/commands/progress.md +1 -1
  21. package/rihal/commands/resume-work.md +1 -1
  22. package/rihal/commands/retrospective.md +18 -0
  23. package/rihal/commands/review-edge-case-hunter.md +18 -0
  24. package/rihal/commands/scaffold-milestone.md +2 -2
  25. package/rihal/commands/scaffold-project.md +18 -0
  26. package/rihal/commands/validate-prd.md +18 -0
  27. package/rihal/references/agent-contracts.md +46 -20
  28. package/rihal/references/model-profile-resolution.md +41 -5
  29. package/rihal/references/phase-argument-parsing.md +31 -7
  30. package/rihal/references/revision-loop.md +36 -9
  31. package/rihal/references/universal-anti-patterns.md +56 -12
  32. package/rihal/skills/SKILLS_INDEX.md +34 -5
  33. package/rihal/skills/actions/1-analysis/research/rihal-domain-research/SKILL.md +1 -0
  34. package/rihal/skills/actions/1-analysis/research/rihal-market-research/SKILL.md +1 -0
  35. package/rihal/skills/actions/1-analysis/research/rihal-technical-research/SKILL.md +1 -0
  36. package/rihal/skills/actions/1-analysis/rihal-document-project/SKILL.md +1 -0
  37. package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +1 -0
  38. package/rihal/skills/actions/1-analysis/rihal-product-brief/SKILL.md +1 -0
  39. package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/SKILL.md +1 -0
  40. package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/workflow.md +12 -0
  41. package/rihal/skills/actions/2-plan/rihal-create-milestone/SKILL.md +1 -0
  42. package/rihal/skills/actions/2-plan/rihal-create-prd/SKILL.md +1 -0
  43. package/rihal/skills/actions/2-plan/rihal-create-story/SKILL.md +1 -0
  44. package/rihal/skills/actions/2-plan/rihal-create-ux-design/SKILL.md +1 -0
  45. package/rihal/skills/actions/2-plan/rihal-edit-prd/SKILL.md +1 -0
  46. package/rihal/skills/actions/2-plan/rihal-frontend-design/SKILL.md +1 -0
  47. package/rihal/skills/actions/2-plan/rihal-validate-prd/SKILL.md +1 -0
  48. package/rihal/skills/actions/3-solutioning/rihal-check-implementation-readiness/SKILL.md +1 -0
  49. package/rihal/skills/actions/3-solutioning/rihal-create-architecture/SKILL.md +1 -0
  50. package/rihal/skills/actions/3-solutioning/rihal-generate-project-context/SKILL.md +1 -0
  51. package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +1 -0
  52. package/rihal/skills/actions/4-implementation/rihal-code-review/SKILL.md +1 -0
  53. package/rihal/skills/actions/4-implementation/rihal-correct-course/SKILL.md +1 -0
  54. package/rihal/skills/actions/4-implementation/rihal-debug/SKILL.md +3 -17
  55. package/rihal/skills/actions/4-implementation/rihal-dev-story/SKILL.md +1 -0
  56. package/rihal/skills/actions/4-implementation/rihal-dev-story/workflow.md +13 -0
  57. package/rihal/skills/actions/4-implementation/rihal-qa-generate-e2e-tests/SKILL.md +1 -0
  58. package/rihal/skills/actions/4-implementation/rihal-retrospective/SKILL.md +1 -0
  59. package/rihal/skills/actions/4-implementation/rihal-retrospective/workflow.md +12 -0
  60. package/rihal/skills/actions/4-implementation/rihal-scaffold-project/SKILL.md +1 -0
  61. package/rihal/skills/actions/4-implementation/rihal-sprint-planning/SKILL.md +1 -0
  62. package/rihal/skills/actions/4-implementation/rihal-sprint-planning/workflow.md +12 -0
  63. package/rihal/skills/actions/4-implementation/rihal-sprint-status/SKILL.md +1 -0
  64. package/rihal/skills/agents/ahmed-hassani-director/SKILL.md +1 -0
  65. package/rihal/skills/agents/dalil-scout/SKILL.md +1 -0
  66. package/rihal/skills/agents/fatima-qa/SKILL.md +1 -0
  67. package/rihal/skills/agents/haitham-frontend/SKILL.md +1 -0
  68. package/rihal/skills/agents/hanzla-engineer/SKILL.md +1 -0
  69. package/rihal/skills/agents/hussain-pm/SKILL.md +1 -0
  70. package/rihal/skills/agents/hussain-sm/SKILL.md +1 -0
  71. package/rihal/skills/agents/layla-designer/SKILL.md +1 -0
  72. package/rihal/skills/agents/majlis-council/SKILL.md +1 -0
  73. package/rihal/skills/agents/mariam-marketing/SKILL.md +1 -0
  74. package/rihal/skills/agents/nasser-eng-manager/SKILL.md +1 -0
  75. package/rihal/skills/agents/noor-writer/SKILL.md +1 -0
  76. package/rihal/skills/agents/raees-orchestrator/SKILL.md +1 -0
  77. package/rihal/skills/agents/rihal-cross-platform-auditor/SKILL.md +1 -0
  78. package/rihal/skills/agents/rihal-dep-auditor/SKILL.md +1 -0
  79. package/rihal/skills/agents/rihal-deviation-analyzer/SKILL.md +1 -0
  80. package/rihal/skills/agents/rihal-i18n-auditor/SKILL.md +1 -0
  81. package/rihal/skills/agents/rihal-observability-auditor/SKILL.md +1 -0
  82. package/rihal/skills/agents/sadiq-analyst/SKILL.md +1 -0
  83. package/rihal/skills/agents/waleed-architect/SKILL.md +1 -0
  84. package/rihal/skills/agents/yousef-backend/SKILL.md +1 -0
  85. package/rihal/skills/agents/zahra-branding/SKILL.md +1 -0
  86. package/rihal/skills/agents/zayd-ml/SKILL.md +1 -0
  87. package/rihal/skills/core/rihal-init/SKILL.md +1 -1
  88. package/rihal/skills/core/rihal-memory-audit/SKILL.md +5 -5
  89. package/rihal/skills/core/rihal-memory-distill/SKILL.md +5 -5
  90. package/rihal/skills/core/rihal-memory-init/SKILL.md +7 -7
  91. package/rihal/skills/core/rihal-memory-update/SKILL.md +4 -4
  92. package/rihal/team.yaml +56 -0
  93. package/rihal/templates/UAT.md +69 -18
  94. package/rihal/templates/memory/INDEX.md +2 -2
  95. package/rihal/templates/memory/distillates/project.distillate.md +2 -2
  96. package/rihal/templates/memory/distillates/stack.distillate.md +2 -2
  97. package/rihal/templates/memory/project/stack.md +1 -1
  98. package/rihal/templates/summary.md +54 -37
  99. package/rihal/templates/verification-report.md +72 -17
  100. package/rihal/workflows/autonomous.md +2 -2
  101. package/rihal/workflows/create-prd.md +1 -1
  102. package/rihal/workflows/diagnose-issues.md +75 -22
  103. package/rihal/workflows/discuss-phase.md +1 -1
  104. package/rihal/workflows/edit-prd.md +1 -1
  105. package/rihal/workflows/health.md +6 -6
  106. package/rihal/workflows/help.md +0 -1
  107. package/rihal/workflows/memory-audit.md +2 -2
  108. package/rihal/workflows/memory-distill.md +2 -2
  109. package/rihal/workflows/memory-init.md +2 -2
  110. package/rihal/workflows/memory-update.md +2 -2
  111. package/rihal/workflows/research-phase.md +1 -1
  112. package/rihal/workflows/scaffold-project.md +1 -1
  113. package/rihal/workflows/validate-prd.md +2 -2
  114. package/rihal/workflows/verify-work.md +1 -1
  115. package/server/lib/html/client/components/App.js +56 -19
  116. package/server/lib/html/client/components/Sidebar.js +13 -0
  117. package/server/lib/html/client/components/Topbar.js +6 -3
  118. package/server/lib/html/client/store.js +30 -0
  119. package/server/lib/html/client/util.js +0 -10
  120. package/server/lib/html/client/views/FilesView.js +4 -1
  121. package/server/lib/html/client/views/KanbanView.js +15 -4
  122. package/server/lib/html/client/views/MemoryView.js +6 -6
  123. package/server/lib/html/client/views/OrchestrationView.js +12 -2
  124. package/server/lib/html/client/views/OverviewView.js +44 -4
  125. package/server/lib/html/client.js +7 -0
  126. package/server/lib/html/css.js +16 -0
  127. package/server/lib/scanner.js +7 -2
@@ -20,12 +20,12 @@ Skill not installed — run: npx @hanzlaa/rcode install
20
20
 
21
21
  ## On Completion
22
22
 
23
- /rihal-create-milestone — build the roadmap from the validated PRD
23
+ /rihal-new-milestone — build the roadmap from the validated PRD
24
24
  /rihal-edit-prd — fix validation findings
25
25
  /rihal-council — escalate ambiguous requirements to the council
26
26
 
27
27
  ## ▶ Next Up
28
28
 
29
- - /rihal-create-milestone
29
+ - /rihal-new-milestone
30
30
  - /rihal-edit-prd
31
31
  - /rihal-council {prd-question}
@@ -50,7 +50,7 @@ If $ARGUMENTS contains a phase number, load context:
50
50
  INIT=$(node ".rihal/bin/rihal-tools.cjs" init verify-work "${PHASE_ARG}" 2>/dev/null)
51
51
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
52
52
  AGENT_SKILLS_PLANNER=$(node ".rihal/bin/rihal-tools.cjs" agent-skills rihal-planner 2>/dev/null)
53
- AGENT_SKILLS_CHECKER=$(node ".rihal/bin/rihal-tools.cjs" agent-skills rihal-checker 2>/dev/null)
53
+ AGENT_SKILLS_CHECKER=$(node ".rihal/bin/rihal-tools.cjs" agent-skills rihal-sprint-checker 2>/dev/null)
54
54
  ```
55
55
 
56
56
  Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { html, useState, useEffect, useRef, useCallback } from '../preact.js';
16
- import { getState, setState, subscribe } from '../store.js';
16
+ import { getState, setState, subscribe, registerRefresh } from '../store.js';
17
17
  import { startSessionsPoll, refreshOrchToken } from '../orchestrator.js';
18
18
  import { Sidebar } from './Sidebar.js';
19
19
  import { Topbar } from './Topbar.js';
@@ -65,6 +65,35 @@ function parseHash() {
65
65
  return { view: resolvedView, subId };
66
66
  }
67
67
 
68
+ /** Full-width banner shown when /api/state polling is failing. */
69
+ function OfflineBanner({ offline }) {
70
+ if (!offline) return null;
71
+ const s = 'display:flex;align-items:center;gap:var(--space-2);'
72
+ + 'padding:var(--space-2) var(--space-4);background:var(--red,#eb5757);'
73
+ + 'color:#fff;font-size:var(--text-sm);font-weight:600;';
74
+ return html`<div style=${s}>⚠ Dashboard offline — retrying every 30s…</div>`;
75
+ }
76
+
77
+ /** Thin IDE-style status bar: project path · rcode version · last refresh. */
78
+ function StatusBar({ projectRoot, projectName, version, updatedAgo, offline, refreshing }) {
79
+ const bar = 'display:flex;align-items:center;gap:var(--space-4);height:24px;'
80
+ + 'padding:0 var(--space-4);background:var(--bg-elev-1);'
81
+ + 'border-top:1px solid var(--border-subtle);font-family:var(--font-mono);'
82
+ + 'font-size:var(--text-2xs);color:var(--text-muted);white-space:nowrap;overflow:hidden;';
83
+ const dot = 'width:6px;height:6px;border-radius:50%;flex-shrink:0;background:'
84
+ + (offline ? 'var(--red,#eb5757)' : 'var(--accent-green)') + ';'
85
+ + (refreshing ? 'animation:pulse-dot 1s ease-in-out infinite;' : '');
86
+ const path = projectRoot || projectName || 'no project';
87
+ return html`
88
+ <footer style=${bar}>
89
+ <span style=${dot}></span>
90
+ <span style="overflow:hidden;text-overflow:ellipsis;" title=${path}>${path}</span>
91
+ <span style="margin-left:auto;">rcode v${version || '?'}</span>
92
+ <span>${offline ? 'offline' : refreshing ? 'syncing…' : 'updated ' + updatedAgo}</span>
93
+ </footer>
94
+ `;
95
+ }
96
+
68
97
  /** Root App component. No props needed — reads everything from the store. */
69
98
  export function App() {
70
99
  // ---- Router state ----
@@ -121,21 +150,21 @@ export function App() {
121
150
  return () => clearInterval(id);
122
151
  }, []);
123
152
 
124
- // ---- Manual refresh ----
153
+ // ---- Refresh (manual + 30s poll share this) ----
125
154
  const lastScannedRef = useRef(null);
126
155
 
127
156
  const fetchAndRerender = useCallback(async () => {
128
- const btn = document.getElementById('refresh-btn');
129
- if (btn) btn.textContent = '↺ …';
157
+ setState({ refreshing: true });
130
158
  try {
131
159
  const r = await fetch('/api/state');
132
- if (!r.ok) return;
160
+ if (!r.ok) { setState({ refreshing: false, offline: true }); return; }
133
161
  const newState = await r.json();
134
162
  lastScannedRef.current = newState.lastScanned;
135
163
  scanTimeRef.current = Date.now();
136
164
  setUpdatedAgo('just now');
165
+ const patch = { refreshing: false, offline: false, lastRefresh: Date.now() };
137
166
  if (newState.raw) {
138
- setState({
167
+ Object.assign(patch, {
139
168
  phases: newState.phaseTree || newState.raw.phases || [],
140
169
  milestone: newState.raw.milestone || '',
141
170
  currentPhase: newState.raw.current_phase || null,
@@ -146,26 +175,23 @@ export function App() {
146
175
  last_session: newState.raw.last_session || null,
147
176
  });
148
177
  }
149
- } catch { /* network errors ignored */ }
150
- if (btn) btn.textContent = '↺ Refresh';
178
+ setState(patch);
179
+ } catch {
180
+ // Network failure → mark offline so the banner shows; the poll keeps retrying.
181
+ setState({ refreshing: false, offline: true });
182
+ }
151
183
  }, []);
152
184
 
153
185
  // ---- 30s auto-refresh ----
154
186
  useEffect(() => {
155
- const id = setInterval(async () => {
156
- try {
157
- const r = await fetch('/api/state');
158
- if (!r.ok) return;
159
- const s = await r.json();
160
- if (s && s.lastScanned !== lastScannedRef.current) await fetchAndRerender();
161
- } catch { /* ignore */ }
162
- }, 30000);
187
+ const id = setInterval(fetchAndRerender, 30000);
163
188
  return () => clearInterval(id);
164
189
  }, [fetchAndRerender]);
165
190
 
166
- // Expose manualRefresh globally for any legacy onclick="manualRefresh()" callers
191
+ // Register the refresh handler with the store so any component can call
192
+ // refresh() directly. Also keeps window._preactRefresh in sync for legacy.
167
193
  useEffect(() => {
168
- window._preactRefresh = fetchAndRerender;
194
+ registerRefresh(fetchAndRerender);
169
195
  }, [fetchAndRerender]);
170
196
 
171
197
  // Start the global session poll and refresh the orchestrator token on boot.
@@ -189,10 +215,11 @@ export function App() {
189
215
  document.body.classList.remove('sidebar-visible');
190
216
  }}></div>
191
217
 
192
- <div class="content-area" id="main-content">
218
+ <div class="content-area" id="main-content" style="grid-template-rows:44px 1fr auto;">
193
219
  <${Topbar}
194
220
  projectName=${storeState.projectName || ''}
195
221
  updatedAgo=${updatedAgo}
222
+ refreshing=${storeState.refreshing}
196
223
  onRefresh=${fetchAndRerender}
197
224
  onToggleTheme=${toggleTheme}
198
225
  onToggleSidebar=${toggleSidebar}
@@ -200,8 +227,18 @@ export function App() {
200
227
  />
201
228
 
202
229
  <div class="main-scroll" id="main-scroll">
230
+ <${OfflineBanner} offline=${storeState.offline} />
203
231
  ${PreactView ? html`<${PreactView} subId=${subId} />` : null}
204
232
  </div>
233
+
234
+ <${StatusBar}
235
+ projectRoot=${storeState.projectRoot}
236
+ projectName=${storeState.projectName}
237
+ version=${storeState.version}
238
+ updatedAgo=${updatedAgo}
239
+ offline=${storeState.offline}
240
+ refreshing=${storeState.refreshing}
241
+ />
205
242
  </div>
206
243
 
207
244
  <${XtermPanel} />
@@ -7,6 +7,9 @@
7
7
 
8
8
  import { html } from '../preact.js';
9
9
  import { Icon } from '../icons-client.js';
10
+ import { useStore } from '../store.js';
11
+ import { allSprints, allTasks } from '../util.js';
12
+ import { AGENTS } from '../agents-data.js';
10
13
 
11
14
  // Nav structure: [ { section, links: [ { view, icon, label } ] } ]
12
15
  const NAV_SECTIONS = [
@@ -47,6 +50,15 @@ const NAV_SECTIONS = [
47
50
  * projectName {string} — displayed under the "Rihal" label
48
51
  */
49
52
  export function Sidebar({ activeView, projectName }) {
53
+ const S = useStore();
54
+ const counts = {
55
+ phases: (S.phases || []).length,
56
+ sprints: allSprints(S.phases).length,
57
+ tasks: allTasks(S.phases).length,
58
+ decisions: (S.decisions || []).length,
59
+ agents: AGENTS.length,
60
+ };
61
+
50
62
  return html`
51
63
  <aside class="sidebar" id="sidebar">
52
64
  <div class="sidebar-project">
@@ -64,6 +76,7 @@ export function Sidebar({ activeView, projectName }) {
64
76
  >
65
77
  <${Icon} name=${icon} size=${14} />
66
78
  ${' ' + label}
79
+ ${counts[view] ? html`<span class="nav-count">${counts[view]}</span>` : null}
67
80
  </button>
68
81
  `)}
69
82
  `)}
@@ -15,7 +15,7 @@
15
15
  import { html } from '../preact.js';
16
16
  import { Icon } from '../icons-client.js';
17
17
 
18
- export function Topbar({ projectName, updatedAgo, onRefresh, onToggleTheme, onToggleSidebar, themeLabel }) {
18
+ export function Topbar({ projectName, updatedAgo, refreshing, onRefresh, onToggleTheme, onToggleSidebar, themeLabel }) {
19
19
  return html`
20
20
  <header>
21
21
  <div class="topbar-start-group">
@@ -36,8 +36,11 @@ export function Topbar({ projectName, updatedAgo, onRefresh, onToggleTheme, onTo
36
36
  </div>
37
37
  </div>
38
38
  <div class="header-actions">
39
- <span class="live" id="live-dot" title="Live"></span>
40
- <span id="updated-ago" class="updated-ago">${updatedAgo || 'just now'}</span>
39
+ <span class="live" id="live-dot" title="Live"
40
+ style=${refreshing ? 'animation-duration:0.7s;background:var(--accent-blue);' : ''}></span>
41
+ <span id="updated-ago" class="updated-ago">
42
+ ${refreshing ? '⟳ syncing…' : (updatedAgo || 'just now')}
43
+ </span>
41
44
  <button class="header-btn" id="refresh-btn" onClick=${onRefresh}>↺ Refresh</button>
42
45
  <!-- icon shows TARGET state (not current): dark→sun means "click to go light"; light→moon means "click to go dark" -->
43
46
  <button class="header-btn" id="theme-btn" onClick=${onToggleTheme} title="Toggle theme"><${Icon} name=${themeLabel === 'light' ? 'moon' : 'sun'} size=${14}/></button>
@@ -27,6 +27,16 @@ let _state = {
27
27
  workstreams: _seed.workstreams || [],
28
28
  pendingHandoff: _seed.pendingHandoff || null,
29
29
  memoryBank: _seed.memoryBank || null,
30
+ // Environment info — surfaced in the bottom status bar.
31
+ projectName: _seed.projectName || '',
32
+ projectRoot: _seed.projectRoot || '',
33
+ version: _seed.version || '',
34
+ // Refresh lifecycle: refreshing flips true during a poll/fetch; offline is
35
+ // true when /api/state fails; lastRefresh is the ms timestamp of the last
36
+ // successful fetch (null until the first one completes).
37
+ refreshing: false,
38
+ offline: false,
39
+ lastRefresh: null,
30
40
  // Live orchestrator sessions (populated by startSessionsPoll in orchestrator.js)
31
41
  activeSessions: [],
32
42
  // File jump bridge: AgentsView sets this to a slug so FilesView opens it.
@@ -75,6 +85,26 @@ export function subscribe(fn) {
75
85
  return () => _subscribers.delete(fn);
76
86
  }
77
87
 
88
+ /**
89
+ * Refresh handler bridge.
90
+ *
91
+ * App owns the actual /api/state fetch (fetchAndRerender). It registers that
92
+ * function here on mount so any component can trigger a refresh via refresh()
93
+ * without reaching for a window global. `window._preactRefresh` is kept in
94
+ * sync for any legacy inline-onclick callers.
95
+ */
96
+ let _refreshHandler = null;
97
+
98
+ export function registerRefresh(fn) {
99
+ _refreshHandler = typeof fn === 'function' ? fn : null;
100
+ if (typeof window !== 'undefined') window._preactRefresh = _refreshHandler;
101
+ }
102
+
103
+ /** Trigger a data refresh, if a handler has been registered. */
104
+ export function refresh() {
105
+ if (typeof _refreshHandler === 'function') return _refreshHandler();
106
+ }
107
+
78
108
  /**
79
109
  * Preact hook. Subscribes the calling component to the store and
80
110
  * returns the current state. The component re-renders on every setState().
@@ -7,16 +7,6 @@
7
7
  * Import here; do NOT duplicate in component files.
8
8
  */
9
9
 
10
- /** HTML-escape a value for safe rendering. */
11
- export function esc(s) {
12
- return String(s || '')
13
- .replace(/&/g, '&amp;')
14
- .replace(/</g, '&lt;')
15
- .replace(/>/g, '&gt;')
16
- .replace(/"/g, '&quot;')
17
- .replace(/'/g, '&#39;');
18
- }
19
-
20
10
  /** Percentage string. Returns '—' if total is 0. */
21
11
  export function pct(done, total) {
22
12
  return total > 0 ? Math.round(done / total * 100) + '%' : '—';
@@ -172,7 +172,10 @@ export function FilesView() {
172
172
  try {
173
173
  const resp = await fetch('/api/file?path=' + encodeURIComponent(file.path));
174
174
  if (!resp.ok) {
175
- setFileContent({ html: null, loading: false, error: 'Failed to load file.' });
175
+ const msg = resp.status === 404
176
+ ? 'File not found: ' + file.path
177
+ : 'Failed to load file (HTTP ' + resp.status + ').';
178
+ setFileContent({ html: null, loading: false, error: msg });
176
179
  return;
177
180
  }
178
181
  const text = await resp.text();
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { html, useState, useCallback } from '../preact.js';
11
- import { useStore } from '../store.js';
11
+ import { useStore, refresh } from '../store.js';
12
12
  import { allTasks } from '../util.js';
13
13
  import { runStory, stopStory, openOrchPanel } from '../orchestrator.js';
14
14
  import { showToast } from '../components/shared.js';
@@ -131,7 +131,7 @@ function KanbanColumn({ col, cards, onDragStart, onDragEnd, onDragOver, onDrop }
131
131
 
132
132
  // ---- Root KanbanView ----
133
133
  export function KanbanView() {
134
- const { phases, activeSessions } = useStore();
134
+ const { phases, activeSessions, currentPhase, milestone } = useStore();
135
135
  const tasks = allTasks(phases);
136
136
 
137
137
  // ---- Local column state (visual DnD overrides) ----
@@ -178,7 +178,7 @@ export function KanbanView() {
178
178
 
179
179
  // ---- Manual refresh ----
180
180
  function handleSync() {
181
- if (typeof window._preactRefresh === 'function') window._preactRefresh();
181
+ refresh();
182
182
  }
183
183
 
184
184
  function handleSessions() {
@@ -200,7 +200,18 @@ export function KanbanView() {
200
200
  </div>
201
201
  <div class="empty" style="margin:24px;">
202
202
  No stories yet.
203
- <div class="empty-action">/rihal-plan to generate tasks</div>
203
+ ${(milestone || currentPhase) ? html`
204
+ <div class="empty-action">
205
+ ${milestone ? html`Milestone <strong>${milestone}</strong>` : null}
206
+ ${milestone && currentPhase ? ' · ' : null}
207
+ ${currentPhase ? html`Phase <strong>${currentPhase}</strong>` : null}
208
+ ${' is active.'}
209
+ </div>
210
+ ` : null}
211
+ <div class="empty-action">
212
+ Run <code>/rihal-plan</code> to generate sprint stories, or browse
213
+ planning docs in the <a href="#files">Files</a> view.
214
+ </div>
204
215
  </div>
205
216
  </div>
206
217
  `;
@@ -13,10 +13,10 @@ import { html, useState, useEffect } from '../preact.js';
13
13
 
14
14
  // ---- Command hints accordion ----
15
15
  const MEMORY_HINTS = [
16
- ['/rcode:memory-init', 'Bootstrap the Memory Bank'],
17
- ['/rcode:memory-update', 'Append a decision, issue, or stakeholder entry'],
18
- ['/rcode:memory-distill', 'Regenerate fast-load distillates'],
19
- ['/rcode:memory-audit', 'Find stale entries and gaps'],
16
+ ['/rihal-memory-init', 'Bootstrap the Memory Bank'],
17
+ ['/rihal-memory-update', 'Append a decision, issue, or stakeholder entry'],
18
+ ['/rihal-memory-distill', 'Regenerate fast-load distillates'],
19
+ ['/rihal-memory-audit', 'Find stale entries and gaps'],
20
20
  ];
21
21
 
22
22
  function CmdAccordion({ hints }) {
@@ -117,7 +117,7 @@ export function MemoryView() {
117
117
  <div class="empty">
118
118
  <h3 style="color:var(--rihal-gold);">Not initialised</h3>
119
119
  <p>The Memory Bank is rcode's structured project context.</p>
120
- <div class="empty-action">Run <code>/rcode:memory-init</code> to bootstrap</div>
120
+ <div class="empty-action">Run <code>/rihal-memory-init</code> to bootstrap</div>
121
121
  </div>
122
122
  </div>
123
123
  `;
@@ -128,7 +128,7 @@ export function MemoryView() {
128
128
  <div class="view active" id="view-memory">
129
129
  <div class="view-title">Memory Bank</div>
130
130
  <div class="empty">
131
- <p>Directory exists but INDEX.md is missing — re-run <code>/rcode:memory-init</code></p>
131
+ <p>Directory exists but INDEX.md is missing — re-run <code>/rihal-memory-init</code></p>
132
132
  </div>
133
133
  </div>
134
134
  `;
@@ -131,6 +131,13 @@ function CommandRunner() {
131
131
  : html`<${Icon} name="play" size=${14}/> Run`}
132
132
  </button>
133
133
  </div>
134
+ <div class="cmd-runner-hint">
135
+ ${isRunning
136
+ ? html`Command is running — output is streaming to the terminal panel.`
137
+ : busy
138
+ ? html`Starting — the terminal panel will open shortly.`
139
+ : html`Select a command and press Run. Output streams live to the terminal panel.`}
140
+ </div>
134
141
  </div>
135
142
  `;
136
143
  }
@@ -152,8 +159,11 @@ export function OrchestrationView() {
152
159
 
153
160
  ${sessions.length === 0 ? html`
154
161
  <div class="empty">
155
- No agent sessions yet.
156
- <div class="empty-action">Run a phase or sprint to start one</div>
162
+ No active execution.
163
+ <div class="empty-action">
164
+ Use the Command Runner above, or run <code>/rihal-execute</code> to
165
+ start a phase or sprint.
166
+ </div>
157
167
  </div>
158
168
  ` : html`
159
169
  <div class="orch-grid">
@@ -7,8 +7,8 @@
7
7
 
8
8
  import { html } from '../preact.js';
9
9
  import { useStore } from '../store.js';
10
- import { pct, humanDate, allSprints, chip, sprintHints as getSprintHints } from '../util.js';
11
- import { ProgressBar, CmdHints } from '../components/shared.js';
10
+ import { pct, humanDate, allSprints, allTasks, chip, sprintHints as getSprintHints } from '../util.js';
11
+ import { ProgressBar, CmdHints, Chip } from '../components/shared.js';
12
12
  import { Icon } from '../icons-client.js';
13
13
 
14
14
  // ---- OverviewView ----
@@ -38,6 +38,41 @@ export function OverviewView() {
38
38
  hints = [['/rihal-resume-work','Resume from the pending handoff'], ...hints];
39
39
  }
40
40
 
41
+ // At-a-glance status tiles — phase, sprint, blocked, last execution.
42
+ function StatusSummary() {
43
+ const curPhase = (S.phases || []).find(
44
+ p => String(p.id) === String(S.currentPhase),
45
+ ) || null;
46
+ const blockedCount = allTasks(S.phases).filter(t => t.status === 'blocked').length;
47
+ const lastExec = S.last_session
48
+ ? humanDate(S.last_session.date || S.last_session.timestamp)
49
+ : null;
50
+ return html`
51
+ <div class="stat">
52
+ <div class="label">Current Phase</div>
53
+ <div class="value">${curPhase ? 'P' + curPhase.id : '—'}</div>
54
+ <div class="sub">
55
+ ${curPhase ? html`${curPhase.name} · <${Chip} status=${curPhase.status}/>` : 'No phase set'}
56
+ </div>
57
+ </div>
58
+ <div class="stat">
59
+ <div class="label">Active Sprint</div>
60
+ <div class="value">${curSprint ? curSprint.id : '—'}</div>
61
+ <div class="sub">${curSprint ? (curSprint.goal || curSprint.status || 'in progress') : 'No active sprint'}</div>
62
+ </div>
63
+ <div class="stat" style=${blockedCount ? 'border-left-color:var(--red,#eb5757)' : ''}>
64
+ <div class="label">Blocked Tasks</div>
65
+ <div class="value" style=${blockedCount ? 'color:var(--red,#eb5757)' : ''}>${blockedCount}</div>
66
+ <div class="sub">${blockedCount ? 'needs attention' : 'all clear'}</div>
67
+ </div>
68
+ <div class="stat">
69
+ <div class="label">Last Execution</div>
70
+ <div class="value" style="font-size:var(--text-lg,1rem);">${lastExec || '—'}</div>
71
+ <div class="sub">${lastExec ? 'most recent session' : 'no sessions yet'}</div>
72
+ </div>
73
+ `;
74
+ }
75
+
41
76
  // Current sprint progress
42
77
  function SprintProgress() {
43
78
  if (!curSprint) return null;
@@ -175,8 +210,12 @@ export function OverviewView() {
175
210
  if (!S.memoryBank || !S.memoryBank.active) return null;
176
211
  const m = S.memoryBank.active;
177
212
  return html`
178
- <section>
179
- <h2 class="section-icon"><${Icon} name="brain" size=${16}/> Memory Bank</h2>
213
+ <section
214
+ class="item-clickable"
215
+ style="cursor:pointer;"
216
+ onClick=${() => { location.hash = 'memory'; }}
217
+ >
218
+ <h2 class="section-icon"><${Icon} name="brain" size=${16}/> Memory Bank →</h2>
180
219
  <div class="body">
181
220
  <div class="attr-grid">
182
221
  <div class="attr-item">
@@ -207,6 +246,7 @@ export function OverviewView() {
207
246
  return html`
208
247
  <div id="view-overview" class="view active">
209
248
  <div class="stats">
249
+ <${StatusSummary}/>
210
250
  <${VelocitySpark}/>
211
251
  </div>
212
252
  <${HandoffBanner}/>
@@ -14,11 +14,18 @@
14
14
 
15
15
  const { ICONS } = require('./icons');
16
16
 
17
+ // rcode version — read once at module load; surfaced in the dashboard status bar.
18
+ let RCODE_VERSION = '';
19
+ try { RCODE_VERSION = require('../../../package.json').version || ''; } catch { /* version unknown */ }
20
+
17
21
  // Fields the client needs from the scanned state. Kept in sync with
18
22
  // store.js initial state and the view components that read it.
19
23
  function clientState(state) {
20
24
  return JSON.stringify({
21
25
  phases: state.phaseTree || state.raw?.phases || [],
26
+ projectName: state.projectName || '',
27
+ projectRoot: state.projectRoot || '',
28
+ version: RCODE_VERSION,
22
29
  milestone: state.raw?.milestone || '',
23
30
  currentPhase: state.raw?.current_phase || null,
24
31
  currentSprint: state.raw?.current_sprint || null,
@@ -226,6 +226,16 @@ html, body {
226
226
  }
227
227
  .nav-link:hover { background: var(--bg-hover); color: var(--text-secondary); }
228
228
  .nav-link.active { background: var(--bg-elev-2); color: var(--text-primary); }
229
+ .nav-count {
230
+ margin-left: auto;
231
+ font-size: 10px;
232
+ font-weight: 600;
233
+ color: var(--text-muted);
234
+ background: var(--bg-elev-2);
235
+ padding: 1px 6px;
236
+ border-radius: var(--radius-2);
237
+ }
238
+ .nav-link.active .nav-count { color: var(--text-secondary); background: var(--bg-hover); }
229
239
 
230
240
  /* Mobile hamburger */
231
241
  .hamburger-btn {
@@ -2230,6 +2240,12 @@ footer {
2230
2240
  gap: var(--space-3);
2231
2241
  align-items: center;
2232
2242
  }
2243
+ .cmd-runner-hint {
2244
+ margin-top: var(--space-3);
2245
+ font-size: var(--text-xs);
2246
+ color: var(--text-muted);
2247
+ line-height: 1.5;
2248
+ }
2233
2249
  .cmd-runner-select {
2234
2250
  flex: 1;
2235
2251
  background: var(--bg-input, var(--bg-elev-2));
@@ -85,11 +85,14 @@ function buildPhaseTree(projectDir, rawPhases) {
85
85
  while ((tm = taskRe.exec(text))) {
86
86
  const idM = tm[1].match(/id="([^"]+)"/);
87
87
  const titleM = tm[2].match(/<title>([\s\S]*?)<\/title>/);
88
- stories.push({
88
+ const acM = tm[2].match(/<acceptance_criteria>\s*([\s\S]*?)\s*<\/acceptance_criteria>/);
89
+ const story = {
89
90
  id: idM ? idM[1] : `${sid}-task-${stories.length + 1}`,
90
91
  title: titleM ? titleM[1].trim() : `Task ${stories.length + 1}`,
91
92
  status: phaseComplete ? 'done' : 'todo',
92
- });
93
+ };
94
+ if (acM && acM[1].trim()) story.acceptance = acM[1].trim();
95
+ stories.push(story);
93
96
  }
94
97
  // Fallback for pre-<task> SPRINT.md format (phases 20-30 era):
95
98
  // "### Story 20.01.01 — title" / "### Task X — title" headings.
@@ -156,6 +159,8 @@ function scanState(rihalDir) {
156
159
  || state.raw?.project
157
160
  || (dirName !== '.' ? dirName : 'Unknown project');
158
161
 
162
+ state.projectRoot = projectDir;
163
+
159
164
  state.currentPhase = state.raw?.current_phase || null;
160
165
  state.currentSprint = state.raw?.current_sprint || null;
161
166
  state.milestone = state.raw?.milestone || null;