@manojkmfsi/monodog 1.0.1

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 (61) hide show
  1. package/.eslintrc.cjs +15 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/CHANGELOG.md +79 -0
  4. package/LICENCE +21 -0
  5. package/README.md +55 -0
  6. package/dist/config-loader.js +116 -0
  7. package/dist/get-db-url.js +10 -0
  8. package/dist/gitService.js +242 -0
  9. package/dist/index.js +1370 -0
  10. package/dist/serve.js +103 -0
  11. package/dist/setup.js +155 -0
  12. package/dist/utils/ci-status.js +446 -0
  13. package/dist/utils/helpers.js +237 -0
  14. package/dist/utils/monorepo-scanner.js +486 -0
  15. package/dist/utils/utilities.js +414 -0
  16. package/monodog-conf.example.json +16 -0
  17. package/monodog-conf.json +16 -0
  18. package/monodog-dashboard/README.md +58 -0
  19. package/monodog-dashboard/dist/assets/index-2d967652.js +71 -0
  20. package/monodog-dashboard/dist/assets/index-504dc418.css +1 -0
  21. package/monodog-dashboard/dist/index.html +15 -0
  22. package/package.json +50 -0
  23. package/prisma/migrations/20251017041048_init/migration.sql +19 -0
  24. package/prisma/migrations/20251017083007_add_package/migration.sql +21 -0
  25. package/prisma/migrations/20251021083705_alter_package/migration.sql +37 -0
  26. package/prisma/migrations/20251022085155_test/migration.sql +2 -0
  27. package/prisma/migrations/20251022160841_/migration.sql +35 -0
  28. package/prisma/migrations/20251023130158_rename_column_name/migration.sql +34 -0
  29. package/prisma/migrations/20251023174837_/migration.sql +34 -0
  30. package/prisma/migrations/20251023175830_uodate_schema/migration.sql +32 -0
  31. package/prisma/migrations/20251024103700_add_dependency_info/migration.sql +13 -0
  32. package/prisma/migrations/20251025192150_add_dependency_info/migration.sql +19 -0
  33. package/prisma/migrations/20251025192342_add_dependency_info/migration.sql +40 -0
  34. package/prisma/migrations/20251025204613_add_dependency_info/migration.sql +8 -0
  35. package/prisma/migrations/20251026071336_add_dependency_info/migration.sql +25 -0
  36. package/prisma/migrations/20251027062626_add_commit/migration.sql +10 -0
  37. package/prisma/migrations/20251027062748_add_commit/migration.sql +23 -0
  38. package/prisma/migrations/20251027092741_add_commit/migration.sql +17 -0
  39. package/prisma/migrations/20251027112736_add_health_status/migration.sql +16 -0
  40. package/prisma/migrations/20251027140546_init_packages/migration.sql +16 -0
  41. package/prisma/migrations/20251029073436_added_package_heath_key/migration.sql +34 -0
  42. package/prisma/migrations/20251029073830_added_package_health_key/migration.sql +49 -0
  43. package/prisma/migrations/20251111091920_/migration.sql +16 -0
  44. package/prisma/migrations/20251211155036_cascade_on_auto_delete/migration.sql +48 -0
  45. package/prisma/migrations/migration_lock.toml +3 -0
  46. package/prisma/schema.prisma +114 -0
  47. package/release.config.js +41 -0
  48. package/src/config-loader.ts +119 -0
  49. package/src/get-db-url.ts +11 -0
  50. package/src/gitService.ts +277 -0
  51. package/src/index.ts +1554 -0
  52. package/src/serve.ts +87 -0
  53. package/src/setup.ts +164 -0
  54. package/src/types/monorepo-scanner.d.ts +32 -0
  55. package/src/utils/ci-status.ts +639 -0
  56. package/src/utils/helpers.js +203 -0
  57. package/src/utils/helpers.js.map +1 -0
  58. package/src/utils/helpers.ts +238 -0
  59. package/src/utils/monorepo-scanner.ts +599 -0
  60. package/src/utils/utilities.ts +483 -0
  61. package/tsconfig.json +16 -0
package/.eslintrc.cjs ADDED
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ root: true,
3
+ parser: '@typescript-eslint/parser',
4
+ plugins: ['@typescript-eslint'],
5
+ extends: [
6
+ 'eslint:recommended',
7
+ 'plugin:@typescript-eslint/recommended',
8
+ ],
9
+ rules: {
10
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
11
+ '@typescript-eslint/no-explicit-any': 'off',
12
+ "@typescript-eslint/no-unused-vars": "off",
13
+ },
14
+ ignorePatterns: ['dist', 'node_modules', '**/*.js'],
15
+ };
@@ -0,0 +1,4 @@
1
+
2
+ > @manojkmfsi/monodog@1.0.1 build /home/runner/work/monodog/monodog/packages/monoapp
3
+ > rm -rf dist && tsc -p tsconfig.json
4
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,79 @@
1
+ # @manojkmfsi/monoapp
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - updated pkg name
8
+
9
+ ## 3.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - updated org
14
+
15
+ ## 3.1.0
16
+
17
+ ### Minor Changes
18
+
19
+ - 76
20
+
21
+ ## 3.0.0
22
+
23
+ ### Major Changes
24
+
25
+ - 75
26
+
27
+ ## 2.1.0
28
+
29
+ ### Minor Changes
30
+
31
+ - 74
32
+
33
+ ## 2.0.2
34
+
35
+ ### Patch Changes
36
+
37
+ - 73
38
+
39
+ ## 2.0.1
40
+
41
+ ### Patch Changes
42
+
43
+ - 72
44
+
45
+ ## 2.0.0
46
+
47
+ ### Major Changes
48
+
49
+ - 71
50
+
51
+ ## 1.1.0
52
+
53
+ ### Minor Changes
54
+
55
+ - 67
56
+
57
+ ## 1.0.6
58
+
59
+ ### Patch Changes
60
+
61
+ - 66
62
+
63
+ ## 1.0.5
64
+
65
+ ### Patch Changes
66
+
67
+ - 40
68
+
69
+ ## 1.0.4
70
+
71
+ ### Patch Changes
72
+
73
+ - 39
74
+
75
+ ## 1.0.3
76
+
77
+ ### Patch Changes
78
+
79
+ - 35
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mindfire Digital LLP
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,55 @@
1
+ # Monodog: Monorepo Analytics and Health API
2
+
3
+ ## 🎯 Overview
4
+
5
+ The dashboard will provide visual management and monitoring capabilities for packages in monorepos using pnpm, turbo, or Nx. It will be distributed as an npm package installable in any monorepo to auto-generate a web UI for package oversight.
6
+
7
+ This service is typically run locally or on a central server and power a dedicated frontend dashboard.
8
+
9
+ ---
10
+
11
+ ## 🛠 Technology Stack
12
+
13
+ | Component | Technology | Description |
14
+ | ------------- | -------------------- | ----------------------------------------------------------------------------------------- |
15
+ | **Language** | TypeScript & Node.js | Core language for runtime execution. |
16
+ | **Framework** | Express.js, React | Express Handles all API routing and middleware and React for building the user interface. |
17
+ | **Styling** | Tailwind CSS | Utility-first framework for responsive, modern, and aesthetic design. |
18
+ | **ORM** | Prisma | Database layer for managing package and health status records. |
19
+ | **Scanning** | monorepo-scanner | Core logic for file system scanning and package metadata extraction. |
20
+ | **VCS** | GitService | Used to fetch and analyze commit history per package path. |
21
+
22
+ ---
23
+
24
+ ## ⚙️ Prerequisites
25
+
26
+ You must have the following installed to run the service:
27
+
28
+ - **Node.js:** Version 18+ recommended
29
+ - **Package Manager:** `pnpm` or `npm` (use the one your monorepo uses)
30
+
31
+ ---
32
+
33
+ ## 🚀 Getting Started
34
+
35
+ ### Install Package in Monorepo
36
+
37
+ Install monoapp in a monorepo workspace root:
38
+
39
+ pnpm install --save-dev @manojkmfsi/monoapp -w
40
+
41
+ Run app using serve script:
42
+
43
+ npm --workspace @manojkmfsi/monoapp run serve
44
+
45
+ ### Key API Endpoints
46
+
47
+ | Method | Route | Purpose | Persistence |
48
+ | ------- | --------------------------- | --------------------------------------------------------------------------------------- | ------------------- |
49
+ | **GET** | `/api/packages` | Retrieve all package metadata from the database. | Cached / Persistent |
50
+ | **GET** | `/api/packages/refresh` | Trigger a full file scan of the monorepo and update/sync the database. | Triggers write |
51
+ | **GET** | `/api/packages/:name` | Get detailed info, reports, and CI status for a package. | Cached / Persistent |
52
+ | **GET** | `/api/health/packages` | Fetch the latest health metrics (score, build status) for all packages. | Persistent |
53
+ | **GET** | `/api/health/refresh` | Recalculate all package health metrics (tests, lint, security) and update the database. | Triggers write |
54
+ | **GET** | `/api/commits/:packagePath` | Fetch Git commit history for a specific package directory. | Generated runtime |
55
+ | **GET** | `/api/config/files` | Scan the monorepo for essential configuration files (e.g., `tsconfig`, `.eslintrc`). | Generated runtime |
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.appConfig = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ // Global variable to hold the loaded config
40
+ let config = null;
41
+ /**
42
+ * Loads the monodog-conf.json file from the monorepo root.
43
+ * This should be called only once during application startup.
44
+ * @returns The application configuration object.
45
+ */
46
+ function loadConfig() {
47
+ if (config) {
48
+ return config; // Return cached config if already loaded
49
+ }
50
+ // 1. Determine the path to the config file
51
+ // We assume the backend package is running from the monorepo root (cwd is root)
52
+ // or that we can navigate up to the root from the current file's location.
53
+ const rootPath = path.resolve(process.cwd()); // Adjust based on your workspace folder depth from root if needed
54
+ const configPath = path.resolve(rootPath, 'monodog-conf.json');
55
+ createConfigFileIfMissing(rootPath);
56
+ if (!fs.existsSync(configPath)) {
57
+ console.error(`ERROR1: Configuration file not found at ${configPath}`);
58
+ process.exit(1);
59
+ }
60
+ try {
61
+ // 2. Read and parse the JSON file
62
+ const fileContent = fs.readFileSync(configPath, 'utf-8');
63
+ const parsedConfig = JSON.parse(fileContent);
64
+ // 3. Optional: Add validation logic here (e.g., check if ports are numbers)
65
+ // Cache and return
66
+ config = parsedConfig;
67
+ process.stderr.write('[Config] Loaded configuration from: ...\n');
68
+ return config;
69
+ }
70
+ catch (error) {
71
+ console.error('ERROR: Failed to read or parse monodog-conf.json.');
72
+ console.error(error);
73
+ process.exit(1);
74
+ }
75
+ }
76
+ function createConfigFileIfMissing(rootPath) {
77
+ // --- CONFIGURATION ---
78
+ const configFileName = 'monodog-conf.json';
79
+ const configFilePath = path.resolve(rootPath, configFileName);
80
+ // The default content for the configuration file
81
+ const defaultContent = {
82
+ workspaces: [],
83
+ database: {
84
+ path: 'file:./monodog.db', // SQLite database file path, relative to prisma schema location
85
+ },
86
+ dashboard: {
87
+ host: '0.0.0.0',
88
+ port: '3010',
89
+ },
90
+ server: {
91
+ host: '0.0.0.0', // Default host for the API server
92
+ port: 8999, // Default port for the API server
93
+ },
94
+ };
95
+ const contentString = JSON.stringify(defaultContent, null, 2);
96
+ // ---------------------
97
+ process.stderr.write(`\n[monodog] Checking for ${configFileName}...`);
98
+ if (fs.existsSync(configFilePath)) {
99
+ process.stderr.write(`[monodog] ${configFileName} already exists at ${configFilePath}. Skipping creation.`);
100
+ }
101
+ else {
102
+ try {
103
+ // Write the default content to the file
104
+ fs.writeFileSync(configFilePath, contentString, 'utf-8');
105
+ process.stderr.write(`[monodog] Successfully generated default ${configFileName} in the workspace root.`);
106
+ process.stderr.write('[monodog] Please review and update settings like "host" and "port".');
107
+ }
108
+ catch (err) {
109
+ const message = err instanceof Error ? err.message : String(err);
110
+ console.error(`[monodog Error] Failed to generate ${configFileName}:`, message);
111
+ process.exit(1);
112
+ }
113
+ }
114
+ }
115
+ const appConfig = loadConfig();
116
+ exports.appConfig = appConfig;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_loader_1 = require("./config-loader");
4
+ function generateUrl() {
5
+ // const appConfig = loadConfig();
6
+ const DATABASE_URL = `${config_loader_1.appConfig.database.path}`;
7
+ process.env.DATABASE_URL = DATABASE_URL;
8
+ process.stdout.write(DATABASE_URL);
9
+ }
10
+ generateUrl();
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ /**
3
+ * GitService.ts
4
+ *
5
+ * This service executes native 'git' commands using Node.js's child_process
6
+ * to retrieve the commit history of the local repository.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.GitService = void 0;
13
+ const child_process_1 = require("child_process");
14
+ const util_1 = require("util");
15
+ const path_1 = __importDefault(require("path"));
16
+ // Promisify the standard 'exec' function for easy async/await usage
17
+ const execPromise = (0, util_1.promisify)(child_process_1.exec);
18
+ /**
19
+ * List of standard Conventional Commit types for validation.
20
+ * Any extracted type not in this list will be set to 'other'.
21
+ */
22
+ const VALID_COMMIT_TYPES = [
23
+ 'feat', // New feature
24
+ 'fix', // Bug fix
25
+ 'docs', // Documentation changes
26
+ 'style', // Code style changes (formatting, etc)
27
+ 'refactor', // Code refactoring
28
+ 'perf', // Performance improvements
29
+ 'test', // Adding or updating tests
30
+ 'chore', // Maintenance tasks (e.g., build scripts, dependency updates)
31
+ 'ci', // CI/CD changes
32
+ 'build', // Build system changes (e.g., pnpm/npm scripts)
33
+ 'revert', // Reverting changes
34
+ ];
35
+ /**
36
+ * The delimiter used to separate individual commit records in the git log output.
37
+ * We use a unique string to ensure reliable splitting.
38
+ */
39
+ // const COMMIT_DELIMITER = '|---COMMIT-END---|';
40
+ /**
41
+ * The custom format string passed to 'git log'.
42
+ * It uses JSON structure and our unique delimiter to make parsing consistent.
43
+ * * %H: Commit hash
44
+ * %pn: packageName name
45
+ * %ae: Author email
46
+ * %ad: Author date (ISO 8601 format)
47
+ * %s: Subject (commit message's first line - used for 'message' and 'type' fields)
48
+ */
49
+ // const GIT_LOG_FORMAT = `{"hash": "%H", "author": "%ae", "packageName": "%pn", "date": "%ad", "message": "%s", "type": "%s"}${COMMIT_DELIMITER}`;
50
+ // export class GitService {
51
+ // private repoPath: string;
52
+ // constructor(repoPath: string = process.cwd()) {
53
+ // // Allows specifying a custom path to the monorepo root
54
+ // this.repoPath = repoPath;
55
+ // }
56
+ // /**
57
+ // * Retrieves commit history for the repository, optionally filtered by a package path.
58
+ // * @param pathFilter The path to a package directory (e.g., 'packages/server').
59
+ // * If provided, only commits affecting that path are returned.
60
+ // * @returns A Promise resolving to an array of GitCommit objects.
61
+ // */
62
+ // public async getAllCommits(pathFilter?: string): Promise<GitCommit[]> {
63
+ // const pathArgument = pathFilter ? ` -- ${pathFilter}` : '';
64
+ // const command = `git log --pretty=format:'${GIT_LOG_FORMAT}' --date=iso-strict${pathArgument}`;
65
+ // try {
66
+ // console.log(`Executing Git command: ${command}`);
67
+ // const { stdout } = await execPromise(command, {
68
+ // cwd: this.repoPath,
69
+ // maxBuffer: 1024 * 5000,
70
+ // }); // Increase buffer for large repos
71
+ // // console.log(stdout)
72
+ // console.log(stdout, '--', this.repoPath);
73
+ // const escapedDelimiter = COMMIT_DELIMITER.replace(
74
+ // /[.*+?^${}()|[\]\\]/g,
75
+ // '\\$&'
76
+ // );
77
+ // // 1. Remove the final trailing delimiter, as it creates an empty string at the end of the array
78
+ // const cleanedOutput = stdout
79
+ // .trim()
80
+ // .replace(new RegExp(`${escapedDelimiter}$`), '');
81
+ // console.log(cleanedOutput);
82
+ // if (!cleanedOutput) {
83
+ // return [];
84
+ // }
85
+ // // 2. Split the output by our custom delimiter to get an array of JSON strings
86
+ // const jsonStrings = cleanedOutput.split(COMMIT_DELIMITER);
87
+ // // 3. Parse each string into a GitCommit object
88
+ // const commits: GitCommit[] = jsonStrings
89
+ // .map(jsonString => {
90
+ // try {
91
+ // // JSON.parse is necessary because the output starts as a string
92
+ // const commit = JSON.parse(jsonString);
93
+ // console.log(commit.type, commit['hash']);
94
+ // try {
95
+ // // FIX: Updated Regex to handle optional scope (e.g., 'feat(scope):')
96
+ // // Matches:
97
+ // // 1. (^(\w+)) - The commit type (e.g., 'feat')
98
+ // // 2. (\([^)]+\))? - The optional scope (e.g., '(setup)')
99
+ // // 3. (:|!) - Ends with a colon or an exclamation point (for breaking changes)
100
+ // const typeMatch = commit.type.match(/^(\w+)(\([^)]+\))?([:!])/);
101
+ // let extractedType = 'other';
102
+ // if (typeMatch) {
103
+ // const rawType = typeMatch[1];
104
+ // // NEW: Validate the extracted type against the list of known types
105
+ // if (VALID_COMMIT_TYPES.includes(rawType)) {
106
+ // extractedType = rawType;
107
+ // }
108
+ // }
109
+ // commit.type = extractedType;
110
+ // } catch (e) {
111
+ // console.error('Failed to match commit type:', commit, e);
112
+ // // Skip malformed entries
113
+ // return null;
114
+ // }
115
+ // // // Set type to 'other' if the conventional format is not found
116
+ // // commit.type = typeMatch ? typeMatch[1] : 'other';
117
+ // return commit;
118
+ // } catch (e) {
119
+ // // console.log(jsonString)
120
+ // console.error('Failed to parse commit JSON:', jsonString, e);
121
+ // // Skip malformed entries
122
+ // return null;
123
+ // }
124
+ // })
125
+ // .filter((commit): commit is GitCommit => commit !== null);
126
+ // return commits;
127
+ // } catch (error) {
128
+ // console.error('Error fetching Git history:', error);
129
+ // // In a real application, you would handle this gracefully (e.g., throwing a custom error)
130
+ // throw new Error(
131
+ // 'Failed to retrieve Git commit history. Is Git installed and is the path correct?'
132
+ // );
133
+ // }
134
+ // }
135
+ // }
136
+ class GitService {
137
+ constructor(repoPath = process.cwd()) {
138
+ this.repoPath = repoPath;
139
+ }
140
+ /**
141
+ * Retrieves commit history for the repository, optionally filtered by a package path.
142
+ */
143
+ async getAllCommits(pathFilter) {
144
+ try {
145
+ let pathArgument = '';
146
+ if (pathFilter) {
147
+ // Normalize the path and ensure it's relative to the repo root
148
+ const normalizedPath = this.normalizePath(pathFilter);
149
+ if (normalizedPath) {
150
+ pathArgument = ` -C ${normalizedPath}`;
151
+ }
152
+ }
153
+ // First, validate we're in a git repo
154
+ await this.validateGitRepository(pathArgument);
155
+ // Use a simpler git log format
156
+ const command = `git ${pathArgument} log --pretty=format:"%H|%an|%ad|%s" --date=iso-strict`;
157
+ console.log(`🔧 Executing Git command in: ${this.repoPath}`);
158
+ console.log(`📝 Git command: ${command}`);
159
+ const { stdout, stderr } = await execPromise(command, {
160
+ cwd: this.repoPath,
161
+ maxBuffer: 1024 * 5000,
162
+ });
163
+ if (stderr) {
164
+ console.warn('Git stderr:', stderr);
165
+ }
166
+ if (!stdout.trim()) {
167
+ console.log('📭 No commits found for path:', pathFilter);
168
+ return [];
169
+ }
170
+ // Parse the output
171
+ const commits = [];
172
+ const lines = stdout.trim().split('\n');
173
+ for (const line of lines) {
174
+ try {
175
+ const [hash, author, date, message] = line.split('|');
176
+ const commit = {
177
+ hash: hash.trim(),
178
+ author: author.trim(),
179
+ packageName: pathFilter || 'root',
180
+ date: new Date(date.trim()),
181
+ message: message.trim(),
182
+ type: this.extractCommitType(message.trim()),
183
+ };
184
+ commits.push(commit);
185
+ }
186
+ catch (parseError) {
187
+ console.error('❌ Failed to parse commit line:', line, parseError);
188
+ }
189
+ }
190
+ console.log(`✅ Successfully parsed ${commits.length} commits`);
191
+ return commits;
192
+ }
193
+ catch (error) {
194
+ console.error('💥 Error in getAllCommits:', error);
195
+ throw error;
196
+ }
197
+ }
198
+ /**
199
+ * Normalize path to be relative to git repo root
200
+ */
201
+ normalizePath(inputPath) {
202
+ // If it's an absolute path, make it relative to repo root
203
+ if (path_1.default.isAbsolute(inputPath)) {
204
+ return path_1.default.relative(this.repoPath, inputPath);
205
+ }
206
+ // If it's already relative, return as-is
207
+ return inputPath;
208
+ }
209
+ /**
210
+ * Extract commit type from message
211
+ */
212
+ extractCommitType(message) {
213
+ try {
214
+ const typeMatch = message.match(/^(\w+)(\([^)]+\))?!?:/);
215
+ if (typeMatch) {
216
+ const rawType = typeMatch[1].toLowerCase();
217
+ if (VALID_COMMIT_TYPES.includes(rawType)) {
218
+ return rawType;
219
+ }
220
+ }
221
+ return 'other';
222
+ }
223
+ catch (error) {
224
+ return 'other';
225
+ }
226
+ }
227
+ /**
228
+ * Validate that we're in a git repository
229
+ */
230
+ async validateGitRepository(pathArgument) {
231
+ try {
232
+ await execPromise('git ' + pathArgument + ' rev-parse --is-inside-work-tree', {
233
+ cwd: this.repoPath,
234
+ });
235
+ console.log('✅ Valid git repository');
236
+ }
237
+ catch (error) {
238
+ throw new Error('Not a git repository (or any of the parent directories)');
239
+ }
240
+ }
241
+ }
242
+ exports.GitService = GitService;