orchestrix-yuri 4.1.5 → 4.1.6
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.
|
@@ -613,12 +613,14 @@ class PhaseOrchestrator {
|
|
|
613
613
|
|
|
614
614
|
if (output && output !== 'NO_STORIES_DIR') {
|
|
615
615
|
for (const line of output.split('\n')) {
|
|
616
|
-
const [
|
|
616
|
+
const [key, countStr] = line.split(':');
|
|
617
617
|
const count = parseInt(countStr, 10) || 0;
|
|
618
|
-
if (
|
|
619
|
-
result.
|
|
618
|
+
if (key === 'Epics') {
|
|
619
|
+
result.totalEpics = count;
|
|
620
|
+
} else if (key && count > 0) {
|
|
621
|
+
result.byStatus[key] = count;
|
|
620
622
|
result.totalStories += count;
|
|
621
|
-
if (
|
|
623
|
+
if (key === 'Done') result.doneStories += count;
|
|
622
624
|
}
|
|
623
625
|
}
|
|
624
626
|
}
|
|
@@ -641,16 +643,14 @@ class PhaseOrchestrator {
|
|
|
641
643
|
}
|
|
642
644
|
}
|
|
643
645
|
|
|
644
|
-
// Count epics from docs/
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
result.totalEpics = epicDirs.length || result.totalEpics;
|
|
653
|
-
} catch { /* ok */ }
|
|
646
|
+
// Count epics from docs/prd/ (epic YAML/MD files) as fallback
|
|
647
|
+
if (result.totalEpics === 0) {
|
|
648
|
+
const prdDir = path.join(this._projectRoot, 'docs', 'prd');
|
|
649
|
+
if (fs.existsSync(prdDir)) {
|
|
650
|
+
try {
|
|
651
|
+
result.totalEpics = fs.readdirSync(prdDir).filter((f) => /^epic/i.test(f)).length;
|
|
652
|
+
} catch { /* ok */ }
|
|
653
|
+
}
|
|
654
654
|
}
|
|
655
655
|
|
|
656
656
|
// Detect current active agent from tmux panes
|
package/lib/gateway/router.js
CHANGED
|
@@ -575,12 +575,14 @@ class Router {
|
|
|
575
575
|
const output = execSync(`bash "${scriptPath}" "${projectRoot}"`, { encoding: 'utf8', timeout: 10000 }).trim();
|
|
576
576
|
if (output && output !== 'NO_STORIES_DIR') {
|
|
577
577
|
for (const line of output.split('\n')) {
|
|
578
|
-
const [
|
|
578
|
+
const [key, cnt] = line.split(':');
|
|
579
579
|
const n = parseInt(cnt, 10) || 0;
|
|
580
|
-
if (
|
|
581
|
-
|
|
580
|
+
if (key === 'Epics') {
|
|
581
|
+
totalEpics = n;
|
|
582
|
+
} else if (n > 0) {
|
|
583
|
+
byStatus[key] = n;
|
|
582
584
|
totalStories += n;
|
|
583
|
-
if (
|
|
585
|
+
if (key === 'Done') doneStories += n;
|
|
584
586
|
}
|
|
585
587
|
}
|
|
586
588
|
}
|
|
@@ -617,15 +619,14 @@ class Router {
|
|
|
617
619
|
}
|
|
618
620
|
}
|
|
619
621
|
|
|
620
|
-
// 4. Count epics
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
} catch { /* ok */ }
|
|
622
|
+
// 4. Count epics from docs/prd/ (epic YAML files)
|
|
623
|
+
if (totalEpics === 0) {
|
|
624
|
+
const prdDir = path.join(projectRoot, 'docs', 'prd');
|
|
625
|
+
if (fs.existsSync(prdDir)) {
|
|
626
|
+
try {
|
|
627
|
+
totalEpics = fs.readdirSync(prdDir).filter((f) => /^epic/i.test(f)).length;
|
|
628
|
+
} catch { /* ok */ }
|
|
629
|
+
}
|
|
629
630
|
}
|
|
630
631
|
|
|
631
632
|
// 5. Running time
|
package/package.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Yuri Story Status Scanner
|
|
3
3
|
# Usage: scan-stories.sh <project_root>
|
|
4
|
+
#
|
|
5
|
+
# Supports two status formats in story files:
|
|
6
|
+
# Format A: "Status: Done" (inline)
|
|
7
|
+
# Format B: "## Status\n\nDone" (heading + next non-empty line)
|
|
8
|
+
#
|
|
9
|
+
# Also outputs epic count based on filename prefix (e.g., 1.x, 2.x)
|
|
4
10
|
set +e
|
|
5
11
|
|
|
6
12
|
STORIES_DIR="$1/docs/stories"
|
|
@@ -10,7 +16,28 @@ if [ ! -d "$STORIES_DIR" ]; then
|
|
|
10
16
|
exit 1
|
|
11
17
|
fi
|
|
12
18
|
|
|
19
|
+
# Scan each story file for status
|
|
13
20
|
for status in Done InProgress Review Blocked Approved AwaitingArchReview RequiresRevision Escalated; do
|
|
14
|
-
count
|
|
21
|
+
count=0
|
|
22
|
+
for f in "$STORIES_DIR"/*.md "$STORIES_DIR"/*.yaml; do
|
|
23
|
+
[ -f "$f" ] || continue
|
|
24
|
+
# Format A: "Status: Done" or "Status:Done"
|
|
25
|
+
if grep -qi "Status:[[:space:]]*$status" "$f" 2>/dev/null; then
|
|
26
|
+
count=$((count + 1))
|
|
27
|
+
continue
|
|
28
|
+
fi
|
|
29
|
+
# Format B: "## Status" heading, then status on a subsequent non-empty line
|
|
30
|
+
if grep -q "## Status" "$f" 2>/dev/null; then
|
|
31
|
+
# Extract the first non-empty line after "## Status"
|
|
32
|
+
extracted=$(awk '/^## Status/{found=1; next} found && /^[[:space:]]*$/{next} found{print; exit}' "$f" 2>/dev/null)
|
|
33
|
+
if echo "$extracted" | grep -qi "^$status$" 2>/dev/null; then
|
|
34
|
+
count=$((count + 1))
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
done
|
|
15
38
|
echo "$status:$count"
|
|
16
39
|
done
|
|
40
|
+
|
|
41
|
+
# Count epics by unique filename prefix (e.g., 1.x → epic 1, 2.x → epic 2)
|
|
42
|
+
epics=$(ls "$STORIES_DIR" 2>/dev/null | grep -oE '^[0-9]+' | sort -u | wc -l | tr -d ' ')
|
|
43
|
+
echo "Epics:$epics"
|