heyiam 0.1.0

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.
Files changed (60) hide show
  1. package/app/dist/assets/index-BGQkmy7q.css +1 -0
  2. package/app/dist/assets/index-CbLHxerW.js +14 -0
  3. package/app/dist/favicon.svg +1 -0
  4. package/app/dist/icons.svg +24 -0
  5. package/app/dist/index.html +20 -0
  6. package/dist/analyzer.d.ts +96 -0
  7. package/dist/analyzer.js +249 -0
  8. package/dist/analyzer.js.map +1 -0
  9. package/dist/auth.d.ts +34 -0
  10. package/dist/auth.js +99 -0
  11. package/dist/auth.js.map +1 -0
  12. package/dist/bridge.d.ts +32 -0
  13. package/dist/bridge.js +211 -0
  14. package/dist/bridge.js.map +1 -0
  15. package/dist/config.d.ts +4 -0
  16. package/dist/config.js +5 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +4 -0
  19. package/dist/index.js +129 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/llm/anthropic-provider.d.ts +11 -0
  22. package/dist/llm/anthropic-provider.js +16 -0
  23. package/dist/llm/anthropic-provider.js.map +1 -0
  24. package/dist/llm/index.d.ts +16 -0
  25. package/dist/llm/index.js +25 -0
  26. package/dist/llm/index.js.map +1 -0
  27. package/dist/llm/project-enhance.d.ts +71 -0
  28. package/dist/llm/project-enhance.js +236 -0
  29. package/dist/llm/project-enhance.js.map +1 -0
  30. package/dist/llm/proxy-provider.d.ts +16 -0
  31. package/dist/llm/proxy-provider.js +60 -0
  32. package/dist/llm/proxy-provider.js.map +1 -0
  33. package/dist/llm/triage.d.ts +66 -0
  34. package/dist/llm/triage.js +283 -0
  35. package/dist/llm/triage.js.map +1 -0
  36. package/dist/llm/types.d.ts +19 -0
  37. package/dist/llm/types.js +2 -0
  38. package/dist/llm/types.js.map +1 -0
  39. package/dist/machine-key.d.ts +10 -0
  40. package/dist/machine-key.js +51 -0
  41. package/dist/machine-key.js.map +1 -0
  42. package/dist/parsers/claude.d.ts +18 -0
  43. package/dist/parsers/claude.js +265 -0
  44. package/dist/parsers/claude.js.map +1 -0
  45. package/dist/parsers/index.d.ts +28 -0
  46. package/dist/parsers/index.js +124 -0
  47. package/dist/parsers/index.js.map +1 -0
  48. package/dist/parsers/types.d.ts +81 -0
  49. package/dist/parsers/types.js +2 -0
  50. package/dist/parsers/types.js.map +1 -0
  51. package/dist/server.d.ts +3 -0
  52. package/dist/server.js +857 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/settings.d.ts +92 -0
  55. package/dist/settings.js +160 -0
  56. package/dist/settings.js.map +1 -0
  57. package/dist/summarize.d.ts +72 -0
  58. package/dist/summarize.js +312 -0
  59. package/dist/summarize.js.map +1 -0
  60. package/package.json +47 -0
@@ -0,0 +1,236 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { getAnthropicApiKey } from '../settings.js';
3
+ // ── Prompts ──────────────────────────────────────────────────────
4
+ const PROJECT_ENHANCE_SYSTEM = `You are building a project narrative from multiple coding sessions for a developer portfolio on heyi.am.
5
+
6
+ Your job:
7
+ 1. Synthesize a 2-3 sentence project description that captures what was built and why it matters. Write in third person about the project, not the developer. No fluff — every word earns its place.
8
+ 2. Identify 4-7 project phases (the "arc") that show how the project evolved. Each phase should have a short title and one-sentence description.
9
+ 3. Deduplicate and rank skills across all sessions.
10
+ 4. Group sessions into timeline periods (e.g., "Week 1", "Days 1-3") with labels describing what happened in each period. Mark featured sessions (the most interesting ones) vs background sessions.
11
+ 5. Generate 2-3 context-aware questions based on patterns you detect in the sessions (see instructions below).
12
+
13
+ For questions, look for these signals and generate questions that reference specific data:
14
+ - High correction counts → ask about override strategy, referencing the count
15
+ - Longest sessions → ask why that area was worth the time investment, referencing duration
16
+ - Zero file overlap between sessions → ask about isolation decisions
17
+ - Technology switches or unusual tool usage → ask about the choice
18
+ - Architectural keywords → ask about design trade-offs
19
+
20
+ Each question must have:
21
+ - A category: "pattern" (behavioral signals), "architecture" (design decisions), or "evolution" (how the project changed)
22
+ - A specific, non-generic question that references actual data from the sessions
23
+ - Context explaining why you're asking (what signal triggered it)
24
+
25
+ Return valid JSON matching this exact structure:
26
+ {
27
+ "narrative": "2-3 sentence project description",
28
+ "arc": [{ "phase": 1, "title": "...", "description": "..." }],
29
+ "skills": ["skill1", "skill2"],
30
+ "timeline": [{
31
+ "period": "Week 1",
32
+ "label": "Foundation and setup",
33
+ "sessions": [{
34
+ "sessionId": "uuid",
35
+ "title": "Session title",
36
+ "featured": true,
37
+ "tag": "key decision"
38
+ }]
39
+ }],
40
+ "questions": [{
41
+ "id": "q1",
42
+ "category": "pattern",
43
+ "question": "You overrode the AI 4 times across sessions. Was that a conscious strategy?",
44
+ "context": "High correction count detected across auth and sealing sessions"
45
+ }]
46
+ }`;
47
+ const REFINE_NARRATIVE_SYSTEM = `You are refining a project narrative by incorporating the developer's own perspective. You have the draft narrative and timeline, plus the developer's answers to context-aware questions.
48
+
49
+ Weave their answers naturally into the existing narrative — don't quote them verbatim, make it sound like the developer wrote it. The narrative should feel like a developer thinking out loud, not an AI explaining.
50
+
51
+ Rules:
52
+ - Keep the narrative to 2-4 sentences. Quality over quantity.
53
+ - If an answer reveals motivation, fold it into the narrative.
54
+ - If an answer explains a decision, strengthen the relevant arc phase or timeline label.
55
+ - Don't add fluff words (leverage, utilize, robust, comprehensive, cutting-edge).
56
+ - Return the same JSON structure with updated narrative and timeline.
57
+
58
+ Return valid JSON:
59
+ {
60
+ "narrative": "refined 2-4 sentence description",
61
+ "timeline": [same structure as input, with updated labels where answers add context]
62
+ }`;
63
+ // ── Core functions ───────────────────────────────────────────────
64
+ function createClient() {
65
+ const apiKey = getAnthropicApiKey();
66
+ return apiKey ? new Anthropic({ apiKey }) : new Anthropic();
67
+ }
68
+ function extractJson(text) {
69
+ const match = text.match(/\{[\s\S]*\}/);
70
+ if (!match)
71
+ throw new Error('No JSON object found in LLM response');
72
+ return JSON.parse(match[0]);
73
+ }
74
+ /**
75
+ * Generate a project narrative, arc, timeline, and context-aware questions
76
+ * from enhanced session summaries. Streams narrative chunks via onProgress
77
+ * as the LLM generates the response.
78
+ */
79
+ export async function enhanceProject(sessions, skippedSessions, onProgress) {
80
+ const client = createClient();
81
+ const input = {
82
+ sessions: sessions.map((s) => ({
83
+ sessionId: s.sessionId,
84
+ title: s.title,
85
+ developerTake: s.developerTake,
86
+ skills: s.skills,
87
+ executionSteps: s.executionSteps.map((e) => e.title),
88
+ duration: s.duration,
89
+ loc: s.loc,
90
+ turns: s.turns,
91
+ files: s.files,
92
+ date: s.date,
93
+ correctionCount: s.correctionCount,
94
+ })),
95
+ skippedSessions,
96
+ totalSessions: sessions.length + skippedSessions.length,
97
+ };
98
+ let fullText = '';
99
+ // State machine for detecting and streaming the "narrative" JSON string value
100
+ let streamPhase = 'scanning';
101
+ let scanBuffer = '';
102
+ let escaped = false; // tracks whether the previous char was an unresolved backslash
103
+ const stream = client.messages.stream({
104
+ model: 'claude-sonnet-4-6',
105
+ max_tokens: 4096,
106
+ system: PROJECT_ENHANCE_SYSTEM,
107
+ messages: [{
108
+ role: 'user',
109
+ content: JSON.stringify(input),
110
+ }],
111
+ });
112
+ for await (const event of stream) {
113
+ if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
114
+ const chunk = event.delta.text;
115
+ fullText += chunk;
116
+ if (!onProgress || streamPhase === 'past_narrative')
117
+ continue;
118
+ for (const char of chunk) {
119
+ if (streamPhase === 'scanning') {
120
+ scanBuffer += char;
121
+ // Detect "narrative":<whitespace>" — the opening of the value string
122
+ const marker = '"narrative"';
123
+ const markerIdx = scanBuffer.indexOf(marker);
124
+ if (markerIdx >= 0) {
125
+ const afterMarker = scanBuffer.slice(markerIdx + marker.length);
126
+ // Match : then optional whitespace then opening "
127
+ const valueStart = afterMarker.match(/^\s*:\s*"/);
128
+ if (valueStart) {
129
+ streamPhase = 'in_narrative';
130
+ // Anything after the opening quote is narrative content
131
+ const remainder = afterMarker.slice(valueStart[0].length);
132
+ if (remainder.length > 0) {
133
+ for (const rc of remainder) {
134
+ // Process remainder through the same escape logic below
135
+ if (escaped) {
136
+ escaped = false;
137
+ const decoded = rc === 'n' ? '\n' : rc === 't' ? '\t' : rc;
138
+ onProgress({ type: 'narrative_chunk', text: decoded });
139
+ }
140
+ else if (rc === '\\') {
141
+ escaped = true;
142
+ }
143
+ else if (rc === '"') {
144
+ streamPhase = 'past_narrative';
145
+ break;
146
+ }
147
+ else {
148
+ onProgress({ type: 'narrative_chunk', text: rc });
149
+ }
150
+ }
151
+ }
152
+ scanBuffer = '';
153
+ }
154
+ }
155
+ // Prevent unbounded buffer growth
156
+ if (scanBuffer.length > 500) {
157
+ scanBuffer = scanBuffer.slice(-200);
158
+ }
159
+ }
160
+ else if (streamPhase === 'in_narrative') {
161
+ if (escaped) {
162
+ escaped = false;
163
+ const decoded = char === 'n' ? '\n' : char === 't' ? '\t' : char;
164
+ onProgress({ type: 'narrative_chunk', text: decoded });
165
+ }
166
+ else if (char === '\\') {
167
+ escaped = true;
168
+ }
169
+ else if (char === '"') {
170
+ // Unescaped quote — end of narrative string
171
+ streamPhase = 'past_narrative';
172
+ break;
173
+ }
174
+ else {
175
+ onProgress({ type: 'narrative_chunk', text: char });
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ const result = extractJson(fullText);
182
+ // Validate required fields
183
+ if (!result.narrative || !Array.isArray(result.arc) || !Array.isArray(result.skills)) {
184
+ throw new Error('LLM returned incomplete project enhance result');
185
+ }
186
+ // Ensure questions have IDs
187
+ if (result.questions) {
188
+ result.questions = result.questions.map((q, i) => ({
189
+ ...q,
190
+ id: q.id || `q${i + 1}`,
191
+ }));
192
+ }
193
+ else {
194
+ result.questions = [];
195
+ }
196
+ return result;
197
+ }
198
+ /**
199
+ * Refine a project narrative by weaving in the developer's answers
200
+ * to context-aware questions.
201
+ */
202
+ export async function refineNarrative(draftNarrative, draftTimeline, answers) {
203
+ const client = createClient();
204
+ const input = {
205
+ draftNarrative,
206
+ draftTimeline,
207
+ answers: answers.filter((a) => a.answer.trim().length > 0),
208
+ };
209
+ // If no answers provided, return draft as-is
210
+ if (input.answers.length === 0) {
211
+ return { narrative: draftNarrative, timeline: draftTimeline };
212
+ }
213
+ const response = await client.messages.create({
214
+ model: 'claude-sonnet-4-6',
215
+ max_tokens: 4096,
216
+ system: REFINE_NARRATIVE_SYSTEM,
217
+ messages: [{
218
+ role: 'user',
219
+ content: JSON.stringify(input),
220
+ }],
221
+ });
222
+ const text = response.content
223
+ .filter((b) => b.type === 'text')
224
+ .map((b) => b.text)
225
+ .join('');
226
+ const result = extractJson(text);
227
+ if (!result.narrative) {
228
+ throw new Error('LLM returned incomplete refined narrative');
229
+ }
230
+ // Fall back to draft timeline if LLM didn't return one
231
+ if (!Array.isArray(result.timeline)) {
232
+ result.timeline = draftTimeline;
233
+ }
234
+ return result;
235
+ }
236
+ //# sourceMappingURL=project-enhance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-enhance.js","sourceRoot":"","sources":["../../src/llm/project-enhance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Session } from '../analyzer.js';
2
+ import type { EnhancementResult } from '../summarize.js';
3
+ import type { LLMProvider } from './types.js';
4
+ /**
5
+ * Proxy provider — sends session data to Phoenix backend for server-side enhancement.
6
+ * Used when user has no local ANTHROPIC_API_KEY but is authenticated.
7
+ */
8
+ export declare class ProxyProvider implements LLMProvider {
9
+ name: "proxy";
10
+ enhance(session: Session): Promise<EnhancementResult>;
11
+ }
12
+ export declare class ProxyError extends Error {
13
+ code: string;
14
+ resetsAt?: string;
15
+ constructor(code: string, message: string, resetsAt?: string);
16
+ }
@@ -0,0 +1,60 @@
1
+ import { getAuthToken } from '../auth.js';
2
+ import { API_URL } from '../config.js';
3
+ /**
4
+ * Proxy provider — sends session data to Phoenix backend for server-side enhancement.
5
+ * Used when user has no local ANTHROPIC_API_KEY but is authenticated.
6
+ */
7
+ export class ProxyProvider {
8
+ name = 'proxy';
9
+ async enhance(session) {
10
+ const auth = getAuthToken();
11
+ if (!auth?.token) {
12
+ throw new ProxyError('AUTH_EXPIRED', 'Not authenticated. Run: heyiam login');
13
+ }
14
+ const response = await fetch(`${API_URL}/api/enhance`, {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ 'Authorization': `Bearer ${auth.token}`,
19
+ },
20
+ body: JSON.stringify({ session: sessionToPayload(session) }),
21
+ });
22
+ if (!response.ok) {
23
+ const errorBody = await response.json().catch(() => ({
24
+ error: { code: 'PROXY_UNREACHABLE', message: 'Could not reach enhancement server' },
25
+ }));
26
+ throw new ProxyError(errorBody.error?.code ?? 'PROXY_ERROR', errorBody.error?.message ?? 'Enhancement failed', errorBody.error?.resets_at);
27
+ }
28
+ const data = await response.json();
29
+ return data.result;
30
+ }
31
+ }
32
+ export class ProxyError extends Error {
33
+ code;
34
+ resetsAt;
35
+ constructor(code, message, resetsAt) {
36
+ super(message);
37
+ this.name = 'ProxyError';
38
+ this.code = code;
39
+ this.resetsAt = resetsAt;
40
+ }
41
+ }
42
+ /**
43
+ * Extracts the session fields that Phoenix expects for enhancement.
44
+ */
45
+ function sessionToPayload(session) {
46
+ return {
47
+ title: session.title,
48
+ projectName: session.projectName,
49
+ durationMinutes: session.durationMinutes,
50
+ turns: session.turns,
51
+ linesOfCode: session.linesOfCode,
52
+ skills: session.skills,
53
+ toolBreakdown: session.toolBreakdown,
54
+ filesChanged: session.filesChanged,
55
+ executionPath: session.executionPath,
56
+ turnTimeline: session.turnTimeline,
57
+ rawLog: session.rawLog,
58
+ };
59
+ }
60
+ //# sourceMappingURL=proxy-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-provider.js","sourceRoot":"","sources":["../../src/llm/proxy-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;;GAGG;AACH,MAAM,OAAO,aAAa;IACxB,IAAI,GAAG,OAAgB,CAAC;IAExB,KAAK,CAAC,OAAO,CAAC,OAAgB;QAC5B,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;YACjB,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,sCAAsC,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;aACxC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;SAC7D,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;gBACnD,KAAK,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,oCAAoC,EAAE;aACpF,CAAC,CAAuB,CAAC;YAE1B,MAAM,IAAI,UAAU,CAClB,SAAS,CAAC,KAAK,EAAE,IAAI,IAAI,aAAa,EACtC,SAAS,CAAC,KAAK,EAAE,OAAO,IAAI,oBAAoB,EAChD,SAAS,CAAC,KAAK,EAAE,SAAS,CAC3B,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0B,CAAC;QAC3D,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAS;IACb,QAAQ,CAAU;IAElB,YAAY,IAAY,EAAE,OAAe,EAAE,QAAiB;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,OAAgB;IACxC,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,66 @@
1
+ export interface SessionSignals {
2
+ correctionCount: number;
3
+ avgUserExplanationLength: number;
4
+ errorRetryCount: number;
5
+ userToAiRatio: number;
6
+ toolDiversity: number;
7
+ multiDirScope: number;
8
+ architecturalKeywords: number;
9
+ }
10
+ /**
11
+ * Extract cheap signals from a session's raw JSONL file.
12
+ * Scans user messages for patterns without full parsing.
13
+ */
14
+ export declare function extractSignals(sessionPath: string): Promise<SessionSignals>;
15
+ export interface SessionMetaWithStats {
16
+ sessionId: string;
17
+ path: string;
18
+ title: string;
19
+ duration: number;
20
+ loc: number;
21
+ turns: number;
22
+ files: number;
23
+ skills: string[];
24
+ date: string;
25
+ }
26
+ export interface TriageResult {
27
+ selected: Array<{
28
+ sessionId: string;
29
+ reason: string;
30
+ }>;
31
+ skipped: Array<{
32
+ sessionId: string;
33
+ reason: string;
34
+ }>;
35
+ triageMethod: 'llm' | 'scoring' | 'auto-select';
36
+ autoSelected?: boolean;
37
+ }
38
+ export type TriageProgressEvent = {
39
+ type: 'scanning';
40
+ total: number;
41
+ } | {
42
+ type: 'hard_floor';
43
+ sessionId: string;
44
+ title: string;
45
+ passed: boolean;
46
+ reason?: string;
47
+ } | {
48
+ type: 'extracting_signals';
49
+ sessionId: string;
50
+ title: string;
51
+ } | {
52
+ type: 'signals_done';
53
+ sessionId: string;
54
+ signals: SessionSignals;
55
+ } | {
56
+ type: 'llm_ranking';
57
+ sessionCount: number;
58
+ } | {
59
+ type: 'scoring_fallback';
60
+ sessionCount: number;
61
+ } | {
62
+ type: 'done';
63
+ selected: number;
64
+ skipped: number;
65
+ };
66
+ export declare function triageSessions(sessions: SessionMetaWithStats[], useLLM?: boolean, onProgress?: (event: TriageProgressEvent) => void): Promise<TriageResult>;