sdd-flow-kit 1.3.11 → 1.3.12
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/README.md
CHANGED
|
@@ -92,6 +92,14 @@ node ./node_modules/sdd-flow-kit/dist/postinstall.js
|
|
|
92
92
|
- Claude Code 安装(`.claude/skills`)时默认用 `claude -p` 执行修复,不限于 Cursor
|
|
93
93
|
- 可用 `--agent claude-code` 或 `SDD_FLOW_KIT_ENABLE_*_AUTORUN=0` 控制
|
|
94
94
|
|
|
95
|
+
**1.3.12** — Confluence 关键字搜索稳定性(dosearchsite 主路径)
|
|
96
|
+
|
|
97
|
+
- `confluence-doc.py`:每个关键词优先打开 `dosearchsite.action?queryString=...` 专用搜索页(不再依赖 `#all-updates` 顶栏搜索)
|
|
98
|
+
- 匹配放宽:**标题命中或 URL 路径命中** 均可得分(`score_link_match` 原已支持,增强前缀匹配与 ADI 加权)
|
|
99
|
+
- `run_search_dosearch`:处理 `ERR_ABORTED`、搜索结果页导航重试;顶栏搜索降为兜底
|
|
100
|
+
- 新增 `CONFLUENCE_DEBUG=1` 输出选择器解析日志
|
|
101
|
+
- 更新 `01-adi-doc-skill-指引` / `doc-skill.SKILL`:关键字搜索为主,pageId 直链为可选兜底
|
|
102
|
+
|
|
95
103
|
**1.3.9** — PRD 不一致自动修复循环(最多 3 次)
|
|
96
104
|
|
|
97
105
|
- 新增 `prd-remediate`:`prd-diff` 失败 → 按 `09` 生成修复指引 → 调 AI 工具 `/opsx-apply` 修代码 → 再 `prd-diff`(**每项**最多 3 次)
|
package/package.json
CHANGED
|
@@ -12,14 +12,18 @@ python3 confluence-doc.py {{VERSION_EXAMPLE}}
|
|
|
12
12
|
|
|
13
13
|
版本号可传:`2.3.2`、`V2.3.2`、`ADI_V2.3.2`、`ADI-V2.3.2`(脚本会归一化)。
|
|
14
14
|
|
|
15
|
-
2.
|
|
15
|
+
2. 脚本会依次用关键词打开 **Confluence 专用搜索页** `dosearchsite.action?queryString=<关键词>`(非首页顶栏搜索),并依次尝试:`ADI_V*` → `ADI-V*` → `ADI_*` → `ADI-*` → `ADI_v*` → `ADI-v*` → `ADI V*` → `ADI_V*结算` → `V*` 等。
|
|
16
16
|
|
|
17
|
-
3.
|
|
17
|
+
3. 匹配规则:**标题包含关键词或以关键词为前缀**,或 **URL 路径包含关键词**,任一满足即命中(Confluence 列表标题常被截断)。
|
|
18
18
|
|
|
19
|
-
4.
|
|
19
|
+
4. **禁止**在 Confluence 脚本未跑完或未确认失败前,用本地 Word/原型替代 `source/PRD.md`。
|
|
20
|
+
|
|
21
|
+
5. 成功后把生成的 `docs/*.md` **主文档**复制/回填到本 run 目录:
|
|
20
22
|
- `source/PRD.md`
|
|
21
23
|
|
|
22
|
-
##
|
|
24
|
+
## 直链兜底(可选,非默认)
|
|
25
|
+
|
|
26
|
+
仅在关键字搜索全部失败、且已知 pageId 时使用:
|
|
23
27
|
|
|
24
28
|
ADI V2.3.4 示例(`yyzcpt` 空间 pageId `109609740`):
|
|
25
29
|
|
|
@@ -44,8 +48,8 @@ python3 confluence-doc.py 2.3.4
|
|
|
44
48
|
|
|
45
49
|
## 失败排查
|
|
46
50
|
|
|
47
|
-
- 输出 `❌ 未找到`:到 Confluence 确认页面标题(ADI 常为 `ADI_V2.3.4结算、开票…`,**下划线**而非 `ADI-V
|
|
48
|
-
-
|
|
51
|
+
- 输出 `❌ 未找到`:到 Confluence 确认页面标题(ADI 常为 `ADI_V2.3.4结算、开票…`,**下划线**而非 `ADI-V`);可尝试输入更长关键词如 `ADI_V2.3.4结算`
|
|
52
|
+
- 页面存在但搜不到:加 `CONFLUENCE_DEBUG=1` 查看选择器解析日志;或(可选)设置 `CONFLUENCE_PAGE_ID` / `CONFLUENCE_PAGE_URL` 直链下载
|
|
49
53
|
- Playwright 失败且设置 `CONFLUENCE_USE_REST=1` 时才回退 Token;登录最多重试 3 次,并用 `expect_navigation` 避免竞态
|
|
50
54
|
- 登录偶发失败:用 `CONFLUENCE_HEADED=1 CONFLUENCE_SLOW_MO=500` 有头观察;失败截图 `confluence-login-fail-*.png`
|
|
51
55
|
- `playwright` 未安装:`pip install playwright && playwright install chromium`
|
|
@@ -177,8 +177,10 @@ def href_path_matches_keyword(href: str, keyword: str) -> bool:
|
|
|
177
177
|
|
|
178
178
|
|
|
179
179
|
def text_matches_keyword(text: str, keyword: str) -> bool:
|
|
180
|
-
"""标题/链接文案匹配:忽略 -/_
|
|
181
|
-
|
|
180
|
+
"""标题/链接文案匹配:忽略 -/_ 差异;支持前缀命中(如 ADI_V2.3.4结算…)。"""
|
|
181
|
+
t_norm = normalize_match_token(text or "")
|
|
182
|
+
kw_norm = normalize_match_token(keyword)
|
|
183
|
+
return kw_norm in t_norm or t_norm.startswith(kw_norm)
|
|
182
184
|
|
|
183
185
|
|
|
184
186
|
def score_link_match(text: str, href: str, prefix: str, version: str, keyword: str) -> int:
|
|
@@ -212,22 +214,34 @@ def score_link_match(text: str, href: str, prefix: str, version: str, keyword: s
|
|
|
212
214
|
space = env("CONFLUENCE_SPACE", "").lower()
|
|
213
215
|
if space and f"/spaces/{space}/" in href.lower():
|
|
214
216
|
score += 5
|
|
217
|
+
# ADI/产品 PRD 标题或 URL 含版本号时加权,降低误命中其他空间页面
|
|
218
|
+
if re.search(r"adi[-_]?v", text, re.IGNORECASE) or re.search(
|
|
219
|
+
r"adi[-_]?v", unquote(href), re.IGNORECASE
|
|
220
|
+
):
|
|
221
|
+
score += 2
|
|
215
222
|
return score
|
|
216
223
|
|
|
217
224
|
|
|
218
225
|
def collect_search_candidates(
|
|
219
226
|
page, base_url: str, prefix: str, version: str, keyword: str
|
|
220
227
|
) -> list[tuple[str, str, int]]:
|
|
221
|
-
"""返回 [(url, title, score), ...]"""
|
|
228
|
+
"""返回 [(url, title, score), ...];标题命中或 URL 路径命中均可得分(>=2)。"""
|
|
222
229
|
seen: set[str] = set()
|
|
223
230
|
scored: list[tuple[int, str, str]] = []
|
|
231
|
+
debug = env_bool("CONFLUENCE_DEBUG")
|
|
224
232
|
|
|
225
233
|
selectors = [
|
|
226
234
|
".search-result .title a",
|
|
227
235
|
".search-result-title a",
|
|
228
236
|
"#search-results .gs-title a",
|
|
237
|
+
"ol.search-results-list a",
|
|
238
|
+
".search-results a",
|
|
239
|
+
".search-result a",
|
|
240
|
+
"#search-results a",
|
|
229
241
|
".search-results-panel a",
|
|
230
242
|
"a[data-linked-resource-id]",
|
|
243
|
+
f"a[href*='{keyword}']",
|
|
244
|
+
f"a:has-text('{keyword}')",
|
|
231
245
|
"a[href*='/pages/']",
|
|
232
246
|
"a[href*='/display/']",
|
|
233
247
|
]
|
|
@@ -235,6 +249,8 @@ def collect_search_candidates(
|
|
|
235
249
|
for selector in selectors:
|
|
236
250
|
links = page.locator(selector)
|
|
237
251
|
count = min(links.count(), 200)
|
|
252
|
+
if debug and count:
|
|
253
|
+
print(f" [debug] selector={selector!r} count={count}")
|
|
238
254
|
for i in range(count):
|
|
239
255
|
href = links.nth(i).get_attribute("href") or ""
|
|
240
256
|
raw_text = (links.nth(i).inner_text() or "").strip()
|
|
@@ -252,6 +268,9 @@ def collect_search_candidates(
|
|
|
252
268
|
if s >= 2:
|
|
253
269
|
scored.append((s, full_url, text[:120]))
|
|
254
270
|
|
|
271
|
+
if debug and not scored:
|
|
272
|
+
print(" [debug] 未解析到 score>=2 的 wiki 页面链接")
|
|
273
|
+
|
|
255
274
|
scored.sort(key=lambda x: x[0], reverse=True)
|
|
256
275
|
return [(u, t, s) for s, u, t in scored]
|
|
257
276
|
|
|
@@ -625,19 +644,36 @@ def try_rest_download(
|
|
|
625
644
|
|
|
626
645
|
|
|
627
646
|
def run_search_dosearch(page, base_url: str, prefix: str, version: str, keyword: str) -> list[tuple[str, str, int]]:
|
|
647
|
+
"""打开 Confluence 全站专用搜索页(dosearchsite),比首页 #all-updates 顶栏搜索更稳定。"""
|
|
628
648
|
url = f"{base_url.rstrip('/')}/dosearchsite.action?queryString={quote(keyword)}"
|
|
629
|
-
print(f" →
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
649
|
+
print(f" → 专用搜索页: .../dosearchsite.action?queryString={quote(keyword)}")
|
|
650
|
+
for attempt in range(2):
|
|
651
|
+
try:
|
|
652
|
+
page.goto(url, wait_until="domcontentloaded", timeout=60000)
|
|
653
|
+
except Exception as exc:
|
|
654
|
+
# 搜索页常有重定向/长连接,networkidle 易误报 ERR_ABORTED
|
|
655
|
+
if "ERR_ABORTED" not in str(exc) and "Timeout" not in str(exc):
|
|
656
|
+
raise
|
|
657
|
+
if attempt == 0:
|
|
658
|
+
print(" 导航被中断,等待搜索结果页稳定...")
|
|
659
|
+
try:
|
|
660
|
+
page.goto(url, wait_until="load", timeout=60000)
|
|
661
|
+
except Exception:
|
|
662
|
+
pass
|
|
663
|
+
page.wait_for_load_state("domcontentloaded", timeout=30000)
|
|
664
|
+
page.wait_for_timeout(2500)
|
|
665
|
+
if is_login_page(page):
|
|
666
|
+
return []
|
|
667
|
+
current = page.url.lower()
|
|
668
|
+
on_search = "dosearchsite" in current or "search" in current
|
|
669
|
+
if on_search or attempt == 1:
|
|
670
|
+
break
|
|
671
|
+
print(" 未停留在搜索结果页,重试一次...")
|
|
637
672
|
return collect_search_candidates(page, base_url, prefix, version, keyword)
|
|
638
673
|
|
|
639
674
|
|
|
640
675
|
def run_search_quick(page, base_url: str, prefix: str, version: str, keyword: str) -> list[tuple[str, str, int]]:
|
|
676
|
+
"""顶栏快速搜索(dosearchsite 无结果时的兜底,非主路径)。"""
|
|
641
677
|
search_selector = None
|
|
642
678
|
for selector in [
|
|
643
679
|
"#quick-search-query",
|
|
@@ -653,10 +689,10 @@ def run_search_quick(page, base_url: str, prefix: str, version: str, keyword: st
|
|
|
653
689
|
break
|
|
654
690
|
if not search_selector:
|
|
655
691
|
return []
|
|
656
|
-
print(f" →
|
|
692
|
+
print(f" → 顶栏搜索框(兜底): {search_selector} 关键词: {keyword}")
|
|
657
693
|
page.fill(search_selector, keyword, timeout=8000)
|
|
658
694
|
page.keyboard.press("Enter")
|
|
659
|
-
page.wait_for_load_state("
|
|
695
|
+
page.wait_for_load_state("domcontentloaded", timeout=30000)
|
|
660
696
|
page.wait_for_timeout(2500)
|
|
661
697
|
return collect_search_candidates(page, base_url, prefix, version, keyword)
|
|
662
698
|
|
|
@@ -60,9 +60,15 @@ python3 confluence-doc.py {{PRODUCT_PREFIX}}_V1.2.3
|
|
|
60
60
|
4. `{{PRODUCT_PREFIX}}-<版本>`(如 ADI-2.3.2)
|
|
61
61
|
5. `ADI_V<版本>结算`(ADI 长标题页,如 V2.3.4 结算对接欢聚)
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
每个关键词优先走 **`dosearchsite.action?queryString=...` 专用搜索页**(比首页 `#all-updates` 顶栏搜索稳定);无结果时再兜底顶栏搜索。匹配时 **标题或 URL 路径** 任一命中即可,并忽略 `-`/`_` 差异;ADI 页按 `ADI_V*` 标题规则识别(面包屑可无 PRD 字样)。
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
调试搜索解析:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
CONFLUENCE_DEBUG=1 python3 confluence-doc.py ADI_V2.3.4
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 直链(可选,跳过搜索)
|
|
66
72
|
|
|
67
73
|
```bash
|
|
68
74
|
CONFLUENCE_PAGE_ID=109609740 python3 confluence-doc.py 2.3.4
|
|
@@ -88,6 +94,6 @@ CONFLUENCE_PAGE_URL="https://confluence.huan.tv/spaces/yyzcpt/pages/109609740/..
|
|
|
88
94
|
- 脚本:`docs/{{SKILL_DIR}}/confluence-doc.py`
|
|
89
95
|
- 脚本已内置默认账号密码;如需使用其他账号可用环境变量覆盖
|
|
90
96
|
- 默认 Confluence 地址为 `https://confluence.huan.tv`(可用 `CONFLUENCE_BASE_URL` 覆盖)
|
|
91
|
-
-
|
|
97
|
+
- 搜索主路径为 `dosearchsite.action?queryString=<关键词>`(全站专用搜索页)
|
|
92
98
|
- 脚本会自动尝试读取仓库根目录的 `.env.pre` / `.env` / `.env.local` / `.env.development` / `.env.test`(若其中存在 `CONFLUENCE_*` 配置可直接复用)
|
|
93
99
|
|