@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/README.md
CHANGED
|
@@ -23,7 +23,7 @@ pnpm dlx @hanzlaa/rcode install
|
|
|
23
23
|
[](https://github.com/hanzlahabib/rcode/actions/workflows/test.yml)
|
|
24
24
|
[](LICENSE)
|
|
25
25
|
|
|
26
|
-
Status: `@hanzlaa/rcode` v4.
|
|
26
|
+
Status: `@hanzlaa/rcode` v4.7.0 on npm. 45 agents · 117 commands · 129 workflows · **1 runtime dependency**. Test status tracked by CI badge above. Actively dogfooded on real projects every week.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -49,7 +49,19 @@ If you're a solo dev or small team using Claude Code (or Cursor, Codex, VS Code)
|
|
|
49
49
|
|
|
50
50
|
<p align="center"><img src="brand/hero-org-in-folder.png" alt="rcode is an engineering org in a folder: agents, commands, workflows, and skills as plain markdown" width="760"></p>
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
In plain words: **rcode is a folder of instructions your AI already knows how to read.** No new app to install, no server to run, no separate "agent brain" — just files. Your IDE (Claude Code, Cursor, whichever) is already an agent; rcode just hands it a really good playbook and a notebook that never forgets.
|
|
53
|
+
|
|
54
|
+
```mermaid
|
|
55
|
+
flowchart LR
|
|
56
|
+
A["You type a command<br/>/rcode-plan"] --> B["rcode's files answer:<br/>what to do, in what order"]
|
|
57
|
+
B --> C["Your IDE's own agent<br/>does the actual work"]
|
|
58
|
+
C --> D["Result gets saved to<br/>.rcode/memory/"]
|
|
59
|
+
D -.->|"next session starts here"| A
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
That loop — command in, memory out, memory feeds the next command — is the whole idea. Everything else in this repo is detail on top of that loop.
|
|
63
|
+
|
|
64
|
+
Three layers make it up:
|
|
53
65
|
|
|
54
66
|
| Layer | What lives here | Example |
|
|
55
67
|
|-------|-----------------|---------|
|
|
@@ -63,15 +75,26 @@ Single agent navigates the structure. No LangChain, no AutoGen, no orchestrator
|
|
|
63
75
|
|
|
64
76
|
## Why I built it
|
|
65
77
|
|
|
66
|
-
I've shipped products solo for years and watched the same failure repeat in every project:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
I've shipped products solo for years and watched the same failure repeat in every project. In one sentence: **the AI forgets everything the moment the chat window closes, so I kept re-explaining the same decisions forever.**
|
|
79
|
+
|
|
80
|
+
```mermaid
|
|
81
|
+
flowchart LR
|
|
82
|
+
subgraph without["😩 Without rcode"]
|
|
83
|
+
direction TB
|
|
84
|
+
w1["Session 1 — pick Postgres"] --> w2["Session 5 — agent forgot,<br/>suggests Mongo, you argue"]
|
|
85
|
+
w2 --> w3["Session 20 — you're pasting<br/>4K tokens of 'here's what<br/>we decided' every time"]
|
|
86
|
+
end
|
|
87
|
+
subgraph with["✅ With rcode"]
|
|
88
|
+
direction TB
|
|
89
|
+
r1["Session 1 — pick Postgres"] --> r2["Decision saved once to<br/>.rcode/memory/decisions.md"]
|
|
90
|
+
r2 --> r3["Session 20 — agent reads it<br/>automatically, no re-explaining"]
|
|
91
|
+
end
|
|
92
|
+
without ~~~ with
|
|
93
|
+
```
|
|
71
94
|
|
|
72
|
-
|
|
95
|
+
That's it. That's the whole pitch. **Write the decision down once, in a file. The agent reads the file. Done.**
|
|
73
96
|
|
|
74
|
-
|
|
97
|
+
The same problem shows up at team scale, just wearing a different costume: onboarding a new hire takes 30 minutes of Slack archaeology, a late requirement quietly shifts the goalposts with no record of why, and six months later nobody remembers why the MVP was built the way it was. rcode's fix is the same either way — write the context down where the agent (and the next human) will actually see it.
|
|
75
98
|
|
|
76
99
|
---
|
|
77
100
|
|
|
@@ -81,6 +104,7 @@ What you'll feel in week one:
|
|
|
81
104
|
|
|
82
105
|
- **No more re-explaining.** Decisions, blockers, conventions live in `.rcode/memory/` — agent reads them at session start automatically (~5K tokens, fully oriented).
|
|
83
106
|
- **Phased delivery without ceremony.** `/rcode-new-project` produces a roadmap with phases → sprints → tasks. `/rcode-plan` produces SPRINT.md files. `/rcode-execute` runs them with atomic commits. No Jira required.
|
|
107
|
+
- **No blank-page starts.** Already know you're building an API, a SaaS product, or a mobile app? `/rcode-from-template api-backend` seeds a real roadmap + requirements doc for that project type — edit it down instead of writing it up from nothing.
|
|
84
108
|
- **Specialist review on tap.** Want a Karpathy-style review of your last commit? `/rcode-review --karpathy`. Want a council debate on a decision? `/rcode-council should I rewrite auth?` — 5 agents answer in parallel, round 2 they challenge each other.
|
|
85
109
|
- **Intent guards.** Run the wrong command and get a one-line redirect, not a useless output.
|
|
86
110
|
- **Health check.** `rcode-tools health` returns JSON — milestone health, state snapshot, project status. Wire it into your dashboard.
|
|
@@ -159,12 +183,21 @@ pnpm dlx @hanzlaa/rcode install
|
|
|
159
183
|
|
|
160
184
|
### The full loop
|
|
161
185
|
|
|
186
|
+
Four commands cover most of a real week of work — decide, plan, build, check in:
|
|
187
|
+
|
|
188
|
+
```mermaid
|
|
189
|
+
flowchart LR
|
|
190
|
+
A["🗣️ /rcode-council<br/>should I rewrite auth?"] --> B["📋 /rcode-plan --research<br/>build a rental app"]
|
|
191
|
+
B --> C["⚙️ /rcode-execute<br/>PLAN.md"]
|
|
192
|
+
C --> D["📊 /rcode-status<br/>phases · decisions · blockers"]
|
|
162
193
|
```
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
194
|
+
|
|
195
|
+
| Command | What it does |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `/rcode-council should I rewrite auth?` | 5 specialist agents debate it, 2 rounds — you get a decision, not a monologue |
|
|
198
|
+
| `/rcode-plan --research build a rental app` | A researcher grounds the plan in your real codebase, a checker verifies it before you build anything |
|
|
199
|
+
| `/rcode-execute .planning/plans/01/PLAN.md` | Runs the plan as atomic git commits, with pass/fail gates between steps |
|
|
200
|
+
| `/rcode-status` | One glance at phases, decisions, and blockers — no digging through chat history |
|
|
168
201
|
|
|
169
202
|
Full install flavors and IDE options: [`docs/install.md`](docs/install.md). Step-by-step first project: [`docs/getting-started.md`](docs/getting-started.md).
|
|
170
203
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.1",
|
|
4
4
|
"description": "rcode — the AI team that never forgets. Persistent memory, specialist agents, and slash commands for AI IDEs. Works in Claude Code, Cursor, Gemini, VS Code, and Antigravity.",
|
|
5
5
|
"main": "cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -4589,6 +4589,7 @@ This file is loaded by Claude Code, Codex, and compatible AI coding tools at the
|
|
|
4589
4589
|
- No "while I'm here" improvements
|
|
4590
4590
|
- No speculative abstractions
|
|
4591
4591
|
- No new files unless necessary
|
|
4592
|
+
- **No freehand status/handoff docs** (\`HANDOFF.md\`, \`AGENT_X_DONE.md\`, anything at the project root summarizing "what I did"). That record is \`<N>-SUMMARY.md\` under \`.planning/phases/\` — even mid-task or in a parallel multi-agent run, write there, not a new root-level file.
|
|
4592
4593
|
|
|
4593
4594
|
---
|
|
4594
4595
|
|
|
@@ -48,6 +48,12 @@
|
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
+
## File placement discipline
|
|
52
|
+
|
|
53
|
+
**Completion notes go in the sprint file, never a freehand root-level doc.** When work finishes, the record is `.planning/phases/{phase}/{sprint}-SUMMARY.md` (written by `/rcode-execute`) or a note in the existing `SPRINT.md`/`REVIEW.md` — never a new `SOMETHING_DONE.md`, `HANDOFF.md`, or `AGENT_X_DONE.md` at the project root or anywhere outside `.planning/`/`.rcode/`. If you're in a parallel multi-agent run and were asked for a "handoff doc," that handoff still belongs inside the sprint's own files — a sibling agent reads the next `SPRINT.md`/`CONTEXT.md`, not a scavenger hunt through the repo root for status files with inconsistent names.
|
|
54
|
+
|
|
55
|
+
**Never invent a new top-level artifact type.** `PROJECT.md`, `ROADMAP.md`, `REQUIREMENTS.md`, `CONTEXT.md`, `RESEARCH.md`, `SPRINT.md`, `SUMMARY.md`, `REVIEW.md` — this is the closed set. If a task genuinely needs a new kind of record, that's a call for the user, not something to freehand mid-task.
|
|
56
|
+
|
|
51
57
|
## Framework discipline
|
|
52
58
|
|
|
53
59
|
**Cite the heuristic by name.** When refusing or recommending, name the rule that drove the call. *"Per the Reversibility test, this is a one-way door — ADR required."* Traceable reasoning beats opinion.
|
|
@@ -26,7 +26,7 @@ If you commit a file under `.planning/` and `git status` afterwards still shows
|
|
|
26
26
|
2. **Load sprint** — Parse SPRINT.md frontmatter (phase, sprint, type, autonomous, wave, depends_on). Honor CONTEXT.md if referenced.
|
|
27
27
|
3. **Determine pattern** — Pattern A (no checkpoints → execute all), B (has checkpoints → stop at first), C (continuation → resume).
|
|
28
28
|
4. **Execute stories** — For each story: if `type="auto"`, execute and commit. If `type="checkpoint:*"`, STOP and return checkpoint. Update story status via `rcode-tools.cjs state story move --id NN.S.TT --status done`.
|
|
29
|
-
5. **Create SUMMARY** — After all auto stories complete, write `.planning/phases/XX-name/{phase}-{sprint}-SUMMARY.md`.
|
|
29
|
+
5. **Create SUMMARY** — After all auto stories complete, write `.planning/phases/XX-name/{phase}-{sprint}-SUMMARY.md`. This is the *only* completion artefact. Never write a parallel status/handoff doc (`AGENT_X_DONE.md`, `HANDOFF.md`, a root-level `*_DONE.md`) — if the run involves multiple parallel executors, each still records its own SUMMARY.md under its own sprint; there is no separate hand-off format.
|
|
30
30
|
6. **Update state** — Run state tools to record metrics, mark stories complete, advance sprint.
|
|
31
31
|
7. **Final commit** — Commit SUMMARY.md, STATE.md, ROADMAP.md with docs message.
|
|
32
32
|
|
|
@@ -12,6 +12,7 @@ hierarchical ID format, and output routing.
|
|
|
12
12
|
|
|
13
13
|
### Context Fidelity
|
|
14
14
|
- **Locked Decisions** (CONTEXT.md): MUST implement exactly. Reference decision ID (D-01, D-02) in task actions.
|
|
15
|
+
- **Reference, don't restate.** A decision ID is a pointer, not a license to re-paste the rationale. `<action>` explains WHAT to do and cites the ID for WHY (`"per D-02's flattening rule"`) — it does not re-explain the decision's reasoning, alternatives, or findings inline. The full "why" already lives in CONTEXT.md/RESEARCH.md; the executor opens those if they need it. A task whose `<action>` reads like a design doc (multi-paragraph rationale, full alternatives-considered writeups) is a sign the planner copied instead of pointed — cut it down to the instruction + the ID.
|
|
15
16
|
- **Deferred Ideas**: MUST NOT appear in plans.
|
|
16
17
|
- **Agent's Discretion**: Use judgment, document choices.
|
|
17
18
|
|
package/rcode/workflows/init.md
CHANGED
|
@@ -285,6 +285,18 @@ After writing both files, refresh the memory bank fingerprint so staleness check
|
|
|
285
285
|
node .rcode/bin/rcode-tools.cjs context refresh >/dev/null 2>&1 || true
|
|
286
286
|
```
|
|
287
287
|
|
|
288
|
+
## Step 4c — Scaffold CLAUDE.md / AGENTS.md if missing
|
|
289
|
+
|
|
290
|
+
`generate-claude-md` (the command routing rule + project rules block every agent needs at session start) previously only ran via the `/rcode-new-project` roadmap flow — a project set up with `/rcode-init` alone (the common "add rcode to an existing codebase" path) never got it, so agents had no ambient instruction to check `do.md` before acting ad-hoc. Close that gap here, unconditionally (not just on `fresh`):
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
if [ ! -f CLAUDE.md ]; then
|
|
294
|
+
node .rcode/bin/rcode-tools.cjs generate-claude-md
|
|
295
|
+
fi
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Never pass `--force` here — an existing `CLAUDE.md` is the user's own file (or was already generated by a prior init/new-project run) and must not be overwritten. Silent no-op when `CLAUDE.md` already exists.
|
|
299
|
+
|
|
288
300
|
## Step 5 — Suggest the next step
|
|
289
301
|
|
|
290
302
|
Print a contextual recommendation, **one line of copy-paste per suggestion** (per `.rcode/references/command-redirect-format.md`):
|
|
@@ -339,6 +351,7 @@ Silent if state tools fail.
|
|
|
339
351
|
- [ ] `.rcode/JOURNEY.md` written (unless `--skip-scan` or no code)
|
|
340
352
|
- [ ] `.rcode/context/active.md` populated with project state (not the placeholder stub)
|
|
341
353
|
- [ ] `.rcode/context/project-brief.md` populated with project overview (not the placeholder stub)
|
|
354
|
+
- [ ] `CLAUDE.md` exists (generated if it didn't already)
|
|
342
355
|
- [ ] State detected correctly (fresh / existing-new-rcode / returning)
|
|
343
356
|
- [ ] Contextual next-step suggestion printed as single-line copy-paste
|
|
344
357
|
|
|
@@ -161,42 +161,32 @@ Use AskUserQuestion:
|
|
|
161
161
|
|
|
162
162
|
**Generate or refresh project instruction file before final commit:**
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```markdown
|
|
167
|
-
# {INSTRUCTION_FILE} — project instructions
|
|
168
|
-
|
|
169
|
-
This project uses rcode for planning and execution. See `.planning/PROJECT.md` for context and `.planning/ROADMAP.md` for phases.
|
|
170
|
-
|
|
171
|
-
Common commands:
|
|
172
|
-
- /rcode-progress — check status and next action
|
|
173
|
-
- /rcode-discuss-phase N — gather context before planning phase N
|
|
174
|
-
- /rcode-plan N — create a SPRINT.md for phase N
|
|
175
|
-
- /rcode-execute N — execute a SPRINT.md
|
|
176
|
-
- /rcode-verify-work — conversational UAT
|
|
177
|
-
- /rcode-complete-milestone — archive milestone and reset
|
|
178
|
-
|
|
179
|
-
Rules:
|
|
180
|
-
- Never run `git push` without explicit user authorization.
|
|
181
|
-
- No Claude/AI attribution in commits.
|
|
182
|
-
- Prefer editing existing files over creating new ones.
|
|
164
|
+
```bash
|
|
165
|
+
node .rcode/bin/rcode-tools.cjs generate-claude-md
|
|
183
166
|
```
|
|
184
167
|
|
|
185
|
-
|
|
168
|
+
Writes `CLAUDE.md` and `AGENTS.md` from rcode's own template (commit rules,
|
|
169
|
+
push rules, phase workflow rules, scope discipline, and the command-routing
|
|
170
|
+
rule pointing at `do.md`). Refuses to touch an existing `CLAUDE.md` — if it
|
|
171
|
+
already exists, this is a silent no-op (respects user-customized content).
|
|
172
|
+
`AGENTS.md` is written only when absent, so an install-appended `## rcode
|
|
173
|
+
Agents (installed)` roster section is never clobbered.
|
|
186
174
|
|
|
187
175
|
**Commit roadmap (guarded):**
|
|
188
176
|
|
|
189
177
|
```bash
|
|
190
|
-
git add
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
178
|
+
# git add fails its whole invocation on any missing pathspec, so only pass
|
|
179
|
+
# paths that actually exist (CLAUDE.md/AGENTS.md are conditional — see above).
|
|
180
|
+
ADD_PATHS=(.planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md)
|
|
181
|
+
for f in CLAUDE.md AGENTS.md; do [ -f "$f" ] && ADD_PATHS+=("$f"); done
|
|
182
|
+
|
|
183
|
+
git add "${ADD_PATHS[@]}" 2>/dev/null \
|
|
195
184
|
&& git commit -m "docs: create roadmap ([N] phases)" 2>/dev/null \
|
|
196
|
-
|| echo "ℹ .planning/ gitignored — roadmap written, not committed (instruction
|
|
185
|
+
|| echo "ℹ .planning/ gitignored — roadmap written, not committed (instruction files committed separately)"
|
|
197
186
|
|
|
198
|
-
# Fallback: also try committing just the instruction
|
|
199
|
-
|
|
187
|
+
# Fallback: also try committing just the instruction files if .planning was ignored
|
|
188
|
+
IFILES=(); for f in CLAUDE.md AGENTS.md; do [ -f "$f" ] && IFILES+=("$f"); done
|
|
189
|
+
[ ${#IFILES[@]} -gt 0 ] && git add "${IFILES[@]}" 2>/dev/null && git commit -m "docs: add project instruction files" 2>/dev/null || true
|
|
200
190
|
|
|
201
191
|
# Sync all roadmapper-created phases into state.json.
|
|
202
192
|
# rcode-roadmapper writes ROADMAP.md as text — it never calls `phase add` — so
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
import { html, useState, useEffect, useRef, useCallback } from '../preact.js';
|
|
16
16
|
import { parseFilters } from '../filter-state.js';
|
|
17
|
-
import { getState, setState, subscribe, registerRefresh } from '../store.js';
|
|
17
|
+
import { getState, setState, subscribe, registerRefresh, closeFileViewer, closeDecisionViewer, closeBlockerViewer } from '../store.js';
|
|
18
|
+
import { FileReader } from './FileReader.js';
|
|
19
|
+
import { DecisionDrawer, BlockerDrawer } from './shared.js';
|
|
18
20
|
import { startSessionsPoll, refreshOrchToken } from '../orchestrator.js';
|
|
19
21
|
import { Sidebar } from './Sidebar.js';
|
|
20
22
|
import { Topbar } from './Topbar.js';
|
|
@@ -32,6 +34,7 @@ import { PhasesView } from '../views/PhasesView.js';
|
|
|
32
34
|
import { SprintsView } from '../views/SprintsView.js';
|
|
33
35
|
import { TasksView } from '../views/TasksView.js';
|
|
34
36
|
import { KanbanView } from '../views/KanbanView.js';
|
|
37
|
+
import { BacklogView } from '../views/BacklogView.js';
|
|
35
38
|
import { FilesView } from '../views/FilesView.js';
|
|
36
39
|
import { AgentsView } from '../views/AgentsView.js';
|
|
37
40
|
import { MemoryView } from '../views/MemoryView.js';
|
|
@@ -48,6 +51,7 @@ const PREACT_VIEWS = {
|
|
|
48
51
|
sprints: SprintsView,
|
|
49
52
|
tasks: TasksView,
|
|
50
53
|
kanban: KanbanView,
|
|
54
|
+
backlog: BacklogView,
|
|
51
55
|
files: FilesView,
|
|
52
56
|
agents: AgentsView,
|
|
53
57
|
memory: MemoryView,
|
|
@@ -213,6 +217,7 @@ export function App() {
|
|
|
213
217
|
timeline: d.timeline || null,
|
|
214
218
|
tasks: d.tasks || null,
|
|
215
219
|
health: d.health || null,
|
|
220
|
+
backlog: d.backlog || [],
|
|
216
221
|
});
|
|
217
222
|
if (newState.raw) {
|
|
218
223
|
Object.assign(patch, {
|
|
@@ -310,6 +315,15 @@ export function App() {
|
|
|
310
315
|
<${RunnerPicker} />
|
|
311
316
|
<${RunConfirmDialog} pending=${storeState.runConfirm} />
|
|
312
317
|
<${CommandPalette} open=${paletteOpen} onClose=${() => setPaletteOpen(false)} />
|
|
318
|
+
${storeState.fileViewer && html`
|
|
319
|
+
<${FileReader}
|
|
320
|
+
path=${storeState.fileViewer.path}
|
|
321
|
+
title=${storeState.fileViewer.title}
|
|
322
|
+
onClose=${closeFileViewer}
|
|
323
|
+
/>
|
|
324
|
+
`}
|
|
325
|
+
<${DecisionDrawer} decision=${storeState.decisionViewer} onClose=${closeDecisionViewer} />
|
|
326
|
+
<${BlockerDrawer} blocker=${storeState.blockerViewer} onClose=${closeBlockerViewer} />
|
|
313
327
|
</div>
|
|
314
328
|
`;
|
|
315
329
|
}
|
|
@@ -171,25 +171,33 @@ function Tooltip({ phase, nodePos, canvasW, canvasH }) {
|
|
|
171
171
|
`;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Sequence row — the honest no-dependencies presentation. There is nothing
|
|
176
|
+
* to lay out as a DAG (no edges), so this doesn't pretend to be a graph: a
|
|
177
|
+
* connected left-to-right chain of small status pills in roadmap order,
|
|
178
|
+
* joined by arrows. Reads as "the order things ship in", not "broken nodes".
|
|
179
|
+
*/
|
|
175
180
|
function FlowRow({ phases }) {
|
|
176
181
|
return html`
|
|
177
|
-
<div class="pg-
|
|
178
|
-
|
|
182
|
+
<div class="pg-hint pg-hint-top">No cross-phase dependencies declared — phases shown in roadmap order.</div>
|
|
183
|
+
<div class="pg-seq">
|
|
184
|
+
${phases.map((p, i) => {
|
|
179
185
|
const kind = statusKind(p.status);
|
|
180
186
|
const sprints = (p.sprints || []).length;
|
|
181
187
|
return html`
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
<span key=${p.id} class="pg-seq-item">
|
|
189
|
+
<button type="button"
|
|
190
|
+
class=${'pg-seq-chip pg-' + kind}
|
|
191
|
+
title=${(p.name || '') + ' — ' + sprints + (sprints === 1 ? ' sprint' : ' sprints')}
|
|
192
|
+
onClick=${() => goToPhase(p.id)}>
|
|
193
|
+
<span class="pg-seq-id">P${p.id}</span>
|
|
194
|
+
<span class="pg-seq-name">${truncate(p.name, 22)}</span>
|
|
195
|
+
</button>
|
|
196
|
+
${i < phases.length - 1 ? html`<span class="pg-seq-arrow" aria-hidden="true">›</span>` : null}
|
|
197
|
+
</span>
|
|
189
198
|
`;
|
|
190
199
|
})}
|
|
191
200
|
</div>
|
|
192
|
-
<div class="pg-hint">No cross-phase dependencies declared — phases shown in roadmap order.</div>
|
|
193
201
|
`;
|
|
194
202
|
}
|
|
195
203
|
|
|
@@ -280,7 +288,7 @@ export function PhaseGraph({ phases }) {
|
|
|
280
288
|
return html`
|
|
281
289
|
<details class="pg-panel" open>
|
|
282
290
|
<summary>
|
|
283
|
-
<${Icon} name="layers" size=${14}/> Dependency Graph
|
|
291
|
+
<${Icon} name="layers" size=${14}/> ${hasDeps ? 'Dependency Graph' : 'Phases'}
|
|
284
292
|
<span class="pg-count">${list.length} ${list.length === 1 ? 'phase' : 'phases'}</span>
|
|
285
293
|
</summary>
|
|
286
294
|
<div class="pg-legend">
|
|
@@ -33,6 +33,7 @@ const NAV_LINKS = [
|
|
|
33
33
|
{ view: 'sprints', icon: 'zap', label: 'Sprints' },
|
|
34
34
|
{ view: 'tasks', icon: 'checkSquare', label: 'Tasks' },
|
|
35
35
|
{ view: 'kanban', icon: 'kanban', label: 'Kanban' },
|
|
36
|
+
{ view: 'backlog', icon: 'hourglass', label: 'Backlog' },
|
|
36
37
|
{ view: 'decisions', icon: 'scale', label: 'Decisions' },
|
|
37
38
|
{ view: 'files', icon: 'file-text', label: 'Files' },
|
|
38
39
|
{ view: 'agents', icon: 'users', label: 'Agents' },
|
|
@@ -92,10 +92,10 @@ export function Topbar({ projectName, updatedAgo, refreshing, onRefresh, onToggl
|
|
|
92
92
|
class="tb-btn tb-btn--icon"
|
|
93
93
|
type="button"
|
|
94
94
|
onClick=${onToggleTheme}
|
|
95
|
-
title=${'
|
|
96
|
-
aria-label="
|
|
95
|
+
title=${'Switch to ' + (themeLabel === 'light' ? 'dark' : 'light') + ' theme'}
|
|
96
|
+
aria-label="Toggle theme"
|
|
97
97
|
>
|
|
98
|
-
|
|
98
|
+
<${Icon} name=${themeLabel === 'light' ? 'moon' : 'sun'} size=${15} />
|
|
99
99
|
</button>
|
|
100
100
|
</div>
|
|
101
101
|
</header>
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { html } from '../../preact.js';
|
|
14
|
-
import { useStore } from '../../store.js';
|
|
15
|
-
import {
|
|
14
|
+
import { useStore, openBlockerViewer } from '../../store.js';
|
|
15
|
+
import { pressable } from '../shared.js';
|
|
16
16
|
|
|
17
17
|
// Severity → pill label (lowercase enum to human-facing label).
|
|
18
18
|
const SEV_LABEL = { high: 'High', medium: 'Medium', low: 'Low' };
|
|
@@ -40,7 +40,7 @@ export function Blockers() {
|
|
|
40
40
|
${blockers.map((b) => {
|
|
41
41
|
const sev = SEV_LABEL[b.severity] ? b.severity : 'low';
|
|
42
42
|
return html`
|
|
43
|
-
<li class="bk-row ovr-link" key=${b.title} ...${
|
|
43
|
+
<li class="bk-row ovr-link" key=${b.title} ...${pressable(() => openBlockerViewer(b))}>
|
|
44
44
|
<span class=${'bk-icon bk-sev-' + sev} aria-hidden="true">⚠</span>
|
|
45
45
|
<div class="bk-body">
|
|
46
46
|
<p class="bk-title">${b.title}</p>
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { html } from '../../preact.js';
|
|
13
|
-
import { useStore } from '../../store.js';
|
|
13
|
+
import { useStore, openFileViewer } from '../../store.js';
|
|
14
14
|
import { humanDate } from '../../util.js';
|
|
15
|
+
import { pressable } from '../shared.js';
|
|
15
16
|
|
|
16
17
|
export function CompletedTasks() {
|
|
17
18
|
const S = useStore();
|
|
@@ -30,7 +31,8 @@ export function CompletedTasks() {
|
|
|
30
31
|
: html`
|
|
31
32
|
<ul class="ct-list">
|
|
32
33
|
${items.map((t, i) => html`
|
|
33
|
-
<li class
|
|
34
|
+
<li class=${'ct-row' + (t.file ? ' ovr-link' : '')} key=${t.title + i}
|
|
35
|
+
...${t.file ? pressable(() => openFileViewer(t.file, t.title)) : {}}>
|
|
34
36
|
<svg class="ct-check" width="16" height="16" viewBox="0 0 24 24"
|
|
35
37
|
fill="none" stroke="currentColor" stroke-width="2.5"
|
|
36
38
|
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { html } from '../../preact.js';
|
|
21
|
-
import { useStore } from '../../store.js';
|
|
22
|
-
import { orchElapsed
|
|
21
|
+
import { useStore, openFileViewer } from '../../store.js';
|
|
22
|
+
import { orchElapsed } from '../../util.js';
|
|
23
23
|
import { openTermPanel } from '../../orchestrator.js';
|
|
24
24
|
import { pressable } from '../shared.js';
|
|
25
25
|
import { TaskPipeline } from '../TaskPipeline.js';
|
|
@@ -60,7 +60,8 @@ export function InProgress() {
|
|
|
60
60
|
<ul class="ip-list">
|
|
61
61
|
${live.map(s => html`<${LiveRow} key=${'live-' + s.storyId} session=${s}/>`)}
|
|
62
62
|
${items.map((t, i) => html`
|
|
63
|
-
<li class
|
|
63
|
+
<li class=${'ip-row' + (t.file ? ' ovr-link' : '')} key=${t.title + i}
|
|
64
|
+
...${t.file ? pressable(() => openFileViewer(t.file, t.title)) : {}}>
|
|
64
65
|
<span class="ip-title">${t.title}</span>
|
|
65
66
|
<${TaskPipeline} task=${t} mini=${true}/>
|
|
66
67
|
${Number.isFinite(t.pct) ? html`<span class="ip-badge">${t.pct}%</span>` : null}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { html } from '../../preact.js';
|
|
15
|
-
import { useStore } from '../../store.js';
|
|
16
|
-
import {
|
|
15
|
+
import { useStore, openFileViewer } from '../../store.js';
|
|
16
|
+
import { pressable } from '../shared.js';
|
|
17
17
|
|
|
18
18
|
// Map phase state → label + badge/segment modifier.
|
|
19
19
|
function stateMeta(state) {
|
|
@@ -84,11 +84,16 @@ export function ProgressTimeline() {
|
|
|
84
84
|
<div class="pt-track">
|
|
85
85
|
${phases.map((p, i) => {
|
|
86
86
|
const m = stateMeta(p.state);
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
87
|
+
// Same "first sprint with a resolved file" lookup PhasesView uses
|
|
88
|
+
// for its "View plan file" button. Falls back to the phase detail
|
|
89
|
+
// page when nothing resolved (e.g. phase has no SPRINT.md yet).
|
|
90
|
+
const sps = Array.isArray(p.sprints) ? p.sprints : [];
|
|
91
|
+
const planFile = (sps.find(s => s.file) || {}).file || null;
|
|
92
|
+
const onActivate = planFile
|
|
93
|
+
? () => openFileViewer(planFile, 'Phase ' + (p.id != null ? p.id : '') + ' plan')
|
|
94
|
+
: () => { location.hash = p.id != null ? 'phases/' + p.id : 'phases'; };
|
|
90
95
|
return html`
|
|
91
|
-
<div class=${'pt-seg ovr-link ' + m.mod} key=${p.name + i} ...${
|
|
96
|
+
<div class=${'pt-seg ovr-link ' + m.mod} key=${p.name + i} ...${pressable(onActivate)}>
|
|
92
97
|
<span class="pt-seg-name">${p.name}</span>
|
|
93
98
|
<span class="pt-seg-range">${p.range || ''}</span>
|
|
94
99
|
<span class="pt-seg-badge">${m.label}</span>
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { html } from '../../preact.js';
|
|
12
|
-
import { useStore } from '../../store.js';
|
|
13
|
-
import { humanDate
|
|
12
|
+
import { useStore, openDecisionViewer } from '../../store.js';
|
|
13
|
+
import { humanDate } from '../../util.js';
|
|
14
|
+
import { pressable } from '../shared.js';
|
|
14
15
|
|
|
15
16
|
// Map a free-form status string to a badge modifier class.
|
|
16
17
|
function statusClass(status) {
|
|
@@ -42,7 +43,7 @@ export function RecentDecisions() {
|
|
|
42
43
|
: html`
|
|
43
44
|
<ul class="rd-list">
|
|
44
45
|
${decisions.map((d, i) => html`
|
|
45
|
-
<li class="rd-row ovr-link" key=${d.title + i} ...${
|
|
46
|
+
<li class="rd-row ovr-link" key=${d.title + i} ...${pressable(() => openDecisionViewer(d))}>
|
|
46
47
|
<span class="rd-title">${d.title}</span>
|
|
47
48
|
${d.status
|
|
48
49
|
? html`<span class=${'rd-badge ' + statusClass(d.status)}>${d.status}</span>`
|