skill-any-code 1.0.0 → 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 (68) hide show
  1. package/dist/adapters/command.schemas.js +18 -0
  2. package/dist/application/analysis.app.service.js +264 -0
  3. package/dist/application/bootstrap.js +21 -0
  4. package/dist/application/services/llm.analysis.service.js +170 -0
  5. package/dist/common/config.js +213 -0
  6. package/dist/common/constants.js +11 -0
  7. package/dist/common/errors.js +37 -0
  8. package/dist/common/logger.js +77 -0
  9. package/dist/common/types.js +2 -0
  10. package/dist/common/ui.js +201 -0
  11. package/dist/common/utils.js +117 -0
  12. package/dist/domain/index.js +17 -0
  13. package/dist/domain/interfaces.js +2 -0
  14. package/dist/domain/services/analysis.service.js +696 -0
  15. package/dist/domain/services/incremental.service.js +81 -0
  16. package/dist/infrastructure/blacklist.service.js +71 -0
  17. package/dist/infrastructure/cache/file.hash.cache.js +140 -0
  18. package/dist/infrastructure/git/git.service.js +159 -0
  19. package/dist/infrastructure/git.service.js +157 -0
  20. package/dist/infrastructure/index.service.js +108 -0
  21. package/dist/infrastructure/llm/llm.usage.tracker.js +58 -0
  22. package/dist/infrastructure/llm/openai.client.js +141 -0
  23. package/{src/infrastructure/llm/prompt.template.ts → dist/infrastructure/llm/prompt.template.js} +31 -36
  24. package/dist/infrastructure/llm.service.js +61 -0
  25. package/dist/infrastructure/skill/skill.generator.js +83 -0
  26. package/{src/infrastructure/skill/templates/resolve.script.ts → dist/infrastructure/skill/templates/resolve.script.js} +18 -15
  27. package/dist/infrastructure/skill/templates/skill.md.template.js +47 -0
  28. package/dist/infrastructure/splitter/code.splitter.js +137 -0
  29. package/dist/infrastructure/storage.service.js +409 -0
  30. package/dist/infrastructure/worker-pool/parse.worker.impl.js +137 -0
  31. package/dist/infrastructure/worker-pool/parse.worker.js +43 -0
  32. package/dist/infrastructure/worker-pool/worker-pool.service.js +171 -0
  33. package/package.json +5 -1
  34. package/jest.config.js +0 -27
  35. package/src/adapters/command.schemas.ts +0 -21
  36. package/src/application/analysis.app.service.ts +0 -272
  37. package/src/application/bootstrap.ts +0 -35
  38. package/src/application/services/llm.analysis.service.ts +0 -237
  39. package/src/cli.ts +0 -297
  40. package/src/common/config.ts +0 -209
  41. package/src/common/constants.ts +0 -8
  42. package/src/common/errors.ts +0 -34
  43. package/src/common/logger.ts +0 -82
  44. package/src/common/types.ts +0 -385
  45. package/src/common/ui.ts +0 -228
  46. package/src/common/utils.ts +0 -81
  47. package/src/domain/index.ts +0 -1
  48. package/src/domain/interfaces.ts +0 -188
  49. package/src/domain/services/analysis.service.ts +0 -735
  50. package/src/domain/services/incremental.service.ts +0 -50
  51. package/src/index.ts +0 -6
  52. package/src/infrastructure/blacklist.service.ts +0 -37
  53. package/src/infrastructure/cache/file.hash.cache.ts +0 -119
  54. package/src/infrastructure/git/git.service.ts +0 -120
  55. package/src/infrastructure/git.service.ts +0 -121
  56. package/src/infrastructure/index.service.ts +0 -94
  57. package/src/infrastructure/llm/llm.usage.tracker.ts +0 -65
  58. package/src/infrastructure/llm/openai.client.ts +0 -162
  59. package/src/infrastructure/llm.service.ts +0 -70
  60. package/src/infrastructure/skill/skill.generator.ts +0 -53
  61. package/src/infrastructure/skill/templates/skill.md.template.ts +0 -45
  62. package/src/infrastructure/splitter/code.splitter.ts +0 -176
  63. package/src/infrastructure/storage.service.ts +0 -413
  64. package/src/infrastructure/worker-pool/parse.worker.impl.ts +0 -135
  65. package/src/infrastructure/worker-pool/parse.worker.ts +0 -9
  66. package/src/infrastructure/worker-pool/worker-pool.service.ts +0 -173
  67. package/tsconfig.json +0 -24
  68. package/tsconfig.test.json +0 -5
@@ -0,0 +1,81 @@
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.IncrementalService = void 0;
37
+ class IncrementalService {
38
+ gitService;
39
+ storageService;
40
+ constructor(gitService, storageService) {
41
+ this.gitService = gitService;
42
+ this.storageService = storageService;
43
+ }
44
+ async canDoIncremental(projectRoot) {
45
+ const isGit = await this.gitService.isGitProject(projectRoot);
46
+ if (!isGit)
47
+ return { available: false, reason: 'Not a git project' };
48
+ // V2.6:不再生成/读取 .analysis_metadata.json,因此无法基于历史记录计算可用的 baseCommit。
49
+ // 主流程会回退到逐文件 commitId/hash 判定,无需依赖此服务。
50
+ return { available: false, reason: 'Metadata disabled (V2.6): fallback to per-file incremental detection' };
51
+ }
52
+ async getChangedFiles(projectRoot, baseCommit, targetCommit) {
53
+ return this.gitService.diffCommits(projectRoot, baseCommit, targetCommit);
54
+ }
55
+ async findNearestCommonAncestor(projectRoot, commits) {
56
+ try {
57
+ const { simpleGit } = await Promise.resolve().then(() => __importStar(require('simple-git')));
58
+ const git = simpleGit(projectRoot);
59
+ const commonAncestor = await git.raw(['merge-base', '--octopus', ...commits]);
60
+ return commonAncestor.trim() || null;
61
+ }
62
+ catch (e) {
63
+ return null;
64
+ }
65
+ }
66
+ getAffectedDirectories(changedFiles) {
67
+ const directories = new Set();
68
+ changedFiles.forEach(file => {
69
+ const parts = file.split('/');
70
+ for (let i = 1; i < parts.length; i++) {
71
+ const dir = parts.slice(0, i).join('/');
72
+ if (dir) {
73
+ directories.add(dir);
74
+ }
75
+ }
76
+ });
77
+ directories.add('');
78
+ return Array.from(directories);
79
+ }
80
+ }
81
+ exports.IncrementalService = IncrementalService;
@@ -0,0 +1,71 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.BlacklistService = void 0;
40
+ const ignore_1 = __importDefault(require("ignore"));
41
+ const fs = __importStar(require("fs-extra"));
42
+ const path = __importStar(require("path"));
43
+ class BlacklistService {
44
+ ig = (0, ignore_1.default)();
45
+ async load(globalBlacklist, projectRoot) {
46
+ this.ig = (0, ignore_1.default)();
47
+ this.ig.add(globalBlacklist);
48
+ const projectIgnorePath = path.join(projectRoot, '.skill-any-code-ignore');
49
+ if (await fs.pathExists(projectIgnorePath)) {
50
+ const content = await fs.readFile(projectIgnorePath, 'utf-8');
51
+ this.ig.add(content);
52
+ }
53
+ const gitignorePath = path.join(projectRoot, '.gitignore');
54
+ if (await fs.pathExists(gitignorePath)) {
55
+ const content = await fs.readFile(gitignorePath, 'utf-8');
56
+ this.ig.add(content);
57
+ }
58
+ }
59
+ isIgnored(relativePath) {
60
+ // 1. 统一为正斜杠(Windows path.relative 可能返回反斜杠)
61
+ let normalized = relativePath.replace(/\\/g, '/');
62
+ // 2. 去掉 leading ./ 或 /(ignore 库拒绝此类路径并抛错,见 REGEX_TEST_INVALID_PATH)
63
+ // path.relative 在部分 Windows 场景下可能返回 ".\" 前缀,归一化后为 "./",会导致 ignore 库抛出 RangeError
64
+ normalized = normalized.replace(/^\.\//, '').replace(/^\/+/, '');
65
+ // ignore 库不接受空字符串路径;空路径表示“项目根自身”,这里视为不忽略
66
+ if (!normalized)
67
+ return false;
68
+ return this.ig.ignores(normalized);
69
+ }
70
+ }
71
+ exports.BlacklistService = BlacklistService;
@@ -0,0 +1,140 @@
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.FileHashCache = void 0;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const path = __importStar(require("path"));
39
+ const crypto_1 = require("crypto");
40
+ const logger_1 = require("../../common/logger");
41
+ class FileHashCache {
42
+ cacheDir;
43
+ maxSizeMb;
44
+ constructor(options) {
45
+ this.cacheDir = options.cacheDir;
46
+ this.maxSizeMb = options.maxSizeMb;
47
+ }
48
+ async getDirSize() {
49
+ try {
50
+ const files = await fs.readdir(this.cacheDir);
51
+ let total = 0;
52
+ for (const f of files) {
53
+ const full = path.join(this.cacheDir, f);
54
+ const stat = await fs.stat(full);
55
+ if (stat.isFile()) {
56
+ total += stat.size;
57
+ }
58
+ }
59
+ return total;
60
+ }
61
+ catch {
62
+ return 0;
63
+ }
64
+ }
65
+ async enforceLimit() {
66
+ if (this.maxSizeMb <= 0) {
67
+ return;
68
+ }
69
+ await fs.ensureDir(this.cacheDir);
70
+ const maxBytes = this.maxSizeMb * 1024 * 1024;
71
+ let total = await this.getDirSize();
72
+ if (total <= maxBytes)
73
+ return;
74
+ const entries = await fs.readdir(this.cacheDir);
75
+ const fileStats = [];
76
+ for (const name of entries) {
77
+ const filePath = path.join(this.cacheDir, name);
78
+ const stat = await fs.stat(filePath);
79
+ if (stat.isFile()) {
80
+ fileStats.push({ filePath, mtimeMs: stat.mtimeMs, size: stat.size });
81
+ }
82
+ }
83
+ fileStats.sort((a, b) => a.mtimeMs - b.mtimeMs);
84
+ for (const f of fileStats) {
85
+ if (total <= maxBytes)
86
+ break;
87
+ try {
88
+ await fs.remove(f.filePath);
89
+ total -= f.size;
90
+ }
91
+ catch (e) {
92
+ logger_1.logger.warn(`Failed to delete cache file: ${f.filePath}`, e);
93
+ }
94
+ }
95
+ }
96
+ async get(fileHash) {
97
+ if (this.maxSizeMb === 0) {
98
+ return null;
99
+ }
100
+ const cachePath = path.join(this.cacheDir, `${fileHash}.json`);
101
+ try {
102
+ if (await fs.pathExists(cachePath)) {
103
+ const data = await fs.readJSON(cachePath);
104
+ return data;
105
+ }
106
+ return null;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ async set(fileHash, result) {
113
+ if (this.maxSizeMb === 0) {
114
+ return;
115
+ }
116
+ try {
117
+ await fs.ensureDir(this.cacheDir);
118
+ await this.enforceLimit();
119
+ const cachePath = path.join(this.cacheDir, `${fileHash}.json`);
120
+ await fs.writeJSON(cachePath, result, { spaces: 2 });
121
+ }
122
+ catch (error) {
123
+ // 缓存写入失败不影响主流程
124
+ logger_1.logger.warn('Failed to write cache:', error);
125
+ }
126
+ }
127
+ async clear(fileHash) {
128
+ if (fileHash) {
129
+ const cachePath = path.join(this.cacheDir, `${fileHash}.json`);
130
+ await fs.remove(cachePath);
131
+ }
132
+ else {
133
+ await fs.emptyDir(this.cacheDir);
134
+ }
135
+ }
136
+ static calculateFileHash(content) {
137
+ return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
138
+ }
139
+ }
140
+ exports.FileHashCache = FileHashCache;
@@ -0,0 +1,159 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.GitService = void 0;
40
+ const simple_git_1 = __importDefault(require("simple-git"));
41
+ const errors_1 = require("../../common/errors");
42
+ const path = __importStar(require("path"));
43
+ class GitService {
44
+ gitInstances = new Map();
45
+ getGitInstance(projectRoot) {
46
+ if (!this.gitInstances.has(projectRoot)) {
47
+ this.gitInstances.set(projectRoot, (0, simple_git_1.default)(projectRoot));
48
+ }
49
+ return this.gitInstances.get(projectRoot);
50
+ }
51
+ async isGitProject(projectRoot) {
52
+ try {
53
+ const git = this.getGitInstance(projectRoot);
54
+ await git.revparse(['--is-inside-work-tree']);
55
+ return true;
56
+ }
57
+ catch (e) {
58
+ return false;
59
+ }
60
+ }
61
+ async getCurrentCommit(projectRoot) {
62
+ try {
63
+ const git = this.getGitInstance(projectRoot);
64
+ const commitHash = await git.revparse(['HEAD']);
65
+ return commitHash.trim();
66
+ }
67
+ catch (e) {
68
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get current commit', e.message);
69
+ }
70
+ }
71
+ async getCurrentBranch(projectRoot) {
72
+ try {
73
+ const git = this.getGitInstance(projectRoot);
74
+ const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
75
+ return branch.trim();
76
+ }
77
+ catch (e) {
78
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get current branch', e.message);
79
+ }
80
+ }
81
+ async getProjectSlug(projectRoot) {
82
+ try {
83
+ const git = this.getGitInstance(projectRoot);
84
+ const remoteUrl = await git.remote(['get-url', 'origin']);
85
+ const url = (remoteUrl || '').trim();
86
+ // 处理SSH和HTTPS格式的URL
87
+ let slug;
88
+ if (url.startsWith('git@')) {
89
+ slug = url.split(':')[1].replace('.git', '');
90
+ }
91
+ else if (url.startsWith('http')) {
92
+ const parts = new URL(url).pathname.split('/');
93
+ slug = `${parts[1]}/${parts[2].replace('.git', '')}`;
94
+ }
95
+ else {
96
+ // 如果没有远程仓库,使用目录名作为slug
97
+ slug = path.basename(projectRoot);
98
+ }
99
+ return slug.toLowerCase().replace(/[^a-z0-9-_/]/g, '-');
100
+ }
101
+ catch (e) {
102
+ // 如果获取远程URL失败,使用目录名作为slug
103
+ return path.basename(projectRoot).toLowerCase().replace(/[^a-z0-9-_]/g, '-');
104
+ }
105
+ }
106
+ async getUncommittedChanges(projectRoot) {
107
+ try {
108
+ const git = this.getGitInstance(projectRoot);
109
+ const status = await git.status();
110
+ return [...status.modified, ...status.created, ...status.deleted, ...status.renamed.map(r => r.to)];
111
+ }
112
+ catch (e) {
113
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get uncommitted changes', e.message);
114
+ }
115
+ }
116
+ async diffCommits(projectRoot, commit1, commit2) {
117
+ try {
118
+ const git = this.getGitInstance(projectRoot);
119
+ const diff = await git.diff(['--name-only', commit1, commit2]);
120
+ return diff.trim().split('\n').filter(line => line.length > 0);
121
+ }
122
+ catch (e) {
123
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to diff commits', e.message);
124
+ }
125
+ }
126
+ async getFileLastCommit(projectRoot, filePath) {
127
+ try {
128
+ const git = this.getGitInstance(projectRoot);
129
+ const result = await git.raw(['log', '-n', '1', '--pretty=format:%H', '--', filePath]);
130
+ const hash = result.trim();
131
+ return hash || null;
132
+ }
133
+ catch {
134
+ // 对于未纳入 Git 管理或无提交记录的文件,返回 null
135
+ return null;
136
+ }
137
+ }
138
+ async isFileDirty(projectRoot, filePath) {
139
+ try {
140
+ const git = this.getGitInstance(projectRoot);
141
+ const status = await git.status();
142
+ const normalizedPath = filePath.replace(/\\/g, '/');
143
+ if (status.modified.includes(normalizedPath))
144
+ return true;
145
+ if (status.created.includes(normalizedPath))
146
+ return true;
147
+ if (status.deleted.includes(normalizedPath))
148
+ return true;
149
+ if (status.renamed.some(r => r.to === normalizedPath || r.from === normalizedPath))
150
+ return true;
151
+ return false;
152
+ }
153
+ catch {
154
+ // Git 不可用时视为非 dirty,交由上层逻辑处理
155
+ return false;
156
+ }
157
+ }
158
+ }
159
+ exports.GitService = GitService;
@@ -0,0 +1,157 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.GitService = void 0;
40
+ const simple_git_1 = __importDefault(require("simple-git"));
41
+ const fs = __importStar(require("fs-extra"));
42
+ const errors_1 = require("../common/errors");
43
+ class GitService {
44
+ git;
45
+ projectRoot;
46
+ constructor(projectRoot) {
47
+ this.projectRoot = projectRoot;
48
+ }
49
+ getGit() {
50
+ if (!this.git) {
51
+ this.git = (0, simple_git_1.default)(this.projectRoot);
52
+ }
53
+ return this.git;
54
+ }
55
+ async isGitProject() {
56
+ try {
57
+ // 先判断目录是否存在
58
+ if (!(await fs.pathExists(this.projectRoot))) {
59
+ return false;
60
+ }
61
+ return await this.getGit().checkIsRepo();
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ async getCurrentCommit() {
68
+ // 重试3次,避免Windows下git命令偶发失败
69
+ const maxRetries = 3;
70
+ for (let i = 0; i < maxRetries; i++) {
71
+ try {
72
+ return await this.getGit().revparse(['HEAD']);
73
+ }
74
+ catch (e) {
75
+ if (i === maxRetries - 1) {
76
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get current commit', e);
77
+ }
78
+ await new Promise(resolve => setTimeout(resolve, 100 * (i + 1)));
79
+ }
80
+ }
81
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get current commit');
82
+ }
83
+ async getCurrentBranch() {
84
+ try {
85
+ return await this.getGit().revparse(['--abbrev-ref', 'HEAD']);
86
+ }
87
+ catch (e) {
88
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get current branch', e);
89
+ }
90
+ }
91
+ async getProjectSlug() {
92
+ try {
93
+ // 尝试从remote url获取slug
94
+ const remoteUrl = (await this.getGit().remote(['get-url', 'origin']));
95
+ const match = remoteUrl?.match(/[:/]([^/]+\/[^/.]+)(\.git)?$/);
96
+ if (match) {
97
+ return match[1];
98
+ }
99
+ }
100
+ catch (e) {
101
+ // 没有remote的情况,使用目录名作为slug
102
+ const path = require('path');
103
+ const crypto = require('crypto');
104
+ const dirName = path.basename(this.projectRoot);
105
+ const hash = crypto.createHash('md5').update(this.projectRoot).digest('hex').slice(0, 8);
106
+ return `${dirName}-${hash}`;
107
+ }
108
+ // 无法解析remote的情况,同样使用目录名加hash
109
+ const path = require('path');
110
+ const crypto = require('crypto');
111
+ const dirName = path.basename(this.projectRoot);
112
+ const hash = crypto.createHash('md5').update(this.projectRoot).digest('hex').slice(0, 8);
113
+ return `${dirName}-${hash}`;
114
+ }
115
+ async getUncommittedChanges() {
116
+ try {
117
+ const status = await this.getGit().status();
118
+ return status.files.map(f => f.path);
119
+ }
120
+ catch (e) {
121
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to get uncommitted changes', e);
122
+ }
123
+ }
124
+ async diffCommits(commit1, commit2) {
125
+ try {
126
+ const diff = await this.getGit().diff([`${commit1}..${commit2}`, '--name-only']);
127
+ return diff.split('\n').filter(Boolean);
128
+ }
129
+ catch (e) {
130
+ throw new errors_1.AppError(errors_1.ErrorCode.GIT_OPERATION_FAILED, 'Failed to diff commits', e);
131
+ }
132
+ }
133
+ async getFileLastCommit(projectRoot, filePath) {
134
+ try {
135
+ const git = this.getGit();
136
+ const relPath = filePath;
137
+ const log = await git.raw(['log', '-n', '1', '--pretty=format:%H', '--', relPath]);
138
+ const trimmed = (log || '').trim();
139
+ return trimmed || null;
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ async isFileDirty(projectRoot, filePath) {
146
+ try {
147
+ const git = this.getGit();
148
+ const status = await git.status();
149
+ return status.files.some(f => f.path === filePath);
150
+ }
151
+ catch {
152
+ // git 不可用时视为非 dirty,交由上层按非 git 场景处理
153
+ return false;
154
+ }
155
+ }
156
+ }
157
+ exports.GitService = GitService;