@hanzlaa/rcode 4.3.0 → 4.3.2
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/package.json +1 -1
- package/rcode/references/auto-init-guard.md +2 -2
- package/rcode/references/output-format.md +5 -5
- package/rcode/skills/actions/2-plan/rcode-create-milestone/steps/step-10-complete.md +1 -1
- package/server/dashboard.js +7 -6
- package/server/lib/html/client/components/StatusSummaryBar.js +20 -2
- package/server/lib/html/client/util.js +41 -0
- package/server/lib/html/client/views/PhasesView.js +1 -12
- package/server/lib/html/client/views/SprintsView.js +1 -12
- package/server/lib/html/css.js +5 -0
- package/server/orchestrator.js +17 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
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": {
|
|
@@ -25,8 +25,8 @@ rcode isn't configured for this project yet. Let me set it up — takes 30 secon
|
|
|
25
25
|
**1. Bootstrap local tooling** — copy bin from the global install:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
|
|
29
|
-
TOOLS_SRC="$
|
|
28
|
+
GLOBAL_RCODE="$HOME/.rcode"
|
|
29
|
+
TOOLS_SRC="$GLOBAL_RCODE/bin/rcode-tools.cjs"
|
|
30
30
|
|
|
31
31
|
if [ ! -f "$TOOLS_SRC" ]; then
|
|
32
32
|
echo "ERROR: Global rcode tools not found at $TOOLS_SRC"
|
|
@@ -42,7 +42,7 @@ Use for major workflow transitions.
|
|
|
42
42
|
|
|
43
43
|
```
|
|
44
44
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
45
|
-
|
|
45
|
+
RCODE ► {STAGE NAME}
|
|
46
46
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -69,7 +69,7 @@ Use this when a router command dispatches to another command:
|
|
|
69
69
|
|
|
70
70
|
```
|
|
71
71
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
72
|
-
|
|
72
|
+
RCODE ► ROUTING
|
|
73
73
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
74
74
|
|
|
75
75
|
Input: {user's question or intent}
|
|
@@ -328,7 +328,7 @@ Use standard markdown pipe tables with status symbols:
|
|
|
328
328
|
**Majlis banner** (multi-agent council):
|
|
329
329
|
```
|
|
330
330
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
331
|
-
|
|
331
|
+
RCODE ► MAJLIS CONVENING
|
|
332
332
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
333
333
|
```
|
|
334
334
|
|
|
@@ -354,7 +354,7 @@ the banner, not inside it.
|
|
|
354
354
|
|
|
355
355
|
```
|
|
356
356
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
357
|
-
|
|
357
|
+
RCODE ► PLANNING SPRINT 01.1
|
|
358
358
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
359
359
|
التخطيط للسباق 01.1 — يرجى الانتظار
|
|
360
360
|
```
|
|
@@ -389,7 +389,7 @@ translated prose goes outside the art, on its own line(s).
|
|
|
389
389
|
|
|
390
390
|
- Varying box/banner widths within same output
|
|
391
391
|
- Mixing banner styles (`===`, `---`, `***`)
|
|
392
|
-
- Skipping `
|
|
392
|
+
- Skipping `RCODE ►` prefix in stage banners
|
|
393
393
|
- Random emoji (`🚀`, `✨`, `💫`) outside the approved set
|
|
394
394
|
- Missing Next Up block after workflow completions
|
|
395
395
|
- Hardcoding references to other methodologies in rcode's UX
|
|
@@ -16,7 +16,7 @@ Append `step-10-complete` to `stepsCompleted`. Add `completedAt: {ISO date}`.
|
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
19
|
-
|
|
19
|
+
RCODE ► ROADMAP CREATED
|
|
20
20
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
21
21
|
|
|
22
22
|
Source PRD: {inputFile}
|
package/server/dashboard.js
CHANGED
|
@@ -160,11 +160,12 @@ function handleRequest(req, res) {
|
|
|
160
160
|
server.listen(PORT, '127.0.0.1', () => {
|
|
161
161
|
console.log(`\n🕌 Majlis (مجلس) — rcode Dashboard`);
|
|
162
162
|
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
163
|
-
console.log(`
|
|
164
|
-
console.log(`
|
|
165
|
-
console.log(` Scanning:
|
|
166
|
-
console.log(` Refresh:
|
|
167
|
-
console.log(`
|
|
163
|
+
console.log(` 👉 OPEN THIS: http://localhost:${PORT}`);
|
|
164
|
+
console.log(` Mode: view-only`);
|
|
165
|
+
console.log(` Scanning: ${RCODE_DIR}`);
|
|
166
|
+
console.log(` Refresh: 30s soft poll`);
|
|
167
|
+
console.log(` Note: port ${PORT + 1} is the internal orchestrator API — not for the browser`);
|
|
168
|
+
console.log(` Stop: kill $(ss -ltnp 'sport = :${PORT}' | awk 'NR>1{match($6,/pid=([0-9]+)/,m); print m[1]}')`);
|
|
168
169
|
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
|
|
169
170
|
});
|
|
170
171
|
|
|
@@ -224,7 +225,7 @@ function spawnOrchestrator() {
|
|
|
224
225
|
try {
|
|
225
226
|
_orchProc = spawn(process.execPath, [ORCH_BIN], {
|
|
226
227
|
cwd: path.join(__dirname, '..'),
|
|
227
|
-
env: { ...process.env, ORCH_TOKEN, RCODE_DIR, PROJECT_ROOT },
|
|
228
|
+
env: { ...process.env, ORCH_TOKEN, RCODE_DIR, PROJECT_ROOT, DASH_PORT: String(PORT) },
|
|
228
229
|
stdio: 'pipe',
|
|
229
230
|
});
|
|
230
231
|
_orchProc.stdout.on('data', chunk => {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { html } from '../preact.js';
|
|
11
11
|
import { useStore } from '../store.js';
|
|
12
|
-
import { allSprints, chip } from '../util.js';
|
|
12
|
+
import { allSprints, chip, sessionChip } from '../util.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Build a `{ [cls]: count }` map from an array of items by normalising each
|
|
@@ -27,6 +27,24 @@ function countByStatus(items) {
|
|
|
27
27
|
return map;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Build a `{ [cls]: count }` map from session objects using `sessionChip()`
|
|
32
|
+
* rather than the phase/sprint-oriented `chip()`. Sessions carry a distinct
|
|
33
|
+
* status vocabulary ('running', 'stopped', 'starting', 'error') that maps to
|
|
34
|
+
* separate CSS classes so they can be styled independently.
|
|
35
|
+
*
|
|
36
|
+
* @param {Array<{ status: string }>} sessions
|
|
37
|
+
* @returns {Object.<string, number>}
|
|
38
|
+
*/
|
|
39
|
+
function countSessionsByStatus(sessions) {
|
|
40
|
+
const map = {};
|
|
41
|
+
for (const session of sessions) {
|
|
42
|
+
const { cls } = sessionChip(session.status || '');
|
|
43
|
+
map[cls] = (map[cls] || 0) + 1;
|
|
44
|
+
}
|
|
45
|
+
return map;
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
/**
|
|
31
49
|
* Render a single group of count chips.
|
|
32
50
|
*
|
|
@@ -58,7 +76,7 @@ export function StatusSummaryBar() {
|
|
|
58
76
|
|
|
59
77
|
const phaseCounts = countByStatus(phases);
|
|
60
78
|
const sprintCounts = countByStatus(sprints);
|
|
61
|
-
const sessionCounts =
|
|
79
|
+
const sessionCounts = countSessionsByStatus(sessions);
|
|
62
80
|
|
|
63
81
|
const hasPhases = phases.length > 0;
|
|
64
82
|
const hasSprints = sprints.length > 0;
|
|
@@ -80,6 +80,22 @@ export function allTasks(phases) {
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Map a numeric phase id to its milestone bucket.
|
|
85
|
+
* Single source of truth — imported by PhasesView and SprintsView so that
|
|
86
|
+
* milestone boundaries (19, 33) never diverge between the two views.
|
|
87
|
+
* M1 = phases 1–19, M2 = 20–33, M3 = 34+.
|
|
88
|
+
*
|
|
89
|
+
* @param {number|string} id — phase id
|
|
90
|
+
* @returns {'M1'|'M2'|'M3'}
|
|
91
|
+
*/
|
|
92
|
+
export function phaseMilestone(id) {
|
|
93
|
+
const n = Number(id);
|
|
94
|
+
if (n <= 19) return 'M1';
|
|
95
|
+
if (n <= 33) return 'M2';
|
|
96
|
+
return 'M3';
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
/**
|
|
84
100
|
* Return a status chip descriptor — NOT an HTML string.
|
|
85
101
|
* Components decide how to render the CSS class and label.
|
|
@@ -98,6 +114,31 @@ export function chip(status) {
|
|
|
98
114
|
return { cls, label: status };
|
|
99
115
|
}
|
|
100
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Return a status chip descriptor for orchestrator session statuses.
|
|
119
|
+
* Session objects use a different vocabulary than phases/sprints
|
|
120
|
+
* ('running', 'stopped', 'starting', 'error'), so a separate normaliser
|
|
121
|
+
* keeps the two status domains from coupling inside chip().
|
|
122
|
+
*
|
|
123
|
+
* Mapping:
|
|
124
|
+
* running → 'sess-running' (accent-blue — live activity)
|
|
125
|
+
* starting → 'sess-starting' (amber — transient / pending)
|
|
126
|
+
* stopped → 'sess-stopped' (text-secondary — idle / muted)
|
|
127
|
+
* error → 'sess-error' (accent-red — needs attention)
|
|
128
|
+
*
|
|
129
|
+
* @param {string} status
|
|
130
|
+
* @returns {{ cls: string, label: string }}
|
|
131
|
+
*/
|
|
132
|
+
export function sessionChip(status) {
|
|
133
|
+
const s = String(status || '').toLowerCase();
|
|
134
|
+
const cls =
|
|
135
|
+
s === 'running' ? 'sess-running' :
|
|
136
|
+
s === 'starting' ? 'sess-starting' :
|
|
137
|
+
s === 'error' ? 'sess-error' :
|
|
138
|
+
s === 'stopped' ? 'sess-stopped' : 'sess-stopped';
|
|
139
|
+
return { cls, label: status };
|
|
140
|
+
}
|
|
141
|
+
|
|
101
142
|
/**
|
|
102
143
|
* Props for a clickable card row that navigates to a hash route.
|
|
103
144
|
* Spread onto a list row (`<li ...${rowLink('tasks')}>`) to make it act like
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { html, useState } from '../preact.js';
|
|
11
11
|
import { useStore } from '../store.js';
|
|
12
|
-
import { pct, humanDate, phaseHints, chip } from '../util.js';
|
|
12
|
+
import { pct, humanDate, phaseHints, chip, phaseMilestone } from '../util.js';
|
|
13
13
|
import {
|
|
14
14
|
Chip, ProgressBar, Breadcrumb, CmdHints, RunningBadge, SprintCard, PhaseCard,
|
|
15
15
|
} from '../components/shared.js';
|
|
@@ -122,17 +122,6 @@ function PhaseDetail({ phase: p, S }) {
|
|
|
122
122
|
`;
|
|
123
123
|
}
|
|
124
124
|
|
|
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
125
|
export function PhasesView({ subId, filters }) {
|
|
137
126
|
const S = useStore();
|
|
138
127
|
const phases = S.phases || [];
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { html, useState } from '../preact.js';
|
|
12
12
|
import { useStore } from '../store.js';
|
|
13
|
-
import { pct, humanDate, allSprints, sprintHints, chip } from '../util.js';
|
|
13
|
+
import { pct, humanDate, allSprints, sprintHints, chip, phaseMilestone } from '../util.js';
|
|
14
14
|
import {
|
|
15
15
|
Chip, ProgressBar, Breadcrumb, CmdHints, RunningBadge, SprintCard, TaskCard,
|
|
16
16
|
} from '../components/shared.js';
|
|
@@ -118,17 +118,6 @@ function SprintDetail({ sprint: s, S }) {
|
|
|
118
118
|
`;
|
|
119
119
|
}
|
|
120
120
|
|
|
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
121
|
export function SprintsView({ subId, filters }) {
|
|
133
122
|
const S = useStore();
|
|
134
123
|
const sprints = allSprints(S.phases || []);
|
package/server/lib/html/css.js
CHANGED
|
@@ -4390,6 +4390,11 @@ summary:focus-visible,
|
|
|
4390
4390
|
.summary-count-chip.blocked { color: var(--accent-red); }
|
|
4391
4391
|
.summary-count-chip.planned,
|
|
4392
4392
|
.summary-count-chip.todo { color: var(--text-secondary); }
|
|
4393
|
+
/* Session status chips — use sessionChip() vocabulary, not chip() */
|
|
4394
|
+
.summary-count-chip.sess-running { color: var(--accent-blue); }
|
|
4395
|
+
.summary-count-chip.sess-starting { color: var(--amber); }
|
|
4396
|
+
.summary-count-chip.sess-stopped { color: var(--text-secondary); }
|
|
4397
|
+
.summary-count-chip.sess-error { color: var(--accent-red); }
|
|
4393
4398
|
|
|
4394
4399
|
/* ── Filter chips ────────────────────────────────────────────────── */
|
|
4395
4400
|
.filter-chips {
|
package/server/orchestrator.js
CHANGED
|
@@ -676,10 +676,25 @@ const server = http.createServer(async (req, res) => {
|
|
|
676
676
|
res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type');
|
|
677
677
|
if (method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
|
|
678
678
|
|
|
679
|
-
if (!authed(req)) { json(res, 401, { error: 'unauthorized' }); return; }
|
|
680
|
-
|
|
681
679
|
const pathOnly = url.indexOf('?') === -1 ? url : url.slice(0, url.indexOf('?'));
|
|
682
680
|
|
|
681
|
+
// Friendly landing for browser navigation (no token needed). Hitting this
|
|
682
|
+
// port directly is a common mistake — the orchestrator is the INTERNAL API,
|
|
683
|
+
// not the dashboard. Point people at the dashboard instead of a bare 401.
|
|
684
|
+
if (method === 'GET' && (pathOnly === '/' || pathOnly === '/favicon.ico')) {
|
|
685
|
+
const dashUrl = 'http://localhost:' + (process.env.DASH_PORT || '7717');
|
|
686
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
687
|
+
res.end('<!doctype html><meta charset="utf-8"><title>rcode orchestrator</title>'
|
|
688
|
+
+ '<body style="font-family:system-ui,sans-serif;background:#05080f;color:#e6edf7;'
|
|
689
|
+
+ 'display:grid;place-items:center;height:100vh;margin:0;text-align:center">'
|
|
690
|
+
+ '<div><h1 style="color:#2dd4bf;margin:0 0 .5rem">rcode orchestrator</h1>'
|
|
691
|
+
+ '<p style="color:#8595ad">This is the internal API (port ' + PORT + ') — not the dashboard.</p>'
|
|
692
|
+
+ '<p>Open the dashboard → <a style="color:#a78bfa" href="' + dashUrl + '">' + dashUrl + '</a></p></div>');
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (!authed(req)) { json(res, 401, { error: 'unauthorized' }); return; }
|
|
697
|
+
|
|
683
698
|
if (method === 'GET' && pathOnly === '/api/status') { json(res, 200, { ok: true, sessions: sessions.size }); return; }
|
|
684
699
|
if (method === 'GET' && pathOnly === '/api/runners') { await handleRunners(res); return; }
|
|
685
700
|
if (method === 'GET' && pathOnly === '/api/sessions') { await handleSessions(res); return; }
|