@hanzlaa/rcode 4.1.1 → 4.3.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.
Files changed (105) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +3 -0
  3. package/README.md +3 -0
  4. package/cli/agent.js +3 -1
  5. package/cli/index.js +29 -0
  6. package/cli/install.js +233 -15
  7. package/cli/lib/config.cjs +4 -2
  8. package/cli/lib/fsutil.cjs +13 -2
  9. package/cli/lib/homedir.cjs +21 -0
  10. package/cli/lib/schemas.cjs +6 -1
  11. package/cli/nuke.js +13 -8
  12. package/cli/postinstall.js +14 -4
  13. package/cli/rcode-slash-router.cjs +118 -0
  14. package/cli/uninstall.js +59 -1
  15. package/cli/update.js +10 -5
  16. package/cli/workflow.js +3 -1
  17. package/dist/rcode.js +241 -227
  18. package/package.json +1 -1
  19. package/rcode/bin/rcode-tools.cjs +15 -6
  20. package/rcode/commands/scaffold-project.md +2 -2
  21. package/rcode/skills/actions/2-plan/rcode-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
  22. package/rcode/skills/actions/2-plan/rcode-create-milestone/steps/README.md +2 -2
  23. package/rcode/skills/actions/2-plan/rcode-create-milestone/steps/step-09-state-sync.md +1 -1
  24. package/rcode/skills/actions/4-implementation/rcode-code-review/steps/step-02-review.md +1 -1
  25. package/rcode/skills/actions/4-implementation/rcode-git-flow/SKILL.md +1 -1
  26. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/SKILL.md +39 -12
  27. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-01-target.md +18 -3
  28. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-02-safety.md +27 -3
  29. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-03-brownfield.md +57 -0
  30. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-03-clone.md +4 -1
  31. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/steps/step-04-post-setup.md +15 -1
  32. package/rcode/skills/actions/4-implementation/rcode-trim/SKILL.md +1 -1
  33. package/rcode/workflows/audit-milestone.md +1 -1
  34. package/rcode/workflows/discuss-phase.md +1 -1
  35. package/rcode/workflows/execute-milestone.md +1 -1
  36. package/rcode/workflows/execute-regression-gates.md +3 -0
  37. package/rcode/workflows/execute-sprint.md +27 -1
  38. package/rcode/workflows/execute-waves.md +6 -0
  39. package/rcode/workflows/execute.md +13 -3
  40. package/rcode/workflows/new-milestone.md +2 -2
  41. package/rcode/workflows/new-project.md +4 -0
  42. package/rcode/workflows/plan-research-validation.md +1 -1
  43. package/rcode/workflows/plan-spawn-planner.md +2 -2
  44. package/rcode/workflows/plan.md +34 -15
  45. package/rcode/workflows/review.md +2 -0
  46. package/rcode/workflows/scaffold-project.md +5 -1
  47. package/rcode/workflows/session-report.md +1 -1
  48. package/rcode/workflows/ship.md +39 -0
  49. package/rcode/workflows/sprint-planning.md +27 -0
  50. package/rcode/workflows/status.md +3 -3
  51. package/server/dashboard.js +26 -7
  52. package/server/lib/api.js +62 -4
  53. package/server/lib/html/client/agents-data.js +22 -18
  54. package/server/lib/html/client/app.js +3 -0
  55. package/server/lib/html/client/components/AgentCard.js +127 -0
  56. package/server/lib/html/client/components/App.js +104 -39
  57. package/server/lib/html/client/components/CommandPalette.js +133 -0
  58. package/server/lib/html/client/components/FileReader.js +116 -0
  59. package/server/lib/html/client/components/FilterChips.js +94 -0
  60. package/server/lib/html/client/components/NotifyCenter.js +117 -0
  61. package/server/lib/html/client/components/OrchPanel.js +80 -52
  62. package/server/lib/html/client/components/PhaseGraph.js +300 -0
  63. package/server/lib/html/client/components/RejectDialog.js +78 -0
  64. package/server/lib/html/client/components/RunnerPicker.js +190 -0
  65. package/server/lib/html/client/components/Sidebar.js +106 -61
  66. package/server/lib/html/client/components/StatusSummaryBar.js +76 -0
  67. package/server/lib/html/client/components/TaskPipeline.js +83 -0
  68. package/server/lib/html/client/components/Topbar.js +86 -39
  69. package/server/lib/html/client/components/dashboard/Blockers.js +57 -0
  70. package/server/lib/html/client/components/dashboard/CompletedTasks.js +47 -0
  71. package/server/lib/html/client/components/dashboard/CurrentPhase.js +107 -0
  72. package/server/lib/html/client/components/dashboard/InProgress.js +72 -0
  73. package/server/lib/html/client/components/dashboard/ProgressDonut.js +101 -0
  74. package/server/lib/html/client/components/dashboard/ProgressTimeline.js +101 -0
  75. package/server/lib/html/client/components/dashboard/ProjectHealth.js +80 -0
  76. package/server/lib/html/client/components/dashboard/RecentDecisions.js +57 -0
  77. package/server/lib/html/client/components/dashboard/Timeline.js +143 -0
  78. package/server/lib/html/client/components/shared.js +47 -11
  79. package/server/lib/html/client/filter-state.js +72 -0
  80. package/server/lib/html/client/icons-client.js +7 -0
  81. package/server/lib/html/client/notify.js +75 -0
  82. package/server/lib/html/client/orchestrator.js +168 -41
  83. package/server/lib/html/client/preact.js +13 -8
  84. package/server/lib/html/client/store.js +70 -6
  85. package/server/lib/html/client/util.js +78 -0
  86. package/server/lib/html/client/vendor/htm.js +1 -0
  87. package/server/lib/html/client/vendor/preact-hooks.js +2 -0
  88. package/server/lib/html/client/vendor/preact.js +2 -0
  89. package/server/lib/html/client/views/AgentsView.js +144 -51
  90. package/server/lib/html/client/views/FilesView.js +20 -103
  91. package/server/lib/html/client/views/KanbanView.js +40 -21
  92. package/server/lib/html/client/views/MemoryView.js +26 -9
  93. package/server/lib/html/client/views/MilestonesView.js +4 -4
  94. package/server/lib/html/client/views/OrchestrationView.js +154 -19
  95. package/server/lib/html/client/views/OverviewView.js +47 -239
  96. package/server/lib/html/client/views/PhasesView.js +50 -6
  97. package/server/lib/html/client/views/RoadmapView.js +6 -3
  98. package/server/lib/html/client/views/SprintsView.js +50 -6
  99. package/server/lib/html/client/views/TasksView.js +4 -3
  100. package/server/lib/html/client.js +21 -4
  101. package/server/lib/html/css.js +2761 -8
  102. package/server/lib/html/icons.js +7 -0
  103. package/server/lib/html/shell.js +10 -3
  104. package/server/lib/scanner.js +376 -39
  105. package/server/orchestrator.js +329 -5
@@ -9,12 +9,15 @@
9
9
 
10
10
  import { html, useState } from '../preact.js';
11
11
  import { useStore } from '../store.js';
12
- import { pct, humanDate, phaseHints } from '../util.js';
12
+ import { pct, humanDate, phaseHints, chip } from '../util.js';
13
13
  import {
14
14
  Chip, ProgressBar, Breadcrumb, CmdHints, RunningBadge, SprintCard, PhaseCard,
15
15
  } from '../components/shared.js';
16
- import { runAndOpenTerm, openTermPanel, runningInPhase } from '../orchestrator.js';
16
+ import { openTermPanel, runningInPhase } from '../orchestrator.js';
17
+ import { openRunnerPicker } from '../components/RunnerPicker.js';
17
18
  import { Icon } from '../icons-client.js';
19
+ import { StatusSummaryBar } from '../components/StatusSummaryBar.js';
20
+ import { FilterChips } from '../components/FilterChips.js';
18
21
 
19
22
  function AttrItem({ label, value }) {
20
23
  return html`
@@ -60,7 +63,9 @@ function PhaseDetail({ phase: p, S }) {
60
63
 
61
64
  function handleRun(e) {
62
65
  e.stopPropagation();
63
- runAndOpenTerm('phase-' + p.id, '/rcode-execute ' + p.id, 'Phase ' + p.id);
66
+ openRunnerPicker(e.currentTarget, {
67
+ kind: 'session', storyId: 'phase-' + p.id, cmd: '/rcode-execute ' + p.id, title: 'Phase ' + p.id,
68
+ });
64
69
  }
65
70
  function handleTerm(e) {
66
71
  e.stopPropagation();
@@ -117,7 +122,18 @@ function PhaseDetail({ phase: p, S }) {
117
122
  `;
118
123
  }
119
124
 
120
- export function PhasesView({ subId }) {
125
+ /**
126
+ * Map a numeric phase id to its milestone bucket.
127
+ * M1 = phases 1–19, M2 = 20–33, M3 = 34+.
128
+ */
129
+ function phaseMilestone(id) {
130
+ const n = Number(id);
131
+ if (n <= 19) return 'M1';
132
+ if (n <= 33) return 'M2';
133
+ return 'M3';
134
+ }
135
+
136
+ export function PhasesView({ subId, filters }) {
121
137
  const S = useStore();
122
138
  const phases = S.phases || [];
123
139
  const [filter, setFilter] = useState('');
@@ -141,7 +157,22 @@ export function PhasesView({ subId }) {
141
157
  `;
142
158
  }
143
159
 
144
- // List mode
160
+ // List mode — normalise incoming filter prop
161
+ const f = filters || { status: '', milestone: '', date: '' };
162
+
163
+ // Build option lists for FilterChips
164
+ const distinctStatus = [...new Set(phases.map(p => chip(p.status).cls))].filter(Boolean);
165
+ const statusOptions = distinctStatus.map(cls => ({ value: cls, label: cls }));
166
+ const milestoneOptions = [
167
+ { value: 'M1', label: 'M1' },
168
+ { value: 'M2', label: 'M2' },
169
+ { value: 'M3', label: 'M3' },
170
+ ];
171
+ const dateOptions = [
172
+ { value: 'has-completed', label: 'Completed' },
173
+ { value: 'no-completed', label: 'In progress' },
174
+ ];
175
+
145
176
  const allComplete =
146
177
  phases.length > 0 &&
147
178
  phases.every(ph => ph.status === 'complete' || ph.status === 'completed' || ph.status === 'done');
@@ -157,13 +188,26 @@ export function PhasesView({ subId }) {
157
188
  }
158
189
 
159
190
  const q = filter.toLowerCase();
160
- const filtered = q
191
+ let filtered = q
161
192
  ? phases.filter(p => (p.name || '').toLowerCase().includes(q) || String(p.id).includes(q))
162
193
  : phases;
163
194
 
195
+ // Apply chip filters
196
+ if (f.status) filtered = filtered.filter(p => chip(p.status).cls === f.status);
197
+ if (f.milestone) filtered = filtered.filter(p => phaseMilestone(p.id) === f.milestone);
198
+ if (f.date === 'has-completed') filtered = filtered.filter(p => !!p.completed_at);
199
+ if (f.date === 'no-completed') filtered = filtered.filter(p => !p.completed_at);
200
+
164
201
  return html`
165
202
  <div id="view-phases" class="view active">
166
203
  <div class="view-title">Phases</div>
204
+ <${StatusSummaryBar}/>
205
+ <${FilterChips}
206
+ filters=${f}
207
+ statusOptions=${statusOptions}
208
+ milestoneOptions=${milestoneOptions}
209
+ dateOptions=${dateOptions}
210
+ />
167
211
  <div class="filter-bar">
168
212
  <input class="filter-input" type="text" placeholder="Filter…"
169
213
  value=${filter} onInput=${e => setFilter(e.target.value)}/>
@@ -16,7 +16,9 @@ import { html, useState, useEffect } from '../preact.js';
16
16
  import { useStore } from '../store.js';
17
17
  import { pctNum, sprintHints, phaseHints } from '../util.js';
18
18
  import { Chip, ProgressBar, CmdHints, RunBtn, RunningBadge } from '../components/shared.js';
19
- import { runningInPhase, runAndOpenTerm } from '../orchestrator.js';
19
+ import { runningInPhase } from '../orchestrator.js';
20
+ import { openRunnerPicker } from '../components/RunnerPicker.js';
21
+ import { PhaseGraph } from '../components/PhaseGraph.js';
20
22
  import { Icon } from '../icons-client.js';
21
23
 
22
24
  /**
@@ -202,6 +204,7 @@ export function RoadmapView() {
202
204
  return html`
203
205
  <div id="view-roadmap" class="view active">
204
206
  <div class="view-title">Roadmap</div>
207
+ <${PhaseGraph} phases=${phases}/>
205
208
  <div class="filter-bar">
206
209
  <input class="filter-input" type="text" placeholder="Filter roadmap…"
207
210
  value=${filterQuery}
@@ -218,11 +221,11 @@ export function RoadmapView() {
218
221
  </span>
219
222
  <span class="ms-actions">
220
223
  <button class="card-run-btn" title="Execute every remaining phase (autonomous run — pauses at checkpoints)"
221
- onClick=${e => { e.stopPropagation(); runAndOpenTerm('milestone-execute-all', '/rcode-autonomous', 'Execute all phases'); }}>
224
+ onClick=${e => { e.stopPropagation(); openRunnerPicker(e.currentTarget, { kind: 'session', storyId: 'milestone-execute-all', cmd: '/rcode-autonomous', title: 'Execute all phases' }); }}>
222
225
  <${Icon} name="play" size=${12}/> Run All
223
226
  </button>
224
227
  <button class="card-run-btn ms-audit-btn" title="Audit the whole milestone"
225
- onClick=${e => { e.stopPropagation(); runAndOpenTerm('milestone-audit', '/rcode-audit-milestone', 'Audit milestone'); }}>
228
+ onClick=${e => { e.stopPropagation(); openRunnerPicker(e.currentTarget, { kind: 'session', storyId: 'milestone-audit', cmd: '/rcode-audit-milestone', title: 'Audit milestone' }); }}>
226
229
  <${Icon} name="clipboard-list" size=${12}/> Audit
227
230
  </button>
228
231
  </span>
@@ -10,12 +10,15 @@
10
10
 
11
11
  import { html, useState } from '../preact.js';
12
12
  import { useStore } from '../store.js';
13
- import { pct, humanDate, allSprints, sprintHints } from '../util.js';
13
+ import { pct, humanDate, allSprints, sprintHints, chip } from '../util.js';
14
14
  import {
15
15
  Chip, ProgressBar, Breadcrumb, CmdHints, RunningBadge, SprintCard, TaskCard,
16
16
  } from '../components/shared.js';
17
- import { runAndOpenTerm, openTermPanel, runningInSprint } from '../orchestrator.js';
17
+ import { openTermPanel, runningInSprint } from '../orchestrator.js';
18
+ import { openRunnerPicker } from '../components/RunnerPicker.js';
18
19
  import { Icon } from '../icons-client.js';
20
+ import { StatusSummaryBar } from '../components/StatusSummaryBar.js';
21
+ import { FilterChips } from '../components/FilterChips.js';
19
22
 
20
23
  function AttrItem({ label, value }) {
21
24
  return html`
@@ -49,7 +52,9 @@ function SprintDetail({ sprint: s, S }) {
49
52
 
50
53
  function handleRun(e) {
51
54
  e.stopPropagation();
52
- runAndOpenTerm('sprint-' + s.id, '/rcode-execute-sprint ' + s.id, 'Sprint ' + s.id);
55
+ openRunnerPicker(e.currentTarget, {
56
+ kind: 'session', storyId: 'sprint-' + s.id, cmd: '/rcode-execute-sprint ' + s.id, title: 'Sprint ' + s.id,
57
+ });
53
58
  }
54
59
  function handleTerm(e) {
55
60
  e.stopPropagation();
@@ -113,7 +118,18 @@ function SprintDetail({ sprint: s, S }) {
113
118
  `;
114
119
  }
115
120
 
116
- export function SprintsView({ subId }) {
121
+ /**
122
+ * Map a numeric phase id to its milestone bucket.
123
+ * M1 = phases 1–19, M2 = 20–33, M3 = 34+.
124
+ */
125
+ function phaseMilestone(id) {
126
+ const n = Number(id);
127
+ if (n <= 19) return 'M1';
128
+ if (n <= 33) return 'M2';
129
+ return 'M3';
130
+ }
131
+
132
+ export function SprintsView({ subId, filters }) {
117
133
  const S = useStore();
118
134
  const sprints = allSprints(S.phases || []);
119
135
  const [filter, setFilter] = useState('');
@@ -135,7 +151,22 @@ export function SprintsView({ subId }) {
135
151
  `;
136
152
  }
137
153
 
138
- // List mode
154
+ // List mode — normalise incoming filter prop
155
+ const f = filters || { status: '', milestone: '', date: '' };
156
+
157
+ // Build option lists for FilterChips
158
+ const distinctStatus = [...new Set(sprints.map(s => chip(s.status).cls))].filter(Boolean);
159
+ const statusOptions = distinctStatus.map(cls => ({ value: cls, label: cls }));
160
+ const milestoneOptions = [
161
+ { value: 'M1', label: 'M1' },
162
+ { value: 'M2', label: 'M2' },
163
+ { value: 'M3', label: 'M3' },
164
+ ];
165
+ const dateOptions = [
166
+ { value: 'has-completed', label: 'Completed' },
167
+ { value: 'no-completed', label: 'In progress' },
168
+ ];
169
+
139
170
  const curSp = sprints.find(sp => sp.id === S.currentSprint);
140
171
  const slHints = [
141
172
  ['/rcode-sprint-planning','Plan a new sprint'],
@@ -147,7 +178,7 @@ export function SprintsView({ subId }) {
147
178
  }
148
179
 
149
180
  const q = filter.toLowerCase();
150
- const filtered = q
181
+ let filtered = q
151
182
  ? sprints.filter(s =>
152
183
  String(s.id).includes(q) ||
153
184
  (s.goal || '').toLowerCase().includes(q) ||
@@ -155,9 +186,22 @@ export function SprintsView({ subId }) {
155
186
  )
156
187
  : sprints;
157
188
 
189
+ // Apply chip filters
190
+ if (f.status) filtered = filtered.filter(s => chip(s.status).cls === f.status);
191
+ if (f.milestone) filtered = filtered.filter(s => phaseMilestone(s.phaseId) === f.milestone);
192
+ if (f.date === 'has-completed') filtered = filtered.filter(s => !!s.completed_at);
193
+ if (f.date === 'no-completed') filtered = filtered.filter(s => !s.completed_at);
194
+
158
195
  return html`
159
196
  <div id="view-sprints" class="view active">
160
197
  <div class="view-title">Sprints</div>
198
+ <${StatusSummaryBar}/>
199
+ <${FilterChips}
200
+ filters=${f}
201
+ statusOptions=${statusOptions}
202
+ milestoneOptions=${milestoneOptions}
203
+ dateOptions=${dateOptions}
204
+ />
161
205
  <div class="filter-bar">
162
206
  <input class="filter-input" type="text" placeholder="Filter…"
163
207
  value=${filter} onInput=${e => setFilter(e.target.value)}/>
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { html, useState, useMemo } from '../preact.js';
10
10
  import { useStore } from '../store.js';
11
- import { allTasks } from '../util.js';
11
+ import { allTasks, currentPhaseId } from '../util.js';
12
12
  import { CmdHints, TaskCard } from '../components/shared.js';
13
13
 
14
14
  function TaskGrouped({ tasks }) {
@@ -96,8 +96,9 @@ export function TasksView() {
96
96
  tHints.push(['/rcode-correct-course','Course-correct blockers']);
97
97
  }
98
98
 
99
- // Empty state
100
- const phaseHint = S.currentPhase ? ' ' + S.currentPhase : '';
99
+ // Empty state — currentPhase is the contract object (or legacy string).
100
+ const cpId = currentPhaseId(S.currentPhase);
101
+ const phaseHint = cpId ? ' ' + cpId : '';
101
102
 
102
103
  return html`
103
104
  <div id="view-tasks" class="view active">
@@ -21,22 +21,39 @@ try { RCODE_VERSION = require('../../../package.json').version || ''; } catch {
21
21
  // Fields the client needs from the scanned state. Kept in sync with
22
22
  // store.js initial state and the view components that read it.
23
23
  function clientState(state) {
24
+ // Redesign dashboard contract (.planning/campaign/DATA-CONTRACT.md), derived
25
+ // by scanner.buildDashboard. Always present and correctly typed.
26
+ const d = state.dashboard || {};
24
27
  return JSON.stringify({
25
- phases: state.phaseTree || state.raw?.phases || [],
28
+ // First-run signal: false when the scanned .rcode dir does not exist, so
29
+ // the Overview shows a "run /rcode-init" state instead of empty cards.
30
+ initialized: state.exists !== false,
31
+ // Redesign contract keys (read by the Overview slot components).
32
+ project: d.project || null,
33
+ progress: d.progress || null,
34
+ timeline: d.timeline || null,
35
+ tasks: d.tasks || null,
36
+ health: d.health || null,
37
+ // Colliding keys — the contract shapes are supersets, so legacy views and
38
+ // the redesign read the same field. currentPhase becomes the contract object.
39
+ phases: d.phases || state.phaseTree || state.raw?.phases || [],
40
+ decisions: d.decisions || state.raw?.decisions || [],
41
+ blockers: d.blockers || state.raw?.blockers || [],
42
+ currentPhase: d.currentPhase || state.raw?.current_phase || null,
43
+ // Existing fields kept working for the legacy views.
26
44
  projectName: state.projectName || '',
27
45
  projectRoot: state.projectRoot || '',
28
46
  version: RCODE_VERSION,
29
47
  milestone: state.raw?.milestone || '',
30
- currentPhase: state.raw?.current_phase || null,
31
48
  currentSprint: state.raw?.current_sprint || null,
32
- decisions: state.raw?.decisions || [],
33
- blockers: state.raw?.blockers || [],
34
49
  council_sessions: state.raw?.council_sessions || [],
35
50
  last_session: state.raw?.last_session || null,
36
51
  chains: state.raw?.chains || [],
37
52
  workstreams: state.raw?.workstreams || [],
38
53
  pendingHandoff: state.pendingHandoff || null,
39
54
  memoryBank: state.memoryBank || null,
55
+ // state.json corruption signal — App.js renders a dismissible banner.
56
+ rawParseError: state.rawParseError || null,
40
57
  })
41
58
  // Prevent a stray "</script>" inside any string from closing the inline
42
59
  // <script> early. Escaping "<" keeps the JSON valid and inert.