@dommaker/harness 1.5.0 → 1.7.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 (209) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +0 -2
  3. package/dist/cli/commands/definitions.d.ts.map +1 -1
  4. package/dist/cli/commands/definitions.js +1 -32
  5. package/dist/cli/commands/definitions.js.map +1 -1
  6. package/dist/cli/commands/passes-gate.d.ts +0 -2
  7. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  8. package/dist/cli/commands/passes-gate.js +0 -1
  9. package/dist/cli/commands/passes-gate.js.map +1 -1
  10. package/dist/cli/commands/performance.d.ts +0 -4
  11. package/dist/cli/commands/performance.d.ts.map +1 -1
  12. package/dist/cli/commands/performance.js +10 -20
  13. package/dist/cli/commands/performance.js.map +1 -1
  14. package/dist/cli/commands/report.d.ts +1 -1
  15. package/dist/cli/commands/report.d.ts.map +1 -1
  16. package/dist/cli/commands/report.js +2 -55
  17. package/dist/cli/commands/report.js.map +1 -1
  18. package/dist/cli/commands/status.d.ts.map +1 -1
  19. package/dist/cli/commands/status.js +4 -8
  20. package/dist/cli/commands/status.js.map +1 -1
  21. package/dist/context/index.d.ts +2 -5
  22. package/dist/context/index.d.ts.map +1 -1
  23. package/dist/context/index.js +2 -8
  24. package/dist/context/index.js.map +1 -1
  25. package/dist/context/token-budget.d.ts +0 -29
  26. package/dist/context/token-budget.d.ts.map +1 -1
  27. package/dist/context/token-budget.js +1 -65
  28. package/dist/context/token-budget.js.map +1 -1
  29. package/dist/context/types.d.ts +0 -1
  30. package/dist/context/types.d.ts.map +1 -1
  31. package/dist/context/types.js +0 -9
  32. package/dist/context/types.js.map +1 -1
  33. package/dist/core/constraints/checker.d.ts +14 -0
  34. package/dist/core/constraints/checker.d.ts.map +1 -1
  35. package/dist/core/constraints/checker.js +22 -2
  36. package/dist/core/constraints/checker.js.map +1 -1
  37. package/dist/core/constraints/git-evidence.d.ts +6 -3
  38. package/dist/core/constraints/git-evidence.d.ts.map +1 -1
  39. package/dist/core/constraints/git-evidence.js +6 -3
  40. package/dist/core/constraints/git-evidence.js.map +1 -1
  41. package/dist/core/index.d.ts +1 -2
  42. package/dist/core/index.d.ts.map +1 -1
  43. package/dist/core/index.js +1 -9
  44. package/dist/core/index.js.map +1 -1
  45. package/dist/core/spec/validator.d.ts +2 -0
  46. package/dist/core/spec/validator.d.ts.map +1 -1
  47. package/dist/core/spec/validator.js +9 -11
  48. package/dist/core/spec/validator.js.map +1 -1
  49. package/dist/core/validators/index.d.ts +1 -1
  50. package/dist/core/validators/index.d.ts.map +1 -1
  51. package/dist/core/validators/passes-gate.d.ts +1 -15
  52. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  53. package/dist/core/validators/passes-gate.js +2 -127
  54. package/dist/core/validators/passes-gate.js.map +1 -1
  55. package/dist/failure/index.d.ts +0 -1
  56. package/dist/failure/index.d.ts.map +1 -1
  57. package/dist/failure/index.js +1 -7
  58. package/dist/failure/index.js.map +1 -1
  59. package/dist/gates/definitions.d.ts.map +1 -1
  60. package/dist/gates/definitions.js +0 -4
  61. package/dist/gates/definitions.js.map +1 -1
  62. package/dist/gates/performance.d.ts +7 -22
  63. package/dist/gates/performance.d.ts.map +1 -1
  64. package/dist/gates/performance.js +7 -89
  65. package/dist/gates/performance.js.map +1 -1
  66. package/dist/gates/types.d.ts +2 -8
  67. package/dist/gates/types.d.ts.map +1 -1
  68. package/dist/index.d.ts +6 -8
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4 -22
  71. package/dist/index.js.map +1 -1
  72. package/dist/knowledge/index.d.ts +0 -1
  73. package/dist/knowledge/index.d.ts.map +1 -1
  74. package/dist/knowledge/index.js +1 -3
  75. package/dist/knowledge/index.js.map +1 -1
  76. package/dist/monitoring/trace-analyzer.d.ts +25 -10
  77. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  78. package/dist/monitoring/trace-analyzer.js +147 -116
  79. package/dist/monitoring/trace-analyzer.js.map +1 -1
  80. package/dist/types/passes-gate.d.ts +0 -32
  81. package/dist/types/passes-gate.d.ts.map +1 -1
  82. package/package.json +1 -1
  83. package/src/__tests__/passes-gate-check.test.ts +20 -4
  84. package/src/__tests__/passes-gate.test.ts +5 -99
  85. package/src/__tests__/performance-gate.test.ts +3 -11
  86. package/src/__tests__/public-exports.test.ts +68 -16
  87. package/src/__tests__/public-type-surface.test.ts +359 -0
  88. package/src/__tests__/trace-analyzer.test.ts +52 -2
  89. package/src/cli/commands/CONTEXT.md +3 -2
  90. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +158 -0
  91. package/src/cli/commands/__tests__/passes-gate.test.ts +7 -3
  92. package/src/cli/commands/__tests__/performance.test.ts +5 -16
  93. package/src/cli/commands/__tests__/registry.test.ts +46 -3
  94. package/src/cli/commands/__tests__/report-extra.test.ts +0 -19
  95. package/src/cli/commands/__tests__/report-false-green.test.ts +46 -0
  96. package/src/cli/commands/__tests__/report.test.ts +0 -20
  97. package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +68 -0
  98. package/src/cli/commands/__tests__/status-extra.test.ts +63 -116
  99. package/src/cli/commands/__tests__/status.test.ts +66 -148
  100. package/src/cli/commands/definitions.ts +1 -32
  101. package/src/cli/commands/passes-gate.ts +0 -3
  102. package/src/cli/commands/performance.ts +13 -27
  103. package/src/cli/commands/report.ts +3 -61
  104. package/src/cli/commands/status.ts +5 -10
  105. package/src/context/CONTEXT.md +2 -3
  106. package/src/context/__tests__/token-budget.test.ts +1 -81
  107. package/src/context/index.ts +2 -7
  108. package/src/context/token-budget.ts +0 -73
  109. package/src/context/types.ts +0 -9
  110. package/src/core/CONTEXT.md +5 -6
  111. package/src/core/constraints/__tests__/collect-constraints.test.ts +66 -0
  112. package/src/core/constraints/checker.ts +33 -2
  113. package/src/core/constraints/git-evidence.ts +6 -3
  114. package/src/core/index.ts +0 -12
  115. package/src/core/spec/__tests__/validator.test.ts +24 -0
  116. package/src/core/spec/validator.ts +9 -10
  117. package/src/core/validators/__tests__/passes-gate.test.ts +2 -9
  118. package/src/core/validators/index.ts +0 -2
  119. package/src/core/validators/passes-gate.ts +3 -152
  120. package/src/failure/CONTEXT.md +8 -6
  121. package/src/failure/index.ts +0 -10
  122. package/src/gates/CONTEXT.md +3 -2
  123. package/src/gates/__tests__/performance.test.ts +6 -74
  124. package/src/gates/definitions.ts +0 -4
  125. package/src/gates/performance.ts +9 -111
  126. package/src/gates/types.ts +2 -8
  127. package/src/index.ts +1 -37
  128. package/src/knowledge/CONTEXT.md +1 -2
  129. package/src/knowledge/index.ts +0 -1
  130. package/src/monitoring/CONTEXT.md +2 -2
  131. package/src/monitoring/trace-analyzer.ts +160 -126
  132. package/src/types/passes-gate.ts +0 -43
  133. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +0 -1
  134. package/dist/cli/commands/analyze-sessions.d.ts +0 -20
  135. package/dist/cli/commands/analyze-sessions.d.ts.map +0 -1
  136. package/dist/cli/commands/analyze-sessions.js +0 -290
  137. package/dist/cli/commands/analyze-sessions.js.map +0 -1
  138. package/dist/cli/commands/update-user-model.d.ts +0 -30
  139. package/dist/cli/commands/update-user-model.d.ts.map +0 -1
  140. package/dist/cli/commands/update-user-model.js +0 -406
  141. package/dist/cli/commands/update-user-model.js.map +0 -1
  142. package/dist/cli/session-mining/corrections.d.ts +0 -17
  143. package/dist/cli/session-mining/corrections.d.ts.map +0 -1
  144. package/dist/cli/session-mining/corrections.js +0 -58
  145. package/dist/cli/session-mining/corrections.js.map +0 -1
  146. package/dist/cli/session-mining/index.d.ts +0 -13
  147. package/dist/cli/session-mining/index.d.ts.map +0 -1
  148. package/dist/cli/session-mining/index.js +0 -29
  149. package/dist/cli/session-mining/index.js.map +0 -1
  150. package/dist/cli/session-mining/text.d.ts +0 -31
  151. package/dist/cli/session-mining/text.d.ts.map +0 -1
  152. package/dist/cli/session-mining/text.js +0 -126
  153. package/dist/cli/session-mining/text.js.map +0 -1
  154. package/dist/cli/session-mining/transcript.d.ts +0 -34
  155. package/dist/cli/session-mining/transcript.d.ts.map +0 -1
  156. package/dist/cli/session-mining/transcript.js +0 -130
  157. package/dist/cli/session-mining/transcript.js.map +0 -1
  158. package/dist/context/compaction.d.ts +0 -57
  159. package/dist/context/compaction.d.ts.map +0 -1
  160. package/dist/context/compaction.js +0 -165
  161. package/dist/context/compaction.js.map +0 -1
  162. package/dist/core/session/clean-state.d.ts +0 -17
  163. package/dist/core/session/clean-state.d.ts.map +0 -1
  164. package/dist/core/session/clean-state.js +0 -192
  165. package/dist/core/session/clean-state.js.map +0 -1
  166. package/dist/core/session/index.d.ts +0 -6
  167. package/dist/core/session/index.d.ts.map +0 -1
  168. package/dist/core/session/index.js +0 -15
  169. package/dist/core/session/index.js.map +0 -1
  170. package/dist/core/session/startup.d.ts +0 -47
  171. package/dist/core/session/startup.d.ts.map +0 -1
  172. package/dist/core/session/startup.js +0 -263
  173. package/dist/core/session/startup.js.map +0 -1
  174. package/dist/failure/constraint-handler.d.ts +0 -83
  175. package/dist/failure/constraint-handler.d.ts.map +0 -1
  176. package/dist/failure/constraint-handler.js +0 -138
  177. package/dist/failure/constraint-handler.js.map +0 -1
  178. package/dist/knowledge/lifecycle-hooks.d.ts +0 -62
  179. package/dist/knowledge/lifecycle-hooks.d.ts.map +0 -1
  180. package/dist/knowledge/lifecycle-hooks.js +0 -143
  181. package/dist/knowledge/lifecycle-hooks.js.map +0 -1
  182. package/dist/types/session.d.ts +0 -113
  183. package/dist/types/session.d.ts.map +0 -1
  184. package/dist/types/session.js +0 -6
  185. package/dist/types/session.js.map +0 -1
  186. package/src/__tests__/clean-state-extra.test.ts +0 -301
  187. package/src/__tests__/clean-state.test.ts +0 -169
  188. package/src/__tests__/constraint-handler.test.ts +0 -132
  189. package/src/__tests__/passes-gate-extra.test.ts +0 -93
  190. package/src/__tests__/session-startup.test.ts +0 -334
  191. package/src/cli/commands/__tests__/analyze-sessions.test.ts +0 -184
  192. package/src/cli/commands/__tests__/update-user-model.test.ts +0 -268
  193. package/src/cli/commands/analyze-sessions.ts +0 -335
  194. package/src/cli/commands/update-user-model.ts +0 -476
  195. package/src/cli/session-mining/__tests__/transcript.test.ts +0 -118
  196. package/src/cli/session-mining/corrections.ts +0 -56
  197. package/src/cli/session-mining/index.ts +0 -13
  198. package/src/cli/session-mining/text.ts +0 -117
  199. package/src/cli/session-mining/transcript.ts +0 -125
  200. package/src/context/__tests__/compaction.test.ts +0 -169
  201. package/src/context/compaction.ts +0 -201
  202. package/src/core/session/__tests__/startup.test.ts +0 -281
  203. package/src/core/session/clean-state.ts +0 -178
  204. package/src/core/session/index.ts +0 -15
  205. package/src/core/session/startup.ts +0 -253
  206. package/src/failure/constraint-handler.ts +0 -171
  207. package/src/knowledge/__tests__/lifecycle-hooks.test.ts +0 -140
  208. package/src/knowledge/lifecycle-hooks.ts +0 -202
  209. package/src/types/session.ts +0 -132
@@ -1,281 +0,0 @@
1
- /**
2
- * SessionStartup 测试
3
- */
4
-
5
- import { SessionStartup } from '../startup';
6
- import { exec } from 'child_process';
7
- import * as fs from 'fs/promises';
8
-
9
- // Mock dependencies
10
- jest.mock('child_process', () => ({
11
- exec: jest.fn(),
12
- }));
13
-
14
- jest.mock('fs/promises', () => ({
15
- readFile: jest.fn(),
16
- access: jest.fn(),
17
- }));
18
-
19
- const mockExec = exec as unknown as jest.Mock;
20
- const mockFs = fs as jest.Mocked<typeof fs>;
21
-
22
- describe('SessionStartup', () => {
23
- let startup: SessionStartup;
24
-
25
- beforeEach(() => {
26
- jest.clearAllMocks();
27
- startup = new SessionStartup('/test/project', {
28
- required: ['pwd'],
29
- optional: [],
30
- });
31
- });
32
-
33
- describe('constructor', () => {
34
- it('should accept workDir and checkpoints', () => {
35
- expect(startup).toBeDefined();
36
- });
37
- });
38
-
39
- describe('run()', () => {
40
- it('should run required checkpoints', async () => {
41
- const result = await startup.run();
42
-
43
- expect(result.results.length).toBe(1);
44
- expect(result.results[0].type).toBe('pwd');
45
- });
46
-
47
- it('should return success when all required pass', async () => {
48
- const result = await startup.run();
49
-
50
- expect(result.success).toBe(true);
51
- });
52
-
53
- it('should run optional checkpoints', async () => {
54
- const s = new SessionStartup('/test', {
55
- required: ['pwd'],
56
- optional: ['git_status'],
57
- });
58
-
59
- mockExec.mockImplementation((cmd, opts, callback) => {
60
- callback(null, { stdout: '', stderr: '' });
61
- });
62
-
63
- const result = await s.run();
64
-
65
- expect(result.results.length).toBe(2);
66
- });
67
- });
68
-
69
- describe('checkpoints', () => {
70
- describe('pwd', () => {
71
- it('should return workDir', async () => {
72
- const s = new SessionStartup('/my/project', {
73
- required: ['pwd'],
74
- optional: [],
75
- });
76
-
77
- const result = await s.run();
78
-
79
- expect(result.results[0].success).toBe(true);
80
- expect(result.results[0].data).toBe('/my/project');
81
- });
82
- });
83
-
84
- describe('git_log', () => {
85
- it('should check git log', async () => {
86
- const s = new SessionStartup('/test', {
87
- required: ['git_log'],
88
- optional: [],
89
- });
90
-
91
- mockExec.mockImplementation((cmd, opts, callback) => {
92
- callback(null, { stdout: 'abc123 Initial commit', stderr: '' });
93
- });
94
-
95
- const result = await s.run();
96
-
97
- expect(result.results[0].success).toBe(true);
98
- });
99
- });
100
-
101
- describe('git_status', () => {
102
- it('should check git status', async () => {
103
- const s = new SessionStartup('/test', {
104
- required: ['git_status'],
105
- optional: [],
106
- });
107
-
108
- mockExec.mockImplementation((cmd, opts, callback) => {
109
- if (cmd.includes('git status')) {
110
- callback(null, { stdout: 'On branch main\nnothing to commit', stderr: '' });
111
- } else {
112
- callback(null, { stdout: '', stderr: '' });
113
- }
114
- });
115
-
116
- const result = await s.run();
117
-
118
- expect(result.results[0].success).toBe(true);
119
- });
120
- });
121
-
122
- describe('read_progress', () => {
123
- it('should read progress file', async () => {
124
- const s = new SessionStartup('/test', {
125
- required: ['read_progress'],
126
- optional: [],
127
- });
128
-
129
- mockFs.readFile.mockResolvedValueOnce('# Progress\n- Task 1 done');
130
-
131
- const result = await s.run();
132
-
133
- expect(result.results[0].success).toBe(true);
134
- });
135
-
136
- it('should return null for missing file', async () => {
137
- const s = new SessionStartup('/test', {
138
- required: ['read_progress'],
139
- optional: [],
140
- });
141
-
142
- mockFs.readFile.mockRejectedValueOnce(new Error('Not found'));
143
-
144
- const result = await s.run();
145
-
146
- // read_progress returns null on error, but doesn't fail
147
- expect(result.results[0].success).toBe(true);
148
- expect(result.results[0].data).toBeNull();
149
- });
150
- });
151
-
152
- describe('read_task_list', () => {
153
- it('should read task list', async () => {
154
- const s = new SessionStartup('/test', {
155
- required: ['read_task_list'],
156
- optional: [],
157
- });
158
-
159
- mockFs.access.mockResolvedValueOnce(undefined);
160
- mockFs.readFile.mockResolvedValueOnce(JSON.stringify({ tasks: [] }));
161
-
162
- const result = await s.run();
163
-
164
- expect(result.results[0].success).toBe(true);
165
- });
166
-
167
- it('should handle missing task list', async () => {
168
- const s = new SessionStartup('/test', {
169
- required: ['read_task_list'],
170
- optional: [],
171
- });
172
-
173
- mockFs.access.mockRejectedValueOnce(new Error('Not found'));
174
-
175
- const result = await s.run();
176
-
177
- // 文件不存在时返回失败
178
- expect(result.results[0].type).toBe('read_task_list');
179
- });
180
-
181
- it('should parse task list steps', async () => {
182
- const s = new SessionStartup('/test', {
183
- required: ['read_task_list'],
184
- optional: [],
185
- });
186
-
187
- mockFs.access.mockResolvedValueOnce(undefined);
188
- mockFs.readFile.mockResolvedValueOnce(JSON.stringify({
189
- tasks: [
190
- { id: 'task-1', steps: [{ status: 'done' }] },
191
- ],
192
- }));
193
-
194
- const result = await s.run();
195
-
196
- expect(result.results[0].success).toBe(true);
197
- });
198
- });
199
-
200
- describe('init_sh', () => {
201
- it('should run init.sh', async () => {
202
- const s = new SessionStartup('/test', {
203
- required: ['init_sh'],
204
- optional: [],
205
- });
206
-
207
- mockExec.mockImplementation((cmd, opts, callback) => {
208
- callback(null, { stdout: 'Init complete', stderr: '' });
209
- });
210
-
211
- const result = await s.run();
212
-
213
- // init_sh 成功或失败取决于 mock 设置
214
- expect(result.results[0].type).toBe('init_sh');
215
- });
216
- });
217
-
218
- describe('basic_verification', () => {
219
- it('should run basic verification', async () => {
220
- const s = new SessionStartup('/test', {
221
- required: ['basic_verification'],
222
- optional: [],
223
- });
224
-
225
- mockExec.mockImplementation((cmd, opts, callback) => {
226
- callback(null, { stdout: 'Tests passed', stderr: '' });
227
- });
228
-
229
- const result = await s.run();
230
-
231
- expect(result.results[0].type).toBe('basic_verification');
232
- });
233
- });
234
-
235
- describe('load_context', () => {
236
- it('should load context', async () => {
237
- const s = new SessionStartup('/test', {
238
- required: ['load_context'],
239
- optional: [],
240
- });
241
-
242
- mockFs.readFile.mockResolvedValueOnce('# Context\nSome content');
243
-
244
- const result = await s.run();
245
-
246
- expect(result.results[0].type).toBe('load_context');
247
- });
248
- });
249
-
250
- describe('unknown checkpoint', () => {
251
- it('should handle unknown checkpoint type', async () => {
252
- const s = new SessionStartup('/test', {
253
- required: ['unknown_checkpoint' as any],
254
- optional: [],
255
- });
256
-
257
- const result = await s.run();
258
-
259
- expect(result.results[0].type).toBe('unknown_checkpoint');
260
- });
261
- });
262
-
263
- describe('multiple checkpoints', () => {
264
- it('should run all required and optional checkpoints', async () => {
265
- const s = new SessionStartup('/test', {
266
- required: ['pwd', 'git_log', 'git_status'],
267
- optional: ['read_progress'],
268
- });
269
-
270
- mockExec.mockImplementation((cmd, opts, callback) => {
271
- callback(null, { stdout: 'output', stderr: '' });
272
- });
273
- mockFs.readFile.mockResolvedValue('# Progress');
274
-
275
- const result = await s.run();
276
-
277
- expect(result.results.length).toBe(4);
278
- });
279
- });
280
- });
281
- });
@@ -1,178 +0,0 @@
1
- /**
2
- * CleanStateManager - Session 结束状态管理
3
- *
4
- * 确保 Agent 结束时必须"打扫干净",不留烂代码。
5
- */
6
-
7
- import { execAsync } from '../../utils/exec';
8
- import { promisify } from 'util';
9
- import * as fs from 'fs/promises';
10
- import * as path from 'path';
11
- import * as yaml from 'js-yaml';
12
- import type {
13
- CleanStateConfig,
14
- CleanStateResult,
15
- DetectedBug,
16
- SessionInfo,
17
- } from '../../types/session';
18
-
19
-
20
- const DEFAULT_CONFIG: Required<CleanStateConfig> = {
21
- enabled: true,
22
- autoCommit: true,
23
- detectBugs: true,
24
- updateProgress: true,
25
- commitMessageTemplate: '[{taskId}] {description} - {status}',
26
- };
27
-
28
- const BUG_PATTERNS = [
29
- { pattern: /console\.(error|warn)\(/, severity: 'low' as const, type: 'logic' as const },
30
- { pattern: /FIXME|HACK/i, severity: 'medium' as const, type: 'logic' as const },
31
- { pattern: /eval\s*\(/, severity: 'high' as const, type: 'security' as const },
32
- { pattern: /dangerouslySetInnerHTML/, severity: 'medium' as const, type: 'security' as const },
33
- ];
34
-
35
- export class CleanStateManager {
36
- private config: Required<CleanStateConfig>;
37
-
38
- constructor(config: Partial<CleanStateConfig> = {}) {
39
- this.config = { ...DEFAULT_CONFIG, ...config };
40
- }
41
-
42
- async onSessionEnd(workDir: string, sessionInfo: SessionInfo): Promise<CleanStateResult> {
43
- const result: CleanStateResult = {
44
- isClean: true,
45
- hasUncommittedChanges: false,
46
- progressUpdated: false,
47
- errors: []
48
- };
49
-
50
- if (!this.config.enabled) return result;
51
-
52
- try {
53
- // 1. 检查未提交变更
54
- result.hasUncommittedChanges = await this.hasUncommittedChanges(workDir);
55
-
56
- // 2. 自动提交(如果配置开启)
57
- if (result.hasUncommittedChanges && this.config.autoCommit) {
58
- result.committedFiles = await this.autoCommit(workDir, sessionInfo);
59
- }
60
-
61
- // 3. 检测 bug(如果配置开启)
62
- if (this.config.detectBugs) {
63
- result.bugs = await this.detectBugs(workDir);
64
- if (result.bugs.length > 0) result.isClean = false;
65
- }
66
-
67
- // 4. 更新 progress(如果配置开启)
68
- if (this.config.updateProgress) {
69
- result.progressUpdated = await this.updateProgress(workDir, sessionInfo, result);
70
- }
71
- } catch (e: any) {
72
- result.errors?.push(e.message);
73
- result.isClean = false;
74
- }
75
-
76
- return result;
77
- }
78
-
79
- private async hasUncommittedChanges(workDir: string): Promise<boolean> {
80
- try {
81
- const { stdout } = await execAsync('git status --porcelain', { cwd: workDir });
82
- return stdout.trim().length > 0;
83
- } catch {
84
- return false;
85
- }
86
- }
87
-
88
- private async autoCommit(workDir: string, sessionInfo: SessionInfo): Promise<string[]> {
89
- try {
90
- const { stdout } = await execAsync('git status --porcelain', { cwd: workDir });
91
- const files = stdout.split('\n').filter(Boolean).map(l => l.substring(3).trim());
92
-
93
- await execAsync('git add -A', { cwd: workDir });
94
-
95
- // 生成提交消息
96
- const msg = sessionInfo.task
97
- ? `[${sessionInfo.task.id}] ${sessionInfo.task.name || sessionInfo.task.id}`
98
- : `[session] ${sessionInfo.workflowId}`;
99
-
100
- // 使用 execFile 避免 shell 注入
101
- const { execFile: execFileCb } = await import('child_process');
102
- const execFileAsync = promisify(execFileCb);
103
- await execFileAsync('git', ['commit', '-m', msg], { cwd: workDir });
104
- return files;
105
- } catch {
106
- return [];
107
- }
108
- }
109
-
110
- private async detectBugs(workDir: string): Promise<DetectedBug[]> {
111
- const bugs: DetectedBug[] = [];
112
- try {
113
- // 获取最近变更的文件
114
- const { stdout } = await execAsync('git diff --name-only HEAD~1', { cwd: workDir });
115
-
116
- for (const file of stdout.split('\n').filter(Boolean)) {
117
- // 只检查代码文件
118
- if (!['.ts', '.tsx', '.js', '.jsx', '.py', '.go'].some(e => file.endsWith(e))) continue;
119
-
120
- try {
121
- const content = await fs.readFile(path.join(workDir, file), 'utf-8');
122
- const lines = content.split('\n');
123
-
124
- lines.forEach((line, i) => {
125
- for (const { pattern, severity, type } of BUG_PATTERNS) {
126
- if (pattern.test(line)) {
127
- bugs.push({
128
- file,
129
- line: i + 1,
130
- message: `Detected pattern: ${pattern.source}`,
131
- severity,
132
- type
133
- });
134
- }
135
- }
136
- });
137
- } catch {
138
- // 文件读取失败,跳过
139
- }
140
- }
141
- } catch {}
142
- return bugs;
143
- }
144
-
145
- private async updateProgress(workDir: string, sessionInfo: SessionInfo, cleanResult: CleanStateResult): Promise<boolean> {
146
- const progressPath = path.join(workDir, '.harness', 'progress.yml');
147
- try {
148
- await fs.mkdir(path.dirname(progressPath), { recursive: true });
149
-
150
- // 读取现有 progress 或创建新的
151
- let data: any = { sessions: [], last_updated: new Date().toISOString() };
152
- try {
153
- data = yaml.load(await fs.readFile(progressPath, 'utf-8')) as any || data;
154
- } catch {}
155
-
156
- // 添加本次 session 记录
157
- data.sessions.push({
158
- id: sessionInfo.sessionId,
159
- workflow: sessionInfo.workflowId,
160
- task: sessionInfo.task?.id,
161
- timestamp: new Date().toISOString(),
162
- clean: cleanResult.isClean,
163
- bugs: cleanResult.bugs?.length || 0,
164
- committed: cleanResult.committedFiles?.length || 0,
165
- });
166
-
167
- data.last_updated = new Date().toISOString();
168
-
169
- await fs.writeFile(progressPath, yaml.dump(data));
170
- return true;
171
- } catch {
172
- return false;
173
- }
174
- }
175
- }
176
-
177
- export const createCleanStateManager = (config?: Partial<CleanStateConfig>) =>
178
- new CleanStateManager(config);
@@ -1,15 +0,0 @@
1
- /**
2
- * Session 模块导出
3
- */
4
-
5
- export {
6
- SessionStartup,
7
- createSessionStartup,
8
- DEFAULT_CODE_CHECKPOINTS,
9
- MINIMAL_CHECKPOINTS,
10
- } from './startup';
11
-
12
- export {
13
- CleanStateManager,
14
- createCleanStateManager,
15
- } from './clean-state';