forked from garrytan/gstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgstack-builder-profile
More file actions
executable file
·134 lines (122 loc) · 4.16 KB
/
Copy pathgstack-builder-profile
File metadata and controls
executable file
·134 lines (122 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env bash
# gstack-builder-profile — read builder profile and output structured summary
#
# Reads ~/.gstack/builder-profile.jsonl (append-only session log from /office-hours).
# Outputs KEY: VALUE pairs for the template to consume. Computes tier, accumulated
# signals, cross-project detection, nudge eligibility, and resource dedup.
#
# Single source of truth for all closing state. No separate config keys or logs.
#
# Exit 0 with defaults if no profile exists (first-time user = introduction tier).
set -euo pipefail
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
PROFILE_FILE="$GSTACK_HOME/builder-profile.jsonl"
# Graceful default: no profile = introduction tier
if [ ! -f "$PROFILE_FILE" ] || [ ! -s "$PROFILE_FILE" ]; then
echo "SESSION_COUNT: 0"
echo "TIER: introduction"
echo "LAST_PROJECT:"
echo "LAST_ASSIGNMENT:"
echo "LAST_DESIGN_TITLE:"
echo "DESIGN_COUNT: 0"
echo "DESIGN_TITLES: []"
echo "ACCUMULATED_SIGNALS:"
echo "TOTAL_SIGNAL_COUNT: 0"
echo "CROSS_PROJECT: false"
echo "NUDGE_ELIGIBLE: false"
echo "RESOURCES_SHOWN:"
echo "RESOURCES_SHOWN_COUNT: 0"
echo "TOPICS:"
exit 0
fi
# Use bun for JSON parsing (same pattern as gstack-learnings-search).
# Fallback to defaults if bun is unavailable.
cat "$PROFILE_FILE" 2>/dev/null | bun -e "
const lines = (await Bun.stdin.text()).trim().split('\n').filter(Boolean);
const entries = [];
for (const line of lines) {
try { entries.push(JSON.parse(line)); } catch {}
}
const count = entries.length;
// Tier computation
let tier = 'introduction';
if (count >= 8) tier = 'inner_circle';
else if (count >= 4) tier = 'regular';
else if (count >= 1) tier = 'welcome_back';
// Last session data
const last = entries[count - 1] || {};
const prev = entries[count - 2] || {};
const crossProject = prev.project_slug && last.project_slug
? prev.project_slug !== last.project_slug
: false;
// Design docs
const designs = entries
.map(e => e.design_doc || '')
.filter(Boolean);
const designTitles = entries
.map(e => {
const doc = e.design_doc || '';
// Extract title from path: ...-design-DATETIME.md -> use the entry's topic or project
return doc ? (e.project_slug || 'unknown') : '';
})
.filter(Boolean);
// Accumulated signals
const signalCounts = {};
let totalSignals = 0;
for (const e of entries) {
for (const s of (e.signals || [])) {
signalCounts[s] = (signalCounts[s] || 0) + 1;
totalSignals++;
}
}
const signalStr = Object.entries(signalCounts)
.map(([k, v]) => k + ':' + v)
.join(',');
// Nudge eligibility: builder-mode + 5+ signals across 3+ sessions
const builderSessions = entries.filter(e => e.mode !== 'startup').length;
const nudgeEligible = builderSessions >= 3 && totalSignals >= 5;
// Resources shown (aggregate all)
const allResources = new Set();
for (const e of entries) {
for (const url of (e.resources_shown || [])) {
allResources.add(url);
}
}
// Topics (aggregate all)
const allTopics = new Set();
for (const e of entries) {
for (const t of (e.topics || [])) {
allTopics.add(t);
}
}
console.log('SESSION_COUNT: ' + count);
console.log('TIER: ' + tier);
console.log('LAST_PROJECT: ' + (last.project_slug || ''));
console.log('LAST_ASSIGNMENT: ' + (last.assignment || ''));
console.log('LAST_DESIGN_TITLE: ' + (last.design_doc || ''));
console.log('DESIGN_COUNT: ' + designs.length);
console.log('DESIGN_TITLES: ' + JSON.stringify(designTitles));
console.log('ACCUMULATED_SIGNALS: ' + signalStr);
console.log('TOTAL_SIGNAL_COUNT: ' + totalSignals);
console.log('CROSS_PROJECT: ' + crossProject);
console.log('NUDGE_ELIGIBLE: ' + nudgeEligible);
console.log('RESOURCES_SHOWN: ' + Array.from(allResources).join(','));
console.log('RESOURCES_SHOWN_COUNT: ' + allResources.size);
console.log('TOPICS: ' + Array.from(allTopics).join(','));
" 2>/dev/null || {
# Fallback if bun is unavailable
echo "SESSION_COUNT: 0"
echo "TIER: introduction"
echo "LAST_PROJECT:"
echo "LAST_ASSIGNMENT:"
echo "LAST_DESIGN_TITLE:"
echo "DESIGN_COUNT: 0"
echo "DESIGN_TITLES: []"
echo "ACCUMULATED_SIGNALS:"
echo "TOTAL_SIGNAL_COUNT: 0"
echo "CROSS_PROJECT: false"
echo "NUDGE_ELIGIBLE: false"
echo "RESOURCES_SHOWN:"
echo "RESOURCES_SHOWN_COUNT: 0"
echo "TOPICS:"
}