Skip to content

Commit cbbf64b

Browse files
thinkthinkingclaude
andcommitted
feat(cli): post/convert 改用位置参数按后缀自动判断;强化 -h 帮助;升级 goreleaser-action
- wechat post <file>:正文文件作为位置参数,按后缀自动判断 .md(转换)/.html(直传), 移除 --markdown-file / --html-file,降低 Agent 与用户操作成本 - wechat convert [file]:同样支持位置参数(兼容 --input / --stdin) - 帮助强化(Agent 主要靠 -h 学用法):root 加输出契约+常用流程+示例; wechat 父命令 Long 完整列出 convert/post 所有用法;convert/post 补 Long+Example - ci: goreleaser-action v6 → v7(运行时 Node 24,消除 Node 20 弃用告警) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent bb6d870 commit cbbf64b

8 files changed

Lines changed: 179 additions & 80 deletions

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ jobs:
3535
cache: true
3636

3737
- name: Run GoReleaser
38-
uses: goreleaser/goreleaser-action@v6
38+
# v7 起 action 运行时升级到 Node 24(v6 跑在 Node 20,已被 GitHub 标记弃用)。
39+
# 输入 distribution/version/args 与 v6 兼容。
40+
uses: goreleaser/goreleaser-action@v7
3941
with:
4042
distribution: goreleaser
4143
version: "~> v2"

README.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ thinkthinking config set wechat.app_id wx_your_appid
5858
thinkthinking config set wechat.app_secret your_appsecret
5959

6060
# 3. Markdown → 微信公众号 HTML
61-
thinkthinking wechat convert --input article.md
61+
thinkthinking wechat convert article.md
6262

6363
# 4. 一步发布为草稿(自动转换 + 上传正文图片与封面)
64-
thinkthinking wechat post \
65-
--markdown-file article.md \
64+
thinkthinking wechat post article.md \
6665
--title "文章标题" --author "你的名字" --cover cover.jpg
6766
```
6867

@@ -78,18 +77,22 @@ thinkthinking wechat post \
7877
| `thinkthinking config get <key>` | 读取配置项 |
7978
| `thinkthinking config set <key> <value>` | 设置配置项 |
8079
| `thinkthinking config list` | 输出完整配置(敏感字段脱敏) |
81-
| `thinkthinking wechat convert` | Markdown → 微信公众号 HTML |
82-
| `thinkthinking wechat post` | 发布微信公众号草稿 |
80+
| `thinkthinking wechat convert <file>` | Markdown → 微信公众号 HTML |
81+
| `thinkthinking wechat post <file>` | 发布微信公众号草稿 |
8382

8483
全局 flags:`--config` `--pretty` `--quiet` `--no-color` `--trace-id` `--verbose`
8584

85+
> 提示:Agent 可对任意命令加 `-h` 查看完整用法与示例,例如 `thinkthinking wechat -h``thinkthinking wechat post -h`
86+
8687
### wechat convert
8788

89+
正文文件作为位置参数传入(也兼容 `--input` / `--stdin`):
90+
8891
```bash
89-
thinkthinking wechat convert --input article.md
90-
thinkthinking wechat convert --input article.md --output dist/article.html
92+
thinkthinking wechat convert article.md
93+
thinkthinking wechat convert article.md --output dist/article.html
94+
thinkthinking wechat convert article.md --theme midnight
9195
thinkthinking wechat convert --stdin < article.md
92-
thinkthinking wechat convert --input article.md --theme midnight
9396
```
9497

9598
内置主题:`default``minimal``midnight``newspaper``tech-modern`
@@ -115,10 +118,10 @@ thinkthinking wechat convert --input article.md --theme midnight
115118

116119
```bash
117120
# macOS:转换并写入剪贴板,然后去公众号 Cmd+V
118-
thinkthinking wechat convert --input article.md --copy
121+
thinkthinking wechat convert article.md --copy
119122

120123
# 任意平台:生成预览页并打开浏览器,页面内一键复制
121-
thinkthinking wechat convert --input article.md --preview
124+
thinkthinking wechat convert article.md --preview
122125
```
123126

124127
- `--copy` 成功后 JSON 含 `"copied": true`;非 macOS 返回 `PLATFORM_NOT_SUPPORTED`,请改用 `--preview``--output`
@@ -127,28 +130,25 @@ thinkthinking wechat convert --input article.md --preview
127130

128131
### wechat post
129132

130-
把一篇文章发布为草稿。`--title``--author``--cover` 必填;正文来源 `--markdown-file``--html-file` 二选一
133+
把一篇文章发布为草稿。正文文件作为位置参数传入,**按后缀自动判断类型**——`.md` / `.markdown` 自动转换,`.html` / `.htm` 直接当正文。`--title``--author``--cover` 三者必填
131134

132135
```bash
133136
# 从 Markdown(自动转换 + 上传正文本地图片)
134-
thinkthinking wechat post \
135-
--markdown-file article.md \
137+
thinkthinking wechat post article.md \
136138
--title "标题" --author "你的名字" --cover cover.jpg
137139

138-
# 从已有 HTML
139-
thinkthinking wechat post \
140-
--html-file article.html \
140+
# 从已有 HTML(直传,不转换)
141+
thinkthinking wechat post article.html \
141142
--title "标题" --author "你的名字" --cover cover.jpg
142143
```
143144

144-
| Flag | 必填 | 说明 |
145+
| 参数 | 必填 | 说明 |
145146
|------|:---:|------|
147+
| `<file>` || 正文文件,按后缀自动判断:`.md`/`.markdown` 转换,`.html`/`.htm` 直传 |
146148
| `--title` || 文章标题 |
147149
| `--author` || 作者 |
148150
| `--cover` || 本地封面图路径,上传为 `thumb_media_id` |
149-
| `--markdown-file` | 二选一 | Markdown 文件路径 |
150-
| `--html-file` | 二选一 | HTML 文件路径 |
151-
| `--theme, -t` | | 主题名(仅 markdown 路径,默认读配置) |
151+
| `--theme, -t` | | 主题名(仅 `.md` 正文生效,默认读配置) |
152152
| `--digest` | | 摘要(默认用转换生成的摘要) |
153153
| `--no-upload-images` | | 禁用正文本地图片自动上传 |
154154

internal/cli/root.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,28 @@ func NewRootCmd() *cobra.Command {
3838
Short: "thinkthinking — 面向 Agent 的个人 CLI 工具箱",
3939
Long: `thinkthinking 是一个面向大语言模型 Agent、自动化脚本和开发者的本地 CLI 工具箱。
4040
41-
第一期聚焦微信公众号能力:将 Markdown 转换为微信公众号兼容 HTML,并上传草稿。
42-
所有命令默认输出统一 JSON envelope,便于 Agent 稳定解析。`,
41+
第一期聚焦微信公众号能力:将 Markdown 转换为微信公众号兼容 HTML,并发布草稿。
42+
43+
输出契约(Agent 友好):
44+
- 所有命令输出统一 JSON envelope:{ "ok": bool, "data": ..., "error": ... }
45+
- JSON 走 stdout,日志/警告走 stderr —— 只 parse stdout 即可。
46+
- 加 --pretty 美化 JSON;任何子命令加 -h 查看用法与示例。
47+
48+
常用流程:
49+
thinkthinking init # 创建配置 ~/.thinkthinking/config.yaml
50+
thinkthinking config set wechat.app_id <id> # 写入凭证
51+
thinkthinking config set wechat.app_secret <s>
52+
thinkthinking wechat convert article.md # Markdown → 公众号 HTML
53+
thinkthinking wechat post article.md \ # 一步发布为草稿
54+
--title "标题" --author "作者" --cover cover.jpg`,
55+
Example: ` # 查看版本
56+
thinkthinking --version
57+
58+
# 转换并写入剪贴板(macOS),到公众号 Cmd+V 直接渲染
59+
thinkthinking wechat convert article.md --copy
60+
61+
# 发布草稿(按后缀自动判断 .md 转换 / .html 直传)
62+
thinkthinking wechat post article.md --title "标题" --author "作者" --cover cover.jpg`,
4363
// 我们自行用 JSON envelope 输出错误,禁用 cobra 的默认错误/用法打印,
4464
// 避免非 JSON 文本污染 stdout。
4565
SilenceErrors: true,

internal/cli/wechat.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,41 @@ import (
88
func newWeChatCmd() *cobra.Command {
99
cmd := &cobra.Command{
1010
Use: "wechat",
11-
Short: "微信公众号能力:convert / post",
12-
Args: cobra.NoArgs,
11+
Short: "微信公众号能力:convert(转 HTML) / post(发布草稿)",
12+
Long: `微信公众号能力。两个子命令:
13+
14+
convert Markdown → 微信公众号兼容 HTML(不联网,纯转换)
15+
post 把一篇文章发布为草稿(联网:转换 → 上传图片 → 上传封面 → 写草稿箱)
16+
17+
前置:需先配置凭证(仅 post 联网时需要):
18+
thinkthinking config set wechat.app_id <appid>
19+
thinkthinking config set wechat.app_secret <secret>
20+
# 或用环境变量 WECHAT_APP_ID / WECHAT_APP_SECRET
21+
22+
------------------------------------------------------------------
23+
convert —— Markdown 转 HTML
24+
------------------------------------------------------------------
25+
thinkthinking wechat convert <file> # 转换,HTML 在 data.html
26+
thinkthinking wechat convert <file> -t midnight # 指定主题
27+
thinkthinking wechat convert <file> --copy # 写富文本剪贴板(macOS)
28+
thinkthinking wechat convert <file> --preview # 打开浏览器预览页
29+
thinkthinking wechat convert <file> -o out.html # 写文件
30+
thinkthinking wechat convert --stdin < file.md # 从 stdin 读
31+
内置主题:default / minimal / midnight / newspaper / tech-modern
32+
关闭增强:--no-darkmode / --no-footnotes / --no-containers
33+
34+
------------------------------------------------------------------
35+
post —— 发布草稿(必填 --title / --author / --cover)
36+
------------------------------------------------------------------
37+
thinkthinking wechat post article.md \
38+
--title "标题" --author "作者" --cover cover.jpg
39+
thinkthinking wechat post article.html \ # .html 直传,不转换
40+
--title "标题" --author "作者" --cover cover.jpg
41+
正文文件按后缀自动判断:.md/.markdown 转换;.html/.htm 直传。
42+
本地图片自动上传并回填 URL(--no-upload-images 可关)。
43+
44+
各子命令更多细节见 ` + "`thinkthinking wechat <cmd> -h`" + `。`,
45+
Args: cobra.NoArgs,
1346
RunE: func(cmd *cobra.Command, args []string) error {
1447
cmd.SetOut(cmd.ErrOrStderr())
1548
return cmd.Help()

internal/cli/wechat_convert.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,43 @@ func newWeChatConvertCmd() *cobra.Command {
2929
)
3030

3131
cmd := &cobra.Command{
32-
Use: "convert",
32+
Use: "convert [file]",
3333
Short: "将 Markdown 转换为微信公众号兼容 HTML",
34-
Args: cobra.NoArgs,
34+
Long: `将 Markdown 转换为微信公众号兼容 HTML。
35+
36+
输入来源(三选一,优先级:位置参数 > --input > --stdin):
37+
thinkthinking wechat convert article.md # 位置参数(推荐)
38+
thinkthinking wechat convert --input a.md
39+
thinkthinking wechat convert --stdin < a.md
40+
41+
默认把 HTML 放进 JSON 的 data.html;指定 --output 则写文件、JSON 只返回路径。
42+
43+
投递到公众号编辑器(直接复制 HTML 文本会显示成源码,需用下面三种方式之一):
44+
--copy 写入富文本剪贴板(仅 macOS),到公众号 Cmd+V 即渲染
45+
--preview 生成预览页并打开浏览器,页面内一键复制
46+
--output 写 .html 文件
47+
或改用 ` + "`wechat post`" + ` 全自动写入草稿箱,无需手动投递。`,
48+
Example: ` # 转换并打印 HTML(在 data.html 字段)
49+
thinkthinking wechat convert article.md --pretty
50+
51+
# 换主题
52+
thinkthinking wechat convert article.md --theme midnight
53+
54+
# 转换并写入剪贴板(macOS)
55+
thinkthinking wechat convert article.md --copy
56+
57+
# 转换并打开浏览器预览页
58+
thinkthinking wechat convert article.md --preview
59+
60+
# 写到文件
61+
thinkthinking wechat convert article.md --output dist/article.html`,
62+
Args: cobra.MaximumNArgs(1),
3563
RunE: func(cmd *cobra.Command, args []string) error {
36-
// 读取 Markdown 来源:--stdin 或 --input 二选一。
64+
// 位置参数(若有)覆盖 --input:thinkthinking wechat convert article.md
65+
if len(args) == 1 {
66+
input = args[0]
67+
}
68+
// 读取 Markdown 来源:位置参数/--input 或 --stdin。
3769
markdown, srcLabel, aerr := readMarkdownSource(cmd, input, stdin)
3870
if aerr != nil {
3971
return aerr
@@ -125,7 +157,7 @@ func newWeChatConvertCmd() *cobra.Command {
125157
}
126158

127159
f := cmd.Flags()
128-
f.StringVarP(&input, "input", "i", "", "Markdown 输入文件路径")
160+
f.StringVarP(&input, "input", "i", "", "Markdown 输入文件路径(等价于位置参数 [file])")
129161
f.StringVarP(&out, "output", "o", "", "输出 HTML 文件路径(指定后 JSON 不返回完整 html)")
130162
f.BoolVar(&stdin, "stdin", false, "从 stdin 读取 Markdown")
131163
f.StringVarP(&theme, "theme", "t", "", "主题名(默认读配置 wechat.default_theme)")
@@ -147,7 +179,7 @@ func readMarkdownSource(cmd *cobra.Command, input string, stdin bool) (string, s
147179
return string(data), "<stdin>", nil
148180
}
149181
if input == "" {
150-
return "", "", output.New(output.CodeInvalidInput, "either --input or --stdin is required")
182+
return "", "", output.New(output.CodeInvalidInput, "missing Markdown input: pass a file as [file] or --input, or use --stdin")
151183
}
152184
data, err := os.ReadFile(input)
153185
if err != nil {

internal/cli/wechat_post.go

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,69 @@ import (
1212
"github.com/thinkthinking/cli/internal/core/wechat"
1313
)
1414

15-
// newWeChatPostCmd 实现 `thinkthinking wechat post`:把 Markdown/HTML 发布为
16-
// 微信公众号草稿(自动转换、上传正文本地图片、上传封面)。
15+
// markdownExts / htmlExts 是 post 命令按文件后缀自动判断正文类型的依据。
16+
var (
17+
markdownExts = map[string]bool{".md": true, ".markdown": true, ".mdown": true, ".mkd": true}
18+
htmlExts = map[string]bool{".html": true, ".htm": true}
19+
)
20+
21+
// newWeChatPostCmd 实现 `thinkthinking wechat post`:把一篇文章发布为微信公众号草稿
22+
// (自动转换、上传正文本地图片、上传封面)。
1723
//
18-
// 设计取舍:post 是面向「发一篇文章」的高频命令,刻意收敛参数——
19-
// --title / --author / --cover 三者必填,--markdown-file 与 --html-file 二选一,
20-
// 由 cobra 的内置校验给出清晰错误(统一包装成 INVALID_INPUT JSON)。
24+
// 设计取舍:post 是「发一篇文章」的高频命令,刻意收敛参数——
25+
// - 正文文件作为位置参数,按后缀自动判断 Markdown / HTML(.md → 转换,.html → 直传),
26+
// 不再要求用户区分 --markdown-file / --html-file。
27+
// - --title / --author / --cover 三者必填,由 cobra 内置校验给出清晰错误
28+
// (统一包装成 INVALID_INPUT JSON)。
2129
func newWeChatPostCmd() *cobra.Command {
2230
var (
23-
title string
24-
markdownFile string
25-
htmlFile string
26-
theme string
27-
author string
28-
digest string
29-
cover string
30-
noUpload bool
31+
title string
32+
theme string
33+
author string
34+
digest string
35+
cover string
36+
noUpload bool
3137
)
3238

3339
cmd := &cobra.Command{
34-
Use: "post",
35-
Short: "把 Markdown/HTML 发布为微信公众号草稿(自动转换 + 上传图片与封面)",
40+
Use: "post <file>",
41+
Short: "把一篇文章发布为微信公众号草稿(按后缀自动转换 + 上传图片与封面)",
3642
Long: `把一篇文章发布为微信公众号草稿。
3743
38-
自动完成:Markdown → 微信兼容 HTML → 上传正文本地图片并回填 URL → 上传封面 → 写入草稿箱。
39-
--title / --author / --cover 必填;正文来源 --markdown-file 与 --html-file 二选一。`,
40-
Args: cobra.NoArgs,
44+
<file> 是正文文件,按后缀自动判断类型:
45+
.md / .markdown → 转换为微信兼容 HTML 后发布
46+
.html / .htm → 直接作为正文发布
47+
48+
自动完成:(必要时转换)→ 上传正文本地图片并回填 URL → 上传封面 → 写入草稿箱。
49+
--title / --author / --cover 三者必填。
50+
51+
示例:
52+
thinkthinking wechat post article.md --title "标题" --author "作者" --cover cover.jpg
53+
thinkthinking wechat post article.html --title "标题" --author "作者" --cover cover.jpg`,
54+
Args: cobra.ExactArgs(1),
4155
RunE: func(cmd *cobra.Command, args []string) error {
56+
file := args[0]
57+
ext := strings.ToLower(filepath.Ext(file))
58+
isMarkdown := markdownExts[ext]
59+
isHTML := htmlExts[ext]
60+
if !isMarkdown && !isHTML {
61+
return output.Newf(output.CodeInvalidInput,
62+
"无法从后缀判断文件类型:%s(支持 .md/.markdown 或 .html/.htm)", file)
63+
}
64+
4265
// 凭证预检:缺失则返回结构化 WECHAT_AUTH_ERROR(不 panic)。
4366
if aerr := checkWeChatCredentials(); aerr != nil {
4467
return aerr
4568
}
4669

70+
data, rerr := os.ReadFile(file)
71+
if rerr != nil {
72+
if os.IsNotExist(rerr) {
73+
return output.Newf(output.CodeFileNotFound, "file not found: %s", file)
74+
}
75+
return output.Wrap(rerr, output.CodeInvalidInput)
76+
}
77+
4778
input := wechat.CreateDraftInput{
4879
Title: title,
4980
Author: author,
@@ -52,26 +83,12 @@ func newWeChatPostCmd() *cobra.Command {
5283
UploadImages: !noUpload,
5384
}
5485

55-
if markdownFile != "" {
56-
data, rerr := os.ReadFile(markdownFile)
57-
if rerr != nil {
58-
if os.IsNotExist(rerr) {
59-
return output.Newf(output.CodeFileNotFound, "markdown file not found: %s", markdownFile)
60-
}
61-
return output.Wrap(rerr, output.CodeInvalidInput)
62-
}
86+
if isMarkdown {
6387
input.Markdown = string(data)
64-
input.MarkdownDir = filepath.Dir(markdownFile)
88+
input.MarkdownDir = filepath.Dir(file)
6589
input.ThemeName = firstNonEmpty(theme, container.Config.WeChat.DefaultTheme)
6690
input.ConvertOptions = wechat.DefaultConvertOptions()
6791
} else {
68-
data, rerr := os.ReadFile(htmlFile)
69-
if rerr != nil {
70-
if os.IsNotExist(rerr) {
71-
return output.Newf(output.CodeFileNotFound, "html file not found: %s", htmlFile)
72-
}
73-
return output.Wrap(rerr, output.CodeInvalidInput)
74-
}
7592
input.HTML = string(data)
7693
}
7794

@@ -95,19 +112,15 @@ func newWeChatPostCmd() *cobra.Command {
95112
f.StringVar(&title, "title", "", "文章标题(必填)")
96113
f.StringVar(&author, "author", "", "作者(必填)")
97114
f.StringVar(&cover, "cover", "", "本地封面图路径,上传为 thumb_media_id(必填)")
98-
f.StringVar(&markdownFile, "markdown-file", "", "Markdown 文件路径(与 --html-file 二选一)")
99-
f.StringVar(&htmlFile, "html-file", "", "HTML 文件路径(与 --markdown-file 二选一)")
100-
f.StringVarP(&theme, "theme", "t", "", "主题名(仅 markdown 路径,默认读配置 wechat.default_theme)")
115+
f.StringVarP(&theme, "theme", "t", "", "主题名(仅 .md 正文生效,默认读配置 wechat.default_theme)")
101116
f.StringVar(&digest, "digest", "", "摘要(默认用转换生成的摘要)")
102117
f.BoolVar(&noUpload, "no-upload-images", false, "禁用正文本地图片自动上传")
103118

104-
// 必填与互斥约束交给 cobra:校验失败返回的 error 由 Execute() 统一包装成
119+
// 必填约束交给 cobra:校验失败返回的 error 由 Execute() 统一包装成
105120
// INVALID_INPUT 的 JSON envelope,保持 Agent 契约。
106121
_ = cmd.MarkFlagRequired("title")
107122
_ = cmd.MarkFlagRequired("author")
108123
_ = cmd.MarkFlagRequired("cover")
109-
cmd.MarkFlagsOneRequired("markdown-file", "html-file")
110-
cmd.MarkFlagsMutuallyExclusive("markdown-file", "html-file")
111124
return cmd
112125
}
113126

npm/package/README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ thinkthinking config set wechat.app_id wx_your_appid
1818
thinkthinking config set wechat.app_secret your_appsecret
1919

2020
# Markdown → 微信公众号 HTML
21-
thinkthinking wechat convert --input article.md
21+
thinkthinking wechat convert article.md
2222

23-
# 一步发布为草稿(自动转换 + 上传正文图片与封面)
24-
thinkthinking wechat post \
25-
--markdown-file article.md \
23+
# 一步发布为草稿(按后缀自动转换 + 上传正文图片与封面)
24+
thinkthinking wechat post article.md \
2625
--title "标题" --author "你的名字" --cover cover.jpg
2726
```
2827

0 commit comments

Comments
 (0)