buildpartner 0.3.4 → 0.3.5
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/dist/bin/cli.js +6 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/api-client.d.ts +13 -9
- package/dist/src/api-client.js +14 -31
- package/dist/src/api-client.js.map +1 -1
- package/dist/src/commands/log-session.d.ts +3 -6
- package/dist/src/commands/log-session.js +10 -149
- package/dist/src/commands/log-session.js.map +1 -1
- package/dist/src/commands/reset.d.ts +1 -0
- package/dist/src/commands/reset.js +130 -0
- package/dist/src/commands/reset.js.map +1 -0
- package/dist/src/commands/setup.js +83 -398
- package/dist/src/commands/setup.js.map +1 -1
- package/dist/src/commands/sync.d.ts +6 -0
- package/dist/src/commands/sync.js +32 -0
- package/dist/src/commands/sync.js.map +1 -0
- package/dist/src/mcp/catch-up.d.ts +6 -0
- package/dist/src/mcp/catch-up.js +28 -0
- package/dist/src/mcp/catch-up.js.map +1 -0
- package/dist/src/mcp/server.d.ts +13 -0
- package/dist/src/mcp/server.js +71 -81
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/subscription-gate.d.ts +2 -2
- package/dist/src/mcp/subscription-gate.js +4 -9
- package/dist/src/mcp/subscription-gate.js.map +1 -1
- package/dist/src/mcp/tools/get-profile.js +7 -70
- package/dist/src/mcp/tools/get-profile.js.map +1 -1
- package/dist/src/mcp/tools/get-stats.d.ts +8 -34
- package/dist/src/mcp/tools/get-stats.js +7 -97
- package/dist/src/mcp/tools/get-stats.js.map +1 -1
- package/dist/src/mcp/tools/get-suggestions.js +10 -121
- package/dist/src/mcp/tools/get-suggestions.js.map +1 -1
- package/dist/src/mcp/tools/log-highlight.d.ts +2 -10
- package/dist/src/mcp/tools/log-highlight.js +5 -19
- package/dist/src/mcp/tools/log-highlight.js.map +1 -1
- package/dist/src/mcp/tools/quest.d.ts +3 -77
- package/dist/src/mcp/tools/quest.js +6 -70
- package/dist/src/mcp/tools/quest.js.map +1 -1
- package/dist/src/readers/extract-signals.d.ts +23 -0
- package/dist/src/readers/extract-signals.js +50 -0
- package/dist/src/readers/extract-signals.js.map +1 -0
- package/dist/src/readers/session-reader.d.ts +0 -14
- package/dist/src/readers/session-reader.js +3 -122
- package/dist/src/readers/session-reader.js.map +1 -1
- package/dist/src/readers/session-sync.d.ts +24 -0
- package/dist/src/readers/session-sync.js +113 -0
- package/dist/src/readers/session-sync.js.map +1 -0
- package/dist/src/store/local-store.d.ts +12 -60
- package/dist/src/store/local-store.js +16 -113
- package/dist/src/store/local-store.js.map +1 -1
- package/dist/src/store/quest-store.d.ts +5 -68
- package/dist/src/store/quest-store.js +9 -277
- package/dist/src/store/quest-store.js.map +1 -1
- package/dist/src/store/status-store.d.ts +5 -68
- package/dist/src/store/status-store.js +8 -281
- package/dist/src/store/status-store.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,31 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { readAuth } from "../../store/local-store.js";
|
|
3
3
|
import { apiGet } from "../../api-client.js";
|
|
4
|
-
import { readSessions, readHighlights, readProfile } from "../../store/local-store.js";
|
|
5
|
-
import { getStatsSummary, getThisWeekActivity, getActivityInRange } from "../../readers/stats-reader.js";
|
|
6
|
-
function toLocalDate(isoTimestamp) {
|
|
7
|
-
const d = new Date(isoTimestamp);
|
|
8
|
-
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
9
|
-
}
|
|
10
|
-
function todayLocal() {
|
|
11
|
-
return toLocalDate(new Date().toISOString());
|
|
12
|
-
}
|
|
13
4
|
export const getStatsSchema = {
|
|
14
5
|
period: z.enum(["week", "month", "all"]).describe("Time period for stats"),
|
|
15
6
|
};
|
|
16
7
|
export async function getStats({ period }) {
|
|
17
8
|
const auth = readAuth();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
9
|
+
if (!auth?.username) {
|
|
10
|
+
return { error: "Not set up. Run: npx buildpartner setup" };
|
|
11
|
+
}
|
|
12
|
+
const data = await apiGet(`/api/buildpartner/dashboard?username=${auth.username}`);
|
|
13
|
+
if (!data) {
|
|
14
|
+
return { error: "Could not fetch stats. Check your connection." };
|
|
24
15
|
}
|
|
25
|
-
// Fallback to local
|
|
26
|
-
return getLocalStats(period);
|
|
27
|
-
}
|
|
28
|
-
function formatApiStats(data, period) {
|
|
29
16
|
const now = new Date();
|
|
30
17
|
let sessions = data.sessions;
|
|
31
18
|
if (period === "week") {
|
|
@@ -40,95 +27,18 @@ function formatApiStats(data, period) {
|
|
|
40
27
|
}
|
|
41
28
|
const totalTokens = sessions.reduce((sum, s) => sum + (s.tokens || 0), 0);
|
|
42
29
|
const distribution = {};
|
|
43
|
-
const dailyBreakdown = {};
|
|
44
30
|
for (const s of sessions) {
|
|
45
31
|
distribution[s.activity_type] = (distribution[s.activity_type] || 0) + 1;
|
|
46
|
-
const date = toLocalDate(s.timestamp);
|
|
47
|
-
if (!dailyBreakdown[date])
|
|
48
|
-
dailyBreakdown[date] = { sessions: 0, types: [], tokens: 0 };
|
|
49
|
-
dailyBreakdown[date].sessions++;
|
|
50
|
-
dailyBreakdown[date].types.push(s.activity_type);
|
|
51
|
-
dailyBreakdown[date].tokens += s.tokens || 0;
|
|
52
32
|
}
|
|
53
33
|
return {
|
|
54
34
|
period,
|
|
55
35
|
sessionCount: sessions.length,
|
|
56
36
|
totalTokens,
|
|
57
|
-
highlights: data.highlights || [],
|
|
58
37
|
distribution,
|
|
59
|
-
|
|
60
|
-
streak: {
|
|
61
|
-
current: data.stats.current_streak,
|
|
62
|
-
best: data.stats.current_streak, // API doesn't track best yet
|
|
63
|
-
},
|
|
64
|
-
fingerprint: "unknown",
|
|
65
|
-
pathScores: {},
|
|
38
|
+
streak: { current: data.stats.current_streak },
|
|
66
39
|
allTime: {
|
|
67
40
|
totalSessions: data.stats.total_sessions,
|
|
68
41
|
totalTokens: data.stats.total_tokens,
|
|
69
|
-
activeSince: null,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function getLocalStats(period) {
|
|
74
|
-
const statsSummary = getStatsSummary();
|
|
75
|
-
const sessions = readSessions();
|
|
76
|
-
const highlights = readHighlights();
|
|
77
|
-
const profile = readProfile();
|
|
78
|
-
const now = new Date();
|
|
79
|
-
let periodSessions = sessions;
|
|
80
|
-
let periodActivity = statsSummary?.dailyActivity || [];
|
|
81
|
-
if (period === "week") {
|
|
82
|
-
const weekAgo = new Date(now);
|
|
83
|
-
weekAgo.setDate(weekAgo.getDate() - 7);
|
|
84
|
-
const startDateLocal = toLocalDate(weekAgo.toISOString());
|
|
85
|
-
periodSessions = sessions.filter((s) => toLocalDate(s.timestamp) >= startDateLocal);
|
|
86
|
-
periodActivity = getThisWeekActivity();
|
|
87
|
-
}
|
|
88
|
-
else if (period === "month") {
|
|
89
|
-
const monthAgo = new Date(now);
|
|
90
|
-
monthAgo.setMonth(monthAgo.getMonth() - 1);
|
|
91
|
-
const startDateLocal = toLocalDate(monthAgo.toISOString());
|
|
92
|
-
periodSessions = sessions.filter((s) => toLocalDate(s.timestamp) >= startDateLocal);
|
|
93
|
-
periodActivity = getActivityInRange(startDateLocal, todayLocal());
|
|
94
|
-
}
|
|
95
|
-
const totalTokens = periodSessions.reduce((sum, s) => sum + s.tokens, 0);
|
|
96
|
-
const distribution = {};
|
|
97
|
-
const dailyBreakdown = {};
|
|
98
|
-
for (const s of periodSessions) {
|
|
99
|
-
distribution[s.activity_type] = (distribution[s.activity_type] || 0) + 1;
|
|
100
|
-
const date = toLocalDate(s.timestamp);
|
|
101
|
-
if (!dailyBreakdown[date])
|
|
102
|
-
dailyBreakdown[date] = { sessions: 0, types: [], tokens: 0 };
|
|
103
|
-
dailyBreakdown[date].sessions++;
|
|
104
|
-
dailyBreakdown[date].types.push(s.activity_type);
|
|
105
|
-
dailyBreakdown[date].tokens += s.tokens;
|
|
106
|
-
}
|
|
107
|
-
const periodHighlights = period === "all" ? highlights : highlights.filter((h) => {
|
|
108
|
-
const cutoff = new Date(now);
|
|
109
|
-
if (period === "week")
|
|
110
|
-
cutoff.setDate(cutoff.getDate() - 7);
|
|
111
|
-
else
|
|
112
|
-
cutoff.setMonth(cutoff.getMonth() - 1);
|
|
113
|
-
return h.created_at >= cutoff.toISOString();
|
|
114
|
-
});
|
|
115
|
-
return {
|
|
116
|
-
period,
|
|
117
|
-
sessionCount: periodSessions.length,
|
|
118
|
-
totalTokens,
|
|
119
|
-
highlights: periodHighlights,
|
|
120
|
-
distribution,
|
|
121
|
-
dailyBreakdown,
|
|
122
|
-
streak: {
|
|
123
|
-
current: profile?.streak_current ?? statsSummary?.currentStreak ?? 0,
|
|
124
|
-
best: profile?.streak_best ?? statsSummary?.bestStreak ?? 0,
|
|
125
|
-
},
|
|
126
|
-
fingerprint: profile?.path ?? "unknown",
|
|
127
|
-
pathScores: profile?.path_scores ?? {},
|
|
128
|
-
allTime: {
|
|
129
|
-
totalSessions: profile?.total_sessions ?? statsSummary?.totalSessions ?? 0,
|
|
130
|
-
totalTokens: profile?.total_tokens ?? 0,
|
|
131
|
-
activeSince: profile?.active_since ?? statsSummary?.firstDate ?? null,
|
|
132
42
|
},
|
|
133
43
|
};
|
|
134
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-stats.js","sourceRoot":"","sources":["../../../../src/mcp/tools/get-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-stats.js","sourceRoot":"","sources":["../../../../src/mcp/tools/get-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;CAC3E,CAAC;AAUF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAE,MAAM,EAAwC;IAC7E,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IACxB,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC;IAC9D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAoB,wCAAwC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACtG,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC;IACpE,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAE7B,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QACvC,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,CAAC;IACtE,CAAC;SAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3C,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1E,MAAM,YAAY,GAA2B,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO;QACL,MAAM;QACN,YAAY,EAAE,QAAQ,CAAC,MAAM;QAC7B,WAAW;QACX,YAAY;QACZ,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QAC9C,OAAO,EAAE;YACP,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc;YACxC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;SACrC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,25 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readAuth } from "../../store/local-store.js";
|
|
2
2
|
import { apiGet } from "../../api-client.js";
|
|
3
|
-
const VIDEO_LINKS = {
|
|
4
|
-
deploy: "How I Deploy Apps in Minutes",
|
|
5
|
-
"mcp-server": "Build Your First MCP Server",
|
|
6
|
-
automation: "How I Automated My Content Pipeline",
|
|
7
|
-
content: "My AI Content Creation System",
|
|
8
|
-
};
|
|
9
|
-
const ALL_ACTIVITY_TYPES = [
|
|
10
|
-
"code", "content", "research", "automation", "design", "ops", "data", "debug",
|
|
11
|
-
];
|
|
12
3
|
export async function getSuggestions() {
|
|
13
4
|
const auth = readAuth();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
5
|
+
if (!auth?.username) {
|
|
6
|
+
return [{ message: "Run npx buildpartner setup to get started.", category: "getting-started" }];
|
|
7
|
+
}
|
|
8
|
+
const data = await apiGet(`/api/buildpartner/dashboard?username=${auth.username}`);
|
|
9
|
+
if (!data) {
|
|
10
|
+
return [{ message: "Could not fetch data. Check your connection.", category: "error" }];
|
|
20
11
|
}
|
|
21
|
-
|
|
22
|
-
return generateLocalSuggestions();
|
|
12
|
+
return generateSuggestions(data);
|
|
23
13
|
}
|
|
24
14
|
function generateSuggestions(data) {
|
|
25
15
|
const suggestions = [];
|
|
@@ -28,16 +18,13 @@ function generateSuggestions(data) {
|
|
|
28
18
|
if (sessions.length < 5) {
|
|
29
19
|
return [{ message: "Keep going! Log a few more sessions and your AI trainer will have personalized tips for you.", category: "getting-started" }];
|
|
30
20
|
}
|
|
31
|
-
// Recent sessions (last 2 weeks)
|
|
32
21
|
const twoWeeksAgo = new Date();
|
|
33
22
|
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14);
|
|
34
23
|
const recent = sessions.filter((s) => new Date(s.timestamp) >= twoWeeksAgo);
|
|
35
24
|
const recentTotal = recent.length || 1;
|
|
36
25
|
const recentDist = {};
|
|
37
|
-
for (const s of recent)
|
|
26
|
+
for (const s of recent)
|
|
38
27
|
recentDist[s.activity_type] = (recentDist[s.activity_type] || 0) + 1;
|
|
39
|
-
}
|
|
40
|
-
// One type > 70%
|
|
41
28
|
for (const [type, count] of Object.entries(recentDist)) {
|
|
42
29
|
if (count / recentTotal > 0.7 && recent.length >= 5) {
|
|
43
30
|
const adjacent = getAdjacentType(type);
|
|
@@ -48,61 +35,13 @@ function generateSuggestions(data) {
|
|
|
48
35
|
break;
|
|
49
36
|
}
|
|
50
37
|
}
|
|
51
|
-
// Never-used types
|
|
52
|
-
for (const type of ALL_ACTIVITY_TYPES) {
|
|
53
|
-
if (!data.distribution[type]) {
|
|
54
|
-
suggestions.push({
|
|
55
|
-
message: `You haven't tried ${capitalize(type)} yet. Here's an idea: ${getTypeExample(type)}`,
|
|
56
|
-
category: "activity-balance",
|
|
57
|
-
});
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// No highlights
|
|
62
38
|
if (data.stats.total_sessions >= 10 && data.stats.total_highlights === 0) {
|
|
63
39
|
suggestions.push({
|
|
64
|
-
message: `You've got ${data.stats.total_sessions} sessions but no highlights. Promote your best work with /
|
|
65
|
-
category: "progression",
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
// No deploys
|
|
69
|
-
const codeSessions = sessions.filter((s) => s.activity_type === "code");
|
|
70
|
-
const hasDeploySignals = sessions.some((s) => s.summary?.toLowerCase().includes("deploy") || s.summary?.toLowerCase().includes("ship"));
|
|
71
|
-
if (codeSessions.length >= 3 && !hasDeploySignals) {
|
|
72
|
-
suggestions.push({
|
|
73
|
-
message: `You've built ${codeSessions.length} things but deployed none. Ship one.`,
|
|
40
|
+
message: `You've got ${data.stats.total_sessions} sessions but no highlights. Promote your best work with /bp-log-experience.`,
|
|
74
41
|
category: "progression",
|
|
75
|
-
video: VIDEO_LINKS["deploy"],
|
|
76
42
|
});
|
|
77
43
|
}
|
|
78
|
-
// Duration trending down
|
|
79
|
-
if (recent.length >= 6) {
|
|
80
|
-
const firstHalf = recent.slice(0, Math.floor(recent.length / 2));
|
|
81
|
-
const secondHalf = recent.slice(Math.floor(recent.length / 2));
|
|
82
|
-
const avgFirst = firstHalf.reduce((s, e) => s + (e.duration_minutes || 0), 0) / firstHalf.length;
|
|
83
|
-
const avgSecond = secondHalf.reduce((s, e) => s + (e.duration_minutes || 0), 0) / secondHalf.length;
|
|
84
|
-
if (avgSecond < avgFirst * 0.8 && avgFirst > 0) {
|
|
85
|
-
suggestions.push({
|
|
86
|
-
message: `Your sessions are getting shorter (${Math.round(avgFirst)} min to ${Math.round(avgSecond)} min). You're getting more efficient.`,
|
|
87
|
-
category: "progression",
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// MCP/workspace recommendations
|
|
92
|
-
const mcpServers = new Set(Object.keys(data.workspace.mcp_servers));
|
|
93
44
|
const tools = data.workspace.tools;
|
|
94
|
-
if (!mcpServers.has("supabase-prod") && !mcpServers.has("supabase-dev") && !mcpServers.has("supabase") && codeSessions.length >= 5) {
|
|
95
|
-
suggestions.push({
|
|
96
|
-
message: "You do a lot of coding but don't use Supabase MCP. It lets you query and manage your database directly from Claude Code.",
|
|
97
|
-
category: "workspace",
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
if ((mcpServers.has("claude_ai_Notion") || mcpServers.has("notion")) && !mcpServers.has("linear")) {
|
|
101
|
-
suggestions.push({
|
|
102
|
-
message: "You use Notion heavily. Try the Linear MCP for faster task tracking directly in Claude Code.",
|
|
103
|
-
category: "workspace",
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
45
|
const readCount = tools["Read"] || 0;
|
|
107
46
|
const grepCount = tools["Grep"] || 0;
|
|
108
47
|
if (readCount > 100 && grepCount < readCount * 0.1) {
|
|
@@ -113,43 +52,6 @@ function generateSuggestions(data) {
|
|
|
113
52
|
}
|
|
114
53
|
return suggestions.slice(0, 3);
|
|
115
54
|
}
|
|
116
|
-
function generateLocalSuggestions() {
|
|
117
|
-
const sessions = readSessions();
|
|
118
|
-
const highlights = readHighlights();
|
|
119
|
-
const profile = readProfile();
|
|
120
|
-
const suggestions = [];
|
|
121
|
-
if (sessions.length < 5) {
|
|
122
|
-
return [{ message: "Keep going! Log a few more sessions and your AI trainer will have personalized tips for you.", category: "getting-started" }];
|
|
123
|
-
}
|
|
124
|
-
const twoWeeksAgo = new Date();
|
|
125
|
-
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14);
|
|
126
|
-
const recent = sessions.filter((s) => s.timestamp >= twoWeeksAgo.toISOString());
|
|
127
|
-
const recentTotal = recent.length || 1;
|
|
128
|
-
const recentDist = {};
|
|
129
|
-
for (const s of recent)
|
|
130
|
-
recentDist[s.activity_type] = (recentDist[s.activity_type] || 0) + 1;
|
|
131
|
-
for (const [type, count] of Object.entries(recentDist)) {
|
|
132
|
-
if (count / recentTotal > 0.7 && recent.length >= 5) {
|
|
133
|
-
suggestions.push({
|
|
134
|
-
message: `You're deep in ${capitalize(type)}. Try ${capitalize(getAdjacentType(type))} to expand your range.`,
|
|
135
|
-
category: "activity-balance",
|
|
136
|
-
});
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (sessions.length >= 10 && highlights.length === 0) {
|
|
141
|
-
suggestions.push({
|
|
142
|
-
message: `You've got ${sessions.length} sessions but no highlights. Promote your best work with /my-player-log-highlight.`,
|
|
143
|
-
category: "progression",
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
const codeSessions = sessions.filter((s) => s.activity_type === "code");
|
|
147
|
-
const hasDeploySignals = sessions.some((s) => s.summary?.toLowerCase().includes("deploy") || s.summary?.toLowerCase().includes("ship"));
|
|
148
|
-
if (codeSessions.length >= 3 && !hasDeploySignals) {
|
|
149
|
-
suggestions.push({ message: `You've built ${codeSessions.length} things but deployed none. Ship one.`, category: "progression", video: VIDEO_LINKS["deploy"] });
|
|
150
|
-
}
|
|
151
|
-
return suggestions.slice(0, 3);
|
|
152
|
-
}
|
|
153
55
|
function capitalize(s) {
|
|
154
56
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
155
57
|
}
|
|
@@ -160,17 +62,4 @@ function getAdjacentType(type) {
|
|
|
160
62
|
};
|
|
161
63
|
return adjacency[type] || "code";
|
|
162
64
|
}
|
|
163
|
-
function getTypeExample(type) {
|
|
164
|
-
const examples = {
|
|
165
|
-
code: "Build a quick prototype of an idea you've been thinking about.",
|
|
166
|
-
content: "Write up what you learned in your last coding session.",
|
|
167
|
-
research: "Deep dive into a new technology that interests you.",
|
|
168
|
-
automation: "Automate something you do manually every week.",
|
|
169
|
-
design: "Plan out the architecture of your next project before building.",
|
|
170
|
-
ops: "Set up CI/CD for one of your projects.",
|
|
171
|
-
data: "Analyze your own usage patterns or project metrics.",
|
|
172
|
-
debug: "Fix that bug you've been putting off.",
|
|
173
|
-
};
|
|
174
|
-
return examples[type] || "Try something new.";
|
|
175
|
-
}
|
|
176
65
|
//# sourceMappingURL=get-suggestions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-suggestions.js","sourceRoot":"","sources":["../../../../src/mcp/tools/get-suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"get-suggestions.js","sourceRoot":"","sources":["../../../../src/mcp/tools/get-suggestions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type { ActivityType } from "../../classifiers/activity-classifier.js";
|
|
3
2
|
export declare const logHighlightSchema: {
|
|
4
3
|
title: z.ZodString;
|
|
5
4
|
description: z.ZodString;
|
|
@@ -19,14 +18,7 @@ export declare const logHighlightSchema: {
|
|
|
19
18
|
export declare function logHighlight({ title, description, activity_type, tech_stack, session_id, }: {
|
|
20
19
|
title: string;
|
|
21
20
|
description: string;
|
|
22
|
-
activity_type:
|
|
21
|
+
activity_type: string;
|
|
23
22
|
tech_stack: string[];
|
|
24
23
|
session_id?: string;
|
|
25
|
-
}): Promise<{
|
|
26
|
-
id: string;
|
|
27
|
-
title: string;
|
|
28
|
-
description: string;
|
|
29
|
-
activity_type: ActivityType;
|
|
30
|
-
tech_stack: string[];
|
|
31
|
-
created_at: string;
|
|
32
|
-
}>;
|
|
24
|
+
}): Promise<{}>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { appendHighlight } from "../../store/local-store.js";
|
|
3
2
|
import { apiPost } from "../../api-client.js";
|
|
4
3
|
export const logHighlightSchema = {
|
|
5
4
|
title: z.string().describe("Title for the highlight build"),
|
|
@@ -11,29 +10,16 @@ export const logHighlightSchema = {
|
|
|
11
10
|
session_id: z.string().optional().describe("Associated session ID"),
|
|
12
11
|
};
|
|
13
12
|
export async function logHighlight({ title, description, activity_type, tech_stack, session_id, }) {
|
|
14
|
-
|
|
15
|
-
const highlight = appendHighlight({
|
|
16
|
-
session_id: session_id || "",
|
|
17
|
-
title,
|
|
18
|
-
description,
|
|
19
|
-
activity_type,
|
|
20
|
-
tech_stack,
|
|
21
|
-
});
|
|
22
|
-
// Sync to server
|
|
23
|
-
await apiPost("/api/buildpartner/highlights", {
|
|
13
|
+
const result = await apiPost("/api/buildpartner/highlights", {
|
|
24
14
|
title,
|
|
25
15
|
description,
|
|
26
16
|
activity_type,
|
|
27
17
|
tech_stack,
|
|
28
18
|
session_id: session_id || null,
|
|
29
19
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
activity_type: highlight.activity_type,
|
|
35
|
-
tech_stack: highlight.tech_stack,
|
|
36
|
-
created_at: highlight.created_at,
|
|
37
|
-
};
|
|
20
|
+
if (!result) {
|
|
21
|
+
return { error: "Could not save highlight. Check your connection." };
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
38
24
|
}
|
|
39
25
|
//# sourceMappingURL=log-highlight.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-highlight.js","sourceRoot":"","sources":["../../../../src/mcp/tools/log-highlight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"log-highlight.js","sourceRoot":"","sources":["../../../../src/mcp/tools/log-highlight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IAC3D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IACjE,aAAa,EAAE,CAAC;SACb,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;SACrF,QAAQ,CAAC,eAAe,CAAC;IAC5B,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IAC7D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;CACpE,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EACjC,KAAK,EACL,WAAW,EACX,aAAa,EACb,UAAU,EACV,UAAU,GAOX;IACC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,8BAA8B,EAAE;QAC3D,KAAK;QACL,WAAW;QACX,aAAa;QACb,UAAU;QACV,UAAU,EAAE,UAAU,IAAI,IAAI;KAC/B,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,KAAK,EAAE,kDAAkD,EAAE,CAAC;IACvE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,78 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Quest tool - now fetches quest state from server.
|
|
3
|
+
*/
|
|
3
4
|
export declare const getQuestSchema: {};
|
|
4
|
-
export declare const completeQuestSchema: {
|
|
5
|
-
quest_id: z.ZodString;
|
|
6
|
-
};
|
|
7
|
-
export declare function getQuestStatus(): Promise<{
|
|
8
|
-
environment: string;
|
|
9
|
-
active_quest: {
|
|
10
|
-
id: string;
|
|
11
|
-
title: string;
|
|
12
|
-
description: string;
|
|
13
|
-
prompt: string;
|
|
14
|
-
path: QuestPath;
|
|
15
|
-
} | null;
|
|
16
|
-
progress: {
|
|
17
|
-
total: string;
|
|
18
|
-
core: {
|
|
19
|
-
completed: number;
|
|
20
|
-
total: number;
|
|
21
|
-
};
|
|
22
|
-
builder: {
|
|
23
|
-
completed: number;
|
|
24
|
-
total: number;
|
|
25
|
-
};
|
|
26
|
-
storyteller: {
|
|
27
|
-
completed: number;
|
|
28
|
-
total: number;
|
|
29
|
-
};
|
|
30
|
-
systems_thinker: {
|
|
31
|
-
completed: number;
|
|
32
|
-
total: number;
|
|
33
|
-
};
|
|
34
|
-
all_done: boolean;
|
|
35
|
-
core_done: boolean;
|
|
36
|
-
paths_unlocked: boolean;
|
|
37
|
-
};
|
|
38
|
-
quests: {
|
|
39
|
-
id: string;
|
|
40
|
-
title: string;
|
|
41
|
-
description: string;
|
|
42
|
-
prompt: string;
|
|
43
|
-
path: QuestPath;
|
|
44
|
-
order: number;
|
|
45
|
-
status: import("../../store/quest-store.js").QuestStatus;
|
|
46
|
-
completed_at: string | null;
|
|
47
|
-
}[];
|
|
48
|
-
coming_soon: {
|
|
49
|
-
status: "coming_soon";
|
|
50
|
-
id: string;
|
|
51
|
-
title: string;
|
|
52
|
-
path: QuestPath;
|
|
53
|
-
description: string;
|
|
54
|
-
}[];
|
|
55
|
-
}>;
|
|
56
|
-
export declare function markQuestComplete({ quest_id }: {
|
|
57
|
-
quest_id: string;
|
|
58
|
-
}): Promise<{
|
|
59
|
-
error: string;
|
|
60
|
-
completed?: undefined;
|
|
61
|
-
completed_path?: undefined;
|
|
62
|
-
paths_unlocked?: undefined;
|
|
63
|
-
next_quest?: undefined;
|
|
64
|
-
message?: undefined;
|
|
65
|
-
} | {
|
|
66
|
-
completed: string;
|
|
67
|
-
completed_path: QuestPath;
|
|
68
|
-
paths_unlocked: boolean;
|
|
69
|
-
next_quest: {
|
|
70
|
-
id: string;
|
|
71
|
-
title: string;
|
|
72
|
-
description: string;
|
|
73
|
-
path: QuestPath;
|
|
74
|
-
} | null;
|
|
75
|
-
message: string;
|
|
76
|
-
error?: undefined;
|
|
77
|
-
}>;
|
|
78
|
-
export declare function getFirstRunTip(skillName: string): string | null;
|
|
@@ -1,72 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Quest tool - now fetches quest state from server.
|
|
3
|
+
*/
|
|
3
4
|
export const getQuestSchema = {};
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export async function getQuestStatus() {
|
|
8
|
-
const active = getActiveQuest();
|
|
9
|
-
const progress = getQuestProgress();
|
|
10
|
-
const state = readQuests();
|
|
11
|
-
const env = getEnvironment();
|
|
12
|
-
const quests = QUEST_DEFINITIONS.map((def) => ({
|
|
13
|
-
id: def.id,
|
|
14
|
-
title: def.title,
|
|
15
|
-
description: def.description,
|
|
16
|
-
prompt: def.prompt,
|
|
17
|
-
path: def.path,
|
|
18
|
-
order: def.order,
|
|
19
|
-
status: state.quests[def.id]?.status || "locked",
|
|
20
|
-
completed_at: state.quests[def.id]?.completed_at || null,
|
|
21
|
-
}));
|
|
22
|
-
const comingSoon = COMING_SOON_QUESTS.map((q) => ({
|
|
23
|
-
...q,
|
|
24
|
-
status: "coming_soon",
|
|
25
|
-
}));
|
|
26
|
-
return {
|
|
27
|
-
environment: env,
|
|
28
|
-
active_quest: active
|
|
29
|
-
? { id: active.id, title: active.title, description: active.description, prompt: active.prompt, path: active.path }
|
|
30
|
-
: null,
|
|
31
|
-
progress: {
|
|
32
|
-
total: `${progress.completed}/${progress.total}`,
|
|
33
|
-
core: progress.core,
|
|
34
|
-
builder: progress.builder,
|
|
35
|
-
storyteller: progress.storyteller,
|
|
36
|
-
systems_thinker: progress.systemsThinker,
|
|
37
|
-
all_done: progress.allDone,
|
|
38
|
-
core_done: progress.core.completed === progress.core.total,
|
|
39
|
-
paths_unlocked: progress.core.completed === progress.core.total,
|
|
40
|
-
},
|
|
41
|
-
quests,
|
|
42
|
-
coming_soon: comingSoon,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
export async function markQuestComplete({ quest_id }) {
|
|
46
|
-
const result = completeQuest(quest_id);
|
|
47
|
-
if (!result) {
|
|
48
|
-
return { error: "Quest not found or already completed." };
|
|
49
|
-
}
|
|
50
|
-
const active = getActiveQuest();
|
|
51
|
-
const progress = getQuestProgress();
|
|
52
|
-
// Check if core just finished (path quests unlocked)
|
|
53
|
-
const pathsJustUnlocked = result.path === "core" && progress.core.completed === progress.core.total;
|
|
54
|
-
return {
|
|
55
|
-
completed: result.title,
|
|
56
|
-
completed_path: result.path,
|
|
57
|
-
paths_unlocked: pathsJustUnlocked,
|
|
58
|
-
next_quest: active ? { id: active.id, title: active.title, description: active.description, path: active.path } : null,
|
|
59
|
-
message: pathsJustUnlocked
|
|
60
|
-
? `Core quests complete! You've unlocked 3 paths: Builder, Storyteller, and Systems Thinker. Type /quest to pick your path.`
|
|
61
|
-
: active
|
|
62
|
-
? `Quest complete! Next up: ${active.title}`
|
|
63
|
-
: "All quests completed! You're a BuildPartner expert.",
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
export function getFirstRunTip(skillName) {
|
|
67
|
-
if (!isFirstSkillRun(skillName))
|
|
68
|
-
return null;
|
|
69
|
-
markSkillRun(skillName);
|
|
70
|
-
return `---\nTip: This is a "skill" -- a markdown file that tells Claude what to do. You can see it at ~/.claude/skills/${skillName}/SKILL.md or create your own by asking Claude: "Create a skill called [name] that does [thing]." Skills are how you teach Claude your workflows.`;
|
|
71
|
-
}
|
|
5
|
+
// getQuestStatus is no longer needed locally.
|
|
6
|
+
// The MCP server calls GET /api/buildpartner/quests directly.
|
|
7
|
+
// This file is kept for the schema export.
|
|
72
8
|
//# sourceMappingURL=quest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quest.js","sourceRoot":"","sources":["../../../../src/mcp/tools/quest.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"quest.js","sourceRoot":"","sources":["../../../../src/mcp/tools/quest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,8CAA8C;AAC9C,8DAA8D;AAC9D,2CAA2C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts classification signals from a parsed session.
|
|
3
|
+
* This is pure data extraction - no scoring or classification.
|
|
4
|
+
* The server handles all classification logic.
|
|
5
|
+
*/
|
|
6
|
+
import type { ParsedSession } from "./session-reader.js";
|
|
7
|
+
export interface SessionSignals {
|
|
8
|
+
timestamp: string;
|
|
9
|
+
end_time: string | null;
|
|
10
|
+
project_name: string;
|
|
11
|
+
user_prompt_count: number;
|
|
12
|
+
duration_minutes: number;
|
|
13
|
+
estimated_tokens: number;
|
|
14
|
+
summary: string;
|
|
15
|
+
permission_mode: string | null;
|
|
16
|
+
tool_counts: Record<string, number>;
|
|
17
|
+
files_touched: string[];
|
|
18
|
+
write_edit_paths: string[];
|
|
19
|
+
bash_commands: string[];
|
|
20
|
+
mcp_servers_used: string[];
|
|
21
|
+
skills_invoked: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare function extractSignals(parsed: ParsedSession): SessionSignals;
|