codeblog-app 1.6.5 → 2.0.1
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/package.json +9 -23
- package/src/ai/__tests__/chat.test.ts +110 -0
- package/src/ai/__tests__/provider.test.ts +184 -0
- package/src/ai/__tests__/tools.test.ts +90 -0
- package/src/ai/chat.ts +14 -14
- package/src/ai/provider.ts +24 -250
- package/src/ai/tools.ts +46 -281
- package/src/auth/oauth.ts +7 -0
- package/src/cli/__tests__/commands.test.ts +225 -0
- package/src/cli/__tests__/setup.test.ts +57 -0
- package/src/cli/cmd/agent.ts +102 -0
- package/src/cli/cmd/chat.ts +1 -1
- package/src/cli/cmd/comment.ts +47 -16
- package/src/cli/cmd/feed.ts +18 -30
- package/src/cli/cmd/forum.ts +123 -0
- package/src/cli/cmd/login.ts +9 -2
- package/src/cli/cmd/me.ts +202 -0
- package/src/cli/cmd/post.ts +6 -88
- package/src/cli/cmd/publish.ts +44 -23
- package/src/cli/cmd/scan.ts +45 -34
- package/src/cli/cmd/search.ts +8 -70
- package/src/cli/cmd/setup.ts +160 -62
- package/src/cli/cmd/vote.ts +29 -14
- package/src/cli/cmd/whoami.ts +7 -36
- package/src/cli/ui.ts +50 -0
- package/src/index.ts +80 -59
- package/src/mcp/__tests__/client.test.ts +149 -0
- package/src/mcp/__tests__/e2e.ts +327 -0
- package/src/mcp/__tests__/integration.ts +148 -0
- package/src/mcp/client.ts +148 -0
- package/src/api/agents.ts +0 -103
- package/src/api/bookmarks.ts +0 -25
- package/src/api/client.ts +0 -96
- package/src/api/debates.ts +0 -35
- package/src/api/feed.ts +0 -25
- package/src/api/notifications.ts +0 -31
- package/src/api/posts.ts +0 -116
- package/src/api/search.ts +0 -29
- package/src/api/tags.ts +0 -13
- package/src/api/trending.ts +0 -38
- package/src/api/users.ts +0 -8
- package/src/cli/cmd/agents.ts +0 -77
- package/src/cli/cmd/ai-publish.ts +0 -118
- package/src/cli/cmd/bookmark.ts +0 -27
- package/src/cli/cmd/bookmarks.ts +0 -42
- package/src/cli/cmd/dashboard.ts +0 -59
- package/src/cli/cmd/debate.ts +0 -89
- package/src/cli/cmd/delete.ts +0 -35
- package/src/cli/cmd/edit.ts +0 -42
- package/src/cli/cmd/explore.ts +0 -63
- package/src/cli/cmd/follow.ts +0 -34
- package/src/cli/cmd/myposts.ts +0 -50
- package/src/cli/cmd/notifications.ts +0 -65
- package/src/cli/cmd/tags.ts +0 -58
- package/src/cli/cmd/trending.ts +0 -64
- package/src/cli/cmd/weekly-digest.ts +0 -117
- package/src/publisher/index.ts +0 -139
- package/src/scanner/__tests__/analyzer.test.ts +0 -67
- package/src/scanner/__tests__/fs-utils.test.ts +0 -50
- package/src/scanner/__tests__/platform.test.ts +0 -27
- package/src/scanner/__tests__/registry.test.ts +0 -56
- package/src/scanner/aider.ts +0 -96
- package/src/scanner/analyzer.ts +0 -237
- package/src/scanner/claude-code.ts +0 -188
- package/src/scanner/codex.ts +0 -127
- package/src/scanner/continue-dev.ts +0 -95
- package/src/scanner/cursor.ts +0 -299
- package/src/scanner/fs-utils.ts +0 -123
- package/src/scanner/index.ts +0 -26
- package/src/scanner/platform.ts +0 -44
- package/src/scanner/registry.ts +0 -68
- package/src/scanner/types.ts +0 -62
- package/src/scanner/vscode-copilot.ts +0 -125
- package/src/scanner/warp.ts +0 -19
- package/src/scanner/windsurf.ts +0 -147
- package/src/scanner/zed.ts +0 -88
package/src/cli/cmd/login.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
2
|
import { OAuth } from "../../auth/oauth"
|
|
3
3
|
import { Auth } from "../../auth"
|
|
4
|
-
import {
|
|
4
|
+
import { McpBridge } from "../../mcp/client"
|
|
5
5
|
import { UI } from "../ui"
|
|
6
6
|
|
|
7
7
|
export const LoginCommand: CommandModule = {
|
|
@@ -21,7 +21,14 @@ export const LoginCommand: CommandModule = {
|
|
|
21
21
|
}),
|
|
22
22
|
handler: async (args) => {
|
|
23
23
|
if (args.key) {
|
|
24
|
-
|
|
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
|
+
}
|
|
25
32
|
UI.success("Logged in with API key")
|
|
26
33
|
return
|
|
27
34
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
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
|
+
}
|
package/src/cli/cmd/post.ts
CHANGED
|
@@ -1,108 +1,26 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
|
-
import {
|
|
2
|
+
import { McpBridge } from "../../mcp/client"
|
|
3
3
|
import { UI } from "../ui"
|
|
4
4
|
|
|
5
5
|
export const PostCommand: CommandModule = {
|
|
6
|
-
command: "post
|
|
7
|
-
describe: "View a post
|
|
6
|
+
command: "post <id>",
|
|
7
|
+
describe: "View a post by ID",
|
|
8
8
|
builder: (yargs) =>
|
|
9
9
|
yargs
|
|
10
10
|
.positional("id", {
|
|
11
11
|
describe: "Post ID to view",
|
|
12
12
|
type: "string",
|
|
13
|
-
|
|
14
|
-
.option("new", {
|
|
15
|
-
describe: "Scan IDE and generate a new post",
|
|
16
|
-
type: "boolean",
|
|
17
|
-
default: false,
|
|
13
|
+
demandOption: true,
|
|
18
14
|
}),
|
|
19
15
|
handler: async (args) => {
|
|
20
|
-
if (args.new) {
|
|
21
|
-
const { Publisher } = await import("../../publisher")
|
|
22
|
-
const results = await Publisher.scanAndPublish({ limit: 1 })
|
|
23
|
-
if (results.length === 0) {
|
|
24
|
-
UI.info("No sessions found to publish.")
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
for (const r of results) {
|
|
28
|
-
if (r.postId) UI.success(`Published: ${r.session.title} → Post ID: ${r.postId}`)
|
|
29
|
-
if (r.error) UI.error(`Failed: ${r.session.title} — ${r.error}`)
|
|
30
|
-
}
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!args.id) {
|
|
35
|
-
UI.error("Please provide a post ID or use --new to create one")
|
|
36
|
-
process.exitCode = 1
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
16
|
try {
|
|
41
|
-
const
|
|
42
|
-
const score = post.upvotes - post.downvotes
|
|
43
|
-
const date = new Date(post.createdAt).toLocaleString()
|
|
17
|
+
const text = await McpBridge.callTool("read_post", { post_id: args.id })
|
|
44
18
|
|
|
45
19
|
console.log("")
|
|
46
|
-
|
|
47
|
-
console.log(` ${UI.Style.TEXT_DIM}by ${post.agent.name} · ${date}${UI.Style.TEXT_NORMAL}`)
|
|
48
|
-
if (post.category) {
|
|
49
|
-
console.log(` ${UI.Style.TEXT_DIM}${post.category.emoji} ${post.category.name}${UI.Style.TEXT_NORMAL}`)
|
|
50
|
-
}
|
|
51
|
-
if (post.language && post.language !== "English") {
|
|
52
|
-
console.log(` ${UI.Style.TEXT_INFO}🌐 ${post.language}${UI.Style.TEXT_NORMAL}`)
|
|
53
|
-
}
|
|
54
|
-
const scoreColor = score > 0 ? UI.Style.TEXT_SUCCESS : score < 0 ? UI.Style.TEXT_DANGER : UI.Style.TEXT_DIM
|
|
55
|
-
console.log(` ${scoreColor}${score > 0 ? "+" : ""}${score} votes${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}💬 ${post.comment_count} 👁 ${post.views}${UI.Style.TEXT_NORMAL}`)
|
|
56
|
-
if (post.tags.length > 0) {
|
|
57
|
-
console.log(` ${post.tags.map((t) => `${UI.Style.TEXT_INFO}#${t}${UI.Style.TEXT_NORMAL}`).join(" ")}`)
|
|
58
|
-
}
|
|
59
|
-
console.log("")
|
|
60
|
-
|
|
61
|
-
if (post.summary) {
|
|
62
|
-
console.log(` ${UI.Style.TEXT_DIM}TL;DR: ${post.summary}${UI.Style.TEXT_NORMAL}`)
|
|
63
|
-
console.log("")
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Indent content
|
|
67
|
-
for (const line of post.content.split("\n")) {
|
|
20
|
+
for (const line of text.split("\n")) {
|
|
68
21
|
console.log(` ${line}`)
|
|
69
22
|
}
|
|
70
23
|
console.log("")
|
|
71
|
-
|
|
72
|
-
if (post.comments.length > 0) {
|
|
73
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Comments (${post.comment_count})${UI.Style.TEXT_NORMAL}`)
|
|
74
|
-
console.log("")
|
|
75
|
-
|
|
76
|
-
// Build reply tree
|
|
77
|
-
const roots = post.comments.filter((c) => !c.parentId)
|
|
78
|
-
const replies = post.comments.filter((c) => c.parentId)
|
|
79
|
-
const replyMap = new Map<string, typeof replies>()
|
|
80
|
-
for (const r of replies) {
|
|
81
|
-
const list = replyMap.get(r.parentId!) || []
|
|
82
|
-
list.push(r)
|
|
83
|
-
replyMap.set(r.parentId!, list)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const comment of roots) {
|
|
87
|
-
const cdate = new Date(comment.createdAt).toLocaleDateString()
|
|
88
|
-
console.log(` ${UI.Style.TEXT_HIGHLIGHT}${comment.user.username}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}${cdate}${UI.Style.TEXT_NORMAL}`)
|
|
89
|
-
console.log(` ${comment.content}`)
|
|
90
|
-
console.log("")
|
|
91
|
-
|
|
92
|
-
const childReplies = replyMap.get(comment.id) || []
|
|
93
|
-
for (const reply of childReplies) {
|
|
94
|
-
const rdate = new Date(reply.createdAt).toLocaleDateString()
|
|
95
|
-
console.log(` ${UI.Style.TEXT_DIM}↳${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_HIGHLIGHT}${reply.user.username}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}${rdate}${UI.Style.TEXT_NORMAL}`)
|
|
96
|
-
console.log(` ${reply.content}`)
|
|
97
|
-
console.log("")
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log(` ${UI.Style.TEXT_DIM}codeblog vote ${post.id} — upvote${UI.Style.TEXT_NORMAL}`)
|
|
103
|
-
console.log(` ${UI.Style.TEXT_DIM}codeblog comment ${post.id} — comment${UI.Style.TEXT_NORMAL}`)
|
|
104
|
-
console.log(` ${UI.Style.TEXT_DIM}codeblog bookmark ${post.id} — bookmark${UI.Style.TEXT_NORMAL}`)
|
|
105
|
-
console.log("")
|
|
106
24
|
} catch (err) {
|
|
107
25
|
UI.error(`Failed to fetch post: ${err instanceof Error ? err.message : String(err)}`)
|
|
108
26
|
process.exitCode = 1
|
package/src/cli/cmd/publish.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
|
-
import {
|
|
2
|
+
import { McpBridge } from "../../mcp/client"
|
|
3
3
|
import { UI } from "../ui"
|
|
4
4
|
|
|
5
5
|
export const PublishCommand: CommandModule = {
|
|
@@ -7,10 +7,9 @@ export const PublishCommand: CommandModule = {
|
|
|
7
7
|
describe: "Scan IDE sessions and publish to CodeBlog",
|
|
8
8
|
builder: (yargs) =>
|
|
9
9
|
yargs
|
|
10
|
-
.option("
|
|
11
|
-
describe: "
|
|
12
|
-
type: "
|
|
13
|
-
default: 5,
|
|
10
|
+
.option("source", {
|
|
11
|
+
describe: "Filter by IDE: claude-code, cursor, codex, etc.",
|
|
12
|
+
type: "string",
|
|
14
13
|
})
|
|
15
14
|
.option("dry-run", {
|
|
16
15
|
describe: "Preview without publishing",
|
|
@@ -20,30 +19,52 @@ export const PublishCommand: CommandModule = {
|
|
|
20
19
|
.option("language", {
|
|
21
20
|
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
22
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,
|
|
23
31
|
}),
|
|
24
32
|
handler: async (args) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|
|
37
50
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
UI.success(`Published: ${r.session.title}`)
|
|
42
|
-
console.log(` ${UI.Style.TEXT_DIM}Post ID: ${r.postId}${UI.Style.TEXT_NORMAL}`)
|
|
51
|
+
UI.info("Scanning IDE sessions and generating post...")
|
|
52
|
+
const mcpArgs: Record<string, unknown> = {
|
|
53
|
+
dry_run: args.dryRun,
|
|
43
54
|
}
|
|
44
|
-
if (
|
|
45
|
-
|
|
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}`)
|
|
46
63
|
}
|
|
64
|
+
console.log("")
|
|
65
|
+
} catch (err) {
|
|
66
|
+
UI.error(`Publish failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
67
|
+
process.exitCode = 1
|
|
47
68
|
}
|
|
48
69
|
},
|
|
49
70
|
}
|
package/src/cli/cmd/scan.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
|
-
import {
|
|
2
|
+
import { McpBridge } from "../../mcp/client"
|
|
3
3
|
import { UI } from "../ui"
|
|
4
4
|
|
|
5
5
|
export const ScanCommand: CommandModule = {
|
|
@@ -22,48 +22,59 @@ export const ScanCommand: CommandModule = {
|
|
|
22
22
|
default: false,
|
|
23
23
|
}),
|
|
24
24
|
handler: async (args) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const icon = s.available ? `${UI.Style.TEXT_SUCCESS}✓${UI.Style.TEXT_NORMAL}` : `${UI.Style.TEXT_DIM}✗${UI.Style.TEXT_NORMAL}`
|
|
34
|
-
console.log(` ${icon} ${UI.Style.TEXT_NORMAL_BOLD}${s.name}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${s.source})${UI.Style.TEXT_NORMAL}`)
|
|
35
|
-
console.log(` ${s.description}`)
|
|
36
|
-
if (s.dirs.length > 0) {
|
|
37
|
-
for (const dir of s.dirs) {
|
|
38
|
-
console.log(` ${UI.Style.TEXT_DIM}${dir}${UI.Style.TEXT_NORMAL}`)
|
|
39
|
-
}
|
|
25
|
+
try {
|
|
26
|
+
if (args.status) {
|
|
27
|
+
const text = await McpBridge.callTool("codeblog_status")
|
|
28
|
+
console.log("")
|
|
29
|
+
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}CodeBlog Status${UI.Style.TEXT_NORMAL}`)
|
|
30
|
+
console.log("")
|
|
31
|
+
for (const line of text.split("\n")) {
|
|
32
|
+
console.log(` ${line}`)
|
|
40
33
|
}
|
|
41
|
-
if (s.error) console.log(` ${UI.Style.TEXT_DANGER}${s.error}${UI.Style.TEXT_NORMAL}`)
|
|
42
34
|
console.log("")
|
|
35
|
+
return
|
|
43
36
|
}
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
const mcpArgs: Record<string, unknown> = { limit: args.limit }
|
|
39
|
+
if (args.source) mcpArgs.source = args.source
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
const text = await McpBridge.callTool("scan_sessions", mcpArgs)
|
|
42
|
+
let sessions: Array<{
|
|
43
|
+
id: string; source: string; project: string; title: string;
|
|
44
|
+
messages: number; human: number; ai: number; modified: string;
|
|
45
|
+
size: string; path: string; preview?: string
|
|
46
|
+
}>
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
try {
|
|
49
|
+
sessions = JSON.parse(text)
|
|
50
|
+
} catch {
|
|
51
|
+
// Fallback: just print the raw text
|
|
52
|
+
console.log(text)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
if (sessions.length === 0) {
|
|
57
|
+
UI.info("No IDE sessions found. Try running with --status to check scanner availability.")
|
|
58
|
+
return
|
|
59
|
+
}
|
|
62
60
|
|
|
63
|
-
console.log(` ${source} ${UI.Style.TEXT_NORMAL_BOLD}${session.project}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}${date}${UI.Style.TEXT_NORMAL}`)
|
|
64
|
-
console.log(` ${session.title}`)
|
|
65
|
-
console.log(` ${msgs} ${UI.Style.TEXT_DIM}${session.id}${UI.Style.TEXT_NORMAL}`)
|
|
66
61
|
console.log("")
|
|
62
|
+
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Found ${sessions.length} sessions${UI.Style.TEXT_NORMAL}`)
|
|
63
|
+
console.log("")
|
|
64
|
+
|
|
65
|
+
for (const session of sessions) {
|
|
66
|
+
const source = `${UI.Style.TEXT_INFO}[${session.source}]${UI.Style.TEXT_NORMAL}`
|
|
67
|
+
const date = new Date(session.modified).toLocaleDateString()
|
|
68
|
+
const msgs = `${UI.Style.TEXT_DIM}${session.human}h/${session.ai}a msgs${UI.Style.TEXT_NORMAL}`
|
|
69
|
+
|
|
70
|
+
console.log(` ${source} ${UI.Style.TEXT_NORMAL_BOLD}${session.project}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}${date}${UI.Style.TEXT_NORMAL}`)
|
|
71
|
+
console.log(` ${session.title}`)
|
|
72
|
+
console.log(` ${msgs} ${UI.Style.TEXT_DIM}${session.id}${UI.Style.TEXT_NORMAL}`)
|
|
73
|
+
console.log("")
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
UI.error(`Scan failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
77
|
+
process.exitCode = 1
|
|
67
78
|
}
|
|
68
79
|
},
|
|
69
80
|
}
|
package/src/cli/cmd/search.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
|
-
import {
|
|
2
|
+
import { McpBridge } from "../../mcp/client"
|
|
3
3
|
import { UI } from "../ui"
|
|
4
4
|
|
|
5
5
|
export const SearchCommand: CommandModule = {
|
|
6
6
|
command: "search <query>",
|
|
7
|
-
describe: "Search posts
|
|
7
|
+
describe: "Search posts on CodeBlog",
|
|
8
8
|
builder: (yargs) =>
|
|
9
9
|
yargs
|
|
10
10
|
.positional("query", {
|
|
@@ -12,88 +12,26 @@ export const SearchCommand: CommandModule = {
|
|
|
12
12
|
type: "string",
|
|
13
13
|
demandOption: true,
|
|
14
14
|
})
|
|
15
|
-
.option("type", {
|
|
16
|
-
describe: "Search type: all, posts, comments, agents, users",
|
|
17
|
-
type: "string",
|
|
18
|
-
default: "all",
|
|
19
|
-
})
|
|
20
|
-
.option("sort", {
|
|
21
|
-
describe: "Sort: relevance, new, top",
|
|
22
|
-
type: "string",
|
|
23
|
-
default: "relevance",
|
|
24
|
-
})
|
|
25
15
|
.option("limit", {
|
|
26
16
|
describe: "Max results",
|
|
27
17
|
type: "number",
|
|
28
18
|
default: 20,
|
|
29
|
-
})
|
|
30
|
-
.option("page", {
|
|
31
|
-
describe: "Page number",
|
|
32
|
-
type: "number",
|
|
33
|
-
default: 1,
|
|
34
19
|
}),
|
|
35
20
|
handler: async (args) => {
|
|
36
21
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
limit: args.limit as number,
|
|
41
|
-
page: args.page as number,
|
|
22
|
+
const text = await McpBridge.callTool("search_posts", {
|
|
23
|
+
query: args.query,
|
|
24
|
+
limit: args.limit,
|
|
42
25
|
})
|
|
43
26
|
|
|
44
|
-
const total = result.counts.posts + result.counts.comments + result.counts.agents + result.counts.users
|
|
45
|
-
if (total === 0) {
|
|
46
|
-
UI.info(`No results for "${args.query}"`)
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
27
|
console.log("")
|
|
51
28
|
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Results for "${args.query}"${UI.Style.TEXT_NORMAL}`)
|
|
52
|
-
console.log(` ${UI.Style.TEXT_DIM}${result.counts.posts} posts · ${result.counts.comments} comments · ${result.counts.agents} agents · ${result.counts.users} users${UI.Style.TEXT_NORMAL}`)
|
|
53
29
|
console.log("")
|
|
54
30
|
|
|
55
|
-
|
|
56
|
-
console.log(` ${
|
|
57
|
-
for (const p of result.posts as Array<Record<string, unknown>>) {
|
|
58
|
-
const score = ((p.upvotes as number) || 0) - ((p.downvotes as number) || 0)
|
|
59
|
-
const votes = `${UI.Style.TEXT_HIGHLIGHT}▲ ${score}${UI.Style.TEXT_NORMAL}`
|
|
60
|
-
const count = (p._count as Record<string, number>)?.comments || 0
|
|
61
|
-
const agent = (p.agent as Record<string, unknown>)?.name || ""
|
|
62
|
-
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${p.title}${UI.Style.TEXT_NORMAL}`)
|
|
63
|
-
console.log(` ${UI.Style.TEXT_DIM}💬 ${count} by ${agent}${UI.Style.TEXT_NORMAL}`)
|
|
64
|
-
console.log(` ${UI.Style.TEXT_DIM}${p.id}${UI.Style.TEXT_NORMAL}`)
|
|
65
|
-
console.log("")
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (result.comments && result.comments.length > 0) {
|
|
70
|
-
console.log(` ${UI.Style.TEXT_INFO_BOLD}Comments${UI.Style.TEXT_NORMAL}`)
|
|
71
|
-
for (const c of result.comments as Array<Record<string, unknown>>) {
|
|
72
|
-
const user = (c.user as Record<string, unknown>)?.username || ""
|
|
73
|
-
const post = (c.post as Record<string, unknown>)?.title || ""
|
|
74
|
-
const content = String(c.content || "").slice(0, 100)
|
|
75
|
-
console.log(` ${UI.Style.TEXT_DIM}@${user}${UI.Style.TEXT_NORMAL} on "${post}"`)
|
|
76
|
-
console.log(` ${content}`)
|
|
77
|
-
console.log("")
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (result.agents && result.agents.length > 0) {
|
|
82
|
-
console.log(` ${UI.Style.TEXT_INFO_BOLD}Agents${UI.Style.TEXT_NORMAL}`)
|
|
83
|
-
for (const a of result.agents as Array<Record<string, unknown>>) {
|
|
84
|
-
const count = (a._count as Record<string, number>)?.posts || 0
|
|
85
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}${a.name}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${a.sourceType})${UI.Style.TEXT_NORMAL} ${count} posts`)
|
|
86
|
-
console.log("")
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (result.users && result.users.length > 0) {
|
|
91
|
-
console.log(` ${UI.Style.TEXT_INFO_BOLD}Users${UI.Style.TEXT_NORMAL}`)
|
|
92
|
-
for (const u of result.users as Array<Record<string, unknown>>) {
|
|
93
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}@${u.username}${UI.Style.TEXT_NORMAL}${u.bio ? ` — ${u.bio}` : ""}`)
|
|
94
|
-
console.log("")
|
|
95
|
-
}
|
|
31
|
+
for (const line of text.split("\n")) {
|
|
32
|
+
console.log(` ${line}`)
|
|
96
33
|
}
|
|
34
|
+
console.log("")
|
|
97
35
|
} catch (err) {
|
|
98
36
|
UI.error(`Search failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
99
37
|
process.exitCode = 1
|