harveyz-skill 0.17.0 → 0.18.0
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 +27 -0
- package/README.md +3 -3
- package/bin/cli.js +17 -10
- package/package.json +5 -4
- package/skills/creative/capture-insight/SKILL.md +86 -4
- package/skills/experiment/learn-paper/SKILL.md +310 -0
- package/skills/experiment/probe-session/SKILL.md +93 -0
- package/skills/experiment/probe-session/data/strategies.json +26 -0
- package/skills/experiment/probe-session/scripts/list_profiles.py +69 -0
- package/skills/experiment/probe-session/scripts/probe.py +246 -0
- package/skills/experiment/probe-session/vars.json +8 -0
- package/skills/meta/archive-skill/SKILL.md +2 -2
- package/skills/meta/init-skill/SKILL.md +157 -0
- package/skills/meta/init-skill/references/skill-authoring-guide.md +298 -0
- package/skills/meta/publish-skill/SKILL.md +62 -7
- package/skills/research/extract-vision/SKILL.md +34 -121
- package/skills/research/extract-vision/scripts/ocr_extract.py +89 -0
- package/skills/writing/forge-doc/scripts/__pycache__/md_to_pdf.cpython-314.pyc +0 -0
- package/skills-index.json +197 -31
- package/tools/p-launch/__pycache__/p_launch.cpython-314.pyc +0 -0
- package/tools/p-launch/p-launch.py +0 -6
- package/tools/p-launch/p-launch.sh +0 -34
- package/tools/p-launch/p_launch.py +0 -708
- package/tools/p-launch/tests/__pycache__/test_p_launch.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/p-launch/tests/__pycache__/test_p_launch.cpython-314-pytest-9.1.0.pyc +0 -0
- package/tools/p-launch/tests/e2e.bats +0 -369
- package/tools/p-launch/tests/test_p_launch.py +0 -378
- package/tools/p-launch/tests/unit.bats +0 -139
- package/tools/p-launch/tool.json +0 -11
- package/tools/p-launch/vars.json +0 -9
- package/tools/p-launch/zshrc.snippet +0 -5
- package/tools/todo-tool/frontend/README.md +0 -73
- package/tools/todo-tool/frontend/components.json +0 -25
- package/tools/todo-tool/frontend/eslint.config.js +0 -22
- package/tools/todo-tool/frontend/index.html +0 -13
- package/tools/todo-tool/frontend/package-lock.json +0 -6566
- package/tools/todo-tool/frontend/package.json +0 -42
- package/tools/todo-tool/frontend/postcss.config.js +0 -6
- package/tools/todo-tool/frontend/public/favicon.svg +0 -1
- package/tools/todo-tool/frontend/public/icons.svg +0 -24
- package/tools/todo-tool/frontend/src/App.css +0 -184
- package/tools/todo-tool/frontend/src/App.tsx +0 -78
- package/tools/todo-tool/frontend/src/assets/hero.png +0 -0
- package/tools/todo-tool/frontend/src/assets/react.svg +0 -1
- package/tools/todo-tool/frontend/src/assets/vite.svg +0 -1
- package/tools/todo-tool/frontend/src/components/AddTaskDialog.tsx +0 -92
- package/tools/todo-tool/frontend/src/components/FilterBar.tsx +0 -74
- package/tools/todo-tool/frontend/src/components/TaskList.tsx +0 -35
- package/tools/todo-tool/frontend/src/components/TaskRow.tsx +0 -44
- package/tools/todo-tool/frontend/src/components/ui/badge.tsx +0 -52
- package/tools/todo-tool/frontend/src/components/ui/button.tsx +0 -58
- package/tools/todo-tool/frontend/src/components/ui/dialog.tsx +0 -159
- package/tools/todo-tool/frontend/src/components/ui/input.tsx +0 -20
- package/tools/todo-tool/frontend/src/components/ui/select.tsx +0 -197
- package/tools/todo-tool/frontend/src/index.css +0 -90
- package/tools/todo-tool/frontend/src/lib/api.ts +0 -42
- package/tools/todo-tool/frontend/src/lib/utils.ts +0 -6
- package/tools/todo-tool/frontend/src/main.tsx +0 -10
- package/tools/todo-tool/frontend/tailwind.config.js +0 -66
- package/tools/todo-tool/frontend/tsconfig.app.json +0 -32
- package/tools/todo-tool/frontend/tsconfig.json +0 -7
- package/tools/todo-tool/frontend/tsconfig.node.json +0 -24
- package/tools/todo-tool/frontend/vite.config.ts +0 -17
- package/tools/todo-tool/pyproject.toml +0 -31
- package/tools/todo-tool/tests/__init__.py +0 -0
- package/tools/todo-tool/tests/__pycache__/__init__.cpython-311.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_api.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_api.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_api.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-314.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_db.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_db.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_db.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_models.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_models.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_models.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_parser.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_parser.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_projects_index.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_sync.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_sync.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/test_api.py +0 -229
- package/tools/todo-tool/tests/test_cli.py +0 -214
- package/tools/todo-tool/tests/test_db.py +0 -214
- package/tools/todo-tool/tests/test_models.py +0 -24
- package/tools/todo-tool/tests/test_parser.py +0 -120
- package/tools/todo-tool/tests/test_projects_index.py +0 -185
- package/tools/todo-tool/tests/test_sync.py +0 -101
- package/tools/todo-tool/todo/__init__.py +0 -0
- package/tools/todo-tool/todo/__pycache__/__init__.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/cli.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/cli.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/db.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/db.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/models.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/models.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/parser.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/parser.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/projects_index.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/projects_index.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/server.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/server.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/cli.py +0 -208
- package/tools/todo-tool/todo/db.py +0 -289
- package/tools/todo-tool/todo/models.py +0 -41
- package/tools/todo-tool/todo/parser.py +0 -122
- package/tools/todo-tool/todo/projects_index.py +0 -152
- package/tools/todo-tool/todo/server.py +0 -98
- package/tools/todo-tool/todo/todo_format.yaml +0 -29
- package/tools/todo-tool/todo-tool.sh +0 -23
- package/tools/todo-tool/tool.json +0 -12
- package/tools/todo-tool/zshrc.snippet +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.18.0] - 2026-06-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `learn-paper` skill:基于三遍阅读法的论文精读 skill(首次以 `read-paper` 命名引入,后改名为 `learn-paper`)
|
|
14
|
+
- `init-skill` skill:从设计文档脚手架生成新 skill
|
|
15
|
+
- `probe-chrome-session` skill(experiment bundle):Chrome profile cookie/session 探测与注入,支持 per-domain 加载策略
|
|
16
|
+
- `experiment` bundle:用于 staging 实验性 skill
|
|
17
|
+
- `publish-skill` F8:基于内容哈希的版本号检查,hash mismatch 时自动 bump
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `extract-vision`:overhaul 至 v1.2.0(PaddleOCR v3 API、lang options 修复)
|
|
21
|
+
- `capture-insight`:通过 `~/.hskill` 配置保存到 Writing Agent project
|
|
22
|
+
- release-profile.md 迁移到 `.hskill/release-project/`
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
- 归档 `npm-release` skill(被 `release-project` 取代)
|
|
26
|
+
- 退役 `p-launch` 与 `todo-tool`(被 `hub` 取代),移除对应测试
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- `publish-skill`:audit 违规修复
|
|
30
|
+
- `read-paper`:真实论文测试后发现的 5 个问题修复
|
|
31
|
+
|
|
32
|
+
## [0.17.1] - 2026-06-15
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- `hskill` TUI:未安装的 skill 现在按 `installScope` 显示推荐图标(`»` essential / `▸` global/project),不再统一显示 `—`
|
|
36
|
+
|
|
10
37
|
## [0.17.0] - 2026-06-15
|
|
11
38
|
|
|
12
39
|
## [0.16.0] - 2026-06-15
|
package/README.md
CHANGED
|
@@ -71,10 +71,10 @@ Shell 工具,安装到 `~/.local/bin/`。
|
|
|
71
71
|
|
|
72
72
|
| 工具 | Bundle | 说明 |
|
|
73
73
|
|------|--------|------|
|
|
74
|
-
| **
|
|
74
|
+
| **hub** | tools | 开发者 OS — 项目管理 + git 状态 + 任务跟踪 |
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
hskill install --tool
|
|
77
|
+
hskill install --tool hub
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
---
|
|
@@ -94,7 +94,7 @@ hskill # 交互式选择安装
|
|
|
94
94
|
hskill # 交互式选择
|
|
95
95
|
hskill install --bundle dev # 安装整个 bundle
|
|
96
96
|
hskill install --skill git-workflow-init # 安装单个 skill
|
|
97
|
-
hskill install --tool
|
|
97
|
+
hskill install --tool hub # 安装 shell 工具
|
|
98
98
|
hskill list # 查看可用 skill
|
|
99
99
|
hskill update # 更新到最新版
|
|
100
100
|
hskill --help # 查看帮助
|
package/bin/cli.js
CHANGED
|
@@ -729,34 +729,41 @@ function fzfSelect() {
|
|
|
729
729
|
const hookItems = getAllHookItems()
|
|
730
730
|
const previewPath = path.join(__dirname, 'preview.mjs')
|
|
731
731
|
|
|
732
|
-
// 构建 fzf 输入:每行 "NAME\tVERSION\tBUNDLE\tKIND\tSRCPATH"
|
|
732
|
+
// 构建 fzf 输入:每行 "NAME\tVERSION\tBUNDLE\tKIND\tSRCPATH\tINSTALLSCOPE"
|
|
733
733
|
const lines = [
|
|
734
734
|
...skillItems.map(s => {
|
|
735
735
|
const bundle = s.srcPath.split('/').slice(-2, -1)[0]
|
|
736
|
-
return `${s.skillName}\t${s.version ?? '—'}\t${bundle}\tskill\t${s.srcPath}`
|
|
736
|
+
return `${s.skillName}\t${s.version ?? '—'}\t${bundle}\tskill\t${s.srcPath}\t${s.installScope ?? ''}`
|
|
737
737
|
}),
|
|
738
|
-
...toolItems.map(t => `${t.toolName}\t${t.version ?? '—'}\tshell-tool\ttool\t${t.srcPath}`),
|
|
739
|
-
...hookItems.map(h => `${h.name}\t${h.version ?? '—'}\thook\thook\t${h.srcPath}`),
|
|
738
|
+
...toolItems.map(t => `${t.toolName}\t${t.version ?? '—'}\tshell-tool\ttool\t${t.srcPath}\t${t.installScope ?? ''}`),
|
|
739
|
+
...hookItems.map(h => `${h.name}\t${h.version ?? '—'}\thook\thook\t${h.srcPath}\t${h.installScope ?? ''}`),
|
|
740
740
|
]
|
|
741
741
|
|
|
742
742
|
const nameWidth = Math.max(...lines.map(l => l.split('\t')[0].length))
|
|
743
743
|
const versionWidth = Math.max(...lines.map(l => l.split('\t')[1].length))
|
|
744
744
|
|
|
745
|
-
const G = '\x1b[32m', Y = '\x1b[33m', D = '\x1b[2m', R = '\x1b[0m'
|
|
745
|
+
const G = '\x1b[32m', Y = '\x1b[33m', C = '\x1b[36m', D = '\x1b[2m', R = '\x1b[0m'
|
|
746
746
|
function colorIcon(status) {
|
|
747
747
|
if (status === 'up-to-date') return G + '✓' + R
|
|
748
748
|
if (status === 'update') return Y + '↑' + R
|
|
749
749
|
return D + '—' + R
|
|
750
750
|
}
|
|
751
|
+
function scopeHint(installScope) {
|
|
752
|
+
if (installScope === 'essential') return Y + '»' + R
|
|
753
|
+
if (installScope === 'global' || installScope === 'project') return C + '▸' + R
|
|
754
|
+
return D + '—' + R
|
|
755
|
+
}
|
|
751
756
|
|
|
752
757
|
// fzf 展示格式:NAME VERSION U:? P:? BUNDLE
|
|
753
758
|
const displayLines = lines.map(l => {
|
|
754
|
-
const [name, ver, bundle, kind, srcPath] = l.split('\t')
|
|
755
|
-
let uIcon =
|
|
759
|
+
const [name, ver, bundle, kind, srcPath, installScope] = l.split('\t')
|
|
760
|
+
let uIcon = scopeHint(installScope), pIcon = D + '—' + R
|
|
756
761
|
if (kind === 'skill') {
|
|
757
762
|
const installed = checkInstalled(name, ver)
|
|
758
|
-
|
|
759
|
-
|
|
763
|
+
const uStatus = scopeSummary(installed.user)
|
|
764
|
+
const pStatus = scopeSummary(installed.project)
|
|
765
|
+
uIcon = uStatus !== 'none' ? colorIcon(uStatus) : scopeHint(installScope)
|
|
766
|
+
pIcon = pStatus !== 'none' ? colorIcon(pStatus) : (installScope === 'project' ? C + '▸' + R : D + '—' + R)
|
|
760
767
|
} else if (kind === 'tool') {
|
|
761
768
|
uIcon = colorIcon(checkToolInstalled(name, srcPath).status)
|
|
762
769
|
} else if (kind === 'hook') {
|
|
@@ -772,7 +779,7 @@ function fzfSelect() {
|
|
|
772
779
|
// 把原始数据附在末尾(隐藏列,用于解析和 preview)
|
|
773
780
|
const fzfInput = displayLines.map((d, i) => `${d}\t${lines[i]}`).join('\n')
|
|
774
781
|
|
|
775
|
-
const header = ` hskill · U=user P=project ${G}✓${R}=ok ${Y}↑${R}=update ${D}—${R}=none · tab 多选 · enter 确认`
|
|
782
|
+
const header = ` hskill · U=user P=project ${G}✓${R}=ok ${Y}↑${R}=update ${Y}»${R}=essential ${C}▸${R}=recommended ${D}—${R}=none · tab 多选 · enter 确认`
|
|
776
783
|
|
|
777
784
|
const result = spawnSync('fzf', [
|
|
778
785
|
'--multi',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harveyz-skill",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Skill manager for Claude Code, Cursor, and Codex",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"prepack": "node scripts/generate-npmignore.js",
|
|
11
|
-
"test": "bats tests/ &&
|
|
11
|
+
"test": "bats tests/ && bash scripts/run-skill-tests.sh"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"bin/",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"skills/design/scout-brand/",
|
|
35
35
|
"skills/design/build-style/",
|
|
36
36
|
"skills/design/sync-design/",
|
|
37
|
+
"skills/meta/init-skill/",
|
|
37
38
|
"skills/meta/analyze-skill/",
|
|
38
39
|
"skills/meta/clean-git/",
|
|
39
40
|
"skills/meta/archive-skill/",
|
|
@@ -42,8 +43,8 @@
|
|
|
42
43
|
"skills/meta/publish-skill/",
|
|
43
44
|
"skills/meta/runby-opencode/",
|
|
44
45
|
"skills/meta/release-project/",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
46
|
+
"skills/experiment/probe-session/",
|
|
47
|
+
"skills/experiment/learn-paper/",
|
|
47
48
|
"tools/hub/",
|
|
48
49
|
"hooks/check-similar-branch/"
|
|
49
50
|
],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: capture-insight
|
|
3
|
-
version: "
|
|
3
|
+
version: "2.0.0"
|
|
4
4
|
user_invocable: true
|
|
5
|
-
description: "Capture and record a fleeting insight or observation. Triggers immediately when the user expresses an observation, judgment, or idea — phrases like 'I noticed', 'just an observation', 'record this', 'insight', 'I have a judgment', or tossing out an opinionated thought. Distinct from 'I have an idea to execute' (that's a task). Clarifies the core with 2-3 quick Q&As, then saves to the insights/ folder. Trigger early — insights are fragile."
|
|
5
|
+
description: "Capture and record a fleeting insight or observation. Triggers immediately when the user expresses an observation, judgment, or idea — phrases like 'I noticed', 'just an observation', 'record this', 'insight', 'I have a judgment', or tossing out an opinionated thought. Distinct from 'I have an idea to execute' (that's a task). Clarifies the core with 2-3 quick Q&As, then saves to the Writing Agent project's insights/ folder. Trigger early — insights are fragile."
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Capture Insight
|
|
@@ -35,6 +35,33 @@ description: "Capture and record a fleeting insight or observation. Triggers imm
|
|
|
35
35
|
|
|
36
36
|
## 流程
|
|
37
37
|
|
|
38
|
+
### 第〇步:确定写入项目路径
|
|
39
|
+
|
|
40
|
+
在开始澄清前,先读取配置:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
~/.hskill/capture-insight/config.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**配置格式:**
|
|
47
|
+
```json
|
|
48
|
+
{ "writing_agent_path": "/path/to/writing-agent" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**若配置文件不存在:**
|
|
52
|
+
|
|
53
|
+
> "没找到 Writing Agent 项目配置。请确认项目路径(默认:`/Users/harveyzhang96/Projects/writing-agent`)?"
|
|
54
|
+
|
|
55
|
+
用户确认后,创建目录并写入配置:
|
|
56
|
+
```bash
|
|
57
|
+
mkdir -p ~/.hskill/capture-insight
|
|
58
|
+
echo '{"writing_agent_path": "/Users/harveyzhang96/Projects/writing-agent"}' > ~/.hskill/capture-insight/config.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
配置确认后,将 `writing_agent_path` 记为 `{project_path}`,insight 写入目标为 `{project_path}/insights/`。
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
38
65
|
### 第一步:捕捉原始 insight
|
|
39
66
|
|
|
40
67
|
用户说出 insight 后,**先用一句话复述你理解的核心**,让用户确认或纠正方向。这一步不算"问问题",是对齐认知用的。
|
|
@@ -57,7 +84,7 @@ description: "Capture and record a fleeting insight or observation. Triggers imm
|
|
|
57
84
|
澄清完成后:
|
|
58
85
|
|
|
59
86
|
1. 给出一段 **insight 摘要**(3-5 句话),让用户确认是否准确捕捉到了核心
|
|
60
|
-
2.
|
|
87
|
+
2. 用户确认后,生成文件名,按以下步骤写入 `{project_path}/insights/`
|
|
61
88
|
|
|
62
89
|
---
|
|
63
90
|
|
|
@@ -95,18 +122,73 @@ description: "Capture and record a fleeting insight or observation. Triggers imm
|
|
|
95
122
|
|
|
96
123
|
---
|
|
97
124
|
|
|
125
|
+
## 写入 & Git 操作
|
|
126
|
+
|
|
127
|
+
确认写入 `{project_path}/insights/YYYY-MM-DD-<slug>.md` 后:
|
|
128
|
+
|
|
129
|
+
### 切换到 chore/insight 分支
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
cd {project_path}
|
|
133
|
+
|
|
134
|
+
# 1. 记录当前分支
|
|
135
|
+
ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
136
|
+
|
|
137
|
+
# 2. 确定基准分支
|
|
138
|
+
if git show-ref --verify --quiet refs/heads/staging; then
|
|
139
|
+
BASE_BRANCH=staging
|
|
140
|
+
else
|
|
141
|
+
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null \
|
|
142
|
+
| sed 's@^refs/remotes/origin/@@' || echo "main")
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# 3. 切换到 chore/insight(不存在则从 BASE_BRANCH 创建)
|
|
146
|
+
if git show-ref --verify --quiet refs/heads/chore/insight; then
|
|
147
|
+
git checkout chore/insight
|
|
148
|
+
else
|
|
149
|
+
git checkout -b chore/insight "$BASE_BRANCH"
|
|
150
|
+
fi
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**非 git 仓库**:若 `{project_path}` 不在 git 仓库中,跳过 git 步骤,直接写入文件。
|
|
154
|
+
|
|
155
|
+
### 写入文件
|
|
156
|
+
|
|
157
|
+
将 insight 内容写入 `{project_path}/insights/YYYY-MM-DD-<slug>.md`。
|
|
158
|
+
|
|
159
|
+
### 提交、合并、切回
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd {project_path}
|
|
163
|
+
|
|
164
|
+
# 1. 在 chore/insight 上提交
|
|
165
|
+
git add insights/YYYY-MM-DD-<slug>.md
|
|
166
|
+
git commit -m "insight: add YYYY-MM-DD-<slug>"
|
|
167
|
+
|
|
168
|
+
# 2. 合并到基准分支
|
|
169
|
+
git checkout "$BASE_BRANCH"
|
|
170
|
+
git merge --no-ff chore/insight -m "Merge chore/insight: YYYY-MM-DD-<slug>"
|
|
171
|
+
|
|
172
|
+
# 3. 切回原分支
|
|
173
|
+
git checkout "$ORIGINAL_BRANCH"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
98
178
|
## 注意事项
|
|
99
179
|
|
|
100
180
|
- **不要代替用户形成判断**。如果用户说"我觉得 XX 可能是个趋势",摘要里写"你观察到 XX 可能是个趋势",而不是"XX 是个重要趋势"。
|
|
101
181
|
- **保留用户的语气和颗粒度**。用户用的是模糊语言("感觉"、"好像"、"也许"),保存时也保留这种不确定性,不要帮他们"升级"成确定性判断。
|
|
102
182
|
- **简洁优先**。insight 文件不是分析报告,核心价值是"快速找回当时的思路",不是写详尽。
|
|
183
|
+
- **无论从哪个项目触发,始终写入 Writing Agent 项目**。`{project_path}` 来自配置,不受当前工作目录影响。
|
|
103
184
|
|
|
104
185
|
---
|
|
105
186
|
|
|
106
187
|
## 保存后告诉用户
|
|
107
188
|
|
|
108
189
|
```
|
|
109
|
-
✓ 已保存到 insights/YYYY-MM-DD-<slug>.md
|
|
190
|
+
✓ 已保存到 {project_path}/insights/YYYY-MM-DD-<slug>.md
|
|
191
|
+
提交到 chore/insight,合并到 {BASE_BRANCH},当前回到 {ORIGINAL_BRANCH}。
|
|
110
192
|
|
|
111
193
|
[如果发现这个 insight 与现有文章方向相关,可以顺带一提,但不要主动推销"你可以写篇文章"]
|
|
112
194
|
```
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: learn-paper
|
|
3
|
+
version: "0.1.0"
|
|
4
|
+
description: "Deep-read a single academic PDF in three passes: 5C structural orientation, full content extraction, and critical reconstruction. Each pass produces a standalone Markdown file. Triggers: 'read this paper', 'deep-read paper', 'analyze this paper', 'take notes on this PDF', 'summarize paper', or when user provides a PDF path and wants to understand its content."
|
|
5
|
+
user_invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# read-paper
|
|
9
|
+
|
|
10
|
+
三遍精读单篇 PDF 论文。每遍独立产出一个 Markdown 文件。
|
|
11
|
+
|
|
12
|
+
底层方法论说明见 `docs/superpowers/specs/2026-06-16-read-paper-design.md`。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 路径变量
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
ConfigPath: ~/.hskill/read-paper/config.json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 执行流程
|
|
25
|
+
|
|
26
|
+
### Step 0:初始化配置
|
|
27
|
+
|
|
28
|
+
用 Read 工具读取 `~/.hskill/read-paper/config.json`。
|
|
29
|
+
|
|
30
|
+
若文件不存在,询问用户:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
论文分析笔记保存到哪个目录?(直接回车使用默认:~/Documents/papers)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
用户回复后,用 Bash 工具写入配置(路径必须用 `$HOME` 展开,不可写字面量 `~`):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
mkdir -p "$HOME/.hskill/read-paper"
|
|
40
|
+
output_dir="${用户指定路径/#\~/$HOME}"
|
|
41
|
+
echo "{\"output_dir\": \"$output_dir\"}" > "$HOME/.hskill/read-paper/config.json"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
若文件已存在,解析 JSON 取出 `output_dir` 字段,并将其中可能残留的 `~` 展开为 `$HOME` 后使用:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
output_dir=$(python3 -c "import json,os; d=json.load(open('$HOME/.hskill/read-paper/config.json')); print(d['output_dir'].replace('~', os.environ['HOME'], 1))")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### Step 1:提取 PDF 路径,准备输出目录
|
|
53
|
+
|
|
54
|
+
从用户消息中提取 PDF 文件路径(绝对路径或 `~` 开头路径均可)。
|
|
55
|
+
|
|
56
|
+
**安全净化(在 Bash 中执行):**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pdf_path=$(echo "<用户提供路径>" | tr -d '\000-\037\177' | xargs)
|
|
60
|
+
pdf_path="${pdf_path/#\~/$HOME}"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**验证文件存在:**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
test -f "$pdf_path" || { echo "ERROR: 文件不存在: $pdf_path"; exit 1; }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
若不存在,报错并终止。
|
|
70
|
+
|
|
71
|
+
**生成 paper slug:**
|
|
72
|
+
|
|
73
|
+
取 PDF 文件名(去掉扩展名),将空格和特殊字符替换为 `-`,转小写。汉字保留原样:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
filename=$(basename "$pdf_path" .pdf)
|
|
77
|
+
slug=$(echo "$filename" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9一-鿿]/-/g' | sed -E 's/-+/-/g' | sed 's/^-//;s/-$//')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
示例:`Attention Is All You Need.pdf` → `attention-is-all-you-need`
|
|
81
|
+
|
|
82
|
+
**创建输出目录:**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
mkdir -p "<output_dir>/$slug"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**解析 `--pass` 参数(若用户在触发消息中指定):**
|
|
89
|
+
|
|
90
|
+
| 参数 | 行为 |
|
|
91
|
+
|------|------|
|
|
92
|
+
| `--pass 1` | 只执行第一遍 |
|
|
93
|
+
| `--pass 2` | 只执行第二遍,需检查 `1-orientation.md` 已存在 |
|
|
94
|
+
| `--pass 3` | 只执行第三遍,需检查 `2-extraction.md` 已存在 |
|
|
95
|
+
| 无参数 | 执行全部三遍 |
|
|
96
|
+
|
|
97
|
+
**依赖检查(单遍模式):**
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# --pass 2
|
|
101
|
+
test -f "<output_dir>/$slug/1-orientation.md" || { echo "请先运行 --pass 1 生成前置文件"; exit 1; }
|
|
102
|
+
|
|
103
|
+
# --pass 3
|
|
104
|
+
test -f "<output_dir>/$slug/2-extraction.md" || { echo "请先运行 --pass 2 生成前置文件"; exit 1; }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Step 2:第一遍 — 5C 定向
|
|
110
|
+
|
|
111
|
+
**读取范围:**
|
|
112
|
+
|
|
113
|
+
先读前 5 页获取标题、摘要、引言:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Read(file_path="<pdf_path>", pages="1-5")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
再从论文末尾向前搜索结论章节(最多尝试 3 次,每次窗口 4 页):
|
|
120
|
+
|
|
121
|
+
**策略:** 先读最后 4 页,若未见"Conclusion"/"结论"/"Discussion"关键词,则继续向前读前 4 页,如此最多 3 次。
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
# 第 1 次:读最后 4 页
|
|
125
|
+
Read(file_path="<pdf_path>", pages="<total-3>-<total>")
|
|
126
|
+
|
|
127
|
+
# 若未找到,第 2 次:再往前 4 页
|
|
128
|
+
Read(file_path="<pdf_path>", pages="<total-7>-<total-4>")
|
|
129
|
+
|
|
130
|
+
# 若仍未找到,第 3 次:再往前 4 页
|
|
131
|
+
Read(file_path="<pdf_path>", pages="<total-11>-<total-8>")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`<total>` 通过读第 1 页时 PDF 工具返回的总页数判断,或从前 5 页内容中的页码推断。找到结论即停,不继续往前读。
|
|
135
|
+
|
|
136
|
+
**分析任务:**
|
|
137
|
+
|
|
138
|
+
基于读取内容完成 5C 评估:
|
|
139
|
+
|
|
140
|
+
| 维度 | 分析问题 |
|
|
141
|
+
|------|----------|
|
|
142
|
+
| **Category(类别)** | 测量类 / 系统描述 / 理论分析 / 综述 / 其他? |
|
|
143
|
+
| **Context(背景)** | 解决什么问题?与哪些已知工作相关? |
|
|
144
|
+
| **Correctness(正确性)** | 核心假设是否明确合理?有无明显漏洞? |
|
|
145
|
+
| **Contributions(贡献)** | 主要创新点是什么? |
|
|
146
|
+
| **Clarity(清晰度)** | 结构是否清晰?摘要是否准确概括内容? |
|
|
147
|
+
|
|
148
|
+
**产出文件,用 Write 工具写入 `<output_dir>/<slug>/1-orientation.md`:**
|
|
149
|
+
|
|
150
|
+
```markdown
|
|
151
|
+
# {论文标题} — 定向
|
|
152
|
+
|
|
153
|
+
**作者**: {从首页提取}
|
|
154
|
+
**年份**: {从首页提取}
|
|
155
|
+
**来源**: {期刊/会议/arXiv,从首页提取}
|
|
156
|
+
**PDF**: <pdf_path>
|
|
157
|
+
**分析日期**: YYYY-MM-DD
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 5C 评估
|
|
162
|
+
|
|
163
|
+
- **Category(类别)**:
|
|
164
|
+
- **Context(背景)**:
|
|
165
|
+
- **Correctness(正确性)**:
|
|
166
|
+
- **Contributions(贡献)**:
|
|
167
|
+
- **Clarity(清晰度)**:
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 初步印象
|
|
172
|
+
|
|
173
|
+
(2-3 句话:论文整体质量判断,是否值得继续第二遍精读)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
写完后告知用户:`✓ 第一遍完成 → <output_dir>/<slug>/1-orientation.md`
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### Step 3:第二遍 — 全文内容提取
|
|
181
|
+
|
|
182
|
+
**读取策略(分段处理,每次最多 10 页):**
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
Read(file_path="<pdf_path>", pages="1-10")
|
|
186
|
+
Read(file_path="<pdf_path>", pages="11-20")
|
|
187
|
+
...循环直到读完全文
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
每段读取后立即提取,不等全文读完再汇总(避免 context 溢出)。
|
|
191
|
+
|
|
192
|
+
**终止条件:** 每段读取后检查是否出现 References / Bibliography / 参考文献 的独立章节标题(通常单独成行、后面紧跟编号列表)。一旦检测到,立即停止继续读取后续页面——References 之后已无需提取的正文内容。
|
|
193
|
+
|
|
194
|
+
**每段提取内容:**
|
|
195
|
+
|
|
196
|
+
- **核心论点**:该段提出的主要主张(用自己的语言转述,非直接引用)
|
|
197
|
+
- **实验与数据**:数据集名称、评估指标、关键数值、与基线的对比结果
|
|
198
|
+
- **方法步骤**:算法/架构/流程的关键步骤描述
|
|
199
|
+
- **图表**:图/表编号 + 标题 + 核心结论(图像内容用 vision 解读)
|
|
200
|
+
- **引用标记**:反复出现或支撑核心论点的参考文献编号
|
|
201
|
+
|
|
202
|
+
**产出文件,用 Write 工具写入 `<output_dir>/<slug>/2-extraction.md`:**
|
|
203
|
+
|
|
204
|
+
```markdown
|
|
205
|
+
# {论文标题} — 内容提取
|
|
206
|
+
|
|
207
|
+
**作者**: {从首页提取}
|
|
208
|
+
**PDF**: <pdf_path>
|
|
209
|
+
**分析日期**: YYYY-MM-DD
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 核心论点与证据
|
|
214
|
+
|
|
215
|
+
(每条论点附支撑来源:实验数据 / 理论推导 / 引用)
|
|
216
|
+
|
|
217
|
+
## 实验设计与结果
|
|
218
|
+
|
|
219
|
+
- **数据集**:
|
|
220
|
+
- **评估指标**:
|
|
221
|
+
- **关键数值**:
|
|
222
|
+
- **与基线对比**:
|
|
223
|
+
|
|
224
|
+
## 方法论摘要
|
|
225
|
+
|
|
226
|
+
(核心方法的步骤/架构/算法,用自己的语言重述,不直接抄论文)
|
|
227
|
+
|
|
228
|
+
## 图表解读
|
|
229
|
+
|
|
230
|
+
| 图/表 | 标题 | 核心结论 |
|
|
231
|
+
|-------|------|----------|
|
|
232
|
+
| Fig.1 | ... | ... |
|
|
233
|
+
|
|
234
|
+
## 作者承认的局限
|
|
235
|
+
|
|
236
|
+
(作者在 Limitation / Future Work 章节明确说明的不足,非推断)
|
|
237
|
+
|
|
238
|
+
## 值得追踪的关键引用
|
|
239
|
+
|
|
240
|
+
(反复出现或支撑核心论点的参考文献,记编号和推测主题)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
写完后告知用户:`✓ 第二遍完成 → <output_dir>/<slug>/2-extraction.md`
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### Step 4:第三遍 — 批判性重构
|
|
248
|
+
|
|
249
|
+
**读取来源(不重读 PDF):**
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
Read(file_path="<output_dir>/<slug>/2-extraction.md")
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**分析任务(针对 2-extraction.md 中每条核心论点):**
|
|
256
|
+
|
|
257
|
+
1. **假设审计**:该论点依赖哪些隐含假设?论文是否明确说明了这些假设?
|
|
258
|
+
2. **缺陷识别**:
|
|
259
|
+
- 实验是否缺少重要对照组?
|
|
260
|
+
- 结论是否超出实验结果可支撑的范围?
|
|
261
|
+
- 评估指标是否存在选择偏差?
|
|
262
|
+
3. **对比已知工作**:与训练知识中的相关方法相比,声称的创新点是否成立?有无遗漏的重要基线对比?
|
|
263
|
+
4. **延伸方向**:若要改进或扩展,最有价值的研究方向是什么?
|
|
264
|
+
|
|
265
|
+
**诚实性标注规则(每条批判意见必须标注):**
|
|
266
|
+
|
|
267
|
+
- `[基于论文]` — 直接来自论文内容,可直接引用
|
|
268
|
+
- `[Agent推断]` — 基于训练知识的判断,可能存在时效性或准确性问题
|
|
269
|
+
- `[需核实]` — 需要查阅外部文献才能确认,不可直接引用
|
|
270
|
+
|
|
271
|
+
**产出文件,用 Write 工具写入 `<output_dir>/<slug>/3-critique.md`:**
|
|
272
|
+
|
|
273
|
+
```markdown
|
|
274
|
+
# {论文标题} — 批判性重构
|
|
275
|
+
|
|
276
|
+
**作者**: {从 2-extraction.md 复制}
|
|
277
|
+
**PDF**: <pdf_path>
|
|
278
|
+
**分析日期**: YYYY-MM-DD
|
|
279
|
+
|
|
280
|
+
> **注意:** 标注 `[Agent推断]` 的内容基于训练知识,非论文原文,存在时效性限制,请独立核实后引用。
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 隐含假设清单
|
|
285
|
+
|
|
286
|
+
(每条注明:论文是否明确说明 → 是/否)
|
|
287
|
+
|
|
288
|
+
## 已识别缺陷
|
|
289
|
+
|
|
290
|
+
(每条附来源标注:[基于论文] / [Agent推断] / [需核实])
|
|
291
|
+
|
|
292
|
+
## 与已知工作对比
|
|
293
|
+
|
|
294
|
+
([Agent推断] 标注,对比训练知识中的相关方法)
|
|
295
|
+
|
|
296
|
+
## 开放问题 / 延伸方向
|
|
297
|
+
|
|
298
|
+
(值得未来研究的方向,[基于论文] 或 [Agent推断] 标注)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
写完后总结:
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
✓ 三遍分析完成
|
|
305
|
+
|
|
306
|
+
<output_dir>/<slug>/
|
|
307
|
+
├── 1-orientation.md ← 5C 定向
|
|
308
|
+
├── 2-extraction.md ← 内容提取
|
|
309
|
+
└── 3-critique.md ← 批判性重构
|
|
310
|
+
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: probe-session
|
|
3
|
+
version: "0.3.0"
|
|
4
|
+
description: "Validate whether Chrome Profile cookie injection works for a target URL. Runs Playwright twice — anonymous vs injected Chrome session — compares title and body to determine if injection succeeded. Triggers: test Chrome cookie injection, verify login state for a URL, debug extract-url authentication issues."
|
|
5
|
+
user_invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# probe-chrome-session
|
|
9
|
+
|
|
10
|
+
验证 Chrome Profile cookie 注入机制是否对目标 URL 生效。
|
|
11
|
+
|
|
12
|
+
底层机制说明见 `docs/explanation/chrome-profile-cookie-injection.md`。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 路径变量
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
ScriptDir: SKILL_DIR/scripts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 执行流程
|
|
25
|
+
|
|
26
|
+
### 步骤 1:从用户消息中提取 URL
|
|
27
|
+
|
|
28
|
+
从用户消息中提取目标 URL,做净化:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import re
|
|
32
|
+
url_safe = re.sub(r'[\x00-\x1f\x7f]', '', url).strip()[:2048]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 步骤 2:列出 Chrome Profile,让用户选择
|
|
36
|
+
|
|
37
|
+
运行 list_profiles.py,解析 JSON,格式化成编号列表展示给用户:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import subprocess, json, sys
|
|
41
|
+
result = subprocess.run(
|
|
42
|
+
['python3', 'SKILL_DIR/scripts/list_profiles.py'],
|
|
43
|
+
capture_output=True, text=True, timeout=30
|
|
44
|
+
)
|
|
45
|
+
profiles = json.loads(result.stdout)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
向用户展示如下格式,**等待用户回复编号**:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
请选择要使用的 Chrome Profile:
|
|
52
|
+
|
|
53
|
+
[1] Default (harvey@gmail.com) ★ X.com 已登录 ← 推荐
|
|
54
|
+
[2] Profile 1 (other@gmail.com) · 无 X.com 登录态
|
|
55
|
+
[3] Profile 9 (dev@gmail.com) ★ X.com 已登录
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
格式规则:
|
|
59
|
+
- `has_xcom_auth: true` → 显示 `★ X.com 已登录`,列表中第一个标注「← 推荐」
|
|
60
|
+
- `has_xcom_auth: false` → 显示 `· 无 X.com 登录态`
|
|
61
|
+
- `error` 非空 → 显示 `⚠ 读取失败: {error}`
|
|
62
|
+
|
|
63
|
+
根据用户输入的编号取对应条目的 `path` 字段,作为 `selected_profile`。
|
|
64
|
+
|
|
65
|
+
### 步骤 3:运行探针脚本
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import subprocess, sys
|
|
69
|
+
result = subprocess.run(
|
|
70
|
+
['python3', 'SKILL_DIR/scripts/probe.py', url_safe, selected_profile],
|
|
71
|
+
capture_output=True, text=True, timeout=120
|
|
72
|
+
)
|
|
73
|
+
print(result.stdout)
|
|
74
|
+
if result.returncode != 0:
|
|
75
|
+
print(result.stderr, file=sys.stderr)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 步骤 4:向用户报告
|
|
79
|
+
|
|
80
|
+
将脚本输出原样展示,并解读"结论"一行:
|
|
81
|
+
|
|
82
|
+
- `Cookie 注入有效 ✓` → 机制正常,登录态成功注入
|
|
83
|
+
- `Title 相同` → cookie 未生效或页面无需登录,需进一步排查
|
|
84
|
+
- `未提取到任何 cookie` → 该 Profile 尚未在 Chrome 中登录目标网站
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 前提条件
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install pycookiecheat playwright
|
|
92
|
+
playwright install chromium
|
|
93
|
+
```
|