@viren/claude-code-dashboard 0.0.6 → 0.0.8

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viren/claude-code-dashboard",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "A visual dashboard for your Claude Code configuration across all repos",
5
5
  "type": "module",
6
6
  "bin": {
package/src/analysis.mjs CHANGED
@@ -90,22 +90,29 @@ export function detectTechStack(repoDir) {
90
90
  return { stacks: [...stacks] };
91
91
  }
92
92
 
93
- export function computeDrift(repoDir, configTimestamp) {
94
- if (!configTimestamp) return { level: "unknown", commitsSince: 0 };
93
+ export function classifyDrift(commitCount) {
94
+ if (commitCount === null || commitCount === undefined || commitCount < 0) {
95
+ return { level: "unknown", commitsSince: 0 };
96
+ }
97
+ const n = Math.max(0, Number(commitCount) || 0);
98
+ if (n === 0) return { level: "synced", commitsSince: 0 };
99
+ if (n <= 5) return { level: "low", commitsSince: n };
100
+ if (n <= 20) return { level: "medium", commitsSince: n };
101
+ return { level: "high", commitsSince: n };
102
+ }
95
103
 
96
- // Count commits since the config was last updated
104
+ /** Count commits since config was last updated. Returns null if unknown. */
105
+ export function getGitRevCount(repoDir, configTimestamp) {
106
+ if (!configTimestamp) return null;
97
107
  const countStr = gitCmd(repoDir, "rev-list", "--count", `--since=${configTimestamp}`, "HEAD");
98
- if (!countStr) return { level: "unknown", commitsSince: 0 };
99
-
108
+ if (!countStr) return null;
100
109
  const parsed = Number(countStr);
101
- if (!Number.isFinite(parsed)) return { level: "unknown", commitsSince: 0 };
102
-
103
- const commitsSince = Math.max(0, parsed - 1); // -1 to exclude the config commit itself
110
+ if (!Number.isFinite(parsed)) return null;
111
+ return Math.max(0, parsed - 1); // -1 to exclude the config commit itself
112
+ }
104
113
 
105
- if (commitsSince === 0) return { level: "synced", commitsSince: 0 };
106
- if (commitsSince <= 5) return { level: "low", commitsSince };
107
- if (commitsSince <= 20) return { level: "medium", commitsSince };
108
- return { level: "high", commitsSince };
114
+ export function computeDrift(repoDir, configTimestamp) {
115
+ return classifyDrift(getGitRevCount(repoDir, configTimestamp));
109
116
  }
110
117
 
111
118
  export function findExemplar(stack, configuredRepos) {
package/src/assembler.mjs CHANGED
@@ -47,6 +47,9 @@ export function generateDashboardHtml(data) {
47
47
  mcpSummary,
48
48
  mcpPromotions,
49
49
  formerMcpServers,
50
+ recommendedMcpServers,
51
+ availableMcpServers,
52
+ registryTotal,
50
53
  consolidationGroups,
51
54
  usageAnalytics,
52
55
  ccusageData,
@@ -90,7 +93,7 @@ export function generateDashboardHtml(data) {
90
93
  const tabOverview = `${overviewCommands}\n ${insightsHtml}\n ${chainsHtml}\n ${consolidationHtml}`;
91
94
 
92
95
  // Skills & MCP tab
93
- const tabSkillsMcp = `${renderSkillsCard(globalSkills)}\n ${renderMcpCard(mcpSummary, mcpPromotions, formerMcpServers)}`;
96
+ const tabSkillsMcp = `${renderSkillsCard(globalSkills)}\n ${renderMcpCard(mcpSummary, mcpPromotions, formerMcpServers, recommendedMcpServers, availableMcpServers, registryTotal)}`;
94
97
 
95
98
  // Analytics tab
96
99
  const insightsReportHtml = renderInsightsReportCard(insightsReport);
package/src/cli.mjs CHANGED
@@ -15,6 +15,7 @@ export function parseArgs(argv) {
15
15
  anonymize: false,
16
16
  demo: false,
17
17
  completions: false,
18
+ offline: false,
18
19
  };
19
20
  let i = 2; // skip node + script
20
21
  if (argv[2] === "init") {
@@ -46,6 +47,7 @@ Options:
46
47
  --watch Regenerate on file changes
47
48
  --diff Show changes since last generation
48
49
  --anonymize Anonymize all data for shareable export
50
+ --offline Skip network fetches (registry, etc.)
49
51
  --demo Generate dashboard with sample data (no scanning)
50
52
  --completions Output shell completion script for bash/zsh
51
53
  --version, -v Show version
@@ -113,6 +115,9 @@ Config file: ~/.claude/dashboard.conf
113
115
  case "--anonymize":
114
116
  args.anonymize = true;
115
117
  break;
118
+ case "--offline":
119
+ args.offline = true;
120
+ break;
116
121
  case "--demo":
117
122
  args.demo = true;
118
123
  break;
@@ -133,11 +138,11 @@ export function generateCompletions() {
133
138
  # eval "$(claude-code-dashboard --completions)"
134
139
  if [ -n "$ZSH_VERSION" ]; then
135
140
  _claude_code_dashboard() {
136
- local -a opts; opts=(init lint --output --open --no-open --json --catalog --quiet --watch --diff --anonymize --demo --completions --help --version)
141
+ local -a opts; opts=(init lint --output --open --no-open --json --catalog --quiet --watch --diff --anonymize --offline --demo --completions --help --version)
137
142
  if (( CURRENT == 2 )); then _describe 'option' opts; fi
138
143
  }; compdef _claude_code_dashboard claude-code-dashboard
139
144
  elif [ -n "$BASH_VERSION" ]; then
140
- _claude_code_dashboard() { COMPREPLY=( $(compgen -W "init lint --output --open --no-open --json --catalog --quiet --watch --diff --anonymize --demo --completions --help --version" -- "\${COMP_WORDS[COMP_CWORD]}") ); }
145
+ _claude_code_dashboard() { COMPREPLY=( $(compgen -W "init lint --output --open --no-open --json --catalog --quiet --watch --diff --anonymize --offline --demo --completions --help --version" -- "\${COMP_WORDS[COMP_CWORD]}") ); }
141
146
  complete -F _claude_code_dashboard claude-code-dashboard
142
147
  fi`);
143
148
  process.exit(0);
package/src/constants.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { join } from "path";
2
2
  import { homedir } from "os";
3
3
 
4
- export const VERSION = "0.0.6";
4
+ export const VERSION = "0.0.8";
5
5
  export const REPO_URL = "https://github.com/VirenMohindra/claude-code-dashboard";
6
6
 
7
7
  export const HOME = homedir();
@@ -98,6 +98,44 @@ export const SKILL_CATEGORIES = {
98
98
  "project-specific": ["storybook", "react-native"],
99
99
  };
100
100
 
101
+ export const MCP_REGISTRY_URL =
102
+ "https://api.anthropic.com/mcp-registry/v0/servers?visibility=commercial&limit=100";
103
+ export const MCP_REGISTRY_TTL_MS = 24 * 60 * 60 * 1000; // 24h
104
+
105
+ /**
106
+ * Maps detected tech stacks and description keywords to relevant MCP server slugs.
107
+ * Used by the pipeline to compute MCP recommendations.
108
+ *
109
+ * Keys: tech stack names (matching STACK_FILES values) or lowercase keywords
110
+ * found in repo descriptions.
111
+ * Values: array of MCP server slugs from the Anthropic registry.
112
+ */
113
+ export const MCP_STACK_HINTS = {
114
+ // Stack-based (keys match STACK_FILES values)
115
+ next: ["vercel", "figma"],
116
+ react: ["figma"],
117
+ python: ["sentry"],
118
+ go: ["sentry"],
119
+ rust: ["sentry"],
120
+ java: ["sentry"],
121
+ expo: ["figma"],
122
+
123
+ // Keyword-based (matched against lowercased repo descriptions)
124
+ supabase: ["supabase"],
125
+ stripe: ["stripe"],
126
+ vercel: ["vercel"],
127
+ sentry: ["sentry"],
128
+ notion: ["notion"],
129
+ linear: ["linear"],
130
+ jira: ["atlassian"],
131
+ confluence: ["atlassian"],
132
+ slack: ["slack"],
133
+ figma: ["figma"],
134
+ github: ["github"],
135
+ huggingface: ["hugging-face"],
136
+ "hugging face": ["hugging-face"],
137
+ };
138
+
101
139
  export const CATEGORY_ORDER = [
102
140
  "workflow",
103
141
  "code-quality",