c456-cli 0.4.0 → 0.5.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/README.md CHANGED
@@ -172,6 +172,12 @@ c456 browser start
172
172
  # 复用该实例截图(默认加载后再等 3s 再截,可用 --wait-after-load 0 取消)
173
173
  c456 screenshot "https://example.com/app" -o ./.tmp/example.png
174
174
 
175
+ # github.com 仓库页:默认隐藏 README 上方的「文件与目录」表格,便于首屏突出说明文档;需要保留表格时加 --keep-github-files-table
176
+ c456 screenshot "https://github.com/owner/repo" -o ./.tmp/repo.png
177
+
178
+ # 调试:截图前后在终端按 Enter,期间保留标签页便于在 DevTools 里看 DOM(需交互式终端)
179
+ c456 screenshot "https://github.com/owner/repo" -o ./.tmp/repo.png --pause
180
+
175
181
  # 用完关闭并释放记录
176
182
  c456 browser stop
177
183
  ```
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command as Command13 } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "c456-cli",
9
- version: "0.4.0",
9
+ version: "0.5.0",
10
10
  description: "C456 CLI - \u5185\u5BB9\u5F55\u5165\u4E0E\u6574\u7406\u5DE5\u5177",
11
11
  type: "module",
12
12
  bin: {
@@ -14,8 +14,6 @@ var package_default = {
14
14
  },
15
15
  files: [
16
16
  "dist",
17
- "docs",
18
- "skills",
19
17
  "README.md"
20
18
  ],
21
19
  scripts: {
@@ -1643,6 +1641,94 @@ function inferScreenshotOutputPath(urlString, cwd = process.cwd()) {
1643
1641
  async function sleep(ms) {
1644
1642
  await new Promise((r) => setTimeout(r, ms));
1645
1643
  }
1644
+ function isGithubComHost(urlString) {
1645
+ let u;
1646
+ try {
1647
+ u = new URL(urlString);
1648
+ } catch {
1649
+ return false;
1650
+ }
1651
+ const h = u.hostname.toLowerCase();
1652
+ return h === "github.com" || h === "www.github.com";
1653
+ }
1654
+ var GITHUB_FILES_TABLE_SELECTOR = 'table[aria-labelledby="folders-and-files"], [aria-labelledby="folders-and-files"]';
1655
+ async function maybeHideGithubFilesTable(page, targetUrl, captureOpts) {
1656
+ if (captureOpts.keepGithubFilesTable) {
1657
+ return;
1658
+ }
1659
+ if (!isGithubComHost(targetUrl)) {
1660
+ return;
1661
+ }
1662
+ try {
1663
+ await page.waitForSelector(GITHUB_FILES_TABLE_SELECTOR, {
1664
+ timeout: 15e3,
1665
+ state: "attached"
1666
+ });
1667
+ } catch {
1668
+ }
1669
+ let hidden = 0;
1670
+ try {
1671
+ hidden = await page.evaluate(() => {
1672
+ const sels = [
1673
+ 'table[aria-labelledby="folders-and-files"]',
1674
+ '[aria-labelledby="folders-and-files"]'
1675
+ ];
1676
+ const seen = /* @__PURE__ */ new Set();
1677
+ let n = 0;
1678
+ for (const sel of sels) {
1679
+ for (const el of document.querySelectorAll(sel)) {
1680
+ if (seen.has(el)) {
1681
+ continue;
1682
+ }
1683
+ seen.add(el);
1684
+ el.style.setProperty("display", "none", "important");
1685
+ n += 1;
1686
+ }
1687
+ }
1688
+ return n;
1689
+ });
1690
+ } catch (e) {
1691
+ console.error(`[c456 screenshot] GitHub \u9690\u85CF\u6587\u4EF6\u8868\u5931\u8D25\uFF1A${e?.message || e}`);
1692
+ return;
1693
+ }
1694
+ if (hidden === 0) {
1695
+ console.error(
1696
+ "[c456 screenshot] \u63D0\u793A\uFF1A\u672A\u627E\u5230\u53EF\u9690\u85CF\u7684\u6587\u4EF6\u8868\u8282\u70B9\uFF08\u9875\u9762\u672A\u542B\u8BE5\u7ED3\u6784\u3001\u4ECD\u5728\u9AA8\u67B6\u3001\u6216 GitHub DOM \u5DF2\u6539\u7248\uFF09\u3002\u53EF\u52A0 --pause \u5728\u6D4F\u89C8\u5668\u91CC\u624B\u52A8\u68C0\u67E5\uFF1B\u4E0D\u9700\u8981\u9690\u85CF\u65F6\u53EF\u7528 --keep-github-files-table\u3002"
1697
+ );
1698
+ }
1699
+ }
1700
+ async function waitForEnterFromStdin(message) {
1701
+ const readline = await import("node:readline");
1702
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1703
+ await new Promise((resolve) => {
1704
+ rl.question(message, () => {
1705
+ rl.close();
1706
+ resolve(void 0);
1707
+ });
1708
+ });
1709
+ }
1710
+ async function captureScreenshotPipeline(page, targetUrl, outPath, captureOpts) {
1711
+ const waitUntil = isGithubComHost(targetUrl) ? "load" : "domcontentloaded";
1712
+ await page.goto(targetUrl, { waitUntil, timeout: 12e4 });
1713
+ if (captureOpts.waitAfterMs > 0) {
1714
+ await sleep(captureOpts.waitAfterMs);
1715
+ }
1716
+ await maybeHideGithubFilesTable(page, targetUrl, captureOpts);
1717
+ if (captureOpts.pause) {
1718
+ await waitForEnterFromStdin(
1719
+ "\uFF08\u8C03\u8BD5\uFF09\u5DF2\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u52A0\u8F7D\u4E0E GitHub \u9690\u85CF\u5904\u7406\uFF1B\u68C0\u67E5\u9875\u9762\u540E\u6309 Enter \u7EE7\u7EED\u622A\u56FE\u2026\n"
1720
+ );
1721
+ }
1722
+ await page.screenshot({
1723
+ path: outPath,
1724
+ fullPage: captureOpts.fullPage
1725
+ });
1726
+ if (captureOpts.pause) {
1727
+ await waitForEnterFromStdin(
1728
+ "\uFF08\u8C03\u8BD5\uFF09\u622A\u56FE\u5DF2\u5199\u5165\uFF1B\u6309 Enter \u5173\u95ED\u672C\u6807\u7B7E\u9875\u5E76\u7ED3\u675F CLI\uFF08\u4E00\u6B21\u6027\u4F1A\u8BDD\u4E0B\u968F\u540E\u4F1A\u9000\u51FA Chrome\uFF09\u2026\n"
1729
+ );
1730
+ }
1731
+ }
1646
1732
  async function captureWithCdp(cdpHttp, targetUrl, outPath, captureOpts) {
1647
1733
  const browser = await chromium.connectOverCDP(cdpHttp);
1648
1734
  try {
@@ -1652,14 +1738,7 @@ async function captureWithCdp(cdpHttp, targetUrl, outPath, captureOpts) {
1652
1738
  }
1653
1739
  const page = await context.newPage();
1654
1740
  try {
1655
- await page.goto(targetUrl, { waitUntil: "domcontentloaded" });
1656
- if (captureOpts.waitAfterMs > 0) {
1657
- await sleep(captureOpts.waitAfterMs);
1658
- }
1659
- await page.screenshot({
1660
- path: outPath,
1661
- fullPage: captureOpts.fullPage
1662
- });
1741
+ await captureScreenshotPipeline(page, targetUrl, outPath, captureOpts);
1663
1742
  } finally {
1664
1743
  await page.close().catch(() => {
1665
1744
  });
@@ -1695,11 +1774,7 @@ async function captureEphemeral(targetUrl, outPath, captureOpts) {
1695
1774
  if (captureOpts.viewport) {
1696
1775
  await page.setViewportSize(captureOpts.viewport);
1697
1776
  }
1698
- await page.goto(targetUrl, { waitUntil: "domcontentloaded" });
1699
- if (captureOpts.waitAfterMs > 0) {
1700
- await sleep(captureOpts.waitAfterMs);
1701
- }
1702
- await page.screenshot({ path: outPath, fullPage: captureOpts.fullPage });
1777
+ await captureScreenshotPipeline(page, targetUrl, outPath, captureOpts);
1703
1778
  await page.close().catch(() => {
1704
1779
  });
1705
1780
  } catch (e) {
@@ -1739,6 +1814,14 @@ var screenshotCmd = new Command10("screenshot").name("screenshot").description("
1739
1814
  ).option(
1740
1815
  "--no-reuse",
1741
1816
  "\u4E0D\u590D\u7528 browser start \u7684\u5B9E\u4F8B\uFF0C\u59CB\u7EC8\u5355\u72EC\u8D77 Chrome \u5E76\u5728\u7ED3\u675F\u540E\u5173\u95ED\u3001\u5220\u9664\u4E34\u65F6 profile"
1817
+ ).option(
1818
+ "--keep-github-files-table",
1819
+ "github.com \u4E0A\u4FDD\u7559\u300C\u6587\u4EF6\u4E0E\u76EE\u5F55\u300D\u8868\u683C\uFF08\u9ED8\u8BA4\u4F1A\u9690\u85CF\u8BE5\u8868\u4EE5\u4FBF\u622A\u56FE\u7A81\u51FA README\uFF09",
1820
+ false
1821
+ ).option(
1822
+ "--pause",
1823
+ "\u8C03\u8BD5\uFF1A\u622A\u56FE\u524D\u540E\u5728\u7EC8\u7AEF\u6309 Enter \u518D\u7EE7\u7EED\uFF1B\u671F\u95F4\u4E0D\u5173\u95ED\u6807\u7B7E\u9875\uFF0C\u4FBF\u4E8E\u5728\u6D4F\u89C8\u5668\u91CC\u68C0\u67E5 DOM\uFF08\u9700\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\uFF09",
1824
+ false
1742
1825
  ).action(async (urlArg, opts) => {
1743
1826
  try {
1744
1827
  const targetUrl = assertHttpUrl(urlArg);
@@ -1748,14 +1831,28 @@ var screenshotCmd = new Command10("screenshot").name("screenshot").description("
1748
1831
  const viewport = parseViewport(opts.viewport);
1749
1832
  const fullPage = Boolean(opts.fullPage);
1750
1833
  const reuse = opts.reuse !== false;
1751
- const captureOpts = { fullPage, waitAfterMs: wait, viewport };
1834
+ const keepGithubFilesTable = Boolean(opts.keepGithubFilesTable);
1835
+ const pause = Boolean(opts.pause);
1836
+ if (pause && !process.stdin.isTTY) {
1837
+ console.error("\u9519\u8BEF\uFF1A--pause \u4EC5\u5728\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\uFF08stdin \u4E3A TTY\uFF09\u4E0B\u53EF\u7528");
1838
+ process.exit(1);
1839
+ }
1840
+ const captureOpts = {
1841
+ fullPage,
1842
+ waitAfterMs: wait,
1843
+ viewport,
1844
+ keepGithubFilesTable,
1845
+ pause
1846
+ };
1752
1847
  if (reuse) {
1753
1848
  const daemon = await loadReconciledDaemonState();
1754
1849
  if (daemon) {
1755
1850
  await captureWithCdp(daemon.cdpHttp, targetUrl, outPath, {
1756
1851
  fullPage,
1757
1852
  waitAfterMs: wait,
1758
- viewport: null
1853
+ viewport: null,
1854
+ keepGithubFilesTable,
1855
+ pause
1759
1856
  });
1760
1857
  console.log(`\u2705 \u5DF2\u622A\u56FE\uFF08\u590D\u7528 CDP ${daemon.cdpHttp}\uFF09\u2192 ${outPath}`);
1761
1858
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c456-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "C456 CLI - 内容录入与整理工具",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,8 +8,6 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
- "docs",
12
- "skills",
13
11
  "README.md"
14
12
  ],
15
13
  "scripts": {
@@ -1,134 +0,0 @@
1
- # 私人知识库:用 AI 客户端使用 C456
2
-
3
- 下文前几节先把 **c456.com 账号**、本机 `**c456` 命令**、以及两个 **Agent 技能**装好,让 **Cursor / OpenCode / Trae / Claude Code** 里的 AI 能按规范代你操作 C456 与本地 Wiki;第 4 节是**第一次**在本文件夹当知识库时的初始化口令。
4
-
5
- **日常用法**:在客户端里**用对话说明意图**即可;**第一次**把某个文件夹当 C456 知识库用时,须先按第 4 节用口令初始化目录。需要访问 c456.com 时,由 Agent 在本机调用已安装的 `c456`(你不必自己背命令)。**要和线上对齐时,用下面两句口令之一即可**(可按需补充范围,例如「只同步刚改的那几页」)。想深究目录与同步规则,再点开文末技能链接。
6
-
7
- ---
8
-
9
- ## 1. 账号(必须)
10
-
11
- 1. 打开 [https://c456.com](https://c456.com) 注册并登录。
12
- 2. 在站内创建 **API Key / 访问令牌**,复制保存(一般只显示一次)。
13
-
14
- ---
15
-
16
- ## 2. 安装命令行(全局执行即可)
17
-
18
- ```bash
19
- npm install -g c456-cli
20
- ```
21
-
22
- 写入密钥(把 `<密钥>` 换成上一步复制的):
23
-
24
- ```bash
25
- c456 config set-key <密钥>
26
- ```
27
-
28
- 检查:
29
-
30
- ```bash
31
- c456 config show
32
- c456 --help
33
- ```
34
-
35
- ---
36
-
37
- ## 3. 安装技能(在「知识库项目」文件夹里执行)
38
-
39
- 先进入你要当知识库根目录的文件夹(没有就 `mkdir` 一个再 `cd` 进去):
40
-
41
- ```bash
42
- cd /path/to/your-kb-project
43
- ```
44
-
45
- 需要本机能跑 `**npx**`,会用到 [Skills CLI](https://skills.sh/)。私人知识库路径须具备三类能力,对应 **三个技能**(缺一不可):
46
-
47
-
48
- | 技能 | 作用 |
49
- | ----------------- | ----------------------------------------------------------------------------------------------------------------------- |
50
- | **karpathy-wiki** | 本地 `raw/`、`wiki/` 等目录与编辑约定([Karpathy Wiki](https://github.com/baklib-tools/skills/tree/main/skills/karpathy-wiki)) |
51
- | **c456-llm-wiki** | 与 C456 双向同步、`c456-sync/` 等([c456-llm-wiki](https://github.com/xiaohui-zhangxh/c456-cli/tree/main/skills/c456-llm-wiki)) |
52
- | **c456-cli** | 终端 `c456` 与 HTTP API 的 Agent 说明([c456-cli 技能](https://github.com/xiaohui-zhangxh/c456-cli/tree/main/skills/c456-cli)) |
53
-
54
-
55
- ```bash
56
- c456 skill install --with-wiki
57
- ```
58
-
59
- ## 4. 首次:初始化当前目录(第一次把本文件夹当 C456 知识库时)
60
-
61
- 技能装好后,**第一次**在本机用某个文件夹承载 C456 知识库时,需要先让 Agent 按 **c456-llm-wiki** 把目录结构初始化好(例如对齐 `raw/`、`wiki/`、`c456-sync/` 等约定,具体以技能正文为准)。
62
-
63
- 做法:
64
-
65
- 1. 用 **Cursor / OpenCode / Trae / Claude Code** 打开你的知识库项目根目录(与上一步 `cd` 的文件夹一致)。
66
- 2. 在 AI 对话里直接说下面这句(可照抄):
67
-
68
- ```
69
- 基于 c456-llm-wiki 初始化当前目录
70
- ```
71
-
72
- Agent 会按技能在该知识库项目根目录下创建或补齐所需目录与约定;完成后再按第 5 节日常放 `raw/`、对话操作即可。若该目录**早已**按技能初始化过,可跳过本节。
73
-
74
- ---
75
-
76
- ## 5. 装好后怎么用
77
-
78
- 1. 用 **Cursor / OpenCode / Trae / Claude Code** 打开你的知识库项目文件夹(若尚未初始化,先完成第 4 节)。
79
- 2. 把资料放进 `**raw/`**(路径按技能约定即可)。
80
- 3. 在对话里直接说你要做什么(见下一节**可复制示例**)。Agent 会按 **karpathy-wiki**、**c456-llm-wiki** 与 **c456-cli** 三个技能,在本机执行 `c456`、维护 `wiki/` 与 `c456-sync/` 等;你主要负责说清楚意图与确认结果。
81
- 4. **需要同步内容时**,对 AI 说下面之一即可(字可以照抄):
82
- - **把本地推到线上**:「**同步内容到 C456。**」
83
- - **把线上拉回本地**:「**从 C456 同步内容。**」
84
-
85
- 若你坚持自己敲终端命令,见 [c456-cli README](https://github.com/xiaohui-zhangxh/c456-cli#常用命令)。
86
-
87
- ---
88
-
89
- ## 6. 使用例子(复制到 AI 对话里)
90
-
91
- 以下整段复制到客户端输入框即可(把路径或网址换成你的)。
92
-
93
- ### 例子 1:收录 `raw/` 里的内容
94
-
95
- ```
96
- 整理 raw/articles/我的笔记.md
97
- ```
98
-
99
- ### 例子 2:收录 baklib.com 这个产品
100
-
101
- ```
102
- 收录 Baklib 这个产品,同步到 C456。
103
- ```
104
-
105
- ### 例子 3:记下对 fly.io 界面设计的喜欢
106
-
107
- ```
108
- https://fly.io/ 的界面设计我很喜欢,请记下来:写成一条以后做产品可以借鉴的笔记,更新 wiki;同步内容到 C456。
109
- ```
110
-
111
- ### 例子 4:只做同步(不说别的)
112
-
113
- 把本地已整理好的内容推到线上:
114
-
115
- ```
116
- 同步内容到 C456。
117
- ```
118
-
119
- 把线上最新状态拉回本地镜像与 wiki:
120
-
121
- ```
122
- 从 C456 同步内容。
123
- ```
124
-
125
- ---
126
-
127
- ## 7. 链接(想细看再点开)
128
-
129
- - [c456.com](https://c456.com)
130
- - [c456-cli 仓库与 README](https://github.com/xiaohui-zhangxh/c456-cli)
131
- - [Karpathy Wiki 技能(目录与约定)](https://github.com/baklib-tools/skills/tree/main/skills/karpathy-wiki)
132
- - [c456-llm-wiki 技能(与 C456 双向同步)](https://github.com/xiaohui-zhangxh/c456-cli/tree/main/skills/c456-llm-wiki)
133
-
134
- 上游安装命令若变更,以对应仓库页面为准。
@@ -1,154 +0,0 @@
1
- ---
2
- name: c456-cli
3
- description: >-
4
- Operates C456 via the c456 Node CLI (HTTP API v1): intakes, playbooks,
5
- assets (media library), search, fetch, and config. Includes headed Chrome via CDP (browser start /
6
- screenshot, system Chrome + playwright-core, no bundled Chromium required) for tool/channel intro
7
- screenshots, then asset upload. Use when the user mentions C456, c456-cli, 收录, 打法, intake,
8
- playbook, c456.com, or syncing content with a self-hosted C456. Skill install delegates to npx skills add
9
- from GitHub only (no local package fallback) plus optional daily npm version notify on next launch.
10
- ---
11
-
12
- # C456 CLI(c456-cli)
13
-
14
- 在终端通过 **`c456`** 调用 C456 的 **HTTP API v1**,供 Agent 将内容写入/查询 C456,而无需在对话中手写原始 REST 细节。
15
-
16
- ## 安装 CLI
17
-
18
- 未安装时可用 **`npx c456-cli …`** 或 **`bunx c456-cli …`**;已全局安装则直接 **`c456`**。
19
-
20
- ## 安装本技能(给其他仓库)
21
-
22
- **推荐**:在目标项目根目录执行(内部调用官方 **`npx skills add`**,需本机可执行 `npx`):
23
-
24
- ```bash
25
- c456 skill install
26
- ```
27
-
28
- 私人知识库场景可**一条命令**装齐 **karpathy-wiki**、**c456-llm-wiki** 与 **c456-cli**:
29
-
30
- ```bash
31
- c456 skill install --with-wiki
32
- ```
33
-
34
- 上述流程均为 **`npx skills add` 从网络拉取**(先 `baklib-tools/skills` 的 **karpathy-wiki**,再 GitHub 源的 **c456-llm-wiki** 与 **c456-cli**)。详见 [`docs/private-knowledge-base.md`](../../docs/private-knowledge-base.md) §3。
35
-
36
- 仅装 **c456-cli**、不要 Wiki 套件时:
37
-
38
- ```bash
39
- c456 skill install
40
- ```
41
-
42
- 顺序为:仅依次尝试 **`c456-cli`** 的 GitHub 源;**失败则退出**(无本地包复制)。可用 **`-C/--cwd`**、**`-g`**、**`-a`**、**`--copy`**,语义与 `skills add` 一致(`-a` 用于指定 Agent,如 `cursor`、`claude-code` 等)。
43
-
44
- 也可自行执行:
45
-
46
- ```bash
47
- npx skills add xiaohui-zhangxh/c456-cli --skill c456-cli -y
48
- ```
49
-
50
- 若已克隆 [c456-cli](https://github.com/xiaohui-zhangxh/c456-cli) 仓库,可在该仓库根目录:
51
-
52
- ```bash
53
- npx skills add . --skill c456-cli -y
54
- ```
55
-
56
- 列出远程包内可用技能而不安装:`npx skills add xiaohui-zhangxh/c456-cli -l`
57
-
58
- ## 鉴权与站点
59
-
60
- | 方式 | 说明 |
61
- | --- | --- |
62
- | **API Key** | `c456 config set-key <token>`(默认写入自 cwd 解析的 **`.c456-cli/config.json`**;全局用户配置加 **`-g`**)或 **`C456_API_KEY`** |
63
- | **站点根 URL** | 默认 `https://c456.com`;自托管用 **`c456 config set-url <url>`**(同上 **`-g`**)、**`C456_URL`**,或单次 **`c456 -B <url> …`**。有效配置为 **全局 + 项目合并**(项目覆盖);工作区由自 cwd 向上的 **`.c456-cli`** 或 **`C456_WORKSPACE`** 决定 |
64
-
65
- **短选项冲突**:子命令里的 **`-k` 表示收录类型(kind)**,**不要**用 `-k` 传 API Key。Key 仅通过 `config` / `C456_API_KEY`。
66
-
67
- **`-B` 与 `-u`**:根级 **`-B` / `--base-url`** 表示 **C456 站点根地址**;`intake` 等子命令里的 **`-u` 常表示「目标资源 URL」**(如 tool/channel 的链接),不要混用。
68
-
69
- ## Agent 执行方式
70
-
71
- 1. 需要真实读写 C456 时,在沙箱/终端中运行 `c456` 子命令,并解析其标准输出(含部分命令附带的 `--- JSON ---` 段)。
72
- 2. 非交互场景为 `intake delete` 等加 **`-f` / `--force`**,避免等待终端确认(删除前仍应确认用户意图)。
73
- 3. 勿在日志或回复中回显完整 API Key。
74
- 4. **严禁编造参数**:只能使用 `c456 <command> --help`(或本仓库源码/文档)明确存在的选项;不确定时先运行 `--help` 再行动。
75
- 5. **严禁重复创建**:若 `intake new` / `playbook new` 输出了 `ID:` 或 `--- JSON ---`(含 `id`),视为已成功创建,后续只能 `show <id>` / `update <id>`,不得再次 `new` 重试(避免重复发布两条)。
76
- 6. **内容一律用文件传入**:创建/更新正文等长文本时,不要在命令行直接写内容(避免引号/换行/转义错误)。必须把内容写到**当前工作目录**的 `.tmp/` 下临时文件,再用 `--body-file` / `--summary-file` 传入。
77
- 7. **自媒体账号默认收录为渠道**:用户要收录 **YouTube / 抖音 / 小红书 / B 站 / 微博** 等**自媒体账号主页或频道**时,**默认使用 `c456 intake new -k channel`**(不要用 `-k tool`),并配合 `-u <主页或频道 URL>`;需要服务端按 URL 自动填资料段时再加 `--auto-resolve-url`。仅做「不落库的 URL 资料预览/抓取」时用 `c456 fetch profile -p social_account -u "<url>"`。
78
- 8. **渠道(及 tool)必须带至少一条「资料」**:`-k channel` 或 `-k tool` 时,服务端要求 **profile_data 里至少有一条资料段**(例如主页 **URL**、**媒体账号** 等对应 facet),常见做法是 `-u <url>` 并加 **`--auto-resolve-url`** 让服务端生成资料段;如需手写 **`--profile-data-json`**,**必须先阅读** [references/intake-profile-data-json.md](references/intake-profile-data-json.md)(含各 `profile_id`、必填字段与最小 JSON 示例)。**不能只写标题/正文而不提供 URL/资料段**,否则会 **422 校验失败**(提示含「至少添加一个资料段或图标」等)。
79
- 9. **素材库与列表图标**:上传、插入正文、设置 tool/channel 列表图标(`list_icon_url`)见 [references/media-library-and-icons.md](references/media-library-and-icons.md);CLI:`c456 asset …`、`c456 intake update … --profile-data-json-file`。
80
- 10. **工具 / 渠道介绍里的产品截图**:优先 **`c456 browser start`**(持久 profile:`~/.cache/c456-cli/chrome-profile`,可保留登录态)→ 需要时在窗口内登录 → **`c456 screenshot <url> [-o .tmp/…]`** 复用 CDP;结束用 **`c456 browser stop`**。无长会话时可只跑 **`c456 screenshot <url>`**(可省略 **`-o`**,在当前目录按 URL 生成文件名)。然后 **`c456 asset upload`** → **`markdownSnippet`** 写入 **`--body-file`**。详见 [references/product-screenshots-for-intake.md](references/product-screenshots-for-intake.md)(**不用** IDE MCP;不强制安装 Playwright 自带 Chromium,见 README)。
81
-
82
- ## 命令速查
83
-
84
- **配置**
85
-
86
- - `c456 config set-key <token> [-g]` / `set-url <url> [-g]` / `show [-g]` / `reset [-g] [-f]`(`-g` = 仅全局 `~/.config/c456`;默认 = 项目 `.c456-cli`)
87
-
88
- **技能 `skill`**
89
-
90
- - `c456 skill install [--with-wiki] [-C <cwd>] [-g] [-a <agent>] [--copy]`(仅 `npx skills add`;`--with-wiki` 时装 karpathy-wiki、c456-llm-wiki 与 c456-cli,见 docs/private-knowledge-base.md §3)
91
-
92
- **浏览器(系统 Chrome + CDP)**
93
-
94
- - `c456 browser start [-p 端口]` · `stop` · `status`(持久 profile 默认 `~/.cache/c456-cli/chrome-profile`)
95
- - `c456 screenshot <url> [-o <path>] [--full-page] [--viewport 1280x720] [--wait-after-load ms] [--no-reuse]`(默认 **`--wait-after-load 3000`** 便于 JS/动画;`0` 为不等待;省略 `-o` 时按 URL 生成文件名;默认复用 `browser start`)
96
-
97
- **收录 `intake`**
98
-
99
- - 新建:`c456 intake new [-k signal|tool|channel] [-u <url>] [--auto-resolve-url] [--profile-data-json '<json>'] [-t 标题] [--body-file <path>]`(`profile_data` 结构见 [references/intake-profile-data-json.md](references/intake-profile-data-json.md);长 JSON 建议写入 `.tmp/` 再用 `"$(cat .tmp/profile.json)"` 传入)
100
- - 查看 / 更新 / 删除 / 列表:`c456 intake show <id>` · `c456 intake update <id> …`(支持 **`--profile-data-json`** / **`--profile-data-json-file`** 合并更新 tool/channel 的 `profile_data` 或仅 `list_icon_url`) · `c456 intake delete <id> [-f]` · `c456 intake list [-k] [-q] [-p 页] [-n 每页]`
101
-
102
- `--auto-resolve-url` 说明:
103
-
104
- - **默认不解析**:`-u/--url` 只保存为 URL 输入;服务端不会默认生成资料段。
105
- - **显式开启才解析**:当 `-k tool|channel` 且传入 `--auto-resolve-url` 时,服务端会尝试检测平台并回填 `profile_data`(可能导致校验失败/成功与否不同;会发起网络请求)。
106
-
107
- **搜索 `search`**
108
-
109
- - `c456 search signals -q "…" [-k kind] [-l n]`
110
- - `c456 search playbooks -q "…" [-l n]`
111
-
112
- **打法 `playbook`**
113
-
114
- - 新建:`c456 playbook new -t "标题" [--body-file <path>] [--ref-intake id …] [--ref-playbook id …]`
115
- - 另有 `show` / `list` / `update` / `delete`(与 `c456 playbook --help` 一致)
116
-
117
- **素材库 `asset`**
118
-
119
- - `c456 asset upload -f <path>` · `list` · `show <id>` · `update <id> --filename <名>` · `delete <id>` · `refresh-markdown` · `fingerprint`(插图与图标流程见 [references/media-library-and-icons.md](references/media-library-and-icons.md))
120
-
121
- **资料 `fetch`**
122
-
123
- - `c456 fetch profile -u <url> -p <profile_id>`(`profile_id` 必填;否则 API 返回「不支持的资料类型」)
124
-
125
- `profile_id` 类型含义:
126
-
127
- - `link_product`:产品/官网等普通链接页(解析 name/icon/description)
128
- - `package_registry`:软件包页(npm、RubyGems 等)
129
- - `github_origin`:代码仓库(GitHub/GitLab/Gitee)
130
- - `social_account`:社交账号主页/频道(YouTube/抖音/小红书等)
131
-
132
- ## 更完整的说明
133
-
134
- 见各命令的 `--help` 与本仓库 `README.md`、`DEVELOPMENT.md`。
135
-
136
- ### 分页参数(list 类命令)
137
-
138
- - `-p, --page`: **1-10000**
139
- - `-n, --per-page`: **1-100**(默认 20;服务端会截断到最大值)
140
-
141
- ### 内容语法(富文本)
142
-
143
- CLI `--help` 中会用 `type: <type_name>` 标注字段类型;Agent 在生成/写入内容时,必须按下表选择语法与约束:
144
-
145
- - `markdown_kramdown` → [references/content-syntax-kramdown.md](references/content-syntax-kramdown.md)(与 `SKILL.md` 同级目录下,随 `npx skills add` 一并安装)
146
-
147
- ### 收录最佳实践
148
-
149
- - **自媒体 / 社交账号**(主页、频道页):**一律按渠道收录** → `-k channel`;抖音场景补充说明见 [references/douyin-channel-intake.md](references/douyin-channel-intake.md)。
150
- - **渠道 / 工具**:新建时务必带上 **至少一种结构化资料**(常见:`-u` + `--auto-resolve-url`,或 `--profile-data-json`),否则服务端会因缺少资料段而拒绝保存。
151
- - **`--profile-data-json`**:键名与校验规则与 Web 端一致,**不要编造字段**;完整说明与示例见 [references/intake-profile-data-json.md](references/intake-profile-data-json.md)(优先自动解析,其次再手写)。**仅改列表图标**见 [references/media-library-and-icons.md](references/media-library-and-icons.md)。
152
- - **软件 / 产品 / 仓库 / 包页**:一般用 `-k tool`(或用户明确要当「工具资料」收录时)。
153
- - **产品界面进介绍**:优先 **`c456 browser` + `c456 screenshot`**(见 [references/product-screenshots-for-intake.md](references/product-screenshots-for-intake.md))→ `asset upload` → `body`(`--body-file`);**不用** IDE MCP。
154
-
@@ -1,80 +0,0 @@
1
- ## 内容语法(Kramdown Markdown)
2
-
3
- 本文件定义:通过 **c456 HTTP API v1** 写入的富文本字段(例如 `body`)应使用的**内容语法**。
4
-
5
- ### 总原则
6
-
7
- - **一律使用 Markdown 字符串**作为富文本内容(例如 `body`)。
8
- - **基线语法**:Kramdown 风格 Markdown(与 c456 Web 端编辑器/展示端实现对齐)。
9
- - **不要依赖任意 HTML** 来实现排版或交互;展示端会做 HTML 净化(sanitize),未在白名单内的标签/属性可能被剥离或失效。
10
-
11
- ### 支持的 Markdown 能力(白名单)
12
-
13
- #### 1) GFM(GitHub Flavored Markdown)常见子集
14
-
15
- 我们支持与 `remark-gfm` / 编辑器常见能力对齐的子集,包括:
16
-
17
- - 标题:`#` ~ `######`
18
- - 段落、换行
19
- - 强调:`**bold**`、`*italic*`
20
- - 删除线:`~~text~~`
21
- - 行内代码与代码块:反引号与 fenced code(```)
22
- - 引用:`>`
23
- - 列表:有序/无序
24
- - 链接/图片:`[label](url)`、`![alt](url)`
25
- - 表格:GFM table
26
- - 任务列表:`- [ ]` / `- [x]`
27
- - 裸链:
28
- - `https://...` 纯文本链接会被识别为可点击链接
29
- - `<https://...>` 形式也可用
30
-
31
- #### 2) Kramdown 块级 IAL(Inline Attribute List)
32
-
33
- 支持 **kramdown 风格块级 IAL**:把属性写在“紧跟某个块之后”的单独一行里,用于对齐与图片尺寸。
34
-
35
- - 语法:`{: align=center width=120 height=120}`
36
- - 关键约束:`{` 与 `:` 之间 **不得有空格**(必须是 `{: ...}`)
37
- - 绑定规则:IAL 行绑定到**上一段/上一标题**
38
- - 支持的 key:
39
- - `align`:`left | center | right | justify`
40
- - `width` / `height`:数字字符串(可带 `px`)
41
-
42
- 示例:
43
-
44
- ```markdown
45
- 这一段将居中。
46
- {: align=center}
47
-
48
- ![logo](https://example.com/logo.png)
49
- {: width=120 height=120}
50
- ```
51
-
52
- #### 3) 扩展:`:::walkthrough{...}` 指令块
53
-
54
- 支持用容器指令在 Markdown 中嵌入 Walkthrough 播放器块:
55
-
56
- ```markdown
57
- :::walkthrough{id=42}
58
- :::
59
-
60
- :::walkthrough{url="https://asciinema.org/a/abc123"}
61
- :::
62
-
63
- :::walkthrough{cast-src="https://example.com/x.cast" title="演示" cols=100 rows=28}
64
- :::
65
- ```
66
-
67
- 属性白名单(其余键会被丢弃,不会注入到 DOM):
68
-
69
- - `id`:数字(仅标记,渲染时不查库)
70
- - `url`:仅允许 `https://asciinema.org/a/...`
71
- - `cast-src`:已解析的 `.cast` URL(`https?://...`)
72
- - `title` / `summary`:展示文案(会被截断)
73
- - `cols` / `rows`:终端尺寸(1~4 位数字)
74
-
75
- ### AI/脚本写入约定
76
-
77
- - 当字段语义为“富文本/正文/说明”(如 `body`),默认输出 **Markdown(Kramdown + 白名单扩展)**。
78
- - 不要输出 HTML 作为正文内容(除非是 `:::walkthrough` 这类受控扩展语法的序列化结果)。
79
- - 需要对齐/尺寸时,优先用 `{: ...}` 块级 IAL,不要用内联 `style`。
80
-
@@ -1,65 +0,0 @@
1
- # 社交账号(抖音)收录为渠道的最佳路径
2
-
3
- ## 路径概述
4
-
5
- 将社交账号(以抖音为例)收录为 `channel` 类型的标准流程。
6
-
7
- ## 步骤
8
-
9
- ### 1. 获取准确的账号主页链接
10
-
11
- 确保拿到的是 **用户主页 URL**,而非单个视频链接。例如:
12
-
13
- ```
14
- https://www.douyin.com/user/MS4wLjABAAAAjb1juHnK9tygA0nuoGgSEMW7ZuJzXNnTMx9XwaQh19k
15
- ```
16
-
17
- ### 2. 执行创建命令
18
-
19
- ```bash
20
- c456 intake new \
21
- -k channel \
22
- -u "<抖音主页链接>" \
23
- --auto-resolve-url \
24
- -t "<账号昵称>抖音账号"
25
- ```
26
-
27
- **参数要点:**
28
-
29
- | 参数 | 作用 | 建议 |
30
- |---|---|---|
31
- | `-k channel` | 指定收录类型为「渠道」 | 社交账号统一用 `channel` |
32
- | `-u` | 目标资源 URL | 填完整主页链接 |
33
- | `--auto-resolve-url` | 服务端自动解析账号资料 | **必加**,自动抓取昵称、头像、粉丝数等 |
34
- | `-t` | 标题 | 建议用「昵称 + 平台」格式 |
35
-
36
- ### 3. 确认解析结果
37
-
38
- ```bash
39
- c456 intake show <ID>
40
- ```
41
-
42
- 开启 `--auto-resolve-url` 后,服务端会自动回填 `profile_data`,通常包含:
43
-
44
- - 昵称、头像、抖音号
45
- - 粉丝数、获赞数、作品数、关注数
46
- - 个人简介 / 签名
47
- - 其他平台信息(如 IP 属地、直播时间等)
48
-
49
- ### 4. 后续补充正文(按需)
50
-
51
- 如需补充分析内容、打法或观察笔记:
52
-
53
- ```bash
54
- c456 intake update <ID> --body-file /path/to/content.md
55
- ```
56
-
57
- 正文内容较长时,请遵循当前技能「内容一律用文件传入」的规范,写入当前工作目录的 `.tmp/` 下临时文件后通过 `--body-file` 传入。
58
-
59
- ## 最佳实践要点
60
-
61
- 1. **类型选 `channel`** — 抖音/小红书/B站/YouTube 等社交账号统一用渠道类型收纳。
62
- 2. **必加 `--auto-resolve-url`** — 省去手动填写资料的麻烦,且数据更准确。
63
- 3. **标题清晰标识平台** — 如「胡说老王抖音账号」,方便检索和区分多平台矩阵账号。
64
- 4. **正文按需后补** — 先用自动解析拿到基础资料,再视需要补充分析或打法。
65
- 5. **避免重复创建** — 若 `intake new` 已返回 `ID:` 或 JSON 中的 `id`,视为成功,后续只能 `update`,禁止再次 `new`。
@@ -1,164 +0,0 @@
1
- # `profile_data` / `--profile-data-json`(收录 tool / channel)
2
-
3
- 服务端模型与校验见 C456 仓库 `IntakeProfileRegistry`、`Intake`(`profile_data` 为 JSON)。CLI 创建/更新收录时通过 `**--profile-data-json '<json>'**` 传入**单个 JSON 字符串**(注意 shell 引号;长 JSON 建议写入 `.tmp/*.json` 后再用 `"$(cat .tmp/xxx.json)"` 传入)。
4
-
5
- **仅改列表图标、不手改整段 facets**:`PATCH /api/v1/intakes/:id` 会与已有 `profile_data` **按键合并**;可只传 `{ "list_icon_url": "<url>" }` 等(详见 [media-library-and-icons.md](media-library-and-icons.md))。CLI:`c456 intake update <id> --profile-data-json-file .tmp/patch.json`。
6
-
7
- ## 顶层结构
8
-
9
- ```json
10
- {
11
- "facets": [
12
- {
13
- "profile_id": "<见下表>",
14
- "facet_id": "<可选,字符串;不传则由服务端视为空>",
15
- "data": { }
16
- }
17
- ],
18
- "primary_profile_id": "<与某条 facet 的 profile_id 一致,建议必填>"
19
- }
20
- ```
21
-
22
- - `**facets**`:至少 **1** 条、最多 **5** 条(服务端 `MAX_FACETS = 5`)。
23
- - `**profile_id`**:必须为下列枚举之一;且必须与 `**kind`(tool/channel)** 匹配(不匹配会报「该资料类型不能用于当前分区」)。
24
- - **手动录入**:部分 profile 使用 `**_entry_mode`**:`"manual"` 表示手填;省略或为其它值时按 `**resolve`(链接解析路径)** 规则校验(见各小节)。
25
-
26
- ---
27
-
28
- ## `profile_id` 与适用 `kind`
29
-
30
-
31
- | profile_id | `tool` | `channel` | 说明 |
32
- | ------------------ | ------ | --------- | -------------------------- |
33
- | `link_product` | ✅ | ✅ | 官网 / 产品页 |
34
- | `package_registry` | ✅ | ❌ | npm / RubyGems 等包页 |
35
- | `github_origin` | ✅ | ✅ | GitHub / GitLab / Gitee 仓库 |
36
- | `social_account` | ❌ | ✅ | 自媒体 / 社交平台账号主页 |
37
- | `saas_commercial` | ✅ | ✅ | 定价 / 商业信息 |
38
-
39
-
40
- ---
41
-
42
- ## 推荐顺序(Agent)
43
-
44
- 1. **优先** `c456 intake new -k channel|tool -u "<url>" --auto-resolve-url`(由服务端检测平台并合并 `profile_data`,避免手搓 JSON)。
45
- 2. 仅当不能自动解析或需补 second facet 时,再构造 `**--profile-data-json`**(或先 `c456 fetch profile -p <profile_id> -u "<url>"` 查看解析结果结构,再按需填入 `data`)。
46
-
47
- ---
48
-
49
- ## 示例:`social_account`(渠道自媒体,最常见)
50
-
51
- ### A. 解析路径(等价于「先填账号页 URL 再解析」)
52
-
53
- `data` 内需能通过校验:`**account_url` 非空**,且 `**platform` 非空**(一般由解析写入;若纯手填且无解析,请改用 B「手动录入」)。
54
-
55
- ```json
56
- {
57
- "facets": [
58
- {
59
- "profile_id": "social_account",
60
- "data": {
61
- "account_url": "https://www.youtube.com/@example",
62
- "platform": "YouTube",
63
- "nickname": "示例频道",
64
- "handle": "@example"
65
- }
66
- }
67
- ],
68
- "primary_profile_id": "social_account"
69
- }
70
- ```
71
-
72
- ### B. 手动录入(`_entry_mode`: `manual`)
73
-
74
- 校验要求(节选):**选择平台或自定义平台**、**平台展示名**,且 `**nickname` / `handle` / `account_url` 至少填一项**。
75
-
76
- ```json
77
- {
78
- "facets": [
79
- {
80
- "profile_id": "social_account",
81
- "data": {
82
- "_entry_mode": "manual",
83
- "_dict_key": "p_system_youtube",
84
- "platform": "YouTube",
85
- "nickname": "示例频道",
86
- "handle": "@example",
87
- "account_url": ""
88
- }
89
- }
90
- ],
91
- "primary_profile_id": "social_account"
92
- }
93
- ```
94
-
95
- `_dict_key` 为词典项 key(可通过 `GET /api/v1/dictionary/items?category=social_platform` 查找);若无对应项,可使用 `**_custom_platform_label**`(与 `_dict_key` 二选一逻辑见服务端 `validate_facet_error`)。
96
-
97
- ---
98
-
99
- ## 示例:`link_product`(官网 / 产品)
100
-
101
- 必填字段(`required: true`):`**url**`、`**name**`。
102
-
103
- ```json
104
- {
105
- "facets": [
106
- {
107
- "profile_id": "link_product",
108
- "data": {
109
- "url": "https://example.com/product",
110
- "name": "示例产品",
111
- "description": "可选简介"
112
- }
113
- }
114
- ],
115
- "primary_profile_id": "link_product"
116
- }
117
- ```
118
-
119
- ---
120
-
121
- ## 示例:`github_origin`(开源仓库)
122
-
123
- - **解析路径**:需 `**repo_url`**,且解析得到的 `**full_name**` 非空(通常由解析写入)。
124
- - **手动录入**:`_entry_mode` 为 `manual` 时,需 `**_dict_key`**(托管方)与 `**full_name**`。
125
-
126
- ```json
127
- {
128
- "facets": [
129
- {
130
- "profile_id": "github_origin",
131
- "data": {
132
- "_entry_mode": "manual",
133
- "_dict_key": "p_system_github",
134
- "full_name": "org/repo",
135
- "repo_url": ""
136
- }
137
- }
138
- ],
139
- "primary_profile_id": "github_origin"
140
- }
141
- ```
142
-
143
- ---
144
-
145
- ## 示例:`package_registry`(仅 tool)
146
-
147
- 手动手填模式需 `**_dict_key**` 与 `**name**`;解析路径需能解析出 `**name**`。细节以服务端 `validate_facet_error` 为准。
148
-
149
- ---
150
-
151
- ## 示例:`saas_commercial`(定价补充段)
152
-
153
- 无 `required: true` 的字段,但整段数据需满足 `coerce_facet_data`;可与其它 facet 并存。
154
-
155
- ---
156
-
157
- ## 常见错误(422)
158
-
159
- - **「请至少添加一个资料段或图标」**:`facets` 为空且未提供图标相关字段(如顶栏 `list_icon_url`)。
160
- - **「资料类型无效」**:`profile_id` 拼写错误。
161
- - **「该资料类型不能用于当前分区」**:如对 `channel` 使用 `package_registry`。
162
- - **社交账号 / 链接产品等**:见各 profile 下 `**validate_facet_error`** 返回的中文提示。
163
-
164
- 若不确定字段键名,以 C456 仓库 `app/services/intake_profile_registry.rb` 中对应 profile 的 `field_order` / `fields` 为准。
@@ -1,48 +0,0 @@
1
- # 素材库(Asset)与工具/渠道列表图标(Agent 操作说明)
2
-
3
- 与 HTTP API v1 及 CLI 行为以 C456 主仓 [`docs/20-engineering/specs/api-v1.md`](https://github.com/xiaohui-zhangxh/c456/blob/main/docs/20-engineering/specs/api-v1.md) 为准。
4
-
5
- ## 素材 CRUD(CLI)
6
-
7
- | 操作 | 命令 |
8
- | --- | --- |
9
- | 上传 | `c456 asset upload -f <本地图片路径>` |
10
- | 列表 | `c456 asset list [-p 页] [-n 每页条数]` |
11
- | 详情 | `c456 asset show <id>`(JSON 含 `markdownSnippet`、`previewUrl`) |
12
- | 改展示名 | `c456 asset update <id> --filename <展示文件名.webp>`(不替换图片字节,仅 ActiveStorage 文件名) |
13
- | 删除 | `c456 asset delete <id>`(正文仍引用 `c456:asset/<id>` 时会失败) |
14
- | 续期正文里的预览链 | `c456 asset refresh-markdown --body-file <.md>`(输出到 stdout) |
15
-
16
- 上传与服务器「上传前字节」去重一致:完全相同文件会 **422**。
17
-
18
- ## 把图片插入收录/打法/讲解正文
19
-
20
- 若图片来自 **`c456 screenshot`** 或自建 Playwright 脚本(对产品页或渠道页的截屏),建议先按 [product-screenshots-for-intake.md](product-screenshots-for-intake.md) 保存到 `.tmp/` 再走下列步骤(**不用**浏览器 MCP,降低配置成本)。
21
-
22
- 1. `c456 asset upload -f ./figure.png` → 终端会打印 **`markdownSnippet`**(一行 Markdown 图片,title 内含 `c456:asset/<id>`)。
23
- 2. 将该行(或经编辑器合并后的段落)写入 **`body`**:创建/更新收录或打法时用 **`--body-file`** 传入整篇 Markdown,**不要**在 shell 里直接塞多行引号。
24
- 3. 预览 URL 会过期时,对整篇 Markdown 跑 `c456 asset refresh-markdown --body-file note.md > note.new.md` 再写回。
25
-
26
- 插图以 **title 中的 `c456:asset/<id>`** 为稳定引用;括号内 URL 可续期。
27
-
28
- ## 工具 / 渠道的「列表图标」(list icon)
29
-
30
- 列表与卡片上的图标来自收录 **`profile_data`** 顶层的 **`list_icon_url` / `list_icon_url_local`**(以及各资料段解析出的 icon)。Web 端可上传文件;**API / CLI** 常见做法:
31
-
32
- 1. **先把图标上传到素材库**:`c456 asset upload -f ./logo.png` → 得到 **`previewUrl`**(签名 URL,可作临时图床链)。
33
- 2. **再 PATCH 收录**(仅 tool / channel),只改图标、不动原有资料段。将 JSON 写入 `.tmp/icon-patch.json` 后执行:
34
-
35
- ```bash
36
- c456 intake update <intake_id> --profile-data-json-file .tmp/icon-patch.json
37
- ```
38
-
39
- 文件内容示例:`{ "list_icon_url": "<上一步 asset show 或 upload 输出的 previewUrl>" }`。
40
-
41
- 服务端会将外链 **转存** 为站内 `list_icon_url_local`(与 Web 行为一致);若需清除图标,传 **`"remove_list_icon": true`**(与 `list_icon_url` 二选一逻辑以 API 为准)。
42
-
43
- 3. **`profile_data` 与 facets 的合并**:`PATCH /api/v1/intakes/:id` 对已有 `profile_data` **按键合并**——请求里**没有** `facets` 键时,不会清空已有资料段;可只传 `list_icon_url`。完整 facets 结构仍见 [intake-profile-data-json.md](intake-profile-data-json.md)。
44
-
45
- ## 相关 CLI
46
-
47
- - `c456 tool …` / `c456 channel …`:新建时常用 `-u` + `--auto-resolve-url`,再按需 `intake update` 补图标。
48
- - `c456 intake update <id> --profile-data-json '…'`:更新 tool/channel 的 `profile_data` 或列表图标。
@@ -1,43 +0,0 @@
1
- # 工具 / 渠道收录:用浏览器(Playwright)截产品图并写入介绍
2
-
3
- 在收录 **`-k tool`** 或 **`-k channel`** 时,若介绍(`body` / Markdown)需要**真实界面**佐证(官网首屏、产品工作台、渠道主页关键区等),**优先使用 c456-cli 内置命令**(系统 Chrome + `playwright-core` 走 CDP,**不**随包下载 Chromium):`c456 browser start` 持久 profile → 需要登录时先手动在窗口内登录 → **`c456 screenshot <url> [-o .tmp/…]`** 复用同一浏览器;无长会话需求时可直接 **`c456 screenshot <url>`**(一次性起停;不传 **`-o`** 时在当前目录按 URL 生成文件名)。再经 **`c456 asset upload`** 把图插入正文。
4
-
5
- 也可在仓库内保留 **自建 Playwright 脚本**(`page.screenshot` 写 `.tmp/`)作为补充;**不推荐 IDE 浏览器 MCP**,以降低配置成本。
6
-
7
- ## 适用场景
8
-
9
- - 工具:产品落地页、文档站、SaaS 控制台(需已登录则由用户说明或跳过敏感区)。
10
- - 渠道:平台内频道/主页的**公开可见**区域(遵守平台 ToS;不要对需付费墙或强反爬页面硬截)。
11
-
12
- ## 推荐流程(与 CLI 衔接)
13
-
14
- ### A. 内置 `browser` + `screenshot`(推荐)
15
-
16
- 1. **`c456 browser start`**:在本机选空闲端口,启动**有头**系统 Chrome;用户数据目录默认 **`~/.cache/c456-cli/chrome-profile`**(可用 `XDG_CACHE_HOME` 改缓存根),**同一路径多次启动可保留 Cookie / 登录态**。状态写入同目录下的 `browser-daemon.json`(含 CDP `http://127.0.0.1:<port>`)。
17
- 2. 在打开的窗口中访问需登录的站点并完成登录(若不需要可跳过)。
18
- 3. **`c456 screenshot <https://…> [-o .tmp/capture.png]`**:通过 CDP 新开页导航并截图;**默认复用**上一步的 Chrome;省略 **`-o`** 时在当前目录生成「URL 安全化片段 + 时间戳」的 `.png`。**`c456 browser stop`**:结束该 Chrome 并删除 daemon 记录、释放端口。
19
- 4. 若**不需要**保留会话、只要一张图:可直接 **`c456 screenshot <url>`** 或带 **`-o`**(无 `browser start` 时 CLI 会**临时**起 Chrome、截图后关闭并删除临时 profile);加 **`--no-reuse`** 可强制走该一次性路径。
20
- 5. **上传素材库**:`c456 asset upload -f .tmp/capture.png` → 取 **`markdownSnippet`**。
21
- 6. **写入介绍正文**:合并进 Markdown,用 **`--body-file`** 传给 `c456 intake new` / `intake update`。
22
- 7. **预览链续期**:`c456 asset refresh-markdown --body-file <path>`(见 [media-library-and-icons.md](media-library-and-icons.md))。
23
-
24
- CLI 细节与可选 Chromium 安装说明见本仓库 **README**「浏览器与截图」。
25
-
26
- ### B. 自建 Playwright 脚本(可选)
27
-
28
- 与 A 相同的后半段(文件落 `.tmp/` → `asset upload` → `body`);导航逻辑由仓库脚本维护。
29
-
30
- 1. **打开目标 URL**:脚本内 `chromium.launch` + `page.goto` 等;视口、等待可参数化。
31
- 2. **导出 PNG/WebP** 到 **`.tmp/`**。
32
- 3. 同 A 的步骤 5–7。
33
-
34
- ## 与「列表图标」的区别
35
-
36
- - **正文插图**:走 **`markdownSnippet` → `body`**(上文流程)。
37
- - **列表 / 卡片小图标**:走 **`list_icon_url`** 与 `profile_data` 补丁,见 [media-library-and-icons.md](media-library-and-icons.md) 第二节。
38
-
39
- ## 约束(与其它技能一致)
40
-
41
- - **严禁编造**:截图须来自真实导航结果;不得用占位图冒充产品界面。
42
- - **鉴权与隐私**:含登录态或敏感数据的页面,仅在用户明确要求且同意展示时截屏;默认优先公开页。
43
- - **自动化栈**:优先 **`c456 browser` / `c456 screenshot`**;依赖为 **`playwright-core` + 本机 Chrome**,**不强制** `npx playwright install chromium`(无 Chrome 时见 README 建议)。亦可用自建脚本;**不用** IDE MCP。
@@ -1,242 +0,0 @@
1
- ---
2
- name: c456-llm-wiki
3
- description: >-
4
- 将 Karpathy LLM Wiki 三层架构与 C456.com 双向同步结合的知识库管理规范。
5
- 支持 raw/wiki/c456-sync 四层架构、多对多映射、双向同步、自动 Ingest。
6
- Use when the user mentions LLM Wiki, C456 sync, knowledge base, c456-sync,
7
- bidirectional sync, or ingesting content to wiki and c456.com.
8
- ---
9
-
10
- # LLM Wiki + C456 双向同步
11
-
12
- ## 核心思想
13
-
14
- 在 Karpathy LLM Wiki 三层架构(raw/wiki/schema)基础上增加 C456 双向同步层,实现本地知识库与 c456.com 之间的内容发布与拉取。
15
-
16
- ## 四层架构
17
-
18
- ```
19
- raw/(原始素材层) ← 你存放,AI 只读
20
- ↑↓
21
- c456-sync/(镜像层)← C456 线上内容的本地镜像
22
- ↑↓
23
- wiki/(知识库层) ← AI 生成的结构化 Markdown,互相链接
24
- ↑↓
25
- AGENTS.md(Schema) ← 定义 AI 如何组织 Wiki
26
- ```
27
-
28
- ## 目录结构
29
-
30
- ```
31
- .
32
- ├── raw/
33
- │ ├── articles/ books/ papers/ courses/
34
- │ ├── resources/ quotes/ tools/ work/
35
- ├── c456-sync/
36
- │ ├── signal/ tool/ channel/ playbook/ walkthrough/
37
- ├── wiki/
38
- │ ├── index.md log.md c456-meta.yml
39
- │ ├── entities/ concepts/ threads/ sources/ agents/
40
- ├── output/
41
- └── AGENTS.md
42
- ```
43
-
44
- ### 四层关系
45
-
46
-
47
- | 层级 | 谁维护 | 用途 | 与 C456 关系 |
48
- | ------------ | ----- | ------- | --------- |
49
- | `raw/` | 用户放入 | 原始素材 | 上行时作为内容来源 |
50
- | `c456-sync/` | AI 同步 | C456 镜像 | 与线上一一对应 |
51
- | `wiki/` | AI 生成 | 提炼后的知识库 | 多对多映射 |
52
- | `output/` | 用户创作 | 主动产出 | 可发布到 C456 |
53
-
54
-
55
- **关键原则**:`c456-sync/` 与 `wiki/` 之间不用 symlink。关联通过 Frontmatter 引用 + `wiki/c456-meta.yml` 实现。
56
-
57
- ---
58
-
59
- ## 页面类型
60
-
61
- ### 实体页 `wiki/entities/`
62
-
63
- - 命名:小写 kebab-case,如 `andrej-karpathy.md`
64
- - Frontmatter:`type: entity` + `c456-id` + `c456-kind` + `c456-sync-path` + `tags: [...]`
65
-
66
- ### 概念页 `wiki/concepts/`
67
-
68
- - 命名:小写 kebab-case,如 `rag.md`
69
-
70
- ### 线索页 `wiki/threads/`
71
-
72
- - 命名:小写 kebab-case,如 `ai-engineering-trilogy.md`
73
-
74
- ### 来源摘要页 `wiki/sources/`
75
-
76
- - 命名:与 raw 文件名呼应
77
- - Frontmatter:`type: source` + `c456-kind` + **`c456-title`** + **`c456-summary`**(上行必备)+ `c456-id` + `c456-status` + `date: YYYY-MM-DD` + `raw: raw/.../xxx.md`
78
-
79
- ---
80
-
81
- ## 链接规范
82
-
83
- - 使用 Obsidian Wikilink:`[[page-name]]`
84
- - 链接目标文件名不带 `.md` 后缀
85
- - 页面标题使用一级标题 `# Title`
86
-
87
- ---
88
-
89
- ## 三种核心操作
90
-
91
- ### Ingest(摄入)
92
-
93
- 1. 读取素材
94
- 2. 判定 C456 类型(signal/tool/channel/playbook/walkthrough)
95
- 3. 创建/更新来源摘要页(Frontmatter 含 `c456-title` + `c456-summary`,供后续上行)
96
- 4. 提取实体(无则新建,有则追加)
97
- 5. 提取概念(无则新建,有则整合)
98
- 6. 更新线索页
99
- 7. 更新 `wiki/index.md`
100
- 8. 追加 `wiki/log.md`
101
-
102
- **C456 类型判定**:介绍工具用法 → `tool`;介绍作者/频道 → `channel`;step-by-step → `walkthrough`;策略/框架 → `playbook`;其余 → `signal`。
103
-
104
- ### Query(查询)
105
-
106
- 1. 先读 `wiki/index.md`
107
- 2. 定位相关页
108
- 3. 读取并综合
109
- 4. 引用来源
110
- 5. 回写好答案:若用户认可,提议保存为 wiki 新页面
111
-
112
- ### Lint(检查)
113
-
114
- 1. 扫描矛盾
115
- 2. 发现孤立页
116
- 3. 检查缺失页
117
- 4. 评估数据缺口
118
- 5. 输出 Markdown 报告
119
-
120
- ---
121
-
122
- ## C456 集成规范
123
-
124
- ### 内容类型映射
125
-
126
-
127
- | C456 类型 | 含义 | 对应 raw/ | 对应 wiki/ | 对应 output/ |
128
- | --------------- | ------ | ------------------------------ | ----------------------------- | ---------- |
129
- | **signal** | 信息片段 | articles/, quotes/, resources/ | wiki/sources/ | 短评 |
130
- | **tool** | 工具/软件 | tools/ | wiki/entities/ | 工具评测 |
131
- | **channel** | 频道/账号 | resources/ | wiki/entities/ | 频道推荐 |
132
- | **playbook** | 方法论/框架 | work/, books/ | wiki/concepts/, wiki/threads/ | 方法论文章 |
133
- | **walkthrough** | 教程 | courses/, articles/ | wiki/threads/ | 教程 |
134
-
135
-
136
- ### Frontmatter 扩展
137
-
138
- 收录五种 C456 类型并准备**上行**时,须在 Frontmatter 中写明 **`c456-title`** 与 **`c456-summary`**,作为同步到 C456 的展示标题信息(与页面正文的一级标题 `# Title` 区分,避免与通用 `title` 字段混淆)。
139
-
140
- - **`c456-title`**:主标题(单行,对应 CLI/API 的标题字段)。
141
- - **`c456-summary`**:紧跟标题语义的一句简短说明,用于列表/卡片上的补充展示;上行时与 `c456-title` 一并交给 Agent 或写入请求(具体拼接格式以当次 CLI/API 为准)。
142
-
143
- **这句简短描述叫什么**:若强调「从属于主标题的补充短语」,中文常用 **副标题**,英文 **subtitle**;若强调「一句话概括、列表摘要」,产品与 API 语境常用 **摘要**,英文 **summary**(C456 Intake 卡片上与标题配对展示的也是 summary)。营销语境也可称 **标语 / tagline**。本规范 Frontmatter 使用字段名 **`c456-summary`**,语义与上述「摘要」对齐。
144
-
145
- ```yaml
146
- ---
147
- type: source
148
- c456-kind: signal # signal | tool | channel | playbook | walkthrough
149
- c456-title: "主标题"
150
- c456-summary: "一句简短描述,用作上行列表/卡片摘要(副标题语义)"
151
- c456-id: 42 # 发布后回填
152
- c456-status: draft # draft | published | outdated | conflict
153
- date: 2026-05-08
154
- ---
155
- ```
156
-
157
- ### 发布工作流(上行)
158
-
159
- 1. 扫描带 `c456-kind` 但缺 `c456-id` 或 `status: draft` 的页面
160
- 2. 转换 Markdown 为 C456 富文本格式(移除 Wikilink、Frontmatter)
161
- 3. 选择命令:signal/tool/channel → `c456 intake new -k <kind>`;playbook/walkthrough → `c456 playbook new`
162
- 4. 正文写入 `.tmp/`,通过 `--body-file` 传入 CLI
163
- 5. 回填 ID 到 Frontmatter `c456-id`,改 `c456-status: published`
164
- 6. 记录日志到 `wiki/log.md`
165
-
166
- 更新已有内容:用 `c456 intake update <id>` 或 `c456 playbook update <id>`,不重复新建。
167
-
168
- ### 双向同步
169
-
170
- **c456-sync/ 目录**:作为 C456 镜像层,按五类型分目录存储。
171
-
172
- **关联方式**:`c456-sync/` 文件 Frontmatter 标注 `local-wiki-source`、`local-wiki-entities` 等字段;`wiki/` 页面 Frontmatter 标注 `c456-id`、`c456-sync-path`;`wiki/c456-meta.yml` 记录总索引。
173
-
174
- **双向索引**:`wiki/index.md` 中已发布条目可标注 `[c456:#id]`。C456 正文可保留回链到本地 Wiki 的链接。
175
-
176
- ### 状态流转
177
-
178
- ```
179
- draft → publishing → published → outdated → published
180
- ↘ conflict
181
- ```
182
-
183
- ---
184
-
185
- ## 特殊文件规范
186
-
187
- ### `wiki/index.md`
188
-
189
- 内容导向的目录,每页一行摘要 + 链接。按分类组织。每次 Ingest 后更新。
190
-
191
- ### `wiki/log.md`
192
-
193
- 时间导向的追加日志。条目格式:`## [YYYY-MM-DD] 操作类型 | 标题/简述`
194
- 操作类型:`ingest`、`query`、`lint`、`update`、`create`、`c456-publish`、`c456-down-ingest`、`c456-conflict`
195
- 保持 append-only。
196
-
197
- ### `wiki/c456-meta.yml`
198
-
199
- C456 ↔ 本地映射总索引。记录每个 C456 ID 的 `sync_path`、`wiki_pages[]`、状态、时间戳、checksum。AI 在同步操作中自动维护。
200
-
201
- ---
202
-
203
- ## 产品录入调研与数据自动补充(Enrichment)
204
-
205
- 录入产品(tool 类型)时,若提供的信息不完整(如仅给 GitHub 仓库 URL),AI 应主动上网调研并自动补充多种数据类型。
206
-
207
- ### 调研来源
208
-
209
- | 信息类型 | 调研方法 | 示例 |
210
- |---|---|---|
211
- | **官网** | 从 GitHub README 或组织页提取 | `github.com/rails/rails` → `rubyonrails.org` |
212
- | **包管理器** | 搜索 npm / RubyGems / PyPI / Crates.io / Homebrew | `github.com/rails/rails` → `gem rails` |
213
- | **GitHub 元数据** | 读取仓库页(stars、license、语言、最新发布) | 自动记录 stars 数、许可证、主要语言 |
214
- | **产品描述** | 从官网首页或 README 提炼一句话简介 | 用于 `c456-summary` |
215
- | **核心功能** | 从官网 Features 页或 README 提取 | 用于正文 |
216
- | **安装方式** | 从 README 或官网提取 | apt / brew / npm / Docker 等 |
217
-
218
- ### 调研流程
219
-
220
- 1. **输入**:用户提供任意信息(官网 URL、GitHub URL、产品名等)
221
- 2. **调研**:AI 使用 WebFetch 工具获取官网、GitHub、包管理器页面
222
- 3. **交叉验证**:确认官网与 GitHub 的对应关系
223
- 4. **补充**:将调研结果填入 raw 素材、c456-sync 镜像、wiki 页面
224
- 5. **发布**:按标准 Ingest 流程创建/更新所有页面
225
-
226
- ### 数据填充规则
227
-
228
- - **c456-title**:`品牌名 | 定位 · 特色后缀`
229
- - **c456-summary**:一句话概括核心价值
230
- - **正文**:包含核心功能模块表、安装方式、适用场景
231
- - **实体页**:包含关键属性表(官网、GitHub、stars、许可证、语言)、核心功能、差异化亮点、竞品对比
232
- - **来源摘要**:包含核心信息、关键功能、开源信息
233
-
234
- ### 示例:GitHub URL → 多类型数据
235
-
236
- 给定 `github.com/rails/rails`,AI 应自动:
237
- 1. 读取 GitHub README → 提取官网 `rubyonrails.org`
238
- 2. 搜索 RubyGems → 找到 `gem rails`
239
- 3. 记录 GitHub stars、许可证(MIT)、主要语言(Ruby)
240
- 4. 访问官网 → 提取产品描述、核心功能
241
- 5. 创建完整 raw / c456-sync / wiki 页面
242
- 6. 发布到 C456