@routerhub/agent-rules 1.5.196 → 1.5.198
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/AGENTS.base.md +24 -0
- package/CHANGELOG.md +6 -0
- package/merge.js +14 -7
- package/package.json +4 -2
- package/scripts/agent-browser-cleanup.sh +152 -0
package/AGENTS.base.md
CHANGED
|
@@ -695,6 +695,30 @@ agent-rules 生成的规则文件分两层,行为与归属不同,评审/发
|
|
|
695
695
|
4. 一旦 `eval` / `snapshot` 返回的内容不是自己操作的页面(URL/内容不符),
|
|
696
696
|
**第一反应是标签页被抢占**:切回自己的 tab id 后重试,禁止盲目重试同一条命令。
|
|
697
697
|
|
|
698
|
+
## 🚨 agent-browser 内存收尾铁律(用完必须收摊)
|
|
699
|
+
|
|
700
|
+
**根因**:agent-browser CLI 本体很轻,但它背后复用的 Chrome Profile 会保留标签页、
|
|
701
|
+
Service Worker、登录态缓存和页面运行时。每次任务只打开不关闭,`9223` / `9226` 的
|
|
702
|
+
Chrome Helper 会越积越多,表现为“agent-browser 用着用着越来越卡”。
|
|
703
|
+
|
|
704
|
+
**必须遵守**:
|
|
705
|
+
|
|
706
|
+
1. 每次 `agent-browser tab new` 后必须记录本次任务创建的 tab id;任务结束前,必须关闭
|
|
707
|
+
本任务打开的所有 tab,禁止把 Cloud Console、GitHub、Slack、Google 登录页等任务页面
|
|
708
|
+
长期留在 agent-browser Chrome 里。
|
|
709
|
+
2. 每次浏览器自动化任务结束前,必须执行一次内存收尾检查:
|
|
710
|
+
`agent-browser-cleanup status`(或仓库源码内 `bash scripts/agent-browser-cleanup.sh status`)。
|
|
711
|
+
3. `9226` 无头 Chrome 优先用于 agent 自动化;若 `9226` 的 tab 数 > 10,或 RSS > 1500MB,
|
|
712
|
+
必须执行 `agent-browser-cleanup auto` 或 `agent-browser-cleanup hard` 释放内存。释放后下次
|
|
713
|
+
需要浏览器时再重新启动,不要为了“可能等会用”长期占着内存。
|
|
714
|
+
4. `9223` 有头 Chrome 只在需要用户手动登录、手动查看动态效果、或必须共享可见页面时启动;
|
|
715
|
+
任务结束且用户不再需要查看时,必须关闭本任务 tab,并在确认没有人工操作中的页面后再
|
|
716
|
+
`agent-browser-cleanup hard`。
|
|
717
|
+
5. 禁止长期同时保留 `9223` + `9226` 两套 agent-browser Chrome 实例,除非当前任务明确需要。
|
|
718
|
+
多会话并行时也必须各自用 namespace 隔离,任务完成各自清理自己创建的 tab。
|
|
719
|
+
6. 禁止用 `kill -9` / `pkill -9` 强杀 Chrome;清理只能使用 `agent-browser-cleanup`、
|
|
720
|
+
`chrome-bridge headless-stop`,或 `/bin/kill -TERM` 这类可让 Chrome 正常退出的方式。
|
|
721
|
+
|
|
698
722
|
## 优先级
|
|
699
723
|
|
|
700
724
|
1. 项目私有规则(AGENTS.private.md)
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
所有对 @routerhub/agent-rules 的重大更改都会记录在这个文件中。
|
|
4
4
|
|
|
5
|
+
## [1.5.198] - 2026-09-08
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **新增「agent-browser 内存收尾铁律」与 `agent-browser-cleanup` 命令**:要求每次 browser 自动化任务结束关闭本任务 tab,并检查 `9223` / `9226` 的 tab 数与 RSS;`9226` 超过 10 个 tab 或 1500MB 时必须释放,避免 Chrome Profile 长期堆积导致 agent-browser 越用越卡。新增 `scripts/agent-browser-cleanup.sh`,支持 `status` / `soft` / `auto` / `hard` 四种模式,默认只读状态,释放时使用正常退出方式,禁止 `kill -9` 强杀 Chrome。
|
|
10
|
+
|
|
5
11
|
## [1.5.164] - 2026-08-31
|
|
6
12
|
|
|
7
13
|
### Changed
|
package/merge.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* AGENTS.md 全量合并(兼容 Cursor/Claude Code)
|
|
18
18
|
* CLAUDE.md 全量合并(Claude Code 自动加载)
|
|
19
19
|
* 团队级(始终生成、随仓库分发共享):
|
|
20
|
-
* .github/copilot-instructions.md
|
|
20
|
+
* .github/copilot-instructions.md 全局+完整私有规则(VS Code Copilot)
|
|
21
21
|
* .github/instructions/*.instructions.md 按域条件加载(VS Code Copilot applyTo)
|
|
22
22
|
* .github/PULL_REQUEST_TEMPLATE.md 统一 PR 模板(GitHub 新建 PR 时预填)
|
|
23
23
|
* .claude/skills/* 同步 Skills 到项目本地
|
|
@@ -715,13 +715,20 @@ function mergeAgents(config) {
|
|
|
715
715
|
.map((rule) => rule.content.trim())
|
|
716
716
|
.join("\n\n");
|
|
717
717
|
|
|
718
|
-
// 4. 生成 .github/copilot-instructions.md
|
|
718
|
+
// 4. 生成 .github/copilot-instructions.md
|
|
719
|
+
// 全局基础规则 + 完整私有规则(含各 @domain 段)。让任意 Agent(Claude Code /
|
|
720
|
+
// VS Code Copilot / Cursor 等)读到同一份完整私有规则:只要 AGENTS.private.md 有
|
|
721
|
+
// 内容就整份追加,不再只取「私有全局段」——否则写在某个 @domain 段、或段落末尾
|
|
722
|
+
// 的私有规则会从 copilot-instructions.md 里静默缺失,造成「我加了规则但 Copilot
|
|
723
|
+
// 那边没同步到」的误解(与 AGENTS.md / CLAUDE.md 的全量注入保持一致)。
|
|
719
724
|
let copilotContent = "# Copilot Agent Rules\n\n" + globalContent;
|
|
720
|
-
if (
|
|
721
|
-
copilotContent +=
|
|
725
|
+
if (privateContent) {
|
|
726
|
+
copilotContent +=
|
|
727
|
+
"\n\n---\n\n# 项目私有规则\n\n以下规则仅适用于本项目,会覆盖基础规则中的相同部分。\n\n" +
|
|
728
|
+
privateContent.trim();
|
|
722
729
|
}
|
|
723
730
|
writeFileIfChanged(copilotOutput, copilotContent);
|
|
724
|
-
console.log(`✅
|
|
731
|
+
console.log(`✅ 全局规则已生成(含完整私有规则): ${copilotOutput}`);
|
|
725
732
|
|
|
726
733
|
// 5. 生成 .github/instructions/*.instructions.md(各域规则)
|
|
727
734
|
ensureParentDir(path.join(instructionsDir, "_placeholder"));
|
|
@@ -797,7 +804,7 @@ function initAgents() {
|
|
|
797
804
|
console.log(" - CLAUDE.md / AGENTS.md(停用时不会生成,并清理本机残留)");
|
|
798
805
|
console.log(" 查看当前 git 身份:git config user.name / git config user.email");
|
|
799
806
|
console.log(" 团队级文件(始终生成、随仓库分发):");
|
|
800
|
-
console.log(" - .github/copilot-instructions.md
|
|
807
|
+
console.log(" - .github/copilot-instructions.md(全局规则 + 完整私有规则)");
|
|
801
808
|
console.log(" - .github/instructions/*.instructions.md(按域条件加载)");
|
|
802
809
|
console.log(" - .claude/skills/*(Claude Code Skills 技能文件)");
|
|
803
810
|
}
|
|
@@ -954,7 +961,7 @@ function main() {
|
|
|
954
961
|
);
|
|
955
962
|
console.log("输出文件(团队级,始终生成):");
|
|
956
963
|
console.log(
|
|
957
|
-
" .github/copilot-instructions.md
|
|
964
|
+
" .github/copilot-instructions.md 全局+完整私有规则(VS Code Copilot)",
|
|
958
965
|
);
|
|
959
966
|
console.log(
|
|
960
967
|
" .github/instructions/*.instructions.md 按域条件加载(VS Code Copilot applyTo)",
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@routerhub/agent-rules",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.198",
|
|
4
4
|
"description": "Shared Copilot agent rules and guidelines for RouterHub projects",
|
|
5
5
|
"main": "AGENTS.base.md",
|
|
6
6
|
"bin": {
|
|
7
|
-
"agent-rules": "merge.js"
|
|
7
|
+
"agent-rules": "merge.js",
|
|
8
|
+
"agent-browser-cleanup": "scripts/agent-browser-cleanup.sh"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"AGENTS.base.md",
|
|
11
12
|
"rules/",
|
|
12
13
|
"skills/",
|
|
14
|
+
"scripts/agent-browser-cleanup.sh",
|
|
13
15
|
"merge.js",
|
|
14
16
|
"AGENTS.private.example.md",
|
|
15
17
|
"PULL_REQUEST_TEMPLATE.md",
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
MODE="${1:-status}"
|
|
5
|
+
TAB_LIMIT="${AGENT_BROWSER_TAB_LIMIT:-10}"
|
|
6
|
+
RSS_LIMIT_MB="${AGENT_BROWSER_RSS_LIMIT_MB:-1500}"
|
|
7
|
+
|
|
8
|
+
profileForPort() {
|
|
9
|
+
case "$1" in
|
|
10
|
+
9223) printf '%s\n' "$HOME/.agent-browser-profile" ;;
|
|
11
|
+
9226) printf '%s\n' "$HOME/.agent-browser-profile-headless" ;;
|
|
12
|
+
*) printf '' ;;
|
|
13
|
+
esac
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
usage() {
|
|
17
|
+
cat <<'USAGE'
|
|
18
|
+
Usage: scripts/agent-browser-cleanup.sh [status|soft|hard|auto]
|
|
19
|
+
|
|
20
|
+
Modes:
|
|
21
|
+
status 只查看 9223/9226 的标签页数量与内存占用
|
|
22
|
+
soft 关闭 about:blank、newtab、devtools 等无业务标签页
|
|
23
|
+
hard 优雅退出 agent-browser 专用 Chrome(9223/9226),释放内存
|
|
24
|
+
auto tab 数或 RSS 超阈值时自动执行 hard,否则只输出状态
|
|
25
|
+
|
|
26
|
+
Env:
|
|
27
|
+
AGENT_BROWSER_TAB_LIMIT 默认 10
|
|
28
|
+
AGENT_BROWSER_RSS_LIMIT_MB 默认 1500
|
|
29
|
+
USAGE
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if [[ "$MODE" == "-h" || "$MODE" == "--help" ]]; then
|
|
33
|
+
usage
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [[ "$MODE" != "status" && "$MODE" != "soft" && "$MODE" != "hard" && "$MODE" != "auto" ]]; then
|
|
38
|
+
usage >&2
|
|
39
|
+
exit 2
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
portStatus() {
|
|
43
|
+
local portNumber="$1"
|
|
44
|
+
local profileDir=""
|
|
45
|
+
local tabCount="0"
|
|
46
|
+
local rssMb="0"
|
|
47
|
+
local profileArg=""
|
|
48
|
+
|
|
49
|
+
profileDir=$(profileForPort "$portNumber")
|
|
50
|
+
profileArg="--user-data-dir=${profileDir}"
|
|
51
|
+
rssMb=$(ps -axo rss=,command= | awk -v profileArg="$profileArg" '
|
|
52
|
+
{
|
|
53
|
+
for (fieldIndex = 2; fieldIndex <= NF; fieldIndex++) {
|
|
54
|
+
if ($fieldIndex == profileArg) {
|
|
55
|
+
sum += $1
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
END {printf "%d", sum / 1024}
|
|
61
|
+
')
|
|
62
|
+
|
|
63
|
+
if curl -sf "http://127.0.0.1:${portNumber}/json/list" >/tmp/agent-browser-tabs-${portNumber}.json 2>/dev/null; then
|
|
64
|
+
tabCount=$(jq 'length' "/tmp/agent-browser-tabs-${portNumber}.json" 2>/dev/null || echo "0")
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
rm -f "/tmp/agent-browser-tabs-${portNumber}.json"
|
|
68
|
+
printf '%s %s %s\n' "$portNumber" "$tabCount" "$rssMb"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
printStatus() {
|
|
72
|
+
printf 'port\ttabs\trssMB\n'
|
|
73
|
+
portStatus 9223 | awk '{printf "%s\t%s\t%s\n", $1, $2, $3}'
|
|
74
|
+
portStatus 9226 | awk '{printf "%s\t%s\t%s\n", $1, $2, $3}'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
closeChromeByPort() {
|
|
78
|
+
local portNumber="$1"
|
|
79
|
+
local profileDir=""
|
|
80
|
+
local processIds=""
|
|
81
|
+
profileDir=$(profileForPort "$portNumber")
|
|
82
|
+
processIds=$(ps -axo pid=,command= | awk -v portArg="--remote-debugging-port=${portNumber}" -v profileArg="--user-data-dir=${profileDir}" '
|
|
83
|
+
{
|
|
84
|
+
hasPort = 0
|
|
85
|
+
hasProfile = 0
|
|
86
|
+
for (fieldIndex = 2; fieldIndex <= NF; fieldIndex++) {
|
|
87
|
+
if ($fieldIndex == portArg) hasPort = 1
|
|
88
|
+
if ($fieldIndex == profileArg) hasProfile = 1
|
|
89
|
+
}
|
|
90
|
+
if (hasPort && hasProfile) print $1
|
|
91
|
+
}
|
|
92
|
+
')
|
|
93
|
+
if [[ -z "$processIds" ]]; then
|
|
94
|
+
echo "port ${portNumber}: no agent-browser Chrome process"
|
|
95
|
+
return
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo "port ${portNumber}: graceful quit"
|
|
99
|
+
printf '%s\n' "$processIds" | xargs -n1 /bin/kill -TERM
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
softCleanup() {
|
|
103
|
+
local portNumber="$1"
|
|
104
|
+
if ! curl -sf "http://127.0.0.1:${portNumber}/json/list" >/tmp/agent-browser-tabs-${portNumber}.json 2>/dev/null; then
|
|
105
|
+
echo "port ${portNumber}: not reachable"
|
|
106
|
+
return
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
jq -r '.[] | select(.url | test("^(about:blank|chrome://newtab|devtools://|chrome://)")).id' "/tmp/agent-browser-tabs-${portNumber}.json" |
|
|
110
|
+
while IFS= read -r tabId; do
|
|
111
|
+
[[ -z "$tabId" ]] && continue
|
|
112
|
+
curl -sf "http://127.0.0.1:${portNumber}/json/close/${tabId}" >/dev/null || true
|
|
113
|
+
echo "port ${portNumber}: closed idle tab ${tabId}"
|
|
114
|
+
done
|
|
115
|
+
|
|
116
|
+
rm -f "/tmp/agent-browser-tabs-${portNumber}.json"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
needsHardCleanup() {
|
|
120
|
+
local overLimit="0"
|
|
121
|
+
while read -r portNumber tabCount rssMb; do
|
|
122
|
+
if (( tabCount > TAB_LIMIT || rssMb > RSS_LIMIT_MB )); then
|
|
123
|
+
echo "port ${portNumber}: over limit tabs=${tabCount}/${TAB_LIMIT} rssMB=${rssMb}/${RSS_LIMIT_MB}"
|
|
124
|
+
overLimit="1"
|
|
125
|
+
fi
|
|
126
|
+
done < <(portStatus 9223 && portStatus 9226)
|
|
127
|
+
[[ "$overLimit" == "1" ]]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case "$MODE" in
|
|
131
|
+
status)
|
|
132
|
+
printStatus
|
|
133
|
+
;;
|
|
134
|
+
soft)
|
|
135
|
+
softCleanup 9223
|
|
136
|
+
softCleanup 9226
|
|
137
|
+
printStatus
|
|
138
|
+
;;
|
|
139
|
+
hard)
|
|
140
|
+
closeChromeByPort 9223
|
|
141
|
+
closeChromeByPort 9226
|
|
142
|
+
;;
|
|
143
|
+
auto)
|
|
144
|
+
printStatus
|
|
145
|
+
if needsHardCleanup; then
|
|
146
|
+
closeChromeByPort 9223
|
|
147
|
+
closeChromeByPort 9226
|
|
148
|
+
else
|
|
149
|
+
echo "agent-browser Chrome is below cleanup thresholds"
|
|
150
|
+
fi
|
|
151
|
+
;;
|
|
152
|
+
esac
|