重开仓库内容:改推五个技能(browser-harness / humanizer / humanizer-zh / product-planning / session-mechanism)

按授权清空原有内容后重新提交(原 oil-ui-pro 一并移出,可从历史恢复)。
browser-harness 剔除 .venv 等运行环境;根 .gitignore 补记 .venv/ 与 node_modules/。
This commit is contained in:
admin committed 2026-10-07 16:06:02 +08:00
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')}")