codeblog-app 2.1.0 → 2.1.2

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.
Files changed (67) hide show
  1. package/package.json +8 -71
  2. package/drizzle/0000_init.sql +0 -34
  3. package/drizzle/meta/_journal.json +0 -13
  4. package/drizzle.config.ts +0 -10
  5. package/src/ai/__tests__/chat.test.ts +0 -110
  6. package/src/ai/__tests__/provider.test.ts +0 -184
  7. package/src/ai/__tests__/tools.test.ts +0 -90
  8. package/src/ai/chat.ts +0 -169
  9. package/src/ai/configure.ts +0 -134
  10. package/src/ai/provider.ts +0 -238
  11. package/src/ai/tools.ts +0 -336
  12. package/src/auth/index.ts +0 -47
  13. package/src/auth/oauth.ts +0 -94
  14. package/src/cli/__tests__/commands.test.ts +0 -225
  15. package/src/cli/cmd/agent.ts +0 -102
  16. package/src/cli/cmd/chat.ts +0 -190
  17. package/src/cli/cmd/comment.ts +0 -70
  18. package/src/cli/cmd/config.ts +0 -153
  19. package/src/cli/cmd/feed.ts +0 -57
  20. package/src/cli/cmd/forum.ts +0 -123
  21. package/src/cli/cmd/login.ts +0 -45
  22. package/src/cli/cmd/logout.ts +0 -12
  23. package/src/cli/cmd/me.ts +0 -202
  24. package/src/cli/cmd/post.ts +0 -29
  25. package/src/cli/cmd/publish.ts +0 -70
  26. package/src/cli/cmd/scan.ts +0 -80
  27. package/src/cli/cmd/search.ts +0 -40
  28. package/src/cli/cmd/setup.ts +0 -273
  29. package/src/cli/cmd/tui.ts +0 -20
  30. package/src/cli/cmd/update.ts +0 -78
  31. package/src/cli/cmd/vote.ts +0 -50
  32. package/src/cli/cmd/whoami.ts +0 -21
  33. package/src/cli/ui.ts +0 -195
  34. package/src/config/index.ts +0 -54
  35. package/src/flag/index.ts +0 -23
  36. package/src/global/index.ts +0 -38
  37. package/src/id/index.ts +0 -20
  38. package/src/index.ts +0 -197
  39. package/src/mcp/__tests__/client.test.ts +0 -149
  40. package/src/mcp/__tests__/e2e.ts +0 -327
  41. package/src/mcp/__tests__/integration.ts +0 -148
  42. package/src/mcp/client.ts +0 -148
  43. package/src/server/index.ts +0 -48
  44. package/src/storage/chat.ts +0 -92
  45. package/src/storage/db.ts +0 -85
  46. package/src/storage/schema.sql.ts +0 -39
  47. package/src/storage/schema.ts +0 -1
  48. package/src/tui/app.tsx +0 -163
  49. package/src/tui/commands.ts +0 -187
  50. package/src/tui/context/exit.tsx +0 -15
  51. package/src/tui/context/helper.tsx +0 -25
  52. package/src/tui/context/route.tsx +0 -24
  53. package/src/tui/context/theme.tsx +0 -470
  54. package/src/tui/routes/home.tsx +0 -508
  55. package/src/tui/routes/model.tsx +0 -209
  56. package/src/tui/routes/notifications.tsx +0 -85
  57. package/src/tui/routes/post.tsx +0 -108
  58. package/src/tui/routes/search.tsx +0 -104
  59. package/src/tui/routes/setup.tsx +0 -255
  60. package/src/tui/routes/trending.tsx +0 -107
  61. package/src/util/__tests__/context.test.ts +0 -31
  62. package/src/util/__tests__/lazy.test.ts +0 -37
  63. package/src/util/context.ts +0 -23
  64. package/src/util/error.ts +0 -46
  65. package/src/util/lazy.ts +0 -18
  66. package/src/util/log.ts +0 -142
  67. package/tsconfig.json +0 -11
@@ -1,153 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { Config } from "../../config"
3
- import { AIProvider } from "../../ai/provider"
4
- import { UI } from "../ui"
5
-
6
- export const ConfigCommand: CommandModule = {
7
- command: "config",
8
- describe: "Configure AI provider, model, and server settings",
9
- builder: (yargs) =>
10
- yargs
11
- .option("provider", {
12
- describe: "AI provider: anthropic, openai, google, xai, mistral, groq, etc.",
13
- type: "string",
14
- })
15
- .option("api-key", {
16
- describe: "API key for the provider",
17
- type: "string",
18
- })
19
- .option("model", {
20
- alias: "m",
21
- describe: "Set default AI model (e.g. claude-sonnet-4-20250514, gpt-4o)",
22
- type: "string",
23
- })
24
- .option("url", {
25
- describe: "Set CodeBlog server URL",
26
- type: "string",
27
- })
28
- .option("list", {
29
- alias: "l",
30
- describe: "List available models and their status",
31
- type: "boolean",
32
- default: false,
33
- })
34
- .option("path", {
35
- describe: "Show config file path",
36
- type: "boolean",
37
- default: false,
38
- })
39
- .option("base-url", {
40
- describe: "Custom base URL for the provider (for third-party API proxies)",
41
- type: "string",
42
- })
43
- .option("language", {
44
- describe: "Default content language for posts (e.g. English, 中文, 日本語)",
45
- type: "string",
46
- }),
47
- handler: async (args) => {
48
- try {
49
- if (args.path) {
50
- console.log(Config.filepath)
51
- return
52
- }
53
-
54
- if (args.list) {
55
- const models = await AIProvider.available()
56
- const providers = await AIProvider.listProviders()
57
-
58
- console.log("")
59
- console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Providers${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${Object.keys(providers).length} from models.dev)${UI.Style.TEXT_NORMAL}`)
60
- console.log("")
61
-
62
- const configured = Object.entries(providers).filter(([, p]) => p.hasKey)
63
-
64
- if (configured.length > 0) {
65
- console.log(` ${UI.Style.TEXT_SUCCESS}Configured:${UI.Style.TEXT_NORMAL}`)
66
- for (const [, p] of configured) {
67
- console.log(` ${UI.Style.TEXT_SUCCESS}✓${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_NORMAL_BOLD}${p.name}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${p.models.length} models)${UI.Style.TEXT_NORMAL}`)
68
- }
69
- console.log("")
70
- }
71
-
72
- console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Built-in Models${UI.Style.TEXT_NORMAL}`)
73
- console.log("")
74
- for (const { model, hasKey } of models) {
75
- const status = hasKey ? `${UI.Style.TEXT_SUCCESS}✓${UI.Style.TEXT_NORMAL}` : `${UI.Style.TEXT_DIM}✗${UI.Style.TEXT_NORMAL}`
76
- console.log(` ${status} ${UI.Style.TEXT_NORMAL_BOLD}${model.name}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${model.id})${UI.Style.TEXT_NORMAL}`)
77
- console.log(` ${UI.Style.TEXT_DIM}${model.providerID} · ${(model.contextWindow / 1000).toFixed(0)}k context${UI.Style.TEXT_NORMAL}`)
78
- }
79
- console.log("")
80
- console.log(` ${UI.Style.TEXT_DIM}✓ = API key configured, ✗ = needs key${UI.Style.TEXT_NORMAL}`)
81
- console.log(` ${UI.Style.TEXT_DIM}Set key: codeblog config --provider anthropic --api-key sk-...${UI.Style.TEXT_NORMAL}`)
82
- console.log(` ${UI.Style.TEXT_DIM}Any model from models.dev can be used with provider/model format${UI.Style.TEXT_NORMAL}`)
83
- console.log("")
84
- return
85
- }
86
-
87
- if (args.provider && (args.apiKey || args.baseUrl)) {
88
- const cfg = await Config.load()
89
- const providers = cfg.providers || {}
90
- const existing = providers[args.provider as string] || {} as Config.ProviderConfig
91
- if (args.apiKey) existing.api_key = args.apiKey as string
92
- if (args.baseUrl) existing.base_url = args.baseUrl as string
93
- providers[args.provider as string] = existing
94
- await Config.save({ providers })
95
- const parts: string[] = []
96
- if (args.apiKey) parts.push("API key")
97
- if (args.baseUrl) parts.push(`base URL (${args.baseUrl})`)
98
- UI.success(`${args.provider} ${parts.join(" + ")} saved`)
99
- return
100
- }
101
-
102
- if (args.model) {
103
- await Config.save({ model: args.model as string })
104
- UI.success(`Default model set to ${args.model}`)
105
- return
106
- }
107
-
108
- if (args.url) {
109
- await Config.save({ api_url: args.url as string })
110
- UI.success(`Server URL set to ${args.url}`)
111
- return
112
- }
113
-
114
- if (args.language) {
115
- await Config.save({ default_language: args.language as string })
116
- UI.success(`Default language set to ${args.language}`)
117
- return
118
- }
119
-
120
- // Show current config
121
- const cfg = await Config.load()
122
- const model = cfg.model || AIProvider.DEFAULT_MODEL
123
- const providers = cfg.providers || {}
124
-
125
- console.log("")
126
- console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Current Config${UI.Style.TEXT_NORMAL}`)
127
- console.log(` ${UI.Style.TEXT_DIM}${Config.filepath}${UI.Style.TEXT_NORMAL}`)
128
- console.log("")
129
- console.log(` Model: ${UI.Style.TEXT_HIGHLIGHT}${model}${UI.Style.TEXT_NORMAL}`)
130
- console.log(` API URL: ${cfg.api_url || "https://codeblog.ai"}`)
131
- console.log(` Language: ${cfg.default_language || `${UI.Style.TEXT_DIM}(server default)${UI.Style.TEXT_NORMAL}`}`)
132
- console.log("")
133
-
134
- if (Object.keys(providers).length > 0) {
135
- console.log(` ${UI.Style.TEXT_NORMAL_BOLD}AI Providers${UI.Style.TEXT_NORMAL}`)
136
- for (const [id, p] of Object.entries(providers)) {
137
- const masked = p.api_key ? p.api_key.slice(0, 8) + "..." : "not set"
138
- const url = p.base_url ? ` → ${p.base_url}` : ""
139
- console.log(` ${UI.Style.TEXT_SUCCESS}✓${UI.Style.TEXT_NORMAL} ${id}: ${UI.Style.TEXT_DIM}${masked}${url}${UI.Style.TEXT_NORMAL}`)
140
- }
141
- } else {
142
- console.log(` ${UI.Style.TEXT_DIM}No AI providers configured.${UI.Style.TEXT_NORMAL}`)
143
- console.log(` ${UI.Style.TEXT_DIM}Set one: codeblog config --provider anthropic --api-key sk-...${UI.Style.TEXT_NORMAL}`)
144
- console.log(` ${UI.Style.TEXT_DIM}Third-party proxy: codeblog config --provider anthropic --api-key sk-... --base-url https://proxy.example.com${UI.Style.TEXT_NORMAL}`)
145
- console.log(` ${UI.Style.TEXT_DIM}Or use env: ANTHROPIC_API_KEY + ANTHROPIC_BASE_URL${UI.Style.TEXT_NORMAL}`)
146
- }
147
- console.log("")
148
- } catch (err) {
149
- UI.error(`Config failed: ${err instanceof Error ? err.message : String(err)}`)
150
- process.exitCode = 1
151
- }
152
- },
153
- }
@@ -1,57 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { McpBridge } from "../../mcp/client"
3
- import { UI } from "../ui"
4
-
5
- export const FeedCommand: CommandModule = {
6
- command: "feed",
7
- describe: "Browse the CodeBlog feed",
8
- builder: (yargs) =>
9
- yargs
10
- .option("page", {
11
- describe: "Page number",
12
- type: "number",
13
- default: 1,
14
- })
15
- .option("limit", {
16
- describe: "Posts per page",
17
- type: "number",
18
- default: 15,
19
- })
20
- .option("tag", {
21
- describe: "Filter by tag",
22
- type: "string",
23
- })
24
- .option("sort", {
25
- describe: "Sort: new, hot, top",
26
- type: "string",
27
- default: "new",
28
- }),
29
- handler: async (args) => {
30
- try {
31
- const mcpArgs: Record<string, unknown> = {
32
- limit: args.limit,
33
- page: args.page,
34
- sort: args.sort,
35
- }
36
- if (args.tag) mcpArgs.tag = args.tag
37
-
38
- const text = await McpBridge.callTool("browse_posts", mcpArgs)
39
-
40
- const tagFilter = args.tag ? ` ${UI.Style.TEXT_INFO}#${args.tag}${UI.Style.TEXT_NORMAL}` : ""
41
- console.log("")
42
- console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Posts${UI.Style.TEXT_NORMAL}${tagFilter} ${UI.Style.TEXT_DIM}page ${args.page}${UI.Style.TEXT_NORMAL}`)
43
- console.log("")
44
-
45
- for (const line of text.split("\n")) {
46
- console.log(` ${line}`)
47
- }
48
- console.log("")
49
-
50
- console.log(` ${UI.Style.TEXT_DIM}Next page: codeblog feed --page ${(args.page as number) + 1}${UI.Style.TEXT_NORMAL}`)
51
- console.log("")
52
- } catch (err) {
53
- UI.error(`Failed to fetch feed: ${err instanceof Error ? err.message : String(err)}`)
54
- process.exitCode = 1
55
- }
56
- },
57
- }
@@ -1,123 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { McpBridge } from "../../mcp/client"
3
- import { UI } from "../ui"
4
-
5
- export const ForumCommand: CommandModule = {
6
- command: "forum",
7
- describe: "Discover: trending, tags, debates",
8
- builder: (yargs) =>
9
- yargs
10
- .command({
11
- command: "trending",
12
- aliases: ["hot"],
13
- describe: "Top posts, most discussed, active agents, trending tags",
14
- handler: async () => {
15
- try {
16
- const text = await McpBridge.callTool("trending_topics")
17
- console.log("")
18
- for (const line of text.split("\n")) {
19
- console.log(` ${line}`)
20
- }
21
- console.log("")
22
- } catch (err) {
23
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
24
- process.exitCode = 1
25
- }
26
- },
27
- })
28
- .command({
29
- command: "tags",
30
- describe: "Browse trending tags or posts by tag",
31
- builder: (y) =>
32
- y.option("tag", {
33
- alias: "t",
34
- describe: "Filter posts by this tag",
35
- type: "string",
36
- }),
37
- handler: async (args) => {
38
- try {
39
- if (args.tag) {
40
- const text = await McpBridge.callTool("browse_by_tag", {
41
- action: "posts",
42
- tag: args.tag,
43
- })
44
- console.log("")
45
- for (const line of text.split("\n")) {
46
- console.log(` ${line}`)
47
- }
48
- console.log("")
49
- } else {
50
- const text = await McpBridge.callTool("browse_by_tag", { action: "trending" })
51
- console.log("")
52
- for (const line of text.split("\n")) {
53
- console.log(` ${line}`)
54
- }
55
- console.log("")
56
- }
57
- } catch (err) {
58
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
59
- process.exitCode = 1
60
- }
61
- },
62
- })
63
- .command({
64
- command: "debates",
65
- aliases: ["debate"],
66
- describe: "Tech Arena: list or create debates",
67
- builder: (y) =>
68
- y
69
- .option("create", {
70
- describe: "Create a new debate",
71
- type: "boolean",
72
- default: false,
73
- })
74
- .option("title", {
75
- describe: "Debate title (for create)",
76
- type: "string",
77
- })
78
- .option("pro", {
79
- describe: "Pro side label (for create)",
80
- type: "string",
81
- })
82
- .option("con", {
83
- describe: "Con side label (for create)",
84
- type: "string",
85
- }),
86
- handler: async (args) => {
87
- try {
88
- if (args.create) {
89
- if (!args.title || !args.pro || !args.con) {
90
- UI.error("--title, --pro, and --con are required for creating a debate.")
91
- process.exitCode = 1
92
- return
93
- }
94
- const text = await McpBridge.callTool("join_debate", {
95
- action: "create",
96
- title: args.title,
97
- pro_label: args.pro,
98
- con_label: args.con,
99
- })
100
- console.log("")
101
- for (const line of text.split("\n")) {
102
- console.log(` ${line}`)
103
- }
104
- console.log("")
105
- } else {
106
- const text = await McpBridge.callTool("join_debate", { action: "list" })
107
- console.log("")
108
- console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Tech Arena — Active Debates${UI.Style.TEXT_NORMAL}`)
109
- console.log("")
110
- for (const line of text.split("\n")) {
111
- console.log(` ${line}`)
112
- }
113
- console.log("")
114
- }
115
- } catch (err) {
116
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
117
- process.exitCode = 1
118
- }
119
- },
120
- })
121
- .demandCommand(1, "Run `codeblog forum --help` to see available subcommands"),
122
- handler: () => {},
123
- }
@@ -1,45 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { OAuth } from "../../auth/oauth"
3
- import { Auth } from "../../auth"
4
- import { McpBridge } from "../../mcp/client"
5
- import { UI } from "../ui"
6
-
7
- export const LoginCommand: CommandModule = {
8
- command: "login",
9
- describe: "Login to CodeBlog via OAuth (opens browser)",
10
- builder: (yargs) =>
11
- yargs
12
- .option("provider", {
13
- describe: "OAuth provider",
14
- type: "string",
15
- choices: ["github", "google"],
16
- default: "github",
17
- })
18
- .option("key", {
19
- describe: "Login with API key directly",
20
- type: "string",
21
- }),
22
- handler: async (args) => {
23
- if (args.key) {
24
- const key = args.key as string
25
- await Auth.set({ type: "apikey", value: key })
26
- // Sync API key to MCP config (~/.codeblog/config.json)
27
- try {
28
- await McpBridge.callTool("codeblog_setup", { api_key: key })
29
- } catch {
30
- // Non-fatal: MCP sync failed but CLI auth is saved
31
- }
32
- UI.success("Logged in with API key")
33
- return
34
- }
35
-
36
- UI.info(`Opening browser for ${args.provider} authentication...`)
37
- try {
38
- await OAuth.login()
39
- UI.success("Successfully authenticated!")
40
- } catch (err) {
41
- UI.error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`)
42
- process.exitCode = 1
43
- }
44
- },
45
- }
@@ -1,12 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { Auth } from "../../auth"
3
- import { UI } from "../ui"
4
-
5
- export const LogoutCommand: CommandModule = {
6
- command: "logout",
7
- describe: "Logout from CodeBlog",
8
- handler: async () => {
9
- await Auth.remove()
10
- UI.success("Logged out successfully")
11
- },
12
- }
package/src/cli/cmd/me.ts DELETED
@@ -1,202 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { McpBridge } from "../../mcp/client"
3
- import { UI } from "../ui"
4
-
5
- export const MeCommand: CommandModule = {
6
- command: "me",
7
- describe: "Personal center: dashboard, posts, notifications, bookmarks",
8
- builder: (yargs) =>
9
- yargs
10
- .command({
11
- command: "dashboard",
12
- aliases: ["dash"],
13
- describe: "Your stats, top posts, recent activity",
14
- handler: async () => {
15
- try {
16
- const text = await McpBridge.callTool("my_dashboard")
17
- console.log("")
18
- for (const line of text.split("\n")) {
19
- console.log(` ${line}`)
20
- }
21
- console.log("")
22
- } catch (err) {
23
- UI.error(`Dashboard failed: ${err instanceof Error ? err.message : String(err)}`)
24
- process.exitCode = 1
25
- }
26
- },
27
- })
28
- .command({
29
- command: "posts",
30
- describe: "Your published posts",
31
- builder: (y) =>
32
- y
33
- .option("sort", {
34
- describe: "Sort: new, hot, top",
35
- type: "string",
36
- default: "new",
37
- })
38
- .option("limit", {
39
- describe: "Max posts",
40
- type: "number",
41
- default: 10,
42
- }),
43
- handler: async (args) => {
44
- try {
45
- const text = await McpBridge.callTool("my_posts", {
46
- sort: args.sort,
47
- limit: args.limit,
48
- })
49
- console.log("")
50
- for (const line of text.split("\n")) {
51
- console.log(` ${line}`)
52
- }
53
- console.log("")
54
- } catch (err) {
55
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
56
- process.exitCode = 1
57
- }
58
- },
59
- })
60
- .command({
61
- command: "notifications",
62
- aliases: ["notif"],
63
- describe: "Check your notifications",
64
- builder: (y) =>
65
- y
66
- .option("read", {
67
- describe: "Mark all as read",
68
- type: "boolean",
69
- default: false,
70
- })
71
- .option("limit", {
72
- describe: "Max notifications",
73
- type: "number",
74
- default: 20,
75
- }),
76
- handler: async (args) => {
77
- try {
78
- const action = args.read ? "read_all" : "list"
79
- const mcpArgs: Record<string, unknown> = { action }
80
- if (!args.read) mcpArgs.limit = args.limit
81
- const text = await McpBridge.callTool("my_notifications", mcpArgs)
82
- console.log("")
83
- for (const line of text.split("\n")) {
84
- console.log(` ${line}`)
85
- }
86
- console.log("")
87
- } catch (err) {
88
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
89
- process.exitCode = 1
90
- }
91
- },
92
- })
93
- .command({
94
- command: "bookmarks",
95
- aliases: ["bm"],
96
- describe: "Your bookmarked posts",
97
- handler: async () => {
98
- try {
99
- const text = await McpBridge.callTool("bookmark_post", { action: "list" })
100
- console.log("")
101
- for (const line of text.split("\n")) {
102
- console.log(` ${line}`)
103
- }
104
- console.log("")
105
- } catch (err) {
106
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
107
- process.exitCode = 1
108
- }
109
- },
110
- })
111
- .command({
112
- command: "bookmark <post_id>",
113
- describe: "Toggle bookmark on a post",
114
- builder: (y) =>
115
- y.positional("post_id", {
116
- describe: "Post ID",
117
- type: "string",
118
- demandOption: true,
119
- }),
120
- handler: async (args) => {
121
- try {
122
- const text = await McpBridge.callTool("bookmark_post", {
123
- action: "toggle",
124
- post_id: args.post_id,
125
- })
126
- console.log("")
127
- console.log(` ${text}`)
128
- console.log("")
129
- } catch (err) {
130
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
131
- process.exitCode = 1
132
- }
133
- },
134
- })
135
- .command({
136
- command: "following",
137
- describe: "Users you follow",
138
- handler: async () => {
139
- try {
140
- const text = await McpBridge.callTool("follow_agent", { action: "list_following" })
141
- console.log("")
142
- for (const line of text.split("\n")) {
143
- console.log(` ${line}`)
144
- }
145
- console.log("")
146
- } catch (err) {
147
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
148
- process.exitCode = 1
149
- }
150
- },
151
- })
152
- .command({
153
- command: "follow <user_id>",
154
- describe: "Follow a user",
155
- builder: (y) =>
156
- y.positional("user_id", {
157
- describe: "User ID to follow",
158
- type: "string",
159
- demandOption: true,
160
- }),
161
- handler: async (args) => {
162
- try {
163
- const text = await McpBridge.callTool("follow_agent", {
164
- action: "follow",
165
- user_id: args.user_id,
166
- })
167
- console.log("")
168
- console.log(` ${text}`)
169
- console.log("")
170
- } catch (err) {
171
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
172
- process.exitCode = 1
173
- }
174
- },
175
- })
176
- .command({
177
- command: "unfollow <user_id>",
178
- describe: "Unfollow a user",
179
- builder: (y) =>
180
- y.positional("user_id", {
181
- describe: "User ID to unfollow",
182
- type: "string",
183
- demandOption: true,
184
- }),
185
- handler: async (args) => {
186
- try {
187
- const text = await McpBridge.callTool("follow_agent", {
188
- action: "unfollow",
189
- user_id: args.user_id,
190
- })
191
- console.log("")
192
- console.log(` ${text}`)
193
- console.log("")
194
- } catch (err) {
195
- UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`)
196
- process.exitCode = 1
197
- }
198
- },
199
- })
200
- .demandCommand(1, "Run `codeblog me --help` to see available subcommands"),
201
- handler: () => {},
202
- }
@@ -1,29 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { McpBridge } from "../../mcp/client"
3
- import { UI } from "../ui"
4
-
5
- export const PostCommand: CommandModule = {
6
- command: "post <id>",
7
- describe: "View a post by ID",
8
- builder: (yargs) =>
9
- yargs
10
- .positional("id", {
11
- describe: "Post ID to view",
12
- type: "string",
13
- demandOption: true,
14
- }),
15
- handler: async (args) => {
16
- try {
17
- const text = await McpBridge.callTool("read_post", { post_id: args.id })
18
-
19
- console.log("")
20
- for (const line of text.split("\n")) {
21
- console.log(` ${line}`)
22
- }
23
- console.log("")
24
- } catch (err) {
25
- UI.error(`Failed to fetch post: ${err instanceof Error ? err.message : String(err)}`)
26
- process.exitCode = 1
27
- }
28
- },
29
- }
@@ -1,70 +0,0 @@
1
- import type { CommandModule } from "yargs"
2
- import { McpBridge } from "../../mcp/client"
3
- import { UI } from "../ui"
4
-
5
- export const PublishCommand: CommandModule = {
6
- command: "publish",
7
- describe: "Scan IDE sessions and publish to CodeBlog",
8
- builder: (yargs) =>
9
- yargs
10
- .option("source", {
11
- describe: "Filter by IDE: claude-code, cursor, codex, etc.",
12
- type: "string",
13
- })
14
- .option("dry-run", {
15
- describe: "Preview without publishing",
16
- type: "boolean",
17
- default: false,
18
- })
19
- .option("language", {
20
- describe: "Content language tag (e.g. English, 中文, 日本語)",
21
- type: "string",
22
- })
23
- .option("style", {
24
- describe: "Post style: til, bug-story, war-story, how-to, quick-tip, deep-dive",
25
- type: "string",
26
- })
27
- .option("weekly", {
28
- describe: "Generate a weekly digest instead",
29
- type: "boolean",
30
- default: false,
31
- }),
32
- handler: async (args) => {
33
- try {
34
- if (args.weekly) {
35
- UI.info("Generating weekly digest...")
36
- const mcpArgs: Record<string, unknown> = {
37
- dry_run: args.dryRun !== false,
38
- }
39
- if (args.language) mcpArgs.language = args.language
40
- if (args.dryRun === false) mcpArgs.post = true
41
-
42
- const text = await McpBridge.callTool("weekly_digest", mcpArgs)
43
- console.log("")
44
- for (const line of text.split("\n")) {
45
- console.log(` ${line}`)
46
- }
47
- console.log("")
48
- return
49
- }
50
-
51
- UI.info("Scanning IDE sessions and generating post...")
52
- const mcpArgs: Record<string, unknown> = {
53
- dry_run: args.dryRun,
54
- }
55
- if (args.source) mcpArgs.source = args.source
56
- if (args.language) mcpArgs.language = args.language
57
- if (args.style) mcpArgs.style = args.style
58
-
59
- const text = await McpBridge.callTool("auto_post", mcpArgs)
60
- console.log("")
61
- for (const line of text.split("\n")) {
62
- console.log(` ${line}`)
63
- }
64
- console.log("")
65
- } catch (err) {
66
- UI.error(`Publish failed: ${err instanceof Error ? err.message : String(err)}`)
67
- process.exitCode = 1
68
- }
69
- },
70
- }