@aida-dev/core 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Francesco Falanga <falanga.fra@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @aida/core
2
+
3
+ Core functionality for AIDA (AI Development Accounting) metrics collection.
4
+
5
+ ## Features
6
+
7
+ - Git commit collection with diff statistics
8
+ - AI-assisted commit detection using configurable heuristics
9
+ - Commit stream normalization and schema validation
10
+ - File system utilities for JSON I/O
11
+
12
+ ## Usage
13
+
14
+ ```typescript
15
+ import { collectCommits, createLogger } from '@aida/core';
16
+
17
+ const commitStream = await collectCommits({
18
+ repoPath: '/path/to/repo',
19
+ since: '90d',
20
+ aiPatterns: ['custom-ai-pattern'],
21
+ logger: createLogger(true),
22
+ });
23
+ ```
24
+
25
+ ## AI Detection Patterns
26
+
27
+ Default patterns include:
28
+ - `\b(ai|copilot|cursor|windsurf|codeium)\b` - Common AI tool mentions
29
+ - `\[ai\]` - AI tags in commit messages
30
+ - Trailer patterns for `AI: true`, `X-AI: true`, and bot co-authors
@@ -0,0 +1,378 @@
1
+ import { z } from 'zod';
2
+ import { SimpleGit } from 'simple-git';
3
+
4
+ declare const FileChange: z.ZodObject<{
5
+ path: z.ZodString;
6
+ status: z.ZodOptional<z.ZodEnum<["added", "modified", "deleted", "renamed"]>>;
7
+ additions: z.ZodNumber;
8
+ deletions: z.ZodNumber;
9
+ }, "strip", z.ZodTypeAny, {
10
+ path: string;
11
+ additions: number;
12
+ deletions: number;
13
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
14
+ }, {
15
+ path: string;
16
+ additions: number;
17
+ deletions: number;
18
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
19
+ }>;
20
+ declare const Commit: z.ZodObject<{
21
+ hash: z.ZodString;
22
+ authorName: z.ZodString;
23
+ authorEmail: z.ZodString;
24
+ authorDate: z.ZodString;
25
+ committerName: z.ZodString;
26
+ committerEmail: z.ZodString;
27
+ committerDate: z.ZodString;
28
+ message: z.ZodString;
29
+ parents: z.ZodArray<z.ZodString, "many">;
30
+ branch: z.ZodOptional<z.ZodString>;
31
+ inDefaultBranchAncestry: z.ZodBoolean;
32
+ tags: z.ZodObject<{
33
+ ai: z.ZodBoolean;
34
+ sources: z.ZodArray<z.ZodString, "many">;
35
+ }, "strip", z.ZodTypeAny, {
36
+ ai: boolean;
37
+ sources: string[];
38
+ }, {
39
+ ai: boolean;
40
+ sources: string[];
41
+ }>;
42
+ stats: z.ZodObject<{
43
+ totalAdditions: z.ZodNumber;
44
+ totalDeletions: z.ZodNumber;
45
+ files: z.ZodArray<z.ZodObject<{
46
+ path: z.ZodString;
47
+ status: z.ZodOptional<z.ZodEnum<["added", "modified", "deleted", "renamed"]>>;
48
+ additions: z.ZodNumber;
49
+ deletions: z.ZodNumber;
50
+ }, "strip", z.ZodTypeAny, {
51
+ path: string;
52
+ additions: number;
53
+ deletions: number;
54
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
55
+ }, {
56
+ path: string;
57
+ additions: number;
58
+ deletions: number;
59
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
60
+ }>, "many">;
61
+ }, "strip", z.ZodTypeAny, {
62
+ totalAdditions: number;
63
+ totalDeletions: number;
64
+ files: {
65
+ path: string;
66
+ additions: number;
67
+ deletions: number;
68
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
69
+ }[];
70
+ }, {
71
+ totalAdditions: number;
72
+ totalDeletions: number;
73
+ files: {
74
+ path: string;
75
+ additions: number;
76
+ deletions: number;
77
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
78
+ }[];
79
+ }>;
80
+ }, "strip", z.ZodTypeAny, {
81
+ message: string;
82
+ hash: string;
83
+ authorName: string;
84
+ authorEmail: string;
85
+ authorDate: string;
86
+ committerName: string;
87
+ committerEmail: string;
88
+ committerDate: string;
89
+ parents: string[];
90
+ inDefaultBranchAncestry: boolean;
91
+ tags: {
92
+ ai: boolean;
93
+ sources: string[];
94
+ };
95
+ stats: {
96
+ totalAdditions: number;
97
+ totalDeletions: number;
98
+ files: {
99
+ path: string;
100
+ additions: number;
101
+ deletions: number;
102
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
103
+ }[];
104
+ };
105
+ branch?: string | undefined;
106
+ }, {
107
+ message: string;
108
+ hash: string;
109
+ authorName: string;
110
+ authorEmail: string;
111
+ authorDate: string;
112
+ committerName: string;
113
+ committerEmail: string;
114
+ committerDate: string;
115
+ parents: string[];
116
+ inDefaultBranchAncestry: boolean;
117
+ tags: {
118
+ ai: boolean;
119
+ sources: string[];
120
+ };
121
+ stats: {
122
+ totalAdditions: number;
123
+ totalDeletions: number;
124
+ files: {
125
+ path: string;
126
+ additions: number;
127
+ deletions: number;
128
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
129
+ }[];
130
+ };
131
+ branch?: string | undefined;
132
+ }>;
133
+ type Commit = z.infer<typeof Commit>;
134
+ declare const CommitStream: z.ZodObject<{
135
+ repoPath: z.ZodString;
136
+ defaultBranch: z.ZodString;
137
+ generatedAt: z.ZodString;
138
+ since: z.ZodOptional<z.ZodString>;
139
+ until: z.ZodOptional<z.ZodString>;
140
+ aiPatterns: z.ZodArray<z.ZodString, "many">;
141
+ commits: z.ZodArray<z.ZodObject<{
142
+ hash: z.ZodString;
143
+ authorName: z.ZodString;
144
+ authorEmail: z.ZodString;
145
+ authorDate: z.ZodString;
146
+ committerName: z.ZodString;
147
+ committerEmail: z.ZodString;
148
+ committerDate: z.ZodString;
149
+ message: z.ZodString;
150
+ parents: z.ZodArray<z.ZodString, "many">;
151
+ branch: z.ZodOptional<z.ZodString>;
152
+ inDefaultBranchAncestry: z.ZodBoolean;
153
+ tags: z.ZodObject<{
154
+ ai: z.ZodBoolean;
155
+ sources: z.ZodArray<z.ZodString, "many">;
156
+ }, "strip", z.ZodTypeAny, {
157
+ ai: boolean;
158
+ sources: string[];
159
+ }, {
160
+ ai: boolean;
161
+ sources: string[];
162
+ }>;
163
+ stats: z.ZodObject<{
164
+ totalAdditions: z.ZodNumber;
165
+ totalDeletions: z.ZodNumber;
166
+ files: z.ZodArray<z.ZodObject<{
167
+ path: z.ZodString;
168
+ status: z.ZodOptional<z.ZodEnum<["added", "modified", "deleted", "renamed"]>>;
169
+ additions: z.ZodNumber;
170
+ deletions: z.ZodNumber;
171
+ }, "strip", z.ZodTypeAny, {
172
+ path: string;
173
+ additions: number;
174
+ deletions: number;
175
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
176
+ }, {
177
+ path: string;
178
+ additions: number;
179
+ deletions: number;
180
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
181
+ }>, "many">;
182
+ }, "strip", z.ZodTypeAny, {
183
+ totalAdditions: number;
184
+ totalDeletions: number;
185
+ files: {
186
+ path: string;
187
+ additions: number;
188
+ deletions: number;
189
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
190
+ }[];
191
+ }, {
192
+ totalAdditions: number;
193
+ totalDeletions: number;
194
+ files: {
195
+ path: string;
196
+ additions: number;
197
+ deletions: number;
198
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
199
+ }[];
200
+ }>;
201
+ }, "strip", z.ZodTypeAny, {
202
+ message: string;
203
+ hash: string;
204
+ authorName: string;
205
+ authorEmail: string;
206
+ authorDate: string;
207
+ committerName: string;
208
+ committerEmail: string;
209
+ committerDate: string;
210
+ parents: string[];
211
+ inDefaultBranchAncestry: boolean;
212
+ tags: {
213
+ ai: boolean;
214
+ sources: string[];
215
+ };
216
+ stats: {
217
+ totalAdditions: number;
218
+ totalDeletions: number;
219
+ files: {
220
+ path: string;
221
+ additions: number;
222
+ deletions: number;
223
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
224
+ }[];
225
+ };
226
+ branch?: string | undefined;
227
+ }, {
228
+ message: string;
229
+ hash: string;
230
+ authorName: string;
231
+ authorEmail: string;
232
+ authorDate: string;
233
+ committerName: string;
234
+ committerEmail: string;
235
+ committerDate: string;
236
+ parents: string[];
237
+ inDefaultBranchAncestry: boolean;
238
+ tags: {
239
+ ai: boolean;
240
+ sources: string[];
241
+ };
242
+ stats: {
243
+ totalAdditions: number;
244
+ totalDeletions: number;
245
+ files: {
246
+ path: string;
247
+ additions: number;
248
+ deletions: number;
249
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
250
+ }[];
251
+ };
252
+ branch?: string | undefined;
253
+ }>, "many">;
254
+ }, "strip", z.ZodTypeAny, {
255
+ repoPath: string;
256
+ defaultBranch: string;
257
+ generatedAt: string;
258
+ aiPatterns: string[];
259
+ commits: {
260
+ message: string;
261
+ hash: string;
262
+ authorName: string;
263
+ authorEmail: string;
264
+ authorDate: string;
265
+ committerName: string;
266
+ committerEmail: string;
267
+ committerDate: string;
268
+ parents: string[];
269
+ inDefaultBranchAncestry: boolean;
270
+ tags: {
271
+ ai: boolean;
272
+ sources: string[];
273
+ };
274
+ stats: {
275
+ totalAdditions: number;
276
+ totalDeletions: number;
277
+ files: {
278
+ path: string;
279
+ additions: number;
280
+ deletions: number;
281
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
282
+ }[];
283
+ };
284
+ branch?: string | undefined;
285
+ }[];
286
+ since?: string | undefined;
287
+ until?: string | undefined;
288
+ }, {
289
+ repoPath: string;
290
+ defaultBranch: string;
291
+ generatedAt: string;
292
+ aiPatterns: string[];
293
+ commits: {
294
+ message: string;
295
+ hash: string;
296
+ authorName: string;
297
+ authorEmail: string;
298
+ authorDate: string;
299
+ committerName: string;
300
+ committerEmail: string;
301
+ committerDate: string;
302
+ parents: string[];
303
+ inDefaultBranchAncestry: boolean;
304
+ tags: {
305
+ ai: boolean;
306
+ sources: string[];
307
+ };
308
+ stats: {
309
+ totalAdditions: number;
310
+ totalDeletions: number;
311
+ files: {
312
+ path: string;
313
+ additions: number;
314
+ deletions: number;
315
+ status?: "added" | "modified" | "deleted" | "renamed" | undefined;
316
+ }[];
317
+ };
318
+ branch?: string | undefined;
319
+ }[];
320
+ since?: string | undefined;
321
+ until?: string | undefined;
322
+ }>;
323
+ type CommitStream = z.infer<typeof CommitStream>;
324
+
325
+ interface Logger {
326
+ info(message: string): void;
327
+ warn(message: string): void;
328
+ error(message: string): void;
329
+ debug(message: string): void;
330
+ }
331
+ declare class ConsoleLogger implements Logger {
332
+ private verbose;
333
+ constructor(verbose?: boolean);
334
+ info(message: string): void;
335
+ warn(message: string): void;
336
+ error(message: string): void;
337
+ debug(message: string): void;
338
+ }
339
+ declare const createLogger: (verbose?: boolean) => Logger;
340
+
341
+ interface CollectOptions {
342
+ repoPath: string;
343
+ since?: string;
344
+ until?: string;
345
+ aiPatterns?: string[];
346
+ defaultBranch?: string;
347
+ logger?: Logger;
348
+ }
349
+ declare function detectDefaultBranch(git: SimpleGit): Promise<string>;
350
+ declare function collectCommits(options: CollectOptions): Promise<CommitStream>;
351
+
352
+ type FileChangeType = z.infer<typeof FileChange>;
353
+ interface DiffStats {
354
+ totalAdditions: number;
355
+ totalDeletions: number;
356
+ files: FileChangeType[];
357
+ }
358
+ declare function getDiffStats(repoPath: string, commitHash: string): Promise<DiffStats>;
359
+
360
+ interface AITagResult {
361
+ ai: boolean;
362
+ sources: string[];
363
+ }
364
+ interface AITagConfig {
365
+ patterns: string[];
366
+ }
367
+ declare function createAITagger(config?: AITagConfig): (message: string) => AITagResult;
368
+
369
+ declare function ensureDir(dirPath: string): Promise<void>;
370
+ declare function writeJSON(filePath: string, data: any): Promise<void>;
371
+ declare function readJSON<T>(filePath: string): Promise<T>;
372
+ declare function fileExists(filePath: string): Promise<boolean>;
373
+
374
+ declare function parseRelativeDate(input: string): Date;
375
+ declare function formatISODate(date: Date): string;
376
+ declare function daysBetween(start: Date, end: Date): number;
377
+
378
+ export { type AITagConfig, type AITagResult, type CollectOptions, Commit, CommitStream, CommitStream as CommitStreamType, ConsoleLogger, type DiffStats, FileChange, type Logger, collectCommits, createAITagger, createLogger, daysBetween, detectDefaultBranch, ensureDir, fileExists, formatISODate, getDiffStats, parseRelativeDate, readJSON, writeJSON };
package/dist/index.js ADDED
@@ -0,0 +1,317 @@
1
+ // src/schema/commit.ts
2
+ import { z } from "zod";
3
+ var FileChange = z.object({
4
+ path: z.string(),
5
+ status: z.enum(["added", "modified", "deleted", "renamed"]).optional(),
6
+ // best-effort
7
+ additions: z.number().int().nonnegative(),
8
+ deletions: z.number().int().nonnegative()
9
+ });
10
+ var Commit = z.object({
11
+ hash: z.string(),
12
+ authorName: z.string(),
13
+ authorEmail: z.string(),
14
+ authorDate: z.string().datetime(),
15
+ // ISO
16
+ committerName: z.string(),
17
+ committerEmail: z.string(),
18
+ committerDate: z.string().datetime(),
19
+ message: z.string(),
20
+ parents: z.array(z.string()),
21
+ branch: z.string().optional(),
22
+ // best-effort if known
23
+ inDefaultBranchAncestry: z.boolean(),
24
+ // set during collect
25
+ tags: z.object({
26
+ ai: z.boolean(),
27
+ sources: z.array(z.string())
28
+ // which heuristic matched
29
+ }),
30
+ stats: z.object({
31
+ totalAdditions: z.number().int().nonnegative(),
32
+ totalDeletions: z.number().int().nonnegative(),
33
+ files: z.array(FileChange)
34
+ })
35
+ });
36
+ var CommitStream = z.object({
37
+ repoPath: z.string(),
38
+ defaultBranch: z.string(),
39
+ generatedAt: z.string().datetime(),
40
+ since: z.string().optional(),
41
+ until: z.string().optional(),
42
+ aiPatterns: z.array(z.string()),
43
+ commits: z.array(Commit)
44
+ });
45
+
46
+ // src/git/collect.ts
47
+ import { simpleGit as simpleGit2 } from "simple-git";
48
+
49
+ // src/tags/ai-tags.ts
50
+ var DEFAULT_PATTERNS = [
51
+ "\\b(ai|copilot|cursor|windsurf|codeium)\\b",
52
+ "\\[ai\\]"
53
+ ];
54
+ var DEFAULT_TRAILER_PATTERNS = [
55
+ "^AI:\\s*true$",
56
+ "^X-AI:\\s*true$",
57
+ "^Co-authored-by:.*bot.*$"
58
+ ];
59
+ function createAITagger(config = { patterns: [] }) {
60
+ const allPatterns = [...DEFAULT_PATTERNS, ...config.patterns];
61
+ const messageRegexes = allPatterns.map((pattern) => new RegExp(pattern, "im"));
62
+ const trailerRegexes = DEFAULT_TRAILER_PATTERNS.map((pattern) => new RegExp(pattern, "m"));
63
+ return (message) => {
64
+ const sources = [];
65
+ let ai = false;
66
+ for (let i = 0; i < messageRegexes.length; i++) {
67
+ if (messageRegexes[i].test(message)) {
68
+ ai = true;
69
+ sources.push(`message_pattern:${allPatterns[i]}`);
70
+ }
71
+ }
72
+ for (let i = 0; i < trailerRegexes.length; i++) {
73
+ if (trailerRegexes[i].test(message)) {
74
+ ai = true;
75
+ sources.push(`trailer_pattern:${DEFAULT_TRAILER_PATTERNS[i]}`);
76
+ }
77
+ }
78
+ return { ai, sources };
79
+ };
80
+ }
81
+
82
+ // src/git/diff.ts
83
+ import { simpleGit } from "simple-git";
84
+ async function getDiffStats(repoPath, commitHash) {
85
+ const git = simpleGit(repoPath);
86
+ try {
87
+ const numstat = await git.raw(["show", "--numstat", "--format=", commitHash]);
88
+ const files = [];
89
+ let totalAdditions = 0;
90
+ let totalDeletions = 0;
91
+ const lines = numstat.trim().split("\n").filter((line) => line.trim());
92
+ for (const line of lines) {
93
+ const parts = line.split(" ");
94
+ if (parts.length >= 3) {
95
+ const additions = parts[0] === "-" ? 0 : parseInt(parts[0], 10) || 0;
96
+ const deletions = parts[1] === "-" ? 0 : parseInt(parts[1], 10) || 0;
97
+ const path = parts[2];
98
+ if (parts[0] === "-" && parts[1] === "-") {
99
+ continue;
100
+ }
101
+ totalAdditions += additions;
102
+ totalDeletions += deletions;
103
+ let status = "modified";
104
+ if (additions > 0 && deletions === 0) {
105
+ status = "added";
106
+ } else if (additions === 0 && deletions > 0) {
107
+ status = "deleted";
108
+ }
109
+ files.push({
110
+ path,
111
+ status,
112
+ additions,
113
+ deletions
114
+ });
115
+ }
116
+ }
117
+ return {
118
+ totalAdditions,
119
+ totalDeletions,
120
+ files
121
+ };
122
+ } catch (error) {
123
+ return {
124
+ totalAdditions: 0,
125
+ totalDeletions: 0,
126
+ files: []
127
+ };
128
+ }
129
+ }
130
+
131
+ // src/utils/dates.ts
132
+ function parseRelativeDate(input) {
133
+ const now = /* @__PURE__ */ new Date();
134
+ if (input.includes("-") || input.includes("T")) {
135
+ return new Date(input);
136
+ }
137
+ const match = input.match(/^(\d+)([dwmy])$/);
138
+ if (match) {
139
+ const value = parseInt(match[1], 10);
140
+ const unit = match[2];
141
+ const result = new Date(now);
142
+ switch (unit) {
143
+ case "d":
144
+ result.setDate(result.getDate() - value);
145
+ break;
146
+ case "w":
147
+ result.setDate(result.getDate() - value * 7);
148
+ break;
149
+ case "m":
150
+ result.setMonth(result.getMonth() - value);
151
+ break;
152
+ case "y":
153
+ result.setFullYear(result.getFullYear() - value);
154
+ break;
155
+ }
156
+ return result;
157
+ }
158
+ throw new Error(`Invalid date format: ${input}`);
159
+ }
160
+ function formatISODate(date) {
161
+ return date.toISOString();
162
+ }
163
+ function daysBetween(start, end) {
164
+ const diffTime = Math.abs(end.getTime() - start.getTime());
165
+ return Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
166
+ }
167
+
168
+ // src/git/collect.ts
169
+ async function detectDefaultBranch(git) {
170
+ try {
171
+ const result = await git.raw(["symbolic-ref", "refs/remotes/origin/HEAD"]);
172
+ const match = result.match(/refs\/remotes\/origin\/(.+)/);
173
+ if (match) {
174
+ return match[1].trim();
175
+ }
176
+ } catch {
177
+ }
178
+ const branches = await git.branch(["-r"]);
179
+ if (branches.all.includes("origin/main")) {
180
+ return "main";
181
+ }
182
+ if (branches.all.includes("origin/master")) {
183
+ return "master";
184
+ }
185
+ const current = await git.branch();
186
+ return current.current || "main";
187
+ }
188
+ async function collectCommits(options) {
189
+ const {
190
+ repoPath,
191
+ since,
192
+ until,
193
+ aiPatterns = [],
194
+ defaultBranch: providedDefaultBranch,
195
+ logger
196
+ } = options;
197
+ const git = simpleGit2(repoPath);
198
+ const defaultBranch = providedDefaultBranch || await detectDefaultBranch(git);
199
+ logger?.info(`Using default branch: ${defaultBranch}`);
200
+ const sinceDate = since ? parseRelativeDate(since) : void 0;
201
+ const untilDate = until ? parseRelativeDate(until) : /* @__PURE__ */ new Date();
202
+ logger?.info(`Collecting commits from ${sinceDate?.toISOString() || "beginning"} to ${untilDate.toISOString()}`);
203
+ const logOptions = {
204
+ from: defaultBranch,
205
+ maxCount: 100
206
+ // Limit for demo
207
+ };
208
+ const logResult = await git.log(logOptions);
209
+ logger?.info(`Found ${logResult.all.length} commits`);
210
+ const aiTagger = createAITagger({ patterns: aiPatterns });
211
+ const commits = [];
212
+ for (const gitCommit of logResult.all) {
213
+ logger?.debug(`Processing commit ${gitCommit.hash}`);
214
+ const stats = await getDiffStats(repoPath, gitCommit.hash);
215
+ const aiTag = aiTagger(gitCommit.message);
216
+ const parents = gitCommit.parents ? gitCommit.parents.split(" ").filter((p) => p) : [];
217
+ const commit = {
218
+ hash: gitCommit.hash,
219
+ authorName: gitCommit.author_name,
220
+ authorEmail: gitCommit.author_email,
221
+ authorDate: new Date(gitCommit.date).toISOString(),
222
+ committerName: gitCommit.author_name,
223
+ // Simple-git doesn't separate these easily
224
+ committerEmail: gitCommit.author_email,
225
+ committerDate: new Date(gitCommit.date).toISOString(),
226
+ message: gitCommit.message,
227
+ parents,
228
+ branch: defaultBranch,
229
+ inDefaultBranchAncestry: true,
230
+ // All commits are from default branch in MVP
231
+ tags: aiTag,
232
+ stats
233
+ };
234
+ commits.push(commit);
235
+ }
236
+ return {
237
+ repoPath,
238
+ defaultBranch,
239
+ generatedAt: formatISODate(/* @__PURE__ */ new Date()),
240
+ since,
241
+ until,
242
+ aiPatterns: [...aiPatterns],
243
+ commits
244
+ };
245
+ }
246
+
247
+ // src/io/fs.ts
248
+ import { promises as fs } from "fs";
249
+ import { dirname } from "path";
250
+ async function ensureDir(dirPath) {
251
+ try {
252
+ await fs.mkdir(dirPath, { recursive: true });
253
+ } catch (error) {
254
+ if (error.code !== "EEXIST") {
255
+ throw error;
256
+ }
257
+ }
258
+ }
259
+ async function writeJSON(filePath, data) {
260
+ await ensureDir(dirname(filePath));
261
+ await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
262
+ }
263
+ async function readJSON(filePath) {
264
+ const content = await fs.readFile(filePath, "utf-8");
265
+ return JSON.parse(content);
266
+ }
267
+ async function fileExists(filePath) {
268
+ try {
269
+ await fs.access(filePath);
270
+ return true;
271
+ } catch {
272
+ return false;
273
+ }
274
+ }
275
+
276
+ // src/utils/log.ts
277
+ var ConsoleLogger = class {
278
+ constructor(verbose = false) {
279
+ this.verbose = verbose;
280
+ }
281
+ info(message) {
282
+ console.log(`[INFO] ${message}`);
283
+ }
284
+ warn(message) {
285
+ console.warn(`[WARN] ${message}`);
286
+ }
287
+ error(message) {
288
+ console.error(`[ERROR] ${message}`);
289
+ }
290
+ debug(message) {
291
+ if (this.verbose) {
292
+ console.log(`[DEBUG] ${message}`);
293
+ }
294
+ }
295
+ };
296
+ var createLogger = (verbose = false) => {
297
+ return new ConsoleLogger(verbose);
298
+ };
299
+ export {
300
+ Commit,
301
+ CommitStream,
302
+ ConsoleLogger,
303
+ FileChange,
304
+ collectCommits,
305
+ createAITagger,
306
+ createLogger,
307
+ daysBetween,
308
+ detectDefaultBranch,
309
+ ensureDir,
310
+ fileExists,
311
+ formatISODate,
312
+ getDiffStats,
313
+ parseRelativeDate,
314
+ readJSON,
315
+ writeJSON
316
+ };
317
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema/commit.ts","../src/git/collect.ts","../src/tags/ai-tags.ts","../src/git/diff.ts","../src/utils/dates.ts","../src/io/fs.ts","../src/utils/log.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const FileChange = z.object({\n path: z.string(),\n status: z.enum([\"added\", \"modified\", \"deleted\", \"renamed\"]).optional(), // best-effort\n additions: z.number().int().nonnegative(),\n deletions: z.number().int().nonnegative(),\n});\n\nexport const Commit = z.object({\n hash: z.string(),\n authorName: z.string(),\n authorEmail: z.string(),\n authorDate: z.string().datetime(), // ISO\n committerName: z.string(),\n committerEmail: z.string(),\n committerDate: z.string().datetime(),\n message: z.string(),\n parents: z.array(z.string()),\n branch: z.string().optional(), // best-effort if known\n inDefaultBranchAncestry: z.boolean(), // set during collect\n tags: z.object({\n ai: z.boolean(),\n sources: z.array(z.string()), // which heuristic matched\n }),\n stats: z.object({\n totalAdditions: z.number().int().nonnegative(),\n totalDeletions: z.number().int().nonnegative(),\n files: z.array(FileChange),\n }),\n});\n\nexport type Commit = z.infer<typeof Commit>;\n\nexport const CommitStream = z.object({\n repoPath: z.string(),\n defaultBranch: z.string(),\n generatedAt: z.string().datetime(),\n since: z.string().optional(),\n until: z.string().optional(),\n aiPatterns: z.array(z.string()),\n commits: z.array(Commit),\n});\n\nexport type CommitStream = z.infer<typeof CommitStream>;\n","import { simpleGit, SimpleGit, LogResult } from 'simple-git';\nimport { Commit, CommitStream } from '../schema/commit.js';\nimport { createAITagger, AITagConfig } from '../tags/ai-tags.js';\nimport { getDiffStats } from './diff.js';\nimport { parseRelativeDate, formatISODate } from '../utils/dates.js';\nimport { Logger } from '../utils/log.js';\n\nexport interface CollectOptions {\n repoPath: string;\n since?: string;\n until?: string;\n aiPatterns?: string[];\n defaultBranch?: string;\n logger?: Logger;\n}\n\nexport async function detectDefaultBranch(git: SimpleGit): Promise<string> {\n try {\n // Try to get the default branch from origin/HEAD\n const result = await git.raw(['symbolic-ref', 'refs/remotes/origin/HEAD']);\n const match = result.match(/refs\\/remotes\\/origin\\/(.+)/);\n if (match) {\n return match[1].trim();\n }\n } catch {\n // Fallback logic\n }\n\n // Fallback to common branch names\n const branches = await git.branch(['-r']);\n if (branches.all.includes('origin/main')) {\n return 'main';\n }\n if (branches.all.includes('origin/master')) {\n return 'master';\n }\n\n // Last resort: use current branch\n const current = await git.branch();\n return current.current || 'main';\n}\n\nexport async function collectCommits(options: CollectOptions): Promise<CommitStream> {\n const {\n repoPath,\n since,\n until,\n aiPatterns = [],\n defaultBranch: providedDefaultBranch,\n logger,\n } = options;\n\n const git = simpleGit(repoPath);\n \n // Detect default branch\n const defaultBranch = providedDefaultBranch || await detectDefaultBranch(git);\n logger?.info(`Using default branch: ${defaultBranch}`);\n\n // Parse date range\n const sinceDate = since ? parseRelativeDate(since) : undefined;\n const untilDate = until ? parseRelativeDate(until) : new Date();\n\n logger?.info(`Collecting commits from ${sinceDate?.toISOString() || 'beginning'} to ${untilDate.toISOString()}`);\n\n // Build log options\n const logOptions: any = {\n from: defaultBranch,\n maxCount: 100, // Limit for demo\n };\n\n // Get commits\n const logResult = await git.log(logOptions);\n logger?.info(`Found ${logResult.all.length} commits`);\n\n // Create AI tagger\n const aiTagger = createAITagger({ patterns: aiPatterns });\n\n // Process commits\n const commits: Commit[] = [];\n for (const gitCommit of logResult.all) {\n logger?.debug(`Processing commit ${gitCommit.hash}`);\n\n // Get diff stats\n const stats = await getDiffStats(repoPath, gitCommit.hash);\n\n // Tag AI\n const aiTag = aiTagger(gitCommit.message);\n\n // Parse parents\n const parents = (gitCommit as any).parents ? (gitCommit as any).parents.split(' ').filter((p: string) => p) : [];\n\n const commit: Commit = {\n hash: gitCommit.hash,\n authorName: gitCommit.author_name,\n authorEmail: gitCommit.author_email,\n authorDate: new Date(gitCommit.date).toISOString(),\n committerName: gitCommit.author_name, // Simple-git doesn't separate these easily\n committerEmail: gitCommit.author_email,\n committerDate: new Date(gitCommit.date).toISOString(),\n message: gitCommit.message,\n parents,\n branch: defaultBranch,\n inDefaultBranchAncestry: true, // All commits are from default branch in MVP\n tags: aiTag,\n stats,\n };\n\n commits.push(commit);\n }\n\n return {\n repoPath,\n defaultBranch,\n generatedAt: formatISODate(new Date()),\n since,\n until,\n aiPatterns: [...aiPatterns],\n commits,\n };\n}\n","export interface AITagResult {\n ai: boolean;\n sources: string[];\n}\n\nexport interface AITagConfig {\n patterns: string[];\n}\n\nconst DEFAULT_PATTERNS = [\n \"\\\\b(ai|copilot|cursor|windsurf|codeium)\\\\b\",\n \"\\\\[ai\\\\]\",\n];\n\nconst DEFAULT_TRAILER_PATTERNS = [\n \"^AI:\\\\s*true$\",\n \"^X-AI:\\\\s*true$\",\n \"^Co-authored-by:.*bot.*$\",\n];\n\nexport function createAITagger(config: AITagConfig = { patterns: [] }): (message: string) => AITagResult {\n const allPatterns = [...DEFAULT_PATTERNS, ...config.patterns];\n const messageRegexes = allPatterns.map(pattern => new RegExp(pattern, 'im'));\n const trailerRegexes = DEFAULT_TRAILER_PATTERNS.map(pattern => new RegExp(pattern, 'm'));\n\n return (message: string): AITagResult => {\n const sources: string[] = [];\n let ai = false;\n\n // Check message patterns\n for (let i = 0; i < messageRegexes.length; i++) {\n if (messageRegexes[i].test(message)) {\n ai = true;\n sources.push(`message_pattern:${allPatterns[i]}`);\n }\n }\n\n // Check trailer patterns\n for (let i = 0; i < trailerRegexes.length; i++) {\n if (trailerRegexes[i].test(message)) {\n ai = true;\n sources.push(`trailer_pattern:${DEFAULT_TRAILER_PATTERNS[i]}`);\n }\n }\n\n return { ai, sources };\n };\n}\n","import { simpleGit } from 'simple-git';\nimport { FileChange } from '../schema/commit.js';\nimport type { z } from 'zod';\n\ntype FileChangeType = z.infer<typeof FileChange>;\n\nexport interface DiffStats {\n totalAdditions: number;\n totalDeletions: number;\n files: FileChangeType[];\n}\n\nexport async function getDiffStats(repoPath: string, commitHash: string): Promise<DiffStats> {\n const git = simpleGit(repoPath);\n \n try {\n // Get numstat for the commit\n const numstat = await git.raw(['show', '--numstat', '--format=', commitHash]);\n \n const files: FileChangeType[] = [];\n let totalAdditions = 0;\n let totalDeletions = 0;\n \n const lines = numstat.trim().split('\\n').filter(line => line.trim());\n \n for (const line of lines) {\n const parts = line.split('\\t');\n if (parts.length >= 3) {\n const additions = parts[0] === '-' ? 0 : parseInt(parts[0], 10) || 0;\n const deletions = parts[1] === '-' ? 0 : parseInt(parts[1], 10) || 0;\n const path = parts[2];\n \n // Skip binary files (marked with -)\n if (parts[0] === '-' && parts[1] === '-') {\n continue;\n }\n \n totalAdditions += additions;\n totalDeletions += deletions;\n \n // Determine status (simplified)\n let status: FileChangeType['status'] = 'modified';\n if (additions > 0 && deletions === 0) {\n status = 'added';\n } else if (additions === 0 && deletions > 0) {\n status = 'deleted';\n }\n \n files.push({\n path,\n status,\n additions,\n deletions,\n });\n }\n }\n \n return {\n totalAdditions,\n totalDeletions,\n files,\n };\n } catch (error) {\n // Return empty stats if diff fails\n return {\n totalAdditions: 0,\n totalDeletions: 0,\n files: [],\n };\n }\n}\n","export function parseRelativeDate(input: string): Date {\n const now = new Date();\n \n // Handle ISO dates\n if (input.includes('-') || input.includes('T')) {\n return new Date(input);\n }\n \n // Handle relative dates like \"90d\", \"30d\", etc.\n const match = input.match(/^(\\d+)([dwmy])$/);\n if (match) {\n const value = parseInt(match[1], 10);\n const unit = match[2];\n \n const result = new Date(now);\n \n switch (unit) {\n case 'd':\n result.setDate(result.getDate() - value);\n break;\n case 'w':\n result.setDate(result.getDate() - (value * 7));\n break;\n case 'm':\n result.setMonth(result.getMonth() - value);\n break;\n case 'y':\n result.setFullYear(result.getFullYear() - value);\n break;\n }\n \n return result;\n }\n \n throw new Error(`Invalid date format: ${input}`);\n}\n\nexport function formatISODate(date: Date): string {\n return date.toISOString();\n}\n\nexport function daysBetween(start: Date, end: Date): number {\n const diffTime = Math.abs(end.getTime() - start.getTime());\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n}\n","import { promises as fs } from 'fs';\nimport { dirname } from 'path';\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n // Ignore if directory already exists\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n}\n\nexport async function writeJSON(filePath: string, data: any): Promise<void> {\n await ensureDir(dirname(filePath));\n await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\nexport async function readJSON<T>(filePath: string): Promise<T> {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","export interface Logger {\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n debug(message: string): void;\n}\n\nexport class ConsoleLogger implements Logger {\n constructor(private verbose: boolean = false) {}\n\n info(message: string): void {\n console.log(`[INFO] ${message}`);\n }\n\n warn(message: string): void {\n console.warn(`[WARN] ${message}`);\n }\n\n error(message: string): void {\n console.error(`[ERROR] ${message}`);\n }\n\n debug(message: string): void {\n if (this.verbose) {\n console.log(`[DEBUG] ${message}`);\n }\n }\n}\n\nexport const createLogger = (verbose: boolean = false): Logger => {\n return new ConsoleLogger(verbose);\n};\n"],"mappings":"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","names":["simpleGit","simpleGit"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@aida-dev/core",
3
+ "version": "0.2.0",
4
+ "description": "Core functionality for AIDA (AI Development Accounting) - Git collection, AI tagging, and schemas",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "keywords": [
13
+ "ai",
14
+ "development",
15
+ "metrics",
16
+ "git",
17
+ "analysis",
18
+ "copilot",
19
+ "cursor"
20
+ ],
21
+ "author": "Francesco Falanga <falanga.fra@gmail.com>",
22
+ "license": "MIT",
23
+ "homepage": "https://github.com/ceccode/aida-metrics#readme",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/ceccode/aida-metrics.git",
27
+ "directory": "packages/core"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/ceccode/aida-metrics/issues"
31
+ },
32
+ "dependencies": {
33
+ "simple-git": "^3.20.0",
34
+ "zod": "^3.22.4"
35
+ },
36
+ "devDependencies": {
37
+ "tsup": "^8.0.0",
38
+ "vitest": "^2.0.0",
39
+ "eslint": "^9.0.0",
40
+ "prettier": "^3.2.5",
41
+ "@types/node": "^20.0.0",
42
+ "@vitest/coverage-v8": "^2.0.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup src/index.ts --dts",
52
+ "dev": "tsup src/index.ts --dts --watch",
53
+ "test": "vitest run",
54
+ "lint": "eslint . --ext .ts",
55
+ "format": "prettier -w ."
56
+ }
57
+ }