Skip to content

Commit bb6d870

Browse files
thinkthinkingclaude
andcommitted
feat(cli): wechat draft create → wechat post,-v/--version 输出版本号
- wechat post 取代 wechat draft create,收敛参数:--title/--author/--cover 必填,--markdown-file 与 --html-file 二选一(cobra 内置校验,统一包装 成 INVALID_INPUT JSON) - 根命令 -v/--version 输出版本信息 JSON;移除 `version` 子命令;--verbose 改为长选项(让出 -v) - README 重写:命令更新为 wechat post、补充手动编辑配置文件的方式、修正 wewrite 上游链接 - SEO/GEO:GitHub 仓库 description/homepage/topics + npm 包 description/ keywords/homepage/bugs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6b70d47 commit bb6d870

7 files changed

Lines changed: 208 additions & 199 deletions

File tree

README.md

Lines changed: 126 additions & 117 deletions
Large diffs are not rendered by default.

internal/cli/root.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ import (
1010
"github.com/spf13/cobra"
1111
"github.com/thinkthinking/cli/internal/app"
1212
"github.com/thinkthinking/cli/internal/core/output"
13+
"github.com/thinkthinking/cli/internal/version"
1314
)
1415

1516
// 全局 flag 的值容器。cobra 在 parse 后填充,PersistentPreRun 据此装配 container。
1617
type globalFlags struct {
17-
configPath string
18-
pretty bool
19-
quiet bool
20-
noColor bool
21-
traceID string
22-
verbose bool
18+
configPath string
19+
pretty bool
20+
quiet bool
21+
noColor bool
22+
traceID string
23+
verbose bool
24+
showVersion bool
2325
}
2426

2527
// container 是装配好的服务集合,供各子命令通过 currentContainer() 获取。
@@ -42,10 +44,16 @@ func NewRootCmd() *cobra.Command {
4244
// 避免非 JSON 文本污染 stdout。
4345
SilenceErrors: true,
4446
SilenceUsage: true,
45-
// 根命令无子命令时打印帮助。关键:强制把帮助写到 stderr,保持 stdout
46-
// 纯净(Agent 契约:stdout 只允许 JSON envelope)。cobra 默认 Help 走 stdout
47-
// 这里显式重定向。
47+
// 根命令:`-v`/`--version` 输出版本信息(JSON envelope,保持 Agent 契约);
48+
// 否则打印帮助。关键:把帮助写到 stderr,保持 stdout 纯净(stdout 只允许
49+
// JSON envelope)。cobra 默认 Help 走 stdout,这里显式重定向。
4850
RunE: func(cmd *cobra.Command, args []string) error {
51+
if gFlags.showVersion {
52+
if err := container.Output.Success(version.Get()); err != nil {
53+
return output.Wrap(err, output.CodeInternalError)
54+
}
55+
return nil
56+
}
4957
cmd.SetOut(cmd.ErrOrStderr())
5058
return cmd.Help()
5159
},
@@ -68,10 +76,13 @@ func NewRootCmd() *cobra.Command {
6876
pf.BoolVar(&gFlags.quiet, "quiet", false, "抑制非必要的 stderr 输出")
6977
pf.BoolVar(&gFlags.noColor, "no-color", false, "禁用彩色输出(预留,第一期默认无颜色)")
7078
pf.StringVar(&gFlags.traceID, "trace-id", "", "调用方追踪 ID,透传到日志便于排查")
71-
pf.BoolVarP(&gFlags.verbose, "verbose", "v", false, "输出 debug 级别日志到 stderr")
79+
pf.BoolVar(&gFlags.verbose, "verbose", false, "输出 debug 级别日志到 stderr")
80+
81+
// `-v` / `--version` 输出版本号。只在根命令上注册(非持久化),
82+
// 由根命令的 RunE 处理。
83+
root.Flags().BoolVarP(&gFlags.showVersion, "version", "v", false, "输出版本信息(JSON)")
7284

7385
// 挂载子命令。
74-
root.AddCommand(newVersionCmd())
7586
root.AddCommand(newInitCmd())
7687
root.AddCommand(newConfigCmd())
7788
root.AddCommand(newWeChatCmd())

internal/cli/version.go

Lines changed: 0 additions & 23 deletions
This file was deleted.

internal/cli/wechat.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import (
88
func newWeChatCmd() *cobra.Command {
99
cmd := &cobra.Command{
1010
Use: "wechat",
11-
Short: "微信公众号能力:convert / draft",
11+
Short: "微信公众号能力:convert / post",
1212
Args: cobra.NoArgs,
1313
RunE: func(cmd *cobra.Command, args []string) error {
1414
cmd.SetOut(cmd.ErrOrStderr())
1515
return cmd.Help()
1616
},
1717
}
1818
cmd.AddCommand(newWeChatConvertCmd())
19-
cmd.AddCommand(newWeChatDraftCmd())
19+
cmd.AddCommand(newWeChatPostCmd())
2020
return cmd
2121
}
Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,13 @@ import (
1212
"github.com/thinkthinking/cli/internal/core/wechat"
1313
)
1414

15-
// newWeChatDraftCmd 是 `thinkthinking wechat draft` 父命令。
16-
func newWeChatDraftCmd() *cobra.Command {
17-
cmd := &cobra.Command{
18-
Use: "draft",
19-
Short: "微信公众号草稿:create",
20-
Args: cobra.NoArgs,
21-
RunE: func(cmd *cobra.Command, args []string) error {
22-
cmd.SetOut(cmd.ErrOrStderr())
23-
return cmd.Help()
24-
},
25-
}
26-
cmd.AddCommand(newWeChatDraftCreateCmd())
27-
return cmd
28-
}
29-
30-
// newWeChatDraftCreateCmd 实现 `thinkthinking wechat draft create`。
31-
func newWeChatDraftCreateCmd() *cobra.Command {
15+
// newWeChatPostCmd 实现 `thinkthinking wechat post`:把 Markdown/HTML 发布为
16+
// 微信公众号草稿(自动转换、上传正文本地图片、上传封面)。
17+
//
18+
// 设计取舍:post 是面向「发一篇文章」的高频命令,刻意收敛参数——
19+
// --title / --author / --cover 三者必填,--markdown-file 与 --html-file 二选一,
20+
// 由 cobra 的内置校验给出清晰错误(统一包装成 INVALID_INPUT JSON)。
21+
func newWeChatPostCmd() *cobra.Command {
3222
var (
3323
title string
3424
markdownFile string
@@ -37,35 +27,28 @@ func newWeChatDraftCreateCmd() *cobra.Command {
3727
author string
3828
digest string
3929
cover string
40-
coverMediaID string
4130
noUpload bool
4231
)
4332

4433
cmd := &cobra.Command{
45-
Use: "create",
46-
Short: "创建微信公众号草稿(支持 Markdown 自动转换与本地图片上传)",
47-
Args: cobra.NoArgs,
48-
RunE: func(cmd *cobra.Command, args []string) error {
49-
// title 必填。
50-
if strings.TrimSpace(title) == "" {
51-
return output.New(output.CodeInvalidInput, "--title is required")
52-
}
53-
// markdown-file 与 html-file 二选一。
54-
if (markdownFile == "") == (htmlFile == "") {
55-
return output.New(output.CodeInvalidInput, "exactly one of --markdown-file or --html-file is required")
56-
}
34+
Use: "post",
35+
Short: "把 Markdown/HTML 发布为微信公众号草稿(自动转换 + 上传图片与封面)",
36+
Long: `把一篇文章发布为微信公众号草稿。
5737
38+
自动完成:Markdown → 微信兼容 HTML → 上传正文本地图片并回填 URL → 上传封面 → 写入草稿箱。
39+
--title / --author / --cover 必填;正文来源 --markdown-file 与 --html-file 二选一。`,
40+
Args: cobra.NoArgs,
41+
RunE: func(cmd *cobra.Command, args []string) error {
5842
// 凭证预检:缺失则返回结构化 WECHAT_AUTH_ERROR(不 panic)。
5943
if aerr := checkWeChatCredentials(); aerr != nil {
6044
return aerr
6145
}
6246

6347
input := wechat.CreateDraftInput{
6448
Title: title,
65-
Author: firstNonEmpty(author, container.Config.WeChat.DefaultAuthor),
49+
Author: author,
6650
Digest: digest,
6751
CoverImage: cover,
68-
CoverMediaID: coverMediaID,
6952
UploadImages: !noUpload,
7053
}
7154

@@ -110,14 +93,21 @@ func newWeChatDraftCreateCmd() *cobra.Command {
11093

11194
f := cmd.Flags()
11295
f.StringVar(&title, "title", "", "文章标题(必填)")
96+
f.StringVar(&author, "author", "", "作者(必填)")
97+
f.StringVar(&cover, "cover", "", "本地封面图路径,上传为 thumb_media_id(必填)")
11398
f.StringVar(&markdownFile, "markdown-file", "", "Markdown 文件路径(与 --html-file 二选一)")
11499
f.StringVar(&htmlFile, "html-file", "", "HTML 文件路径(与 --markdown-file 二选一)")
115-
f.StringVarP(&theme, "theme", "t", "", "主题名(仅 markdown 路径,默认读配置)")
116-
f.StringVar(&author, "author", "", "作者(默认读配置 wechat.default_author)")
100+
f.StringVarP(&theme, "theme", "t", "", "主题名(仅 markdown 路径,默认读配置 wechat.default_theme)")
117101
f.StringVar(&digest, "digest", "", "摘要(默认用转换生成的摘要)")
118-
f.StringVar(&cover, "cover", "", "本地封面图路径(上传为 thumb_media_id)")
119-
f.StringVar(&coverMediaID, "cover-media-id", "", "已有的封面 media_id(与 --cover 二选一)")
120102
f.BoolVar(&noUpload, "no-upload-images", false, "禁用正文本地图片自动上传")
103+
104+
// 必填与互斥约束交给 cobra:校验失败返回的 error 由 Execute() 统一包装成
105+
// INVALID_INPUT 的 JSON envelope,保持 Agent 契约。
106+
_ = cmd.MarkFlagRequired("title")
107+
_ = cmd.MarkFlagRequired("author")
108+
_ = cmd.MarkFlagRequired("cover")
109+
cmd.MarkFlagsOneRequired("markdown-file", "html-file")
110+
cmd.MarkFlagsMutuallyExclusive("markdown-file", "html-file")
121111
return cmd
122112
}
123113

@@ -172,7 +162,7 @@ func mapWeChatError(err error) *output.AppError {
172162
}
173163
}
174164

175-
// firstNonEmpty 返回第一个非空字符串
165+
// firstNonEmpty 返回第一个非空(去空白后非空)字符串
176166
func firstNonEmpty(values ...string) string {
177167
for _, v := range values {
178168
if strings.TrimSpace(v) != "" {

npm/package/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @thinkthinking/cli
22

3-
面向 **LLM Agent、自动化脚本和开发者** 的本地 CLI 工具箱。第一期聚焦微信公众号能力:将 Markdown 转换为微信公众号兼容 HTML,并上传草稿。所有命令默认输出统一 JSON envelope。
3+
面向 **LLM Agent、自动化脚本与开发者** 的本地 CLI 工具箱。第一期聚焦微信公众号:把 **Markdown 一键转成微信公众号兼容 HTML** 并直接**发布草稿**。所有命令输出统一 JSON envelope,便于 Claude Code、Codex 等 Agent 或脚本稳定解析
44

55
## 安装
66

@@ -16,8 +16,14 @@ npm install -g @thinkthinking/cli
1616
thinkthinking init
1717
thinkthinking config set wechat.app_id wx_your_appid
1818
thinkthinking config set wechat.app_secret your_appsecret
19+
20+
# Markdown → 微信公众号 HTML
1921
thinkthinking wechat convert --input article.md
20-
thinkthinking wechat draft create --markdown-file article.md --title "标题"
22+
23+
# 一步发布为草稿(自动转换 + 上传正文图片与封面)
24+
thinkthinking wechat post \
25+
--markdown-file article.md \
26+
--title "标题" --author "你的名字" --cover cover.jpg
2127
```
2228

2329
> 若在极少数环境下安装时带了 `--no-optional` / `--omit=optional`,平台子包会被跳过、命令会报「找不到平台二进制」。此时去掉该参数重装即可,或用 [GitHub Releases](https://github.com/thinkthinking/cli/releases) 的一键脚本安装。

npm/package/package.json

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@thinkthinking/cli",
3-
"version": "0.1.2",
4-
"description": "面向 Agent 的个人 CLI 工具箱:Markdown 转微信公众号 HTML,并上传草稿。Go binary 通过 npm 分发",
3+
"version": "0.1.3",
4+
"description": "面向 LLM Agent、自动化脚本与开发者的本地 CLI 工具箱:Markdown 一键转成微信公众号兼容 HTML 并发布草稿,所有命令输出统一 JSON envelope。Go 单文件二进制,npm 装完即用、运行时不依赖 Node",
55
"bin": {
66
"thinkthinking": "bin/thinkthinking.js"
77
},
@@ -14,19 +14,35 @@
1414
"node": ">=16"
1515
},
1616
"optionalDependencies": {
17-
"@thinkthinking/cli-darwin-x64": "0.1.2",
18-
"@thinkthinking/cli-darwin-arm64": "0.1.2",
19-
"@thinkthinking/cli-linux-x64": "0.1.2",
20-
"@thinkthinking/cli-linux-arm64": "0.1.2",
21-
"@thinkthinking/cli-win32-x64": "0.1.2"
17+
"@thinkthinking/cli-darwin-x64": "0.1.3",
18+
"@thinkthinking/cli-darwin-arm64": "0.1.3",
19+
"@thinkthinking/cli-linux-x64": "0.1.3",
20+
"@thinkthinking/cli-linux-arm64": "0.1.3",
21+
"@thinkthinking/cli-win32-x64": "0.1.3"
2222
},
2323
"keywords": [
2424
"wechat",
25+
"weixin",
26+
"wechat-mp",
27+
"gongzhonghao",
28+
"微信公众号",
2529
"markdown",
30+
"markdown-to-html",
31+
"markdown-to-wechat",
2632
"cli",
33+
"golang",
2734
"agent",
35+
"llm-agent",
36+
"ai-agent",
37+
"mcp",
38+
"content-publishing",
39+
"json-output",
2840
"thinkthinking"
2941
],
42+
"homepage": "https://github.com/thinkthinking/cli#readme",
43+
"bugs": {
44+
"url": "https://github.com/thinkthinking/cli/issues"
45+
},
3046
"license": "MIT",
3147
"repository": {
3248
"type": "git",

0 commit comments

Comments
 (0)