orchestrix-yuri 4.2.3 → 4.2.5
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/lib/gateway/router.js +43 -0
- package/package.json +1 -1
- package/skill/tasks/yuri-status.md +50 -14
package/lib/gateway/router.js
CHANGED
|
@@ -69,6 +69,48 @@ class Router {
|
|
|
69
69
|
if (projectRoot) {
|
|
70
70
|
this.orchestrator.tryRecover(projectRoot);
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
// Independent progress reporter: sends periodic status card
|
|
74
|
+
// when dev phase is active (detected from focus.yaml + tmux),
|
|
75
|
+
// regardless of whether orchestrator is tracking it.
|
|
76
|
+
this._reportTimer = null;
|
|
77
|
+
this._startAutoReporter(config);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Start an independent timer that checks if dev phase is running
|
|
82
|
+
* and sends a progress card periodically.
|
|
83
|
+
*/
|
|
84
|
+
_startAutoReporter(config) {
|
|
85
|
+
const interval = (config.engine && config.engine.report_interval) || 1800000; // 30 min
|
|
86
|
+
this._reportTimer = setInterval(() => {
|
|
87
|
+
// Skip if orchestrator is already reporting (avoid duplicates)
|
|
88
|
+
if (this.orchestrator.isRunning()) return;
|
|
89
|
+
|
|
90
|
+
// Check if dev phase is active
|
|
91
|
+
const projectRoot = engine.resolveProjectRoot();
|
|
92
|
+
if (!projectRoot) return;
|
|
93
|
+
|
|
94
|
+
const focusPath = path.join(projectRoot, '.yuri', 'focus.yaml');
|
|
95
|
+
if (!fs.existsSync(focusPath)) return;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const focus = yaml.load(fs.readFileSync(focusPath, 'utf8')) || {};
|
|
99
|
+
if (parseInt(focus.phase, 10) !== 3) return;
|
|
100
|
+
|
|
101
|
+
// Check tmux session is alive
|
|
102
|
+
const { execSync } = require('child_process');
|
|
103
|
+
const sessions = execSync('tmux list-sessions -F "#{session_name}" 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
104
|
+
if (!sessions.split('\n').some((s) => s.startsWith('orchestrix-'))) return;
|
|
105
|
+
|
|
106
|
+
// Generate and send progress card
|
|
107
|
+
const card = this._buildStatusCard(projectRoot, focus);
|
|
108
|
+
if (card) {
|
|
109
|
+
log.router('Auto-reporting dev progress');
|
|
110
|
+
this._sendProactive(card);
|
|
111
|
+
}
|
|
112
|
+
} catch { /* silent */ }
|
|
113
|
+
}, interval);
|
|
72
114
|
}
|
|
73
115
|
|
|
74
116
|
/**
|
|
@@ -725,6 +767,7 @@ class Router {
|
|
|
725
767
|
* Graceful shutdown.
|
|
726
768
|
*/
|
|
727
769
|
async shutdown() {
|
|
770
|
+
if (this._reportTimer) clearInterval(this._reportTimer);
|
|
728
771
|
this.orchestrator.shutdown();
|
|
729
772
|
if (engine.destroySession) engine.destroySession();
|
|
730
773
|
}
|
package/package.json
CHANGED
|
@@ -105,27 +105,63 @@ Read `{project}/.yuri/state/phase2.yaml`.
|
|
|
105
105
|
|
|
106
106
|
### IF Phase 3 (Develop) is in progress or complete:
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
**Generate a progress card** using these data sources:
|
|
109
109
|
|
|
110
|
-
Scan
|
|
110
|
+
**1. Scan story statuses:**
|
|
111
111
|
```bash
|
|
112
112
|
SCRIPT_DIR="${CLAUDE_SKILL_DIR}/scripts"
|
|
113
113
|
bash "$SCRIPT_DIR/scan-stories.sh" "$PROJECT_DIR"
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
| In Progress | {in_progress count} |
|
|
124
|
-
| Blocked | {blocked count} |
|
|
125
|
-
| Remaining | {remaining count} |
|
|
116
|
+
The script outputs:
|
|
117
|
+
- `Total:{N}` — total planned stories from all `docs/prd/epic-*.yaml` definitions
|
|
118
|
+
- `Created:{N}` — story files in `docs/stories/`
|
|
119
|
+
- `StatusDone:{filename}` / `StatusInProgress:{filename}` / etc. — per-file status
|
|
120
|
+
- `Epics:{N}` — total epics (max N from `docs/prd/epic-N-*`)
|
|
121
|
+
- `CurrentEpic:{N}` — epic of current story
|
|
122
|
+
- `CurrentStory:{filename}` — InProgress or last non-Done story
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
```
|
|
124
|
+
**2. Detect active tmux agent:**
|
|
125
|
+
```bash
|
|
126
|
+
# Check which orchestrix-* session exists
|
|
127
|
+
tmux list-sessions -F "#{session_name}" 2>/dev/null | grep "^orchestrix-"
|
|
128
|
+
|
|
129
|
+
# For each window (0=Architect, 1=SM, 2=Dev, 3=QA):
|
|
130
|
+
# - Window showing "Command | Description" table = IDLE
|
|
131
|
+
# - Window WITHOUT that table = actively executing
|
|
132
|
+
for w in 0 1 2 3; do
|
|
133
|
+
OUTPUT=$(tmux capture-pane -t "$DEV_SESSION:$w" -p -S -15)
|
|
134
|
+
if ! echo "$OUTPUT" | grep -q "Command.*Description"; then
|
|
135
|
+
echo "Window $w is ACTIVE"
|
|
136
|
+
fi
|
|
137
|
+
done
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**3. Format the progress card:**
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
📊 Dev Progress Report
|
|
144
|
+
━━━━━━━━━━━━━━━━━━━━━
|
|
145
|
+
Epic: {CurrentEpic}/{Epics}
|
|
146
|
+
Story: {done}/{total} done ({pct}%) | {created} created
|
|
147
|
+
▓▓▓▓▓▓░░░░░░░░░░░░░░ {pct}%
|
|
148
|
+
━━━━━━━━━━━━━━━━━━━━━
|
|
149
|
+
✅ Done: {N}
|
|
150
|
+
🔄 InProgress: {N}
|
|
151
|
+
📋 Approved: {N}
|
|
152
|
+
📝 Draft: {N}
|
|
153
|
+
━━━━━━━━━━━━━━━━━━━━━
|
|
154
|
+
📝 Current: {CurrentStory}
|
|
155
|
+
🤖 Agent: {active_agent} (window {N})
|
|
156
|
+
⏱ Running for {elapsed}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Where:
|
|
160
|
+
- `{pct}` = round(done / total * 100)
|
|
161
|
+
- Progress bar: `▓` for filled, `░` for empty (20 chars total)
|
|
162
|
+
- Only show status categories with count > 0
|
|
163
|
+
- `{elapsed}` = time since `focus.updated_at` or `phase3.started_at`
|
|
164
|
+
- If no agent is active (all windows show Command table), show "All agents idle (waiting for handoff)"
|
|
129
165
|
|
|
130
166
|
### IF Phase 4 (Test) is in progress or complete:
|
|
131
167
|
|