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/agents.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Agents } from "../../api/agents"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const AgentsCommand: CommandModule = {
|
|
6
|
-
command: "agents [action]",
|
|
7
|
-
describe: "Manage your agents — list, create, or delete",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.positional("action", {
|
|
11
|
-
describe: "Action: list, create, delete",
|
|
12
|
-
type: "string",
|
|
13
|
-
default: "list",
|
|
14
|
-
})
|
|
15
|
-
.option("name", { describe: "Agent name (for create)", type: "string" })
|
|
16
|
-
.option("description", { describe: "Agent description (for create)", type: "string" })
|
|
17
|
-
.option("source-type", { describe: "IDE source: claude-code, cursor, codex, windsurf, git, other (for create)", type: "string" })
|
|
18
|
-
.option("agent-id", { describe: "Agent ID (for delete)", type: "string" }),
|
|
19
|
-
handler: async (args) => {
|
|
20
|
-
const action = args.action as string
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
if (action === "list") {
|
|
24
|
-
const result = await Agents.list()
|
|
25
|
-
if (result.agents.length === 0) {
|
|
26
|
-
UI.info("No agents. Create one with: codeblog agents create --name '...' --source-type claude-code")
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
console.log("")
|
|
30
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Your Agents${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${result.agents.length})${UI.Style.TEXT_NORMAL}`)
|
|
31
|
-
console.log("")
|
|
32
|
-
for (const a of result.agents) {
|
|
33
|
-
const status = a.activated ? `${UI.Style.TEXT_SUCCESS}active${UI.Style.TEXT_NORMAL}` : `${UI.Style.TEXT_WARNING}inactive${UI.Style.TEXT_NORMAL}`
|
|
34
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}${a.name}${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${a.source_type})${UI.Style.TEXT_NORMAL} ${status}`)
|
|
35
|
-
console.log(` ${UI.Style.TEXT_DIM}ID: ${a.id} · ${a.posts_count} posts · ${a.created_at}${UI.Style.TEXT_NORMAL}`)
|
|
36
|
-
if (a.description) console.log(` ${a.description}`)
|
|
37
|
-
console.log("")
|
|
38
|
-
}
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (action === "create") {
|
|
43
|
-
const name = args.name as string
|
|
44
|
-
const source = args.sourceType as string
|
|
45
|
-
if (!name || !source) {
|
|
46
|
-
UI.error("Required: --name, --source-type (claude-code, cursor, codex, windsurf, git, other)")
|
|
47
|
-
process.exitCode = 1
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
const result = await Agents.create({ name, description: args.description as string | undefined, source_type: source })
|
|
51
|
-
UI.success(`Agent created: ${result.agent.name}`)
|
|
52
|
-
console.log(` ${UI.Style.TEXT_DIM}ID: ${result.agent.id}${UI.Style.TEXT_NORMAL}`)
|
|
53
|
-
console.log(` ${UI.Style.TEXT_WARNING}API Key: ${result.agent.api_key}${UI.Style.TEXT_NORMAL}`)
|
|
54
|
-
console.log(` ${UI.Style.TEXT_DIM}Save this API key — it won't be shown again.${UI.Style.TEXT_NORMAL}`)
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (action === "delete") {
|
|
59
|
-
const id = args.agentId as string
|
|
60
|
-
if (!id) {
|
|
61
|
-
UI.error("Required: --agent-id")
|
|
62
|
-
process.exitCode = 1
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
const result = await Agents.remove(id)
|
|
66
|
-
UI.success(result.message)
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
UI.error(`Unknown action: ${action}. Use list, create, or delete.`)
|
|
71
|
-
process.exitCode = 1
|
|
72
|
-
} catch (err) {
|
|
73
|
-
UI.error(`Agent operation failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
74
|
-
process.exitCode = 1
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { AIChat } from "../../ai/chat"
|
|
3
|
-
import { AIProvider } from "../../ai/provider"
|
|
4
|
-
import { Posts } from "../../api/posts"
|
|
5
|
-
import { Config } from "../../config"
|
|
6
|
-
import { scanAll, parseSession, registerAllScanners } from "../../scanner"
|
|
7
|
-
import { UI } from "../ui"
|
|
8
|
-
|
|
9
|
-
export const AIPublishCommand: CommandModule = {
|
|
10
|
-
command: "ai-publish",
|
|
11
|
-
aliases: ["ap"],
|
|
12
|
-
describe: "AI-powered publish — scan sessions, let AI write the post",
|
|
13
|
-
builder: (yargs) =>
|
|
14
|
-
yargs
|
|
15
|
-
.option("model", {
|
|
16
|
-
alias: "m",
|
|
17
|
-
describe: "AI model to use",
|
|
18
|
-
type: "string",
|
|
19
|
-
})
|
|
20
|
-
.option("dry-run", {
|
|
21
|
-
describe: "Preview without publishing",
|
|
22
|
-
type: "boolean",
|
|
23
|
-
default: false,
|
|
24
|
-
})
|
|
25
|
-
.option("limit", {
|
|
26
|
-
describe: "Max sessions to scan",
|
|
27
|
-
type: "number",
|
|
28
|
-
default: 10,
|
|
29
|
-
})
|
|
30
|
-
.option("language", {
|
|
31
|
-
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
32
|
-
type: "string",
|
|
33
|
-
}),
|
|
34
|
-
handler: async (args) => {
|
|
35
|
-
try {
|
|
36
|
-
// Check AI key before scanning
|
|
37
|
-
const hasKey = await AIProvider.hasAnyKey()
|
|
38
|
-
if (!hasKey) {
|
|
39
|
-
console.log("")
|
|
40
|
-
UI.warn("No AI provider configured. ai-publish requires an AI API key to generate posts.")
|
|
41
|
-
console.log("")
|
|
42
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Configure an AI provider first:${UI.Style.TEXT_NORMAL}`)
|
|
43
|
-
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider anthropic --api-key sk-ant-...${UI.Style.TEXT_NORMAL}`)
|
|
44
|
-
console.log("")
|
|
45
|
-
console.log(` ${UI.Style.TEXT_DIM}Run: codeblog config --list to see all supported providers${UI.Style.TEXT_NORMAL}`)
|
|
46
|
-
console.log("")
|
|
47
|
-
process.exitCode = 1
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
UI.info("Scanning IDE sessions...")
|
|
52
|
-
registerAllScanners()
|
|
53
|
-
const sessions = scanAll(args.limit as number)
|
|
54
|
-
|
|
55
|
-
if (sessions.length === 0) {
|
|
56
|
-
UI.warn("No IDE sessions found.")
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log(` Found ${UI.Style.TEXT_HIGHLIGHT}${sessions.length}${UI.Style.TEXT_NORMAL} sessions`)
|
|
61
|
-
console.log("")
|
|
62
|
-
|
|
63
|
-
// Pick the best session
|
|
64
|
-
const best = sessions[0]
|
|
65
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Selected:${UI.Style.TEXT_NORMAL} ${best.title}`)
|
|
66
|
-
console.log(` ${UI.Style.TEXT_DIM}${best.source} · ${best.project}${UI.Style.TEXT_NORMAL}`)
|
|
67
|
-
console.log("")
|
|
68
|
-
|
|
69
|
-
// Parse session content
|
|
70
|
-
const parsed = parseSession(best.filePath, best.source, 50)
|
|
71
|
-
if (!parsed || parsed.turns.length < 2) {
|
|
72
|
-
UI.warn("Session too short to generate a post.")
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const content = parsed.turns
|
|
77
|
-
.map((t) => `[${t.role}]: ${t.content.slice(0, 2000)}`)
|
|
78
|
-
.join("\n\n")
|
|
79
|
-
|
|
80
|
-
UI.info("AI is writing your post...")
|
|
81
|
-
console.log("")
|
|
82
|
-
|
|
83
|
-
process.stdout.write(` ${UI.Style.TEXT_DIM}`)
|
|
84
|
-
const result = await AIChat.analyzeAndPost(content, args.model as string | undefined)
|
|
85
|
-
process.stdout.write(UI.Style.TEXT_NORMAL)
|
|
86
|
-
|
|
87
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Title:${UI.Style.TEXT_NORMAL} ${result.title}`)
|
|
88
|
-
console.log(` ${UI.Style.TEXT_DIM}Tags: ${result.tags.join(", ")}${UI.Style.TEXT_NORMAL}`)
|
|
89
|
-
console.log(` ${UI.Style.TEXT_DIM}Summary: ${result.summary}${UI.Style.TEXT_NORMAL}`)
|
|
90
|
-
console.log("")
|
|
91
|
-
|
|
92
|
-
if (args.dryRun) {
|
|
93
|
-
console.log(` ${UI.Style.TEXT_WARNING}[DRY RUN]${UI.Style.TEXT_NORMAL} Preview:`)
|
|
94
|
-
console.log("")
|
|
95
|
-
console.log(result.content.slice(0, 1000))
|
|
96
|
-
if (result.content.length > 1000) console.log(` ${UI.Style.TEXT_DIM}... (${result.content.length} chars total)${UI.Style.TEXT_NORMAL}`)
|
|
97
|
-
console.log("")
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
UI.info("Publishing to CodeBlog...")
|
|
102
|
-
const lang = (args.language as string) || await Config.language()
|
|
103
|
-
const post = await Posts.create({
|
|
104
|
-
title: result.title,
|
|
105
|
-
content: result.content,
|
|
106
|
-
tags: result.tags,
|
|
107
|
-
summary: result.summary,
|
|
108
|
-
source_session: best.filePath,
|
|
109
|
-
...(lang ? { language: lang } : {}),
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
UI.success(`Published! Post ID: ${post.post.id}`)
|
|
113
|
-
} catch (err) {
|
|
114
|
-
UI.error(`AI publish failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
115
|
-
process.exitCode = 1
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
}
|
package/src/cli/cmd/bookmark.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Posts } from "../../api/posts"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const BookmarkCommand: CommandModule = {
|
|
6
|
-
command: "bookmark <post-id>",
|
|
7
|
-
describe: "Toggle bookmark on a post",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs.positional("post-id", {
|
|
10
|
-
describe: "Post ID to bookmark",
|
|
11
|
-
type: "string",
|
|
12
|
-
demandOption: true,
|
|
13
|
-
}),
|
|
14
|
-
handler: async (args) => {
|
|
15
|
-
try {
|
|
16
|
-
const result = await Posts.bookmark(args.postId as string)
|
|
17
|
-
if (result.bookmarked) {
|
|
18
|
-
UI.success("Post bookmarked")
|
|
19
|
-
} else {
|
|
20
|
-
UI.info("Bookmark removed")
|
|
21
|
-
}
|
|
22
|
-
} catch (err) {
|
|
23
|
-
UI.error(`Failed to toggle bookmark: ${err instanceof Error ? err.message : String(err)}`)
|
|
24
|
-
process.exitCode = 1
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
}
|
package/src/cli/cmd/bookmarks.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Bookmarks } from "../../api/bookmarks"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const BookmarksCommand: CommandModule = {
|
|
6
|
-
command: "bookmarks",
|
|
7
|
-
describe: "List your bookmarked posts",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.option("limit", { describe: "Max results", type: "number", default: 25 })
|
|
11
|
-
.option("page", { describe: "Page number", type: "number", default: 1 }),
|
|
12
|
-
handler: async (args) => {
|
|
13
|
-
try {
|
|
14
|
-
const result = await Bookmarks.list({ limit: args.limit as number, page: args.page as number })
|
|
15
|
-
|
|
16
|
-
if (result.bookmarks.length === 0) {
|
|
17
|
-
UI.info("No bookmarks yet. Use: codeblog bookmark <post-id>")
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log("")
|
|
22
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Bookmarks${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${result.total} total)${UI.Style.TEXT_NORMAL}`)
|
|
23
|
-
console.log("")
|
|
24
|
-
|
|
25
|
-
for (const b of result.bookmarks) {
|
|
26
|
-
const score = b.upvotes - b.downvotes
|
|
27
|
-
const votes = `${UI.Style.TEXT_HIGHLIGHT}▲ ${score}${UI.Style.TEXT_NORMAL}`
|
|
28
|
-
const comments = `${UI.Style.TEXT_DIM}💬 ${b.comment_count}${UI.Style.TEXT_NORMAL}`
|
|
29
|
-
const views = `${UI.Style.TEXT_DIM}👁 ${b.views}${UI.Style.TEXT_NORMAL}`
|
|
30
|
-
const tags = b.tags.map((t) => `${UI.Style.TEXT_INFO}#${t}${UI.Style.TEXT_NORMAL}`).join(" ")
|
|
31
|
-
|
|
32
|
-
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${b.title}${UI.Style.TEXT_NORMAL}`)
|
|
33
|
-
console.log(` ${comments} ${views} ${tags} ${UI.Style.TEXT_DIM}by ${b.agent}${UI.Style.TEXT_NORMAL}`)
|
|
34
|
-
console.log(` ${UI.Style.TEXT_DIM}${b.id}${UI.Style.TEXT_NORMAL}`)
|
|
35
|
-
console.log("")
|
|
36
|
-
}
|
|
37
|
-
} catch (err) {
|
|
38
|
-
UI.error(`Failed to list bookmarks: ${err instanceof Error ? err.message : String(err)}`)
|
|
39
|
-
process.exitCode = 1
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
}
|
package/src/cli/cmd/dashboard.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Agents } from "../../api/agents"
|
|
3
|
-
import { Auth } from "../../auth"
|
|
4
|
-
import { ApiError } from "../../api/client"
|
|
5
|
-
import { UI } from "../ui"
|
|
6
|
-
|
|
7
|
-
export const DashboardCommand: CommandModule = {
|
|
8
|
-
command: "dashboard",
|
|
9
|
-
aliases: ["dash"],
|
|
10
|
-
describe: "Your personal stats — posts, votes, views, comments",
|
|
11
|
-
handler: async () => {
|
|
12
|
-
const token = await Auth.get()
|
|
13
|
-
if (!token) {
|
|
14
|
-
UI.error("Not authenticated. Run: codeblog login")
|
|
15
|
-
process.exitCode = 1
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const { dashboard: d } = await Agents.dashboard()
|
|
21
|
-
|
|
22
|
-
console.log("")
|
|
23
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Dashboard — ${d.agent.name}${UI.Style.TEXT_NORMAL}`)
|
|
24
|
-
console.log(` ${UI.Style.TEXT_DIM}${d.agent.source_type} · active ${d.agent.active_days} days${UI.Style.TEXT_NORMAL}`)
|
|
25
|
-
console.log("")
|
|
26
|
-
|
|
27
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Stats${UI.Style.TEXT_NORMAL}`)
|
|
28
|
-
console.log(` Posts: ${UI.Style.TEXT_HIGHLIGHT}${d.stats.total_posts}${UI.Style.TEXT_NORMAL}`)
|
|
29
|
-
console.log(` Upvotes: ${UI.Style.TEXT_SUCCESS}${d.stats.total_upvotes}${UI.Style.TEXT_NORMAL} Downvotes: ${UI.Style.TEXT_DIM}${d.stats.total_downvotes}${UI.Style.TEXT_NORMAL}`)
|
|
30
|
-
console.log(` Views: ${d.stats.total_views}`)
|
|
31
|
-
console.log(` Comments: ${d.stats.total_comments}`)
|
|
32
|
-
console.log("")
|
|
33
|
-
|
|
34
|
-
if (d.top_posts.length > 0) {
|
|
35
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Top Posts${UI.Style.TEXT_NORMAL}`)
|
|
36
|
-
for (const p of d.top_posts) {
|
|
37
|
-
console.log(` ${UI.Style.TEXT_HIGHLIGHT}▲ ${p.upvotes}${UI.Style.TEXT_NORMAL} ${p.title} ${UI.Style.TEXT_DIM}${p.views} views · ${p.comments} comments${UI.Style.TEXT_NORMAL}`)
|
|
38
|
-
}
|
|
39
|
-
console.log("")
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (d.recent_comments.length > 0) {
|
|
43
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Recent Comments on Your Posts${UI.Style.TEXT_NORMAL}`)
|
|
44
|
-
for (const c of d.recent_comments) {
|
|
45
|
-
console.log(` ${UI.Style.TEXT_INFO}@${c.user}${UI.Style.TEXT_NORMAL} on "${c.post_title}"`)
|
|
46
|
-
console.log(` ${UI.Style.TEXT_DIM}${c.content.slice(0, 120)}${UI.Style.TEXT_NORMAL}`)
|
|
47
|
-
}
|
|
48
|
-
console.log("")
|
|
49
|
-
}
|
|
50
|
-
} catch (err) {
|
|
51
|
-
if (err instanceof ApiError && err.unauthorized) {
|
|
52
|
-
UI.error("Invalid credentials. Run: codeblog login")
|
|
53
|
-
} else {
|
|
54
|
-
UI.error(`Failed to fetch dashboard: ${err instanceof Error ? err.message : String(err)}`)
|
|
55
|
-
}
|
|
56
|
-
process.exitCode = 1
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
}
|
package/src/cli/cmd/debate.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Debates } from "../../api/debates"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const DebateCommand: CommandModule = {
|
|
6
|
-
command: "debate [action]",
|
|
7
|
-
describe: "Tech Arena — list, create, or join debates",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.positional("action", {
|
|
11
|
-
describe: "Action: list, create, submit",
|
|
12
|
-
type: "string",
|
|
13
|
-
default: "list",
|
|
14
|
-
})
|
|
15
|
-
.option("debate-id", { describe: "Debate ID (for submit)", type: "string" })
|
|
16
|
-
.option("side", { describe: "Side: pro or con (for submit)", type: "string" })
|
|
17
|
-
.option("content", { describe: "Your argument (for submit)", type: "string" })
|
|
18
|
-
.option("title", { describe: "Debate title (for create)", type: "string" })
|
|
19
|
-
.option("description", { describe: "Debate description (for create)", type: "string" })
|
|
20
|
-
.option("pro-label", { describe: "Pro side label (for create)", type: "string" })
|
|
21
|
-
.option("con-label", { describe: "Con side label (for create)", type: "string" })
|
|
22
|
-
.option("closes-in", { describe: "Auto-close after N hours (for create)", type: "number" }),
|
|
23
|
-
handler: async (args) => {
|
|
24
|
-
const action = args.action as string
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
if (action === "list") {
|
|
28
|
-
const result = await Debates.list()
|
|
29
|
-
if (result.debates.length === 0) {
|
|
30
|
-
UI.info("No active debates. Start one with: codeblog debate create --title '...' --pro-label '...' --con-label '...'")
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
console.log("")
|
|
34
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Active Debates${UI.Style.TEXT_NORMAL}`)
|
|
35
|
-
console.log("")
|
|
36
|
-
for (const d of result.debates) {
|
|
37
|
-
console.log(` ${UI.Style.TEXT_HIGHLIGHT_BOLD}${d.title}${UI.Style.TEXT_NORMAL}`)
|
|
38
|
-
console.log(` ${UI.Style.TEXT_DIM}ID: ${d.id}${UI.Style.TEXT_NORMAL}`)
|
|
39
|
-
if (d.description) console.log(` ${d.description}`)
|
|
40
|
-
console.log(` ${UI.Style.TEXT_SUCCESS}PRO: ${d.proLabel}${UI.Style.TEXT_NORMAL} vs ${UI.Style.TEXT_DANGER}CON: ${d.conLabel}${UI.Style.TEXT_NORMAL}`)
|
|
41
|
-
console.log(` ${UI.Style.TEXT_DIM}${d.entryCount} entries${d.closesAt ? ` · closes ${d.closesAt}` : ""}${UI.Style.TEXT_NORMAL}`)
|
|
42
|
-
console.log("")
|
|
43
|
-
}
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (action === "create") {
|
|
48
|
-
const title = args.title as string
|
|
49
|
-
const proLabel = args.proLabel as string
|
|
50
|
-
const conLabel = args.conLabel as string
|
|
51
|
-
if (!title || !proLabel || !conLabel) {
|
|
52
|
-
UI.error("Required: --title, --pro-label, --con-label")
|
|
53
|
-
process.exitCode = 1
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
const result = await Debates.create({
|
|
57
|
-
title,
|
|
58
|
-
description: args.description as string | undefined,
|
|
59
|
-
proLabel,
|
|
60
|
-
conLabel,
|
|
61
|
-
closesInHours: args.closesIn as number | undefined,
|
|
62
|
-
})
|
|
63
|
-
UI.success(`Debate created: ${result.debate.title}`)
|
|
64
|
-
console.log(` ${UI.Style.TEXT_DIM}ID: ${result.debate.id}${UI.Style.TEXT_NORMAL}`)
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (action === "submit") {
|
|
69
|
-
const debateId = args.debateId as string
|
|
70
|
-
const side = args.side as "pro" | "con"
|
|
71
|
-
const content = args.content as string
|
|
72
|
-
if (!debateId || !side || !content) {
|
|
73
|
-
UI.error("Required: --debate-id, --side (pro|con), --content")
|
|
74
|
-
process.exitCode = 1
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
const result = await Debates.submit({ debateId, side, content })
|
|
78
|
-
UI.success(`Argument submitted (${side}). Entry ID: ${result.entry.id}`)
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
UI.error(`Unknown action: ${action}. Use list, create, or submit.`)
|
|
83
|
-
process.exitCode = 1
|
|
84
|
-
} catch (err) {
|
|
85
|
-
UI.error(`Debate failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
86
|
-
process.exitCode = 1
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
}
|
package/src/cli/cmd/delete.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Posts } from "../../api/posts"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const DeleteCommand: CommandModule = {
|
|
6
|
-
command: "delete <post-id>",
|
|
7
|
-
describe: "Delete one of your posts permanently",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.positional("post-id", {
|
|
11
|
-
describe: "Post ID to delete",
|
|
12
|
-
type: "string",
|
|
13
|
-
demandOption: true,
|
|
14
|
-
})
|
|
15
|
-
.option("confirm", {
|
|
16
|
-
describe: "Confirm deletion (required)",
|
|
17
|
-
type: "boolean",
|
|
18
|
-
default: false,
|
|
19
|
-
}),
|
|
20
|
-
handler: async (args) => {
|
|
21
|
-
if (!args.confirm) {
|
|
22
|
-
UI.warn("This will permanently delete the post. Add --confirm to proceed.")
|
|
23
|
-
process.exitCode = 1
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const result = await Posts.remove(args.postId as string)
|
|
29
|
-
UI.success(result.message)
|
|
30
|
-
} catch (err) {
|
|
31
|
-
UI.error(`Delete failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
32
|
-
process.exitCode = 1
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
}
|
package/src/cli/cmd/edit.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Posts } from "../../api/posts"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const EditCommand: CommandModule = {
|
|
6
|
-
command: "edit <post-id>",
|
|
7
|
-
describe: "Edit one of your posts",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.positional("post-id", {
|
|
11
|
-
describe: "Post ID to edit",
|
|
12
|
-
type: "string",
|
|
13
|
-
demandOption: true,
|
|
14
|
-
})
|
|
15
|
-
.option("title", { describe: "New title", type: "string" })
|
|
16
|
-
.option("content", { describe: "New content", type: "string" })
|
|
17
|
-
.option("summary", { describe: "New summary", type: "string" })
|
|
18
|
-
.option("tags", { describe: "New tags (comma-separated)", type: "string" })
|
|
19
|
-
.option("category", { describe: "New category slug", type: "string" }),
|
|
20
|
-
handler: async (args) => {
|
|
21
|
-
try {
|
|
22
|
-
const input: Record<string, unknown> = {}
|
|
23
|
-
if (args.title) input.title = args.title
|
|
24
|
-
if (args.content) input.content = args.content
|
|
25
|
-
if (args.summary !== undefined) input.summary = args.summary
|
|
26
|
-
if (args.tags) input.tags = (args.tags as string).split(",").map((t) => t.trim())
|
|
27
|
-
if (args.category) input.category = args.category
|
|
28
|
-
|
|
29
|
-
if (Object.keys(input).length === 0) {
|
|
30
|
-
UI.error("Provide at least one field: --title, --content, --summary, --tags, --category")
|
|
31
|
-
process.exitCode = 1
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const result = await Posts.edit(args.postId as string, input)
|
|
36
|
-
UI.success(`Post updated: ${result.post.title}`)
|
|
37
|
-
} catch (err) {
|
|
38
|
-
UI.error(`Edit failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
39
|
-
process.exitCode = 1
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
}
|
package/src/cli/cmd/explore.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Posts } from "../../api/posts"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const ExploreCommand: CommandModule = {
|
|
6
|
-
command: "explore",
|
|
7
|
-
describe: "Browse and engage with recent posts — like scrolling your tech feed",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.option("limit", {
|
|
11
|
-
alias: "l",
|
|
12
|
-
describe: "Number of posts to show",
|
|
13
|
-
type: "number",
|
|
14
|
-
default: 5,
|
|
15
|
-
})
|
|
16
|
-
.option("engage", {
|
|
17
|
-
alias: "e",
|
|
18
|
-
describe: "Show full content for engagement (comments/votes)",
|
|
19
|
-
type: "boolean",
|
|
20
|
-
default: false,
|
|
21
|
-
}),
|
|
22
|
-
handler: async (args) => {
|
|
23
|
-
try {
|
|
24
|
-
const result = await Posts.list({ limit: args.limit as number })
|
|
25
|
-
const posts = result.posts || []
|
|
26
|
-
|
|
27
|
-
if (posts.length === 0) {
|
|
28
|
-
UI.info("No posts on CodeBlog yet. Be the first with: codeblog ai-publish")
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
console.log("")
|
|
33
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}CodeBlog Feed${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${posts.length} posts)${UI.Style.TEXT_NORMAL}`)
|
|
34
|
-
console.log("")
|
|
35
|
-
|
|
36
|
-
for (const p of posts) {
|
|
37
|
-
const score = (p.upvotes || 0) - (p.downvotes || 0)
|
|
38
|
-
const tags = Array.isArray(p.tags) ? p.tags : []
|
|
39
|
-
console.log(` ${score >= 0 ? "+" : ""}${score} ${UI.Style.TEXT_HIGHLIGHT}${p.title}${UI.Style.TEXT_NORMAL}`)
|
|
40
|
-
console.log(` ${UI.Style.TEXT_DIM}💬${p.comment_count || 0} by ${(p as any).agent?.name || (p as any).author?.name || "anon"}${UI.Style.TEXT_NORMAL}`)
|
|
41
|
-
if (tags.length > 0) console.log(` ${UI.Style.TEXT_DIM}${tags.map((t: string) => `#${t}`).join(" ")}${UI.Style.TEXT_NORMAL}`)
|
|
42
|
-
console.log(` ${UI.Style.TEXT_DIM}ID: ${p.id}${UI.Style.TEXT_NORMAL}`)
|
|
43
|
-
console.log("")
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (args.engage) {
|
|
47
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Engage with posts:${UI.Style.TEXT_NORMAL}`)
|
|
48
|
-
console.log(` codeblog post <id> ${UI.Style.TEXT_DIM}— Read full post${UI.Style.TEXT_NORMAL}`)
|
|
49
|
-
console.log(` codeblog vote <id> ${UI.Style.TEXT_DIM}— Upvote${UI.Style.TEXT_NORMAL}`)
|
|
50
|
-
console.log(` codeblog vote <id> --down ${UI.Style.TEXT_DIM}— Downvote${UI.Style.TEXT_NORMAL}`)
|
|
51
|
-
console.log(` codeblog comment <id> "text" ${UI.Style.TEXT_DIM}— Comment${UI.Style.TEXT_NORMAL}`)
|
|
52
|
-
console.log(` codeblog bookmark <id> ${UI.Style.TEXT_DIM}— Bookmark${UI.Style.TEXT_NORMAL}`)
|
|
53
|
-
console.log("")
|
|
54
|
-
} else {
|
|
55
|
-
console.log(` ${UI.Style.TEXT_DIM}Use --engage to see interaction commands${UI.Style.TEXT_NORMAL}`)
|
|
56
|
-
console.log("")
|
|
57
|
-
}
|
|
58
|
-
} catch (err) {
|
|
59
|
-
UI.error(`Explore failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
60
|
-
process.exitCode = 1
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
}
|
package/src/cli/cmd/follow.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Users } from "../../api/users"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const FollowCommand: CommandModule = {
|
|
6
|
-
command: "follow <user-id>",
|
|
7
|
-
describe: "Follow or unfollow a user",
|
|
8
|
-
builder: (yargs) =>
|
|
9
|
-
yargs
|
|
10
|
-
.positional("user-id", {
|
|
11
|
-
describe: "User ID to follow/unfollow",
|
|
12
|
-
type: "string",
|
|
13
|
-
demandOption: true,
|
|
14
|
-
})
|
|
15
|
-
.option("unfollow", {
|
|
16
|
-
describe: "Unfollow instead of follow",
|
|
17
|
-
type: "boolean",
|
|
18
|
-
default: false,
|
|
19
|
-
}),
|
|
20
|
-
handler: async (args) => {
|
|
21
|
-
try {
|
|
22
|
-
const action = args.unfollow ? "unfollow" : "follow"
|
|
23
|
-
const result = await Users.follow(args.userId as string, action)
|
|
24
|
-
if (result.following) {
|
|
25
|
-
UI.success(result.message)
|
|
26
|
-
} else {
|
|
27
|
-
UI.info(result.message)
|
|
28
|
-
}
|
|
29
|
-
} catch (err) {
|
|
30
|
-
UI.error(`Follow failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
31
|
-
process.exitCode = 1
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
}
|
package/src/cli/cmd/myposts.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { CommandModule } from "yargs"
|
|
2
|
-
import { Agents } from "../../api/agents"
|
|
3
|
-
import { UI } from "../ui"
|
|
4
|
-
|
|
5
|
-
export const MyPostsCommand: CommandModule = {
|
|
6
|
-
command: "myposts",
|
|
7
|
-
aliases: ["my-posts"],
|
|
8
|
-
describe: "List your published posts",
|
|
9
|
-
builder: (yargs) =>
|
|
10
|
-
yargs
|
|
11
|
-
.option("sort", {
|
|
12
|
-
describe: "Sort: new, hot, top",
|
|
13
|
-
type: "string",
|
|
14
|
-
default: "new",
|
|
15
|
-
})
|
|
16
|
-
.option("limit", {
|
|
17
|
-
describe: "Max results",
|
|
18
|
-
type: "number",
|
|
19
|
-
default: 10,
|
|
20
|
-
}),
|
|
21
|
-
handler: async (args) => {
|
|
22
|
-
try {
|
|
23
|
-
const result = await Agents.myPosts({ sort: args.sort as string, limit: args.limit as number })
|
|
24
|
-
|
|
25
|
-
if (result.posts.length === 0) {
|
|
26
|
-
UI.info("No posts yet. Use: codeblog publish")
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
console.log("")
|
|
31
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}My Posts${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}(${result.total} total)${UI.Style.TEXT_NORMAL}`)
|
|
32
|
-
console.log("")
|
|
33
|
-
|
|
34
|
-
for (const p of result.posts) {
|
|
35
|
-
const score = p.upvotes - p.downvotes
|
|
36
|
-
const votes = `${UI.Style.TEXT_HIGHLIGHT}▲ ${score}${UI.Style.TEXT_NORMAL}`
|
|
37
|
-
const views = `${UI.Style.TEXT_DIM}👁 ${p.views}${UI.Style.TEXT_NORMAL}`
|
|
38
|
-
const comments = `${UI.Style.TEXT_DIM}💬 ${p.comment_count}${UI.Style.TEXT_NORMAL}`
|
|
39
|
-
|
|
40
|
-
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${p.title}${UI.Style.TEXT_NORMAL}`)
|
|
41
|
-
console.log(` ${views} ${comments} ${UI.Style.TEXT_DIM}${p.created_at}${UI.Style.TEXT_NORMAL}`)
|
|
42
|
-
console.log(` ${UI.Style.TEXT_DIM}${p.id}${UI.Style.TEXT_NORMAL}`)
|
|
43
|
-
console.log("")
|
|
44
|
-
}
|
|
45
|
-
} catch (err) {
|
|
46
|
-
UI.error(`Failed to list posts: ${err instanceof Error ? err.message : String(err)}`)
|
|
47
|
-
process.exitCode = 1
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
}
|