@link-assistant/hive-mind 1.50.13 → 1.50.15
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/CHANGELOG.md +12 -0
- package/README.md +120 -0
- package/package.json +1 -1
- package/src/solve.auto-merge.lib.mjs +3 -3
- package/src/solve.config.lib.mjs +2 -2
- package/src/solve.validation.lib.mjs +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.50.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7cecf09: Fix auto-resume reset time parsing when usage-limit output includes a month/day prefix such as `Apr 17, 4:00 AM`.
|
|
8
|
+
|
|
9
|
+
## 1.50.14
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- f013f53: Fix PR mergeability consensus to ignore unrelated repo actions by default
|
|
14
|
+
|
|
3
15
|
## 1.50.13
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -804,6 +804,126 @@ s=$(screen -ls | awk '/Detached/ {print $1; exit}'); echo "Entering $s"; screen
|
|
|
804
804
|
s=$(screen -ls | awk '/Detached/ {last=$1} END{print last}'); echo "Entering $s"; screen -r "$s"; echo "Left $s";
|
|
805
805
|
```
|
|
806
806
|
|
|
807
|
+
### Script for managing screens
|
|
808
|
+
|
|
809
|
+
```bash
|
|
810
|
+
cat <<'EOF' > hive-screens.sh
|
|
811
|
+
#!/usr/bin/env bash
|
|
812
|
+
|
|
813
|
+
enter=false
|
|
814
|
+
close=false
|
|
815
|
+
oldest=false
|
|
816
|
+
newest=false
|
|
817
|
+
all=false
|
|
818
|
+
|
|
819
|
+
# --- parse args ---
|
|
820
|
+
for arg in "$@"; do
|
|
821
|
+
case "$arg" in
|
|
822
|
+
--enter) enter=true ;;
|
|
823
|
+
--close) close=true ;;
|
|
824
|
+
--oldest) oldest=true ;;
|
|
825
|
+
--newest) newest=true ;;
|
|
826
|
+
--all) all=true ;;
|
|
827
|
+
*)
|
|
828
|
+
echo "Unknown option: $arg"
|
|
829
|
+
exit 1
|
|
830
|
+
;;
|
|
831
|
+
esac
|
|
832
|
+
done
|
|
833
|
+
|
|
834
|
+
# --- validate ---
|
|
835
|
+
if ! $enter && ! $close; then
|
|
836
|
+
echo "Must specify --enter or --close"
|
|
837
|
+
exit 1
|
|
838
|
+
fi
|
|
839
|
+
|
|
840
|
+
# --- default ---
|
|
841
|
+
if ! $oldest && ! $newest && ! $all; then
|
|
842
|
+
oldest=true
|
|
843
|
+
fi
|
|
844
|
+
|
|
845
|
+
matches=()
|
|
846
|
+
|
|
847
|
+
# --- sorting ---
|
|
848
|
+
if $newest; then
|
|
849
|
+
sorter="sort -nr"
|
|
850
|
+
else
|
|
851
|
+
sorter="sort -n"
|
|
852
|
+
fi
|
|
853
|
+
|
|
854
|
+
# --- scan sessions ---
|
|
855
|
+
while read -r sess; do
|
|
856
|
+
tmp=$(mktemp)
|
|
857
|
+
clean=$(mktemp)
|
|
858
|
+
|
|
859
|
+
# FIX: better capture
|
|
860
|
+
screen -S "$sess" -X scrollback 200000 2>/dev/null
|
|
861
|
+
sleep 0.15
|
|
862
|
+
screen -S "$sess" -X hardcopy -h "$tmp" 2>/dev/null
|
|
863
|
+
|
|
864
|
+
# strip garbage / non-printable chars
|
|
865
|
+
tr -cd '\11\12\15\40-\176' < "$tmp" > "$clean"
|
|
866
|
+
|
|
867
|
+
if grep -qi 'process completed' "$clean" &&
|
|
868
|
+
grep -qiE 'pr is mergeable!|pr merged!' "$clean"; then
|
|
869
|
+
|
|
870
|
+
log_path=$(tac "$clean" | grep -m1 -i 'full log file:' \
|
|
871
|
+
| sed 's/.*Full log file:[[:space:]]*//')
|
|
872
|
+
|
|
873
|
+
issue=$(tac "$clean" | grep -m1 -i 'Issue:[[:space:]]*https://github\.com/' \
|
|
874
|
+
| sed 's/Issue:[[:space:]]*//')
|
|
875
|
+
|
|
876
|
+
matches+=("$sess|$log_path|$issue")
|
|
877
|
+
fi
|
|
878
|
+
|
|
879
|
+
rm -f "$tmp" "$clean"
|
|
880
|
+
done < <(screen -ls | awk '/Detached/ {print $1}' | $sorter)
|
|
881
|
+
|
|
882
|
+
# --- no matches ---
|
|
883
|
+
if [ ${#matches[@]} -eq 0 ]; then
|
|
884
|
+
echo "No matching sessions"
|
|
885
|
+
exit 0
|
|
886
|
+
fi
|
|
887
|
+
|
|
888
|
+
process_one() {
|
|
889
|
+
IFS="|" read -r sess log issue <<< "$1"
|
|
890
|
+
|
|
891
|
+
echo "Session: $sess"
|
|
892
|
+
|
|
893
|
+
if $enter; then
|
|
894
|
+
echo "Entering $sess"
|
|
895
|
+
screen -r "$sess"
|
|
896
|
+
echo "Left $sess"
|
|
897
|
+
fi
|
|
898
|
+
|
|
899
|
+
[ -n "$log" ] && echo "Log: $log" || echo "Log: (not found)"
|
|
900
|
+
[ -n "$issue" ] && echo "Issue: $issue" || echo "Issue: (not found)"
|
|
901
|
+
|
|
902
|
+
if $close; then
|
|
903
|
+
echo "Closing $sess"
|
|
904
|
+
screen -S "$sess" -X stuff $'exit\n'
|
|
905
|
+
fi
|
|
906
|
+
|
|
907
|
+
echo "-----------------------------------"
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
# --- execution ---
|
|
911
|
+
if $all; then
|
|
912
|
+
for m in "${matches[@]}"; do
|
|
913
|
+
process_one "$m"
|
|
914
|
+
done
|
|
915
|
+
elif $oldest; then
|
|
916
|
+
process_one "${matches[0]}"
|
|
917
|
+
elif $newest; then
|
|
918
|
+
last_index=$((${#matches[@]} - 1))
|
|
919
|
+
process_one "${matches[$last_index]}"
|
|
920
|
+
fi
|
|
921
|
+
|
|
922
|
+
EOF
|
|
923
|
+
|
|
924
|
+
chmod +x hive-screens.sh
|
|
925
|
+
```
|
|
926
|
+
|
|
807
927
|
### Reboot server.
|
|
808
928
|
|
|
809
929
|
```bash
|
package/package.json
CHANGED
|
@@ -69,8 +69,8 @@ export const watchUntilMergeable = async params => {
|
|
|
69
69
|
const MIN_CI_CHECK_INTERVAL_SECONDS = 120;
|
|
70
70
|
const watchInterval = Math.max(rawWatchInterval, MIN_CI_CHECK_INTERVAL_SECONDS);
|
|
71
71
|
const isAutoMerge = argv.autoMerge || false;
|
|
72
|
-
// Issue #1503/#1573:
|
|
73
|
-
//
|
|
72
|
+
// Issue #1503/#1573/#1612: repo-wide action gating is opt-in strict mode.
|
|
73
|
+
// The config default may be bypassed when this module is reused directly, so normalize here.
|
|
74
74
|
const waitForAllRepoActionsFlag = argv.waitForAllActionsInRepositoryBeforeMergeable ?? argv['wait-for-all-actions-in-repository-before-mergeable'] ?? argv.waitForAllActionsInRepositoryBeforeMergable ?? argv['wait-for-all-actions-in-repository-before-mergable'] ?? false;
|
|
75
75
|
|
|
76
76
|
// Track latest session data across all iterations for accurate pricing
|
|
@@ -98,7 +98,7 @@ export const watchUntilMergeable = async params => {
|
|
|
98
98
|
await log(formatAligned('', 'Mode:', isAutoMerge ? 'Auto-merge (will merge when ready)' : 'Auto-restart-until-mergeable (will NOT auto-merge)', 2));
|
|
99
99
|
await log(formatAligned('', 'Checking interval:', `${watchInterval} seconds (minimum: ${MIN_CI_CHECK_INTERVAL_SECONDS}s)`, 2));
|
|
100
100
|
await log(formatAligned('', 'Initial cooldown:', `${INITIAL_COOLDOWN_SECONDS} seconds`, 2));
|
|
101
|
-
await log(formatAligned('', 'Wait for all repo actions:', waitForAllRepoActionsFlag ? 'Yes (
|
|
101
|
+
await log(formatAligned('', 'Wait for all repo actions:', waitForAllRepoActionsFlag ? 'Yes (strict repo-wide safety)' : 'No (PR-scoped CI only)', 2));
|
|
102
102
|
await log(formatAligned('', 'Stop conditions:', 'PR merged, PR closed, or becomes mergeable', 2));
|
|
103
103
|
await log(formatAligned('', 'Restart triggers:', 'New non-bot comments, CI failures, merge conflicts', 2));
|
|
104
104
|
await log('');
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -188,8 +188,8 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
188
188
|
},
|
|
189
189
|
'wait-for-all-actions-in-repository-before-mergeable': {
|
|
190
190
|
type: 'boolean',
|
|
191
|
-
description: 'Wait for ALL active GitHub Actions workflow runs in the entire repository to complete before declaring PR mergeable. When enabled, blocks merge if ANY CI/CD run in the repository is active, regardless of branch — this
|
|
192
|
-
default:
|
|
191
|
+
description: 'Wait for ALL active GitHub Actions workflow runs in the entire repository to complete before declaring PR mergeable. When enabled, blocks merge if ANY CI/CD run in the repository is active, regardless of branch — this is a strict safety mode for repositories with cross-branch CI/CD coupling. Disabled by default.',
|
|
192
|
+
default: false,
|
|
193
193
|
},
|
|
194
194
|
'wait-for-all-actions-in-repository-before-mergable': {
|
|
195
195
|
type: 'boolean',
|
|
@@ -371,10 +371,12 @@ export const parseUrlComponents = issueUrl => {
|
|
|
371
371
|
export const parseResetTime = timeStr => {
|
|
372
372
|
// Normalize and parse time formats like:
|
|
373
373
|
// "5:30am", "11:45pm", "12:16 PM", "07:05 Am", "5am", "5 AM"
|
|
374
|
+
// Also accepts date+time forms like "Apr 17, 4:00 AM" and ignores the date portion.
|
|
374
375
|
const normalized = (timeStr || '').toString().trim();
|
|
376
|
+
const timePortion = normalized.replace(/^(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+\d{1,2},\s+/i, '');
|
|
375
377
|
|
|
376
378
|
// Accept both HH:MM am/pm and HH am/pm
|
|
377
|
-
let match =
|
|
379
|
+
let match = timePortion.match(/^(\d{1,2})(?::(\d{2}))?\s*([ap]m)$/i);
|
|
378
380
|
if (!match) {
|
|
379
381
|
throw new Error(`Invalid time format: ${timeStr}`);
|
|
380
382
|
}
|