projex-cli 0.2.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 (85) hide show
  1. package/dist/cli.d.ts +6 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +321 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/event-bus.d.ts +35 -0
  6. package/dist/event-bus.d.ts.map +1 -0
  7. package/dist/event-bus.js +71 -0
  8. package/dist/event-bus.js.map +1 -0
  9. package/dist/events/event-bus.d.ts +35 -0
  10. package/dist/events/event-bus.d.ts.map +1 -0
  11. package/dist/events/event-bus.js +71 -0
  12. package/dist/events/event-bus.js.map +1 -0
  13. package/dist/events/index.d.ts +3 -0
  14. package/dist/events/index.d.ts.map +1 -0
  15. package/dist/events/index.js +3 -0
  16. package/dist/events/index.js.map +1 -0
  17. package/dist/events/types.d.ts +143 -0
  18. package/dist/events/types.d.ts.map +1 -0
  19. package/dist/events/types.js +6 -0
  20. package/dist/events/types.js.map +1 -0
  21. package/dist/index.d.ts +11 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +12 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/inference/enricher.d.ts +18 -0
  26. package/dist/inference/enricher.d.ts.map +1 -0
  27. package/dist/inference/enricher.js +290 -0
  28. package/dist/inference/enricher.js.map +1 -0
  29. package/dist/inference/index.d.ts +5 -0
  30. package/dist/inference/index.d.ts.map +1 -0
  31. package/dist/inference/index.js +5 -0
  32. package/dist/inference/index.js.map +1 -0
  33. package/dist/inference/lifecycle-tracker.d.ts +40 -0
  34. package/dist/inference/lifecycle-tracker.d.ts.map +1 -0
  35. package/dist/inference/lifecycle-tracker.js +132 -0
  36. package/dist/inference/lifecycle-tracker.js.map +1 -0
  37. package/dist/inference/portfolio-generator.d.ts +44 -0
  38. package/dist/inference/portfolio-generator.d.ts.map +1 -0
  39. package/dist/inference/portfolio-generator.js +239 -0
  40. package/dist/inference/portfolio-generator.js.map +1 -0
  41. package/dist/inference/project-detector.d.ts +17 -0
  42. package/dist/inference/project-detector.d.ts.map +1 -0
  43. package/dist/inference/project-detector.js +127 -0
  44. package/dist/inference/project-detector.js.map +1 -0
  45. package/dist/ingestion/github-client.d.ts +56 -0
  46. package/dist/ingestion/github-client.d.ts.map +1 -0
  47. package/dist/ingestion/github-client.js +213 -0
  48. package/dist/ingestion/github-client.js.map +1 -0
  49. package/dist/ingestion/github-poller.d.ts +41 -0
  50. package/dist/ingestion/github-poller.d.ts.map +1 -0
  51. package/dist/ingestion/github-poller.js +121 -0
  52. package/dist/ingestion/github-poller.js.map +1 -0
  53. package/dist/ingestion/normalizer.d.ts +41 -0
  54. package/dist/ingestion/normalizer.d.ts.map +1 -0
  55. package/dist/ingestion/normalizer.js +173 -0
  56. package/dist/ingestion/normalizer.js.map +1 -0
  57. package/dist/integrations/analyzer.d.ts +74 -0
  58. package/dist/integrations/analyzer.d.ts.map +1 -0
  59. package/dist/integrations/analyzer.js +287 -0
  60. package/dist/integrations/analyzer.js.map +1 -0
  61. package/dist/integrations/index.d.ts +4 -0
  62. package/dist/integrations/index.d.ts.map +1 -0
  63. package/dist/integrations/index.js +5 -0
  64. package/dist/integrations/index.js.map +1 -0
  65. package/dist/integrations/injectors.d.ts +60 -0
  66. package/dist/integrations/injectors.d.ts.map +1 -0
  67. package/dist/integrations/injectors.js +361 -0
  68. package/dist/integrations/injectors.js.map +1 -0
  69. package/dist/integrations/template-generator.d.ts +45 -0
  70. package/dist/integrations/template-generator.d.ts.map +1 -0
  71. package/dist/integrations/template-generator.js +187 -0
  72. package/dist/integrations/template-generator.js.map +1 -0
  73. package/dist/storage/index.d.ts +2 -0
  74. package/dist/storage/index.d.ts.map +1 -0
  75. package/dist/storage/index.js +2 -0
  76. package/dist/storage/index.js.map +1 -0
  77. package/dist/storage/project-store.d.ts +23 -0
  78. package/dist/storage/project-store.d.ts.map +1 -0
  79. package/dist/storage/project-store.js +107 -0
  80. package/dist/storage/project-store.js.map +1 -0
  81. package/dist/types.d.ts +143 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +6 -0
  84. package/dist/types.js.map +1 -0
  85. package/package.json +55 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Lifecycle Tracker - Rule 4: Project completion detection
3
+ * Detects when projects become inactive or are explicitly archived.
4
+ */
5
+ export class LifecycleTracker {
6
+ store;
7
+ eventBus;
8
+ completionThresholdDays;
9
+ minCommitsForCompletion;
10
+ constructor(store, eventBus, config) {
11
+ this.store = store;
12
+ this.eventBus = eventBus;
13
+ this.completionThresholdDays = config.inference.completionThresholdDays;
14
+ this.minCommitsForCompletion = config.inference.minCommitsForCompletion;
15
+ // Subscribe to archived events
16
+ this.eventBus.subscribe(['repo.archived'], (event) => this.handleArchived(event));
17
+ }
18
+ /**
19
+ * Handle explicit archive events - high confidence completion
20
+ */
21
+ async handleArchived(event) {
22
+ const project = this.store.getProject(event.repo.id);
23
+ if (!project) {
24
+ console.log(`[LifecycleTracker] Project not found for archive: ${event.repo.id}`);
25
+ return;
26
+ }
27
+ console.log(`[LifecycleTracker] Project archived: ${event.repo.id}`);
28
+ project.status = 'ARCHIVED';
29
+ project.completionDate = event.payload.archivedAt;
30
+ project.confidence.completion = 1.0; // Explicit archive = certain
31
+ project.updatedAt = new Date();
32
+ this.store.saveProject(project);
33
+ }
34
+ /**
35
+ * Check all projects for inactivity-based completion
36
+ * Should be called periodically (e.g., after each poll)
37
+ */
38
+ checkForCompletedProjects() {
39
+ const now = new Date();
40
+ const thresholdMs = this.completionThresholdDays * 24 * 60 * 60 * 1000;
41
+ const completedProjects = [];
42
+ for (const project of this.store.getAllProjects()) {
43
+ // Skip already completed/archived/ignored projects
44
+ if (project.status !== 'ACTIVE')
45
+ continue;
46
+ const daysSinceActivity = (now.getTime() - project.lastActivityDate.getTime()) / (24 * 60 * 60 * 1000);
47
+ // Check completion criteria
48
+ if (daysSinceActivity >= this.completionThresholdDays) {
49
+ const hasSubstantialContent = this.hasSubstantialContent(project);
50
+ if (hasSubstantialContent) {
51
+ console.log(`[LifecycleTracker] Marking as likely completed (${daysSinceActivity.toFixed(0)} days inactive): ${project.id}`);
52
+ project.status = 'LIKELY_COMPLETED';
53
+ project.completionDate = project.lastActivityDate;
54
+ project.confidence.completion = this.calculateCompletionConfidence(project, daysSinceActivity);
55
+ project.updatedAt = now;
56
+ this.store.saveProject(project);
57
+ completedProjects.push(project);
58
+ }
59
+ }
60
+ }
61
+ return completedProjects;
62
+ }
63
+ /**
64
+ * Check if project has enough content to be considered a real project
65
+ */
66
+ hasSubstantialContent(project) {
67
+ // Must have a README
68
+ if (!project.rawData.readme)
69
+ return false;
70
+ // Must have detected tech stack (indicates code exists)
71
+ if (project.techStack.length === 0)
72
+ return false;
73
+ // Must have some languages detected
74
+ const languageCount = Object.keys(project.rawData.languages).length;
75
+ if (languageCount === 0)
76
+ return false;
77
+ return true;
78
+ }
79
+ /**
80
+ * Calculate confidence in completion status
81
+ */
82
+ calculateCompletionConfidence(project, daysSinceActivity) {
83
+ let confidence = 0.5; // Base confidence for inactivity threshold
84
+ // More days = more confident it's complete
85
+ if (daysSinceActivity > 180)
86
+ confidence += 0.2;
87
+ else if (daysSinceActivity > 120)
88
+ confidence += 0.1;
89
+ // Has README = more likely a finished project
90
+ if (project.rawData.readme)
91
+ confidence += 0.1;
92
+ // Has description = intentionally created
93
+ if (project.rawData.description)
94
+ confidence += 0.05;
95
+ // Has substantial code (multiple languages or large files)
96
+ const totalBytes = Object.values(project.rawData.languages).reduce((a, b) => a + b, 0);
97
+ if (totalBytes > 10000)
98
+ confidence += 0.1;
99
+ // Has topics = organized project
100
+ if (project.topics.length > 0)
101
+ confidence += 0.05;
102
+ return Math.min(confidence, 0.95); // Cap at 0.95 for inactivity detection
103
+ }
104
+ /**
105
+ * Manually mark a project as completed
106
+ */
107
+ markAsCompleted(projectId) {
108
+ const project = this.store.getProject(projectId);
109
+ if (!project)
110
+ return false;
111
+ project.status = 'COMPLETED';
112
+ project.completionDate = new Date();
113
+ project.confidence.completion = 1.0;
114
+ project.updatedAt = new Date();
115
+ this.store.saveProject(project);
116
+ return true;
117
+ }
118
+ /**
119
+ * Manually mark a project to be ignored (not a portfolio candidate)
120
+ */
121
+ ignoreProject(projectId) {
122
+ const project = this.store.getProject(projectId);
123
+ if (!project)
124
+ return false;
125
+ project.status = 'IGNORED';
126
+ project.portfolioStatus = 'REJECTED';
127
+ project.updatedAt = new Date();
128
+ this.store.saveProject(project);
129
+ return true;
130
+ }
131
+ }
132
+ //# sourceMappingURL=lifecycle-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-tracker.js","sourceRoot":"","sources":["../../src/inference/lifecycle-tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Portfolio Generator - Rule 5: Creates portfolio drafts from completed projects
3
+ */
4
+ import type { Project, PortfolioDraft } from '../events/types.js';
5
+ import type { ProjectStore } from '../storage/project-store.js';
6
+ export declare class PortfolioGenerator {
7
+ private store;
8
+ constructor(store: ProjectStore);
9
+ /**
10
+ * Generate portfolio drafts for all completed projects that don't have one
11
+ */
12
+ generatePendingDrafts(): PortfolioDraft[];
13
+ /**
14
+ * Generate a single portfolio draft for a project
15
+ */
16
+ generateDraft(project: Project): PortfolioDraft;
17
+ /**
18
+ * Regenerate draft for a specific project
19
+ */
20
+ regenerateDraft(projectId: string): PortfolioDraft | null;
21
+ /**
22
+ * Apply user edits to a draft
23
+ */
24
+ applyUserEdits(projectId: string, edits: Partial<PortfolioDraft>): boolean;
25
+ /**
26
+ * Approve a portfolio draft
27
+ */
28
+ approveDraft(projectId: string): boolean;
29
+ /**
30
+ * Reject a portfolio draft
31
+ */
32
+ rejectDraft(projectId: string): boolean;
33
+ /**
34
+ * Get the final portfolio entry (with user edits applied)
35
+ */
36
+ getFinalPortfolioEntry(projectId: string): PortfolioDraft | null;
37
+ private shouldGenerateDraft;
38
+ private generateTagline;
39
+ private generateDescription;
40
+ private formatTechStack;
41
+ private extractHighlights;
42
+ private extractFeaturesFromReadme;
43
+ }
44
+ //# sourceMappingURL=portfolio-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-generator.d.ts","sourceRoot":"","sources":["../../src/inference/portfolio-generator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACR,OAAO,EACP,cAAc,EAEjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,qBAAa,kBAAkB;IACf,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY;IAEvC;;OAEG;IACH,qBAAqB,IAAI,cAAc,EAAE;IAmBzC;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc;IAkB/C;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAazD;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO;IAc1E;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAaxC;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAWvC;;OAEG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAchE,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,mBAAmB;IAuB3B,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,yBAAyB;CA6BpC"}
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Portfolio Generator - Rule 5: Creates portfolio drafts from completed projects
3
+ */
4
+ export class PortfolioGenerator {
5
+ store;
6
+ constructor(store) {
7
+ this.store = store;
8
+ }
9
+ /**
10
+ * Generate portfolio drafts for all completed projects that don't have one
11
+ */
12
+ generatePendingDrafts() {
13
+ const generated = [];
14
+ for (const project of this.store.getAllProjects()) {
15
+ if (this.shouldGenerateDraft(project)) {
16
+ const draft = this.generateDraft(project);
17
+ project.portfolioDraft = draft;
18
+ project.portfolioStatus = 'PENDING_REVIEW';
19
+ project.updatedAt = new Date();
20
+ this.store.saveProject(project);
21
+ generated.push(draft);
22
+ console.log(`[PortfolioGenerator] Generated draft for: ${project.id}`);
23
+ }
24
+ }
25
+ return generated;
26
+ }
27
+ /**
28
+ * Generate a single portfolio draft for a project
29
+ */
30
+ generateDraft(project) {
31
+ return {
32
+ title: project.displayName,
33
+ tagline: this.generateTagline(project),
34
+ description: this.generateDescription(project),
35
+ techStack: this.formatTechStack(project.techStack),
36
+ timelineStart: project.startDate,
37
+ timelineEnd: project.completionDate,
38
+ highlights: this.extractHighlights(project),
39
+ githubUrl: project.url,
40
+ liveUrl: null,
41
+ userEdits: {},
42
+ approved: false,
43
+ approvedAt: null,
44
+ };
45
+ }
46
+ /**
47
+ * Regenerate draft for a specific project
48
+ */
49
+ regenerateDraft(projectId) {
50
+ const project = this.store.getProject(projectId);
51
+ if (!project)
52
+ return null;
53
+ const draft = this.generateDraft(project);
54
+ project.portfolioDraft = draft;
55
+ project.portfolioStatus = 'PENDING_REVIEW';
56
+ project.updatedAt = new Date();
57
+ this.store.saveProject(project);
58
+ return draft;
59
+ }
60
+ /**
61
+ * Apply user edits to a draft
62
+ */
63
+ applyUserEdits(projectId, edits) {
64
+ const project = this.store.getProject(projectId);
65
+ if (!project?.portfolioDraft)
66
+ return false;
67
+ project.portfolioDraft.userEdits = {
68
+ ...project.portfolioDraft.userEdits,
69
+ ...edits,
70
+ };
71
+ project.updatedAt = new Date();
72
+ this.store.saveProject(project);
73
+ return true;
74
+ }
75
+ /**
76
+ * Approve a portfolio draft
77
+ */
78
+ approveDraft(projectId) {
79
+ const project = this.store.getProject(projectId);
80
+ if (!project?.portfolioDraft)
81
+ return false;
82
+ project.portfolioDraft.approved = true;
83
+ project.portfolioDraft.approvedAt = new Date();
84
+ project.portfolioStatus = 'APPROVED';
85
+ project.updatedAt = new Date();
86
+ this.store.saveProject(project);
87
+ return true;
88
+ }
89
+ /**
90
+ * Reject a portfolio draft
91
+ */
92
+ rejectDraft(projectId) {
93
+ const project = this.store.getProject(projectId);
94
+ if (!project)
95
+ return false;
96
+ project.portfolioStatus = 'REJECTED';
97
+ project.updatedAt = new Date();
98
+ this.store.saveProject(project);
99
+ return true;
100
+ }
101
+ /**
102
+ * Get the final portfolio entry (with user edits applied)
103
+ */
104
+ getFinalPortfolioEntry(projectId) {
105
+ const project = this.store.getProject(projectId);
106
+ if (!project?.portfolioDraft)
107
+ return null;
108
+ // Merge user edits on top of generated draft
109
+ return {
110
+ ...project.portfolioDraft,
111
+ ...project.portfolioDraft.userEdits,
112
+ userEdits: project.portfolioDraft.userEdits,
113
+ approved: project.portfolioDraft.approved,
114
+ approvedAt: project.portfolioDraft.approvedAt,
115
+ };
116
+ }
117
+ shouldGenerateDraft(project) {
118
+ // Generate for completed/likely completed projects without a draft
119
+ if (!['COMPLETED', 'LIKELY_COMPLETED'].includes(project.status))
120
+ return false;
121
+ if (project.portfolioStatus !== 'NONE')
122
+ return false;
123
+ if (project.portfolioDraft)
124
+ return false;
125
+ // Skip private repos by default
126
+ if (project.isPrivate)
127
+ return false;
128
+ // Must have some content
129
+ if (!project.purpose && project.techStack.length === 0)
130
+ return false;
131
+ return true;
132
+ }
133
+ generateTagline(project) {
134
+ const frameworks = project.techStack
135
+ .filter(t => t.category === 'framework')
136
+ .slice(0, 2)
137
+ .map(t => t.name);
138
+ const languages = project.techStack
139
+ .filter(t => t.category === 'language')
140
+ .slice(0, 2)
141
+ .map(t => t.name);
142
+ if (frameworks.length > 0) {
143
+ return `Built with ${frameworks.join(' and ')}`;
144
+ }
145
+ if (languages.length > 0) {
146
+ return `${languages.join('/')} project`;
147
+ }
148
+ return 'Software project';
149
+ }
150
+ generateDescription(project) {
151
+ if (project.purpose && project.purpose !== 'Purpose not detected') {
152
+ return project.purpose;
153
+ }
154
+ // Generate from available info
155
+ const parts = [];
156
+ if (project.topics.length > 0) {
157
+ parts.push(`A project focused on ${project.topics.slice(0, 3).join(', ')}.`);
158
+ }
159
+ const frameworks = project.techStack
160
+ .filter(t => t.category === 'framework')
161
+ .map(t => t.name);
162
+ if (frameworks.length > 0) {
163
+ parts.push(`Built using ${frameworks.join(', ')}.`);
164
+ }
165
+ return parts.join(' ') || `${project.displayName} project.`;
166
+ }
167
+ formatTechStack(techStack) {
168
+ // Group by category and take top items
169
+ const byCategory = new Map();
170
+ for (const item of techStack) {
171
+ const existing = byCategory.get(item.category) ?? [];
172
+ existing.push(item);
173
+ byCategory.set(item.category, existing);
174
+ }
175
+ const result = [];
176
+ // Languages first
177
+ const languages = byCategory.get('language') ?? [];
178
+ result.push(...languages.slice(0, 3).map(l => l.name));
179
+ // Then frameworks
180
+ const frameworks = byCategory.get('framework') ?? [];
181
+ result.push(...frameworks.slice(0, 3).map(f => f.name));
182
+ // Then key libraries
183
+ const libraries = byCategory.get('library') ?? [];
184
+ result.push(...libraries.slice(0, 2).map(l => l.name));
185
+ // Platforms
186
+ const platforms = byCategory.get('platform') ?? [];
187
+ result.push(...platforms.slice(0, 2).map(p => p.name));
188
+ return result;
189
+ }
190
+ extractHighlights(project) {
191
+ const highlights = [];
192
+ // Extract from README if available
193
+ if (project.rawData.readme) {
194
+ const features = this.extractFeaturesFromReadme(project.rawData.readme);
195
+ highlights.push(...features.slice(0, 5));
196
+ }
197
+ // Add tech-based highlights if no features found
198
+ if (highlights.length === 0) {
199
+ const frameworks = project.techStack
200
+ .filter(t => t.category === 'framework')
201
+ .map(t => t.name);
202
+ if (frameworks.length > 0) {
203
+ highlights.push(`Built with ${frameworks[0]}`);
204
+ }
205
+ const platforms = project.techStack
206
+ .filter(t => t.category === 'platform')
207
+ .map(t => t.name);
208
+ if (platforms.includes('Docker')) {
209
+ highlights.push('Containerized with Docker');
210
+ }
211
+ }
212
+ return highlights;
213
+ }
214
+ extractFeaturesFromReadme(readme) {
215
+ const features = [];
216
+ // Look for Features/Highlights section
217
+ const featureMatch = readme.match(/##?\s*(Features|Highlights|Key Features)[^\n]*\n([\s\S]*?)(?=\n##|\n$|$)/i);
218
+ if (featureMatch) {
219
+ const section = featureMatch[2];
220
+ // Extract bullet points
221
+ const bullets = section.match(/^[\s]*[-*]\s+(.+)$/gm);
222
+ if (bullets) {
223
+ for (const bullet of bullets.slice(0, 5)) {
224
+ const text = bullet.replace(/^[\s]*[-*]\s+/, '').trim();
225
+ // Clean up markdown formatting
226
+ const cleaned = text
227
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
228
+ .replace(/`([^`]+)`/g, '$1')
229
+ .replace(/\*\*([^*]+)\*\*/g, '$1');
230
+ if (cleaned.length > 10 && cleaned.length < 100) {
231
+ features.push(cleaned);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ return features;
237
+ }
238
+ }
239
+ //# sourceMappingURL=portfolio-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolio-generator.js","sourceRoot":"","sources":["../../src/inference/portfolio-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Project Detector - Rule 1: Creates draft projects from repo events
3
+ */
4
+ import type { ProjectStore } from '../storage/project-store.js';
5
+ import { EventBus } from '../events/event-bus.js';
6
+ export declare class ProjectDetector {
7
+ private store;
8
+ private eventBus;
9
+ constructor(store: ProjectStore, eventBus: EventBus);
10
+ private handleEvent;
11
+ private handleRepoCreated;
12
+ private handleRepoPushed;
13
+ private createDraftProject;
14
+ private createProjectFromPush;
15
+ private humanizeName;
16
+ }
17
+ //# sourceMappingURL=project-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-detector.d.ts","sourceRoot":"","sources":["../../src/inference/project-detector.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,qBAAa,eAAe;IAEpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;gBADR,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,QAAQ;YAQhB,WAAW;YAQX,iBAAiB;YAcjB,gBAAgB;IAuB9B,OAAO,CAAC,kBAAkB;IA0C1B,OAAO,CAAC,qBAAqB;IA0C7B,OAAO,CAAC,YAAY;CASvB"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Project Detector - Rule 1: Creates draft projects from repo events
3
+ */
4
+ export class ProjectDetector {
5
+ store;
6
+ eventBus;
7
+ constructor(store, eventBus) {
8
+ this.store = store;
9
+ this.eventBus = eventBus;
10
+ // Subscribe to relevant events
11
+ this.eventBus.subscribe(['repo.created', 'repo.pushed'], (event) => this.handleEvent(event));
12
+ }
13
+ async handleEvent(event) {
14
+ if (event.eventType === 'repo.created') {
15
+ await this.handleRepoCreated(event);
16
+ }
17
+ else if (event.eventType === 'repo.pushed') {
18
+ await this.handleRepoPushed(event);
19
+ }
20
+ }
21
+ async handleRepoCreated(event) {
22
+ const existingProject = this.store.getProject(event.repo.id);
23
+ if (existingProject) {
24
+ console.log(`[ProjectDetector] Project already exists: ${event.repo.id}`);
25
+ return;
26
+ }
27
+ console.log(`[ProjectDetector] Creating new project: ${event.repo.id}`);
28
+ const project = this.createDraftProject(event);
29
+ this.store.saveProject(project);
30
+ }
31
+ async handleRepoPushed(event) {
32
+ const existingProject = this.store.getProject(event.repo.id);
33
+ if (existingProject) {
34
+ // Update last activity date
35
+ existingProject.lastActivityDate = event.timestamp;
36
+ existingProject.updatedAt = new Date();
37
+ // If it was marked as likely completed, reactivate it
38
+ if (existingProject.status === 'LIKELY_COMPLETED') {
39
+ existingProject.status = 'ACTIVE';
40
+ console.log(`[ProjectDetector] Reactivated project: ${event.repo.id}`);
41
+ }
42
+ this.store.saveProject(existingProject);
43
+ }
44
+ else {
45
+ // Unknown repo with push activity - create project
46
+ console.log(`[ProjectDetector] Creating project from push: ${event.repo.id}`);
47
+ const project = this.createProjectFromPush(event);
48
+ this.store.saveProject(project);
49
+ }
50
+ }
51
+ createDraftProject(event) {
52
+ const now = new Date();
53
+ return {
54
+ id: event.repo.id,
55
+ name: event.repo.name,
56
+ displayName: this.humanizeName(event.repo.name),
57
+ owner: event.repo.owner,
58
+ url: event.repo.url,
59
+ status: 'ACTIVE',
60
+ isPrivate: event.payload.isPrivate,
61
+ purpose: event.payload.description,
62
+ techStack: [],
63
+ topics: event.payload.topics,
64
+ startDate: event.timestamp,
65
+ lastActivityDate: event.timestamp,
66
+ completionDate: null,
67
+ confidence: {
68
+ purpose: event.payload.description ? 0.3 : 0,
69
+ techStack: 0,
70
+ completion: 0,
71
+ },
72
+ rawData: {
73
+ description: event.payload.description,
74
+ readme: null,
75
+ languages: {},
76
+ packageFiles: [],
77
+ },
78
+ portfolioStatus: 'NONE',
79
+ portfolioDraft: null,
80
+ createdAt: now,
81
+ updatedAt: now,
82
+ };
83
+ }
84
+ createProjectFromPush(event) {
85
+ const now = new Date();
86
+ return {
87
+ id: event.repo.id,
88
+ name: event.repo.name,
89
+ displayName: this.humanizeName(event.repo.name),
90
+ owner: event.repo.owner,
91
+ url: event.repo.url,
92
+ status: 'ACTIVE',
93
+ isPrivate: false,
94
+ purpose: null,
95
+ techStack: [],
96
+ topics: [],
97
+ startDate: event.timestamp,
98
+ lastActivityDate: event.timestamp,
99
+ completionDate: null,
100
+ confidence: {
101
+ purpose: 0,
102
+ techStack: 0,
103
+ completion: 0,
104
+ },
105
+ rawData: {
106
+ description: null,
107
+ readme: null,
108
+ languages: {},
109
+ packageFiles: [],
110
+ },
111
+ portfolioStatus: 'NONE',
112
+ portfolioDraft: null,
113
+ createdAt: now,
114
+ updatedAt: now,
115
+ };
116
+ }
117
+ humanizeName(name) {
118
+ // Convert kebab-case or snake_case to Title Case
119
+ return name
120
+ .replace(/[-_]/g, ' ')
121
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
122
+ .split(' ')
123
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
124
+ .join(' ');
125
+ }
126
+ }
127
+ //# sourceMappingURL=project-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-detector.js","sourceRoot":"","sources":["../../src/inference/project-detector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,OAAO,eAAe;IAEZ;IACA;IAFZ,YACY,KAAmB,EACnB,QAAkB;QADlB,UAAK,GAAL,KAAK,CAAc;QACnB,aAAQ,GAAR,QAAQ,CAAU;QAE1B,+BAA+B;QAC/B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAC/D,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAC1B,CAAC;IACN,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,KAAkB;QACxC,IAAI,KAAK,CAAC,SAAS,KAAK,cAAc,EAAE,CAAC;YACrC,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAyB,CAAC,CAAC;QAC5D,CAAC;aAAM,IAAI,KAAK,CAAC,SAAS,KAAK,aAAa,EAAE,CAAC;YAC3C,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAwB,CAAC,CAAC;QAC1D,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,KAAuB;QACnD,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE7D,IAAI,eAAe,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,6CAA6C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1E,OAAO;QACX,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,2CAA2C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAExE,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,KAAsB;QACjD,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE7D,IAAI,eAAe,EAAE,CAAC;YAClB,4BAA4B;YAC5B,eAAe,CAAC,gBAAgB,GAAG,KAAK,CAAC,SAAS,CAAC;YACnD,eAAe,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAEvC,sDAAsD;YACtD,IAAI,eAAe,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;gBAChD,eAAe,CAAC,MAAM,GAAG,QAAQ,CAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,0CAA0C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3E,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACJ,mDAAmD;YACnD,OAAO,CAAC,GAAG,CAAC,iDAAiD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAClD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;IAEO,kBAAkB,CAAC,KAAuB;QAC9C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,OAAO;YACH,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;YACjB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YACrB,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK;YACvB,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG;YAEnB,MAAM,EAAE,QAAyB;YACjC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS;YAElC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW;YAClC,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;YAE5B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,gBAAgB,EAAE,KAAK,CAAC,SAAS;YACjC,cAAc,EAAE,IAAI;YAEpB,UAAU,EAAE;gBACR,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC5C,SAAS,EAAE,CAAC;gBACZ,UAAU,EAAE,CAAC;aAChB;YAED,OAAO,EAAE;gBACL,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW;gBACtC,MAAM,EAAE,IAAI;gBACZ,SAAS,EAAE,EAAE;gBACb,YAAY,EAAE,EAAE;aACnB;YAED,eAAe,EAAE,MAAM;YACvB,cAAc,EAAE,IAAI;YAEpB,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACjB,CAAC;IACN,CAAC;IAEO,qBAAqB,CAAC,KAAsB;QAChD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,OAAO;YACH,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;YACjB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YACrB,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK;YACvB,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG;YAEnB,MAAM,EAAE,QAAyB;YACjC,SAAS,EAAE,KAAK;YAEhB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,EAAE;YAEV,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,gBAAgB,EAAE,KAAK,CAAC,SAAS;YACjC,cAAc,EAAE,IAAI;YAEpB,UAAU,EAAE;gBACR,OAAO,EAAE,CAAC;gBACV,SAAS,EAAE,CAAC;gBACZ,UAAU,EAAE,CAAC;aAChB;YAED,OAAO,EAAE;gBACL,WAAW,EAAE,IAAI;gBACjB,MAAM,EAAE,IAAI;gBACZ,SAAS,EAAE,EAAE;gBACb,YAAY,EAAE,EAAE;aACnB;YAED,eAAe,EAAE,MAAM;YACvB,cAAc,EAAE,IAAI;YAEpB,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;SACjB,CAAC;IACN,CAAC;IAEO,YAAY,CAAC,IAAY;QAC7B,iDAAiD;QACjD,OAAO,IAAI;aACN,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC;aACnC,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;aACvE,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;CACJ"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * GitHub API client wrapper using Octokit.
3
+ * Provides typed access to repository data and events.
4
+ */
5
+ import type { PackageFileInfo } from '../events/types.js';
6
+ export interface GitHubRepoData {
7
+ id: number;
8
+ name: string;
9
+ fullName: string;
10
+ owner: string;
11
+ url: string;
12
+ description: string | null;
13
+ topics: string[];
14
+ isPrivate: boolean;
15
+ isArchived: boolean;
16
+ defaultBranch: string;
17
+ createdAt: Date;
18
+ updatedAt: Date;
19
+ pushedAt: Date | null;
20
+ starCount: number;
21
+ forkCount: number;
22
+ }
23
+ export interface GitHubEnrichmentData {
24
+ languages: Record<string, number>;
25
+ readme: string | null;
26
+ packageFiles: PackageFileInfo[];
27
+ }
28
+ export declare class GitHubClient {
29
+ private octokit;
30
+ private username;
31
+ constructor(token: string, username: string);
32
+ /**
33
+ * Fetch all repositories for the configured user
34
+ */
35
+ listUserRepos(): Promise<GitHubRepoData[]>;
36
+ /**
37
+ * Get detailed repo info including topics
38
+ */
39
+ getRepo(owner: string, repo: string): Promise<GitHubRepoData>;
40
+ /**
41
+ * Fetch enrichment data: languages, readme, package files
42
+ */
43
+ getEnrichmentData(owner: string, repo: string): Promise<GitHubEnrichmentData>;
44
+ private getLanguages;
45
+ private getReadme;
46
+ private getPackageFiles;
47
+ private parsePackageFile;
48
+ /**
49
+ * Get recent commits for activity tracking
50
+ */
51
+ getRecentCommits(owner: string, repo: string, since?: Date): Promise<{
52
+ sha: string;
53
+ date: Date;
54
+ }[]>;
55
+ }
56
+ //# sourceMappingURL=github-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-client.d.ts","sourceRoot":"","sources":["../../src/ingestion/github-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,eAAe,EAAE,CAAC;CACnC;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAK3C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAgChD;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAsBnE;;OAEG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAUrE,YAAY;YASZ,SAAS;YAWT,eAAe;IA0C7B,OAAO,CAAC,gBAAgB;IAqDxB;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,EAAE,CAAC;CAqB5G"}