Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/daily-digest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ jobs:
git push
fi

- name: Send Telegram notification
- name: Send notifications
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID || '@agents_radar' }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
PAGES_URL: https://duanyytop.github.io/agents-radar
run: pnpm notify

2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@
'ai-cli-en': 'AI CLI Tools',
'ai-agents': 'AI Agents 生态',
'ai-agents-en': 'AI Agents Ecosystem',
'ai-mcp': 'MCP 生态',
'ai-mcp-en': 'MCP Ecosystem',
'ai-web': 'OpenAI & Anthropic 官网动态',
'ai-web-en': 'OpenAI & Anthropic Updates',
'ai-trending': 'GitHub AI 趋势',
Expand Down
21 changes: 19 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ interface RawRepoEntry {

interface RawConfig {
cli_repos?: RawRepoEntry[];
mcp_repos?: RawRepoEntry[];
skills_repo?: string;
openclaw?: RawRepoEntry;
openclaw_peers?: RawRepoEntry[];
}

export interface RadarConfig {
cliRepos: RepoConfig[];
mcpRepos: RepoConfig[];
skillsRepo: string;
openclaw: RepoConfig;
openclawPeers: RepoConfig[];
Expand Down Expand Up @@ -56,6 +58,15 @@ const DEFAULT_OPENCLAW: RepoConfig = {
paginated: true,
};

const DEFAULT_MCP_REPOS: RepoConfig[] = [
{ id: "mcp-spec", repo: "modelcontextprotocol/specification", name: "MCP Specification" },
{ id: "mcp-ts-sdk", repo: "modelcontextprotocol/typescript-sdk", name: "MCP TypeScript SDK" },
{ id: "mcp-py-sdk", repo: "modelcontextprotocol/python-sdk", name: "MCP Python SDK" },
{ id: "mcp-servers", repo: "modelcontextprotocol/servers", name: "MCP Official Servers" },
{ id: "github-mcp", repo: "github/github-mcp-server", name: "GitHub MCP Server" },
{ id: "context7", repo: "upstash/context7", name: "Context7" },
];

const DEFAULT_OPENCLAW_PEERS: RepoConfig[] = [
{ id: "nanobot", repo: "HKUDS/nanobot", name: "NanoBot", paginated: true },
{ id: "zeroclaw", repo: "zeroclaw-labs/zeroclaw", name: "Zeroclaw" },
Expand Down Expand Up @@ -84,6 +95,7 @@ export function loadConfig(configPath = "config.yml"): RadarConfig {
console.log(`[config] ${configPath} not found — using built-in defaults.`);
return {
cliRepos: DEFAULT_CLI_REPOS,
mcpRepos: DEFAULT_MCP_REPOS,
skillsRepo: DEFAULT_SKILLS_REPO,
openclaw: DEFAULT_OPENCLAW,
openclawPeers: DEFAULT_OPENCLAW_PEERS,
Expand All @@ -104,15 +116,20 @@ export function loadConfig(configPath = "config.yml"): RadarConfig {

const openclaw = raw?.openclaw?.id && raw.openclaw.repo ? toRepoConfig(raw.openclaw) : DEFAULT_OPENCLAW;

const mcpRepos =
Array.isArray(raw?.mcp_repos) && raw.mcp_repos.length > 0
? raw.mcp_repos.map(toRepoConfig)
: DEFAULT_MCP_REPOS;

const openclawPeers =
Array.isArray(raw?.openclaw_peers) && raw.openclaw_peers.length > 0
? raw.openclaw_peers.map(toRepoConfig)
: DEFAULT_OPENCLAW_PEERS;

console.log(
`[config] Loaded from ${configPath}: ` +
`${cliRepos.length} CLI repos, ${openclawPeers.length} OpenClaw peers`,
`${cliRepos.length} CLI repos, ${mcpRepos.length} MCP repos, ${openclawPeers.length} OpenClaw peers`,
);

return { cliRepos, skillsRepo, openclaw, openclawPeers };
return { cliRepos, mcpRepos, skillsRepo, openclaw, openclawPeers };
}
4 changes: 4 additions & 0 deletions src/generate-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const REPORT_FILES = [
"ai-cli-en",
"ai-agents",
"ai-agents-en",
"ai-mcp",
"ai-mcp-en",
"ai-web",
"ai-web-en",
"ai-trending",
Expand All @@ -29,6 +31,8 @@ const REPORT_LABELS: Record<string, string> = {
"ai-cli-en": "AI CLI Tools Digest",
"ai-agents": "AI Agents 生态日报",
"ai-agents-en": "AI Agents Ecosystem Digest",
"ai-mcp": "MCP 生态日报",
"ai-mcp-en": "MCP Ecosystem Digest",
"ai-web": "AI 官方内容追踪报告",
"ai-web-en": "Official AI Content Report",
"ai-trending": "AI 开源趋势日报",
Expand Down
2 changes: 2 additions & 0 deletions src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ export async function createGitHubIssue(title: string, body: string, label: stri
}
const LABEL_COLORS: Record<string, string> = {
openclaw: "e11d48",
mcp: "8b5cf6",
"mcp-en": "a78bfa",
trending: "f9a825",
hn: "ff6600",
weekly: "7c3aed",
Expand Down
72 changes: 63 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ import {
buildPeerPrompt,
buildComparisonPrompt,
buildPeersComparisonPrompt,
buildMcpComparisonPrompt,
buildSkillsPrompt,
buildWebReportPrompt,
buildTrendingPrompt,
buildHnPrompt,
} from "./prompts.ts";
import { callLlm, saveFile, autoGenFooter } from "./report.ts";
import { buildCliReportContent, buildOpenclawReportContent } from "./report-builders.ts";
import {
buildCliReportContent,
buildOpenclawReportContent,
buildMcpReportContent,
} from "./report-builders.ts";
import { loadWebState, saveWebState, fetchSiteContent, type WebFetchResult, type WebState } from "./web.ts";
import { fetchTrendingData, type TrendingData } from "./trending.ts";
import { fetchHnData, type HnData } from "./hn.ts";
Expand All @@ -42,6 +47,7 @@ import { loadConfig } from "./config.ts";

const {
cliRepos: CLI_REPOS,
mcpRepos: MCP_REPOS,
skillsRepo: CLAUDE_SKILLS_REPO,
openclaw: OPENCLAW,
openclawPeers: OPENCLAW_PEERS,
Expand Down Expand Up @@ -82,7 +88,7 @@ async function fetchAllData(
trendingData: TrendingData;
hnData: HnData;
}> {
const allConfigs = [...CLI_REPOS, OPENCLAW, ...OPENCLAW_PEERS];
const allConfigs = [...CLI_REPOS, ...MCP_REPOS, OPENCLAW, ...OPENCLAW_PEERS];
console.log(` Tracking: ${allConfigs.map((r) => r.id).join(", ")}, claude-code-skills, web, hn`);

const [fetched, skillsData, webResults, trendingData, hnData] = await Promise.all([
Expand Down Expand Up @@ -420,7 +426,11 @@ async function main(): Promise<void> {
const { fetched, skillsData, webResults, trendingData, hnData } = await fetchAllData(since, webState);

const peerIds = new Set(OPENCLAW_PEERS.map((p) => p.id));
const fetchedCli = fetched.filter((f) => f.cfg.id !== OPENCLAW.id && !peerIds.has(f.cfg.id));
const mcpIds = new Set(MCP_REPOS.map((r) => r.id));
const fetchedCli = fetched.filter(
(f) => f.cfg.id !== OPENCLAW.id && !peerIds.has(f.cfg.id) && !mcpIds.has(f.cfg.id),
);
const fetchedMcp = fetched.filter((f) => mcpIds.has(f.cfg.id));
const fetchedOpenclaw = fetched.find((f) => f.cfg.id === OPENCLAW.id)!;
const fetchedPeers = fetched.filter((f) => peerIds.has(f.cfg.id));

Expand All @@ -431,6 +441,36 @@ async function main(): Promise<void> {
generateSummaries(fetchedCli, fetchedOpenclaw, skillsData, fetchedPeers, trendingData, dateStr, "en"),
]);

// 2b. Generate MCP per-repo summaries (zh + en)
console.log(" Generating MCP summaries in ZH and EN...");
const generateMcpDigests = async (lang: "zh" | "en"): Promise<RepoDigest[]> => {
const noActivity = lang === "en" ? "No activity in the last 24 hours." : "过去24小时无活动。";
const summaryFailed = lang === "en" ? "⚠️ Summary generation failed." : "⚠️ 摘要生成失败。";
return Promise.all(
fetchedMcp.map(async ({ cfg, issues, prs, releases }): Promise<RepoDigest> => {
const hasData = issues.length || prs.length || releases.length;
if (!hasData) {
console.log(` [${cfg.id}] No activity, skipping LLM call`);
return { config: cfg, issues, prs, releases, summary: noActivity };
}
console.log(` [${cfg.id}] Calling LLM for MCP summary (${lang})...`);
try {
const summary = await callLlm(
buildPeerPrompt(cfg, issues, prs, releases, dateStr, undefined, undefined, lang),
);
return { config: cfg, issues, prs, releases, summary };
Comment on lines +456 to +461
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCP per-repo summaries are generated with buildPeerPrompt(), which is explicitly tailored to “AI agent and personal AI assistant open-source projects”. For MCP spec/SDK/server repos this mismatches the domain and will likely produce irrelevant/incorrect summaries. Introduce an MCP-specific per-repo prompt (or parameterize buildPeerPrompt wording) and use that here instead.

Copilot uses AI. Check for mistakes.
} catch (err) {
console.error(` [${cfg.id}] LLM call failed: ${err}`);
return { config: cfg, issues, prs, releases, summary: summaryFailed };
}
}),
);
};
const [zhMcpDigests, enMcpDigests] = await Promise.all([
generateMcpDigests("zh"),
generateMcpDigests("en"),
]);

// 3. Generate cross-repo comparisons in parallel (zh + en)
console.log(" Calling LLM for comparative analyses (ZH + EN)...");
const openclawDigest: RepoDigest = {
Expand All @@ -447,12 +487,15 @@ async function main(): Promise<void> {
releases: fetchedOpenclaw.releases,
summary: enSummaries.openclawSummary,
};
const [comparison, peersComparison, enComparison, enPeersComparison] = await Promise.all([
callLlm(buildComparisonPrompt(zhSummaries.cliDigests, dateStr, "zh")),
callLlm(buildPeersComparisonPrompt(openclawDigest, zhSummaries.peerDigests, dateStr, "zh")),
callLlm(buildComparisonPrompt(enSummaries.cliDigests, dateStr, "en")),
callLlm(buildPeersComparisonPrompt(enOpenclawDigest, enSummaries.peerDigests, dateStr, "en")),
]);
const [comparison, peersComparison, enComparison, enPeersComparison, mcpComparison, enMcpComparison] =
await Promise.all([
callLlm(buildComparisonPrompt(zhSummaries.cliDigests, dateStr, "zh")),
callLlm(buildPeersComparisonPrompt(openclawDigest, zhSummaries.peerDigests, dateStr, "zh")),
callLlm(buildComparisonPrompt(enSummaries.cliDigests, dateStr, "en")),
callLlm(buildPeersComparisonPrompt(enOpenclawDigest, enSummaries.peerDigests, dateStr, "en")),
callLlm(buildMcpComparisonPrompt(zhMcpDigests, dateStr, "zh")),
callLlm(buildMcpComparisonPrompt(enMcpDigests, dateStr, "en")),
]);

const footer = autoGenFooter("zh");
const enFooter = autoGenFooter("en");
Expand Down Expand Up @@ -503,10 +546,15 @@ async function main(): Promise<void> {
"en",
);

const mcpContent = buildMcpReportContent(zhMcpDigests, mcpComparison, utcStr, dateStr, footer, "zh");
const enMcpContent = buildMcpReportContent(enMcpDigests, enMcpComparison, utcStr, dateStr, enFooter, "en");

console.log(` Saved ${saveFile(digestContent, dateStr, "ai-cli.md")}`);
console.log(` Saved ${saveFile(openclawContent, dateStr, "ai-agents.md")}`);
console.log(` Saved ${saveFile(mcpContent, dateStr, "ai-mcp.md")}`);
console.log(` Saved ${saveFile(enDigestContent, dateStr, "ai-cli-en.md")}`);
console.log(` Saved ${saveFile(enOpenclawContent, dateStr, "ai-agents-en.md")}`);
console.log(` Saved ${saveFile(enMcpContent, dateStr, "ai-mcp-en.md")}`);

// Web report: zh saves state, en skips state save
await saveWebReport(webResults, webState, utcStr, dateStr, digestRepo, footer, "zh");
Expand Down Expand Up @@ -552,6 +600,12 @@ async function main(): Promise<void> {
"openclaw-en",
);
console.log(` Created OpenClaw issue (en): ${openclawEnUrl}`);

const mcpUrl = await createGitHubIssue(`🔌 MCP 生态日报 ${dateStr}`, mcpContent, "mcp");
console.log(` Created MCP issue (zh): ${mcpUrl}`);

const mcpEnUrl = await createGitHubIssue(`🔌 MCP Ecosystem Digest ${dateStr}`, enMcpContent, "mcp-en");
console.log(` Created MCP issue (en): ${mcpEnUrl}`);
}

console.log("Done!");
Expand Down
Loading