@9000ai/cli 0.3.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.
@@ -1,130 +0,0 @@
1
- ---
2
- name: content-init
3
- description: 内容创作者工作空间初始化。创建完整的目录结构、agent 人格、主张库、用户画像模板,配置中台连接,并生成使用指南。只需运行一次。
4
- triggers:
5
- - /content-init
6
- - /初始化工作空间
7
- - 初始化内容工作空间
8
- - 初始化
9
- - 第一次用
10
- role: initializer
11
- scope: workspace-setup
12
- output-format: file-creation
13
- ---
14
-
15
- # 内容工作空间初始化
16
-
17
- 你负责帮内容创作者完成工作空间的一次性初始化。完成后,用户就拥有了完整的内容创作文件系统和使用指南。
18
-
19
- ---
20
-
21
- ## 前置条件:中台连接配置
22
-
23
- 在创建文件之前,先确认中台已连上。
24
-
25
- **检查方式**:
26
-
27
- ```bash
28
- 9000ai config show
29
- ```
30
-
31
- - 输出了 `base_url` 和 `api_key` → 继续下一步
32
- - 提示未配置 → 问用户要中台地址和 API Key,然后运行:
33
-
34
- ```bash
35
- 9000ai config set --base-url <地址> --api-key <key>
36
- ```
37
-
38
- > **不知道地址和 key?**
39
- > - `base-url`:找中台管理员要,或查看你收到的 9000AI 服务开通通知。
40
- > - `api-key`:找中台管理员要,每个用户的 key 独立分配。
41
-
42
- ---
43
-
44
- ## 幂等性规则
45
-
46
- 这个初始化可以安全地多次运行,不会破坏已有数据。
47
-
48
- | 情况 | 处理 |
49
- |------|------|
50
- | 目录已存在 | 跳过,不重建 |
51
- | `profile/` 下的文件已存在 | **一律跳过**(用户可能已开始填写) |
52
- | `claims/README.md` 是旧版(含"每个主张是一个文件夹") | 备份为 `.bak`,替换为新版 |
53
- | `claims/` 数据文件已存在(json/jsonl) | 跳过 |
54
- | `agents/`、`CLAUDE.md`、`guide.md` 不存在 | 创建 |
55
- | `agents/`、`CLAUDE.md`、`guide.md` 已存在 | 跳过 |
56
-
57
- 核心原则:只创建不存在的东西,绝不覆盖用户已编辑的内容。
58
-
59
- ---
60
-
61
- ## 模板来源
62
-
63
- 所有模板文件位于本 skill 的 `templates/` 子目录中,初始化时直接复制到用户项目根目录。
64
-
65
- 模板根路径(相对于本 SKILL.md):`init/templates/`
66
-
67
- ---
68
-
69
- ## 创建清单
70
-
71
- 按以下顺序在**用户当前项目根目录**下创建文件和目录。每一步先检查是否已存在。
72
-
73
- | # | 目标路径 | 模板来源 | 跳过条件 |
74
- |---|---------|---------|---------|
75
- | 1 | `agents/` | — | 目录已存在 |
76
- | 2 | `agents/content-agent.md` | `templates/agents/content-agent.md` | 文件已存在 |
77
- | 3 | `agents/README.md` | `templates/agents/README.md` | 文件已存在 |
78
- | 4 | `profile/` | — | 目录已存在 |
79
- | 5 | `profile/identity.md` | `templates/profile/identity.md` | 文件已存在 |
80
- | 6 | `profile/voice.md` | `templates/profile/voice.md` | 文件已存在 |
81
- | 7 | `profile/topics.md` | `templates/profile/topics.md` | 文件已存在 |
82
- | 8 | `profile/product.md` | `templates/profile/product.md` | 文件已存在 |
83
- | 9 | `inbox/` | — | 目录已存在 |
84
- | 10 | `inbox/README.md` | `templates/inbox/README.md` | 文件已存在 |
85
- | 11 | `claims/` | — | 目录已存在 |
86
- | 12 | `claims/README.md` | `templates/claims/README.md` | 已存在且**不含**"每个主张是一个文件夹" |
87
- | 13 | `claims/claims.json` | `templates/claims/claims.json` | 文件已存在 |
88
- | 14 | `claims/angles.jsonl` | 空文件 | 文件已存在 |
89
- | 15 | `claims/cases.jsonl` | 空文件 | 文件已存在 |
90
- | 16 | `claims/outputs.jsonl` | 空文件 | 文件已存在 |
91
- | 17 | `projects/` | — | 目录已存在 |
92
- | 18 | `projects/README.md` | `templates/projects/README.md` | 文件已存在 |
93
- | 19 | `CLAUDE.md` | `templates/CLAUDE.md` | 文件已存在 |
94
- | 20 | `guide.md` | `templates/guide.md` | 文件已存在 |
95
-
96
- 对于第 12 项:如果 `claims/README.md` 已存在且内容含"每个主张是一个文件夹",先备份为 `claims/README.md.bak`,再用模板覆盖。
97
-
98
- ---
99
-
100
- ## 初始化完成后汇报
101
-
102
- 所有文件创建完毕后,输出以下格式的汇报:
103
-
104
- ```
105
- ## 工作空间初始化完成
106
-
107
- ### 已创建
108
- - [文件路径] — [一句话说明]
109
- - ...
110
-
111
- ### 已跳过(已存在)
112
- - [文件路径]
113
- - ...
114
-
115
- ### 下一步
116
-
117
- 1. **填写画像** → 打开 profile/ 下的文件,把"待填写"替换成真实信息
118
- 2. **建立主张** → 编辑 claims/claims.json
119
- 3. **开始使用** → 查看 guide.md 了解全部用法
120
-
121
- 如果目录中有散落的素材文件(如会议转写),可以移入 inbox/,我会帮你处理。
122
- ```
123
-
124
- ---
125
-
126
- ## 注意事项
127
-
128
- - 不要在初始化过程中自动移动任何已有用户文件
129
- - 初始化只负责搭骨架,不填内容
130
- - 如果 `claims/` 中已有按主张命名的子目录(旧结构),不要删除,在汇报中告知用户新结构的差异
@@ -1,24 +0,0 @@
1
- 请载入 agents/content-agent.md 作为你的角色设定。
2
-
3
- ## 工作空间概览
4
-
5
- | 目录 | 用途 |
6
- |------|------|
7
- | profile/ | 用户画像(只读) |
8
- | inbox/ | 素材投喂入口 |
9
- | claims/ | 主张库(核心资产) |
10
- | projects/ | 选题项目 |
11
-
12
- 详细使用指南见 guide.md。
13
-
14
- ## 平台能力映射
15
-
16
- | 能力 | CLI 命令 |
17
- |------|---------|
18
- | 配置中台连接 | `9000ai config set --base-url <url> --api-key <key>` |
19
- | 热点榜获取 | `9000ai search hot` |
20
- | 关键词搜索 | `9000ai search keyword` |
21
- | 对标账号监控 | `9000ai monitor` |
22
- | 视频转文字 | `9000ai transcribe` |
23
- | 查看任务状态 | `9000ai task status --task-id <id>` |
24
- | 身份验证 | `9000ai auth whoami` |
@@ -1 +0,0 @@
1
- """Shared execution helpers shipped with 9000AI hub."""
@@ -1,135 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- import sys
6
- from dataclasses import dataclass
7
- from pathlib import Path
8
- from typing import Any
9
-
10
- import requests
11
-
12
-
13
- DEFAULT_BASE_URL = "http://127.0.0.1:8025"
14
- DEFAULT_TIMEOUT_SECONDS = 300
15
- DEFAULT_API_KEY_HEADER = "X-API-Key"
16
- HUB_BASE_URL_ENV = "9000AI_BASE_URL"
17
- HUB_API_KEY_ENV = "9000AI_API_KEY"
18
-
19
- # hub 自身的全局配置文件,位于 9000AI-hub-9000AI/local/config.json
20
- _HUB_CONFIG_PATH = Path(__file__).resolve().parents[1] / "local" / "config.json"
21
-
22
-
23
- @dataclass(frozen=True)
24
- class ModuleSpec:
25
- module: str
26
- skill_root: Path
27
- default_base_url: str = DEFAULT_BASE_URL
28
- api_key_header: str = DEFAULT_API_KEY_HEADER
29
- timeout_seconds: int = DEFAULT_TIMEOUT_SECONDS
30
-
31
- @property
32
- def config_path(self) -> Path:
33
- return self.skill_root / "local" / "config.json"
34
-
35
- @property
36
- def output_dir(self) -> Path:
37
- return self.skill_root / "output"
38
-
39
-
40
- def configure_stdout_encoding() -> None:
41
- encoding = (sys.stdout.encoding or "").lower()
42
- if encoding != "utf-8" and hasattr(sys.stdout, "reconfigure"):
43
- sys.stdout.reconfigure(encoding="utf-8", errors="replace")
44
-
45
-
46
- def load_local_config(spec: ModuleSpec) -> dict[str, Any]:
47
- if not spec.config_path.exists():
48
- return {}
49
- return json.loads(spec.config_path.read_text(encoding="utf-8"))
50
-
51
-
52
- def load_hub_config() -> dict[str, Any]:
53
- if not _HUB_CONFIG_PATH.exists():
54
- return {}
55
- return json.loads(_HUB_CONFIG_PATH.read_text(encoding="utf-8"))
56
-
57
-
58
- def save_local_config(spec: ModuleSpec, *, base_url: str, api_key: str) -> None:
59
- spec.config_path.parent.mkdir(parents=True, exist_ok=True)
60
- spec.config_path.write_text(
61
- json.dumps({"base_url": base_url.rstrip("/"), "api_key": api_key}, ensure_ascii=False, indent=2),
62
- encoding="utf-8",
63
- )
64
-
65
-
66
- def save_hub_config(*, base_url: str, api_key: str) -> None:
67
- """保存 hub 全局配置,所有 skill 共享。"""
68
- _HUB_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
69
- _HUB_CONFIG_PATH.write_text(
70
- json.dumps({"base_url": base_url.rstrip("/"), "api_key": api_key}, ensure_ascii=False, indent=2),
71
- encoding="utf-8",
72
- )
73
-
74
-
75
- def resolve_base_url(spec: ModuleSpec, override: str | None) -> str:
76
- hub_config = load_hub_config()
77
- skill_config = load_local_config(spec)
78
- value = (
79
- override
80
- or os.getenv(HUB_BASE_URL_ENV)
81
- or hub_config.get("base_url")
82
- or skill_config.get("base_url")
83
- or spec.default_base_url
84
- )
85
- return str(value).rstrip("/")
86
-
87
-
88
- def resolve_api_key(spec: ModuleSpec, override: str | None) -> str:
89
- hub_config = load_hub_config()
90
- skill_config = load_local_config(spec)
91
- value = override or os.getenv(HUB_API_KEY_ENV) or hub_config.get("api_key") or skill_config.get("api_key")
92
- if not value:
93
- raise SystemExit(
94
- "还没连上 9000AI 中台。请先初始化:\n"
95
- " 9000ai config set --base-url <中台地址> --api-key <你的key>\n"
96
- "\n"
97
- "不知道地址和 key?找中台管理员要。"
98
- )
99
- return str(value)
100
-
101
-
102
- def load_json_file(path: str) -> Any:
103
- return json.loads(Path(path).read_text(encoding="utf-8"))
104
-
105
-
106
- def request_json(
107
- spec: ModuleSpec,
108
- *,
109
- method: str,
110
- base_url: str,
111
- api_key: str,
112
- path: str,
113
- payload: Any | None = None,
114
- timeout_seconds: int | None = None,
115
- ) -> Any:
116
- response = requests.request(
117
- method=method,
118
- url=f"{base_url}{path}",
119
- headers={spec.api_key_header: api_key, "Content-Type": "application/json"},
120
- json=payload,
121
- timeout=timeout_seconds or spec.timeout_seconds,
122
- )
123
- data = response.json()
124
- if response.status_code >= 400:
125
- raise SystemExit(json.dumps(data, ensure_ascii=False, indent=2))
126
- return data
127
-
128
-
129
- def ensure_output_dir(spec: ModuleSpec) -> Path:
130
- spec.output_dir.mkdir(parents=True, exist_ok=True)
131
- return spec.output_dir
132
-
133
-
134
- def print_json(payload: Any) -> None:
135
- print(json.dumps(payload, ensure_ascii=False, indent=2))
@@ -1,3 +0,0 @@
1
- display_name: douyin-monitor-9000AI
2
- short_description: 通过 /douyin-monitor-9000AI 提交主页监控任务并回查结果
3
- default_prompt: 帮我通过 /douyin-monitor-9000AI 提交抖音主页监控任务,拿到 task_id 后稍后回查结果。
@@ -1,273 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import argparse
4
- import sys
5
- from pathlib import Path
6
-
7
- REPO_ROOT = Path(__file__).resolve().parents[2]
8
- HUB_ROOT = REPO_ROOT / "9000AI-hub-9000AI"
9
- if str(HUB_ROOT) not in sys.path:
10
- sys.path.insert(0, str(HUB_ROOT))
11
-
12
- from shared.runner import ( # noqa: E402
13
- ModuleSpec,
14
- configure_stdout_encoding,
15
- load_json_file,
16
- print_json,
17
- request_json,
18
- resolve_api_key,
19
- resolve_base_url,
20
- save_local_config,
21
- )
22
-
23
- SKILL_ROOT = Path(__file__).resolve().parents[1]
24
- MODULE_SPEC = ModuleSpec(
25
- module="douyin-monitor",
26
- skill_root=SKILL_ROOT,
27
- )
28
-
29
-
30
- def build_parser() -> argparse.ArgumentParser:
31
- parser = argparse.ArgumentParser(description="Douyin monitor API client")
32
- parser.add_argument("--base-url", default=None)
33
- parser.add_argument("--api-key", default=None)
34
- subparsers = parser.add_subparsers(dest="command", required=True)
35
-
36
- subparsers.add_parser("configure", help="写入本地配置文件")
37
- subparsers.add_parser("whoami", help="查看当前 key 对应的调用方")
38
- subparsers.add_parser("capabilities", help="查看当前 key 开通的能力")
39
- subparsers.add_parser("list-creators", help="查看监控对象列表")
40
-
41
- create_creator = subparsers.add_parser("create-creator", help="新增监控对象")
42
- create_creator.add_argument("--json-file", required=True)
43
-
44
- get_creator = subparsers.add_parser("get-creator", help="查看单个监控对象")
45
- get_creator.add_argument("--creator-id", required=True)
46
-
47
- update_creator = subparsers.add_parser("update-creator", help="修改监控对象")
48
- update_creator.add_argument("--creator-id", required=True)
49
- update_creator.add_argument("--json-file", required=True)
50
-
51
- delete_creator = subparsers.add_parser("delete-creator", help="删除监控对象")
52
- delete_creator.add_argument("--creator-id", required=True)
53
-
54
- enable_creator = subparsers.add_parser("enable-creator", help="启用监控对象")
55
- enable_creator.add_argument("--creator-id", required=True)
56
-
57
- disable_creator = subparsers.add_parser("disable-creator", help="停用监控对象")
58
- disable_creator.add_argument("--creator-id", required=True)
59
-
60
- reset_creator = subparsers.add_parser("reset-creator", help="重置监控对象状态")
61
- reset_creator.add_argument("--creator-id", required=True)
62
-
63
- submit = subparsers.add_parser("submit", help="提交主页监控任务")
64
- submit.add_argument("--json-file", required=True)
65
-
66
- list_runs = subparsers.add_parser("list-runs", help="查看运行历史")
67
- list_runs.add_argument("--page", type=int, default=1)
68
- list_runs.add_argument("--page-size", type=int, default=20)
69
- list_runs.add_argument("--status", default=None)
70
-
71
- run_detail = subparsers.add_parser("run-detail", help="查看单次运行详情")
72
- run_detail.add_argument("--run-id", required=True)
73
-
74
- task = subparsers.add_parser("task", help="查看任务状态")
75
- task.add_argument("--task-id", required=True)
76
-
77
- results = subparsers.add_parser("results", help="查看任务结果")
78
- results.add_argument("--task-id", required=True)
79
- return parser
80
-
81
-
82
- def main() -> None:
83
- configure_stdout_encoding()
84
- parser = build_parser()
85
- args = parser.parse_args()
86
-
87
- if args.command == "configure":
88
- if not args.base_url or not args.api_key:
89
- raise SystemExit("configure 需要同时传 --base-url 和 --api-key")
90
- save_local_config(MODULE_SPEC, base_url=args.base_url, api_key=args.api_key)
91
- print_json({"message": "配置已写入", "config_path": str(MODULE_SPEC.config_path)})
92
- return
93
-
94
- base_url = resolve_base_url(MODULE_SPEC, args.base_url)
95
- api_key = resolve_api_key(MODULE_SPEC, args.api_key)
96
-
97
- if args.command == "whoami":
98
- print_json(
99
- request_json(MODULE_SPEC, method="GET", base_url=base_url, api_key=api_key, path="/api/v1/auth/me")
100
- )
101
- return
102
-
103
- if args.command == "capabilities":
104
- print_json(
105
- request_json(
106
- MODULE_SPEC,
107
- method="GET",
108
- base_url=base_url,
109
- api_key=api_key,
110
- path="/api/v1/auth/capability-permissions",
111
- )
112
- )
113
- return
114
-
115
- if args.command == "list-creators":
116
- print_json(
117
- request_json(
118
- MODULE_SPEC,
119
- method="GET",
120
- base_url=base_url,
121
- api_key=api_key,
122
- path="/api/v1/douyin/monitor/creators",
123
- )
124
- )
125
- return
126
-
127
- if args.command == "create-creator":
128
- print_json(
129
- request_json(
130
- MODULE_SPEC,
131
- method="POST",
132
- base_url=base_url,
133
- api_key=api_key,
134
- path="/api/v1/douyin/monitor/creators",
135
- payload=load_json_file(args.json_file),
136
- )
137
- )
138
- return
139
-
140
- if args.command == "get-creator":
141
- print_json(
142
- request_json(
143
- MODULE_SPEC,
144
- method="GET",
145
- base_url=base_url,
146
- api_key=api_key,
147
- path=f"/api/v1/douyin/monitor/creators/{args.creator_id}",
148
- )
149
- )
150
- return
151
-
152
- if args.command == "update-creator":
153
- print_json(
154
- request_json(
155
- MODULE_SPEC,
156
- method="PATCH",
157
- base_url=base_url,
158
- api_key=api_key,
159
- path=f"/api/v1/douyin/monitor/creators/{args.creator_id}",
160
- payload=load_json_file(args.json_file),
161
- )
162
- )
163
- return
164
-
165
- if args.command == "delete-creator":
166
- print_json(
167
- request_json(
168
- MODULE_SPEC,
169
- method="DELETE",
170
- base_url=base_url,
171
- api_key=api_key,
172
- path=f"/api/v1/douyin/monitor/creators/{args.creator_id}",
173
- )
174
- )
175
- return
176
-
177
- if args.command == "enable-creator":
178
- print_json(
179
- request_json(
180
- MODULE_SPEC,
181
- method="POST",
182
- base_url=base_url,
183
- api_key=api_key,
184
- path=f"/api/v1/douyin/monitor/creators/{args.creator_id}/enable",
185
- )
186
- )
187
- return
188
-
189
- if args.command == "disable-creator":
190
- print_json(
191
- request_json(
192
- MODULE_SPEC,
193
- method="POST",
194
- base_url=base_url,
195
- api_key=api_key,
196
- path=f"/api/v1/douyin/monitor/creators/{args.creator_id}/disable",
197
- )
198
- )
199
- return
200
-
201
- if args.command == "reset-creator":
202
- print_json(
203
- request_json(
204
- MODULE_SPEC,
205
- method="POST",
206
- base_url=base_url,
207
- api_key=api_key,
208
- path=f"/api/v1/douyin/monitor/creators/{args.creator_id}/reset",
209
- )
210
- )
211
- return
212
-
213
- if args.command == "submit":
214
- print_json(
215
- request_json(
216
- MODULE_SPEC,
217
- method="POST",
218
- base_url=base_url,
219
- api_key=api_key,
220
- path="/api/v1/douyin/monitor/run",
221
- payload=load_json_file(args.json_file),
222
- )
223
- )
224
- return
225
-
226
- if args.command == "list-runs":
227
- query = f"/api/v1/douyin/monitor/runs?page={args.page}&page_size={args.page_size}"
228
- if args.status:
229
- query += f"&status={args.status}"
230
- print_json(
231
- request_json(MODULE_SPEC, method="GET", base_url=base_url, api_key=api_key, path=query)
232
- )
233
- return
234
-
235
- if args.command == "run-detail":
236
- print_json(
237
- request_json(
238
- MODULE_SPEC,
239
- method="GET",
240
- base_url=base_url,
241
- api_key=api_key,
242
- path=f"/api/v1/douyin/monitor/runs/{args.run_id}",
243
- )
244
- )
245
- return
246
-
247
- if args.command == "task":
248
- print_json(
249
- request_json(
250
- MODULE_SPEC,
251
- method="GET",
252
- base_url=base_url,
253
- api_key=api_key,
254
- path=f"/api/v1/tasks/{args.task_id}",
255
- )
256
- )
257
- return
258
-
259
- if args.command == "results":
260
- print_json(
261
- request_json(
262
- MODULE_SPEC,
263
- method="GET",
264
- base_url=base_url,
265
- api_key=api_key,
266
- path=f"/api/v1/tasks/{args.task_id}/results",
267
- )
268
- )
269
- return
270
-
271
-
272
- if __name__ == "__main__":
273
- main()
@@ -1,3 +0,0 @@
1
- display_name: douyin-topic-discovery-9000AI
2
- short_description: 通过 /douyin-topic-discovery-9000AI 获取热点榜和搜索流结果,并落到本地 output 文件
3
- default_prompt: 帮我通过 /douyin-topic-discovery-9000AI 查抖音热点榜或搜索流,把结果落到 output 文件里,再给我一个简短摘要。