重开仓库内容:改推五个技能(browser-harness / humanizer / humanizer-zh / product-planning / session-mechanism)
按授权清空原有内容后重新提交(原 oil-ui-pro 一并移出,可从历史恢复)。 browser-harness 剔除 .venv 等运行环境;根 .gitignore 补记 .venv/ 与 node_modules/。
This commit is contained in:
1 parent
9a152a1952
commit
777f7fe5d0
240 files changed
+52518
-4409
No files matched your search
@@ -0,0 +1,68 @@
|
||||
"""Extract a transcript from a claude.ai share URL.
|
||||
|
||||
Usage (run inside browser-harness):
|
||||
CLAUDE_SHARE_URL=https://claude.ai/share/<uuid> \
|
||||
OUTPUT_DIR=/path/to/transcripts \
|
||||
bh -c "$(cat agent-workspace/domain-skills/claude-ai/extract-share-transcript.py)"
|
||||
|
||||
Requires: the user's running Chrome must be signed into claude.ai (share pages
|
||||
render the conversation only for authenticated viewers — see share-export.md).
|
||||
|
||||
Writes two files into OUTPUT_DIR, named from the conversation title slug:
|
||||
<slug>.json — {title, source_url, turns: [{role, text}]}
|
||||
<slug>.md — Markdown with ## Human / ## Assistant headers
|
||||
"""
|
||||
import json, os, pathlib, re, sys, time
|
||||
|
||||
share_url = os.environ.get("CLAUDE_SHARE_URL")
|
||||
out_dir = os.environ.get("OUTPUT_DIR")
|
||||
if not share_url or not out_dir:
|
||||
sys.exit("set CLAUDE_SHARE_URL and OUTPUT_DIR env vars")
|
||||
|
||||
new_tab(share_url) # noqa: F821 — provided by browser-harness
|
||||
wait_for_load() # noqa: F821
|
||||
time.sleep(2) # let the conversation tree render
|
||||
|
||||
js_code = """
|
||||
(() => {
|
||||
const userMsgs = [...document.querySelectorAll("[data-testid=user-message]")];
|
||||
if (!userMsgs.length) return JSON.stringify({error: "no user messages found — is the user logged in to claude.ai?"});
|
||||
let p = userMsgs[0];
|
||||
while (p && !p.contains(userMsgs[userMsgs.length-1])) p = p.parentElement;
|
||||
const container = p;
|
||||
const turns = [];
|
||||
for (const child of container.children) {
|
||||
const u = child.querySelector("[data-testid=user-message]");
|
||||
const a = child.querySelector(".font-claude-response");
|
||||
if (u) turns.push({role: "user", text: u.innerText.trim()});
|
||||
else if (a) turns.push({role: "assistant", text: a.innerText.trim()});
|
||||
}
|
||||
const ph = document.querySelector("[data-testid=page-header]");
|
||||
const title = (ph?.innerText.split("\\n")[0] || document.title || "").trim();
|
||||
return JSON.stringify({title, turns});
|
||||
})()
|
||||
"""
|
||||
data = json.loads(js(js_code)) # noqa: F821
|
||||
if data.get("error"):
|
||||
sys.exit(data["error"])
|
||||
|
||||
title = data["title"] or "claude-share"
|
||||
turns = data["turns"]
|
||||
slug = re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-") or "claude-share"
|
||||
|
||||
out = pathlib.Path(out_dir)
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
payload = {"title": title, "source_url": share_url, "turns": turns}
|
||||
(out / f"{slug}.json").write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
parts = [f"# {title}", "", f"Source: {share_url}", f"Turns: {len(turns)}", ""]
|
||||
for t in turns:
|
||||
label = "Human" if t["role"] == "user" else "Assistant"
|
||||
parts += [f"## {label}", "", t["text"], ""]
|
||||
(out / f"{slug}.md").write_text("\n".join(parts), encoding="utf-8")
|
||||
|
||||
print(f"title: {title}")
|
||||
print(f"turns: {len(turns)}")
|
||||
print(f"json: {out / (slug + '.json')}")
|
||||
print(f"md: {out / (slug + '.md')}")
|
||||
Reference in new issue
Block a user