@zoebuildsai/trace 1.5.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 (130) hide show
  1. package/.gitignore +115 -0
  2. package/.trace/progress.json +22 -0
  3. package/README.md +466 -0
  4. package/RELEASE-NOTES-1.5.0.md +410 -0
  5. package/STATUS.md +245 -0
  6. package/dist/auto-commit.d.ts +66 -0
  7. package/dist/auto-commit.d.ts.map +1 -0
  8. package/dist/auto-commit.js +180 -0
  9. package/dist/auto-commit.js.map +1 -0
  10. package/dist/cli.d.ts +7 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +246 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands.d.ts +46 -0
  15. package/dist/commands.d.ts.map +1 -0
  16. package/dist/commands.js +256 -0
  17. package/dist/commands.js.map +1 -0
  18. package/dist/diff.d.ts +23 -0
  19. package/dist/diff.d.ts.map +1 -0
  20. package/dist/diff.js +106 -0
  21. package/dist/diff.js.map +1 -0
  22. package/dist/github.d.ts.map +1 -0
  23. package/dist/github.js.map +1 -0
  24. package/dist/index-cache.d.ts +35 -0
  25. package/dist/index-cache.d.ts.map +1 -0
  26. package/dist/index-cache.js +114 -0
  27. package/dist/index-cache.js.map +1 -0
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +25 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/storage.d.ts +45 -0
  33. package/dist/storage.d.ts.map +1 -0
  34. package/dist/storage.js +151 -0
  35. package/dist/storage.js.map +1 -0
  36. package/dist/sync.d.ts +60 -0
  37. package/dist/sync.js +184 -0
  38. package/dist/tags.d.ts +85 -0
  39. package/dist/tags.d.ts.map +1 -0
  40. package/dist/tags.js +219 -0
  41. package/dist/tags.js.map +1 -0
  42. package/dist/types.d.ts +102 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +6 -0
  45. package/dist/types.js.map +1 -0
  46. package/docs/.nojekyll +0 -0
  47. package/docs/README.md +73 -0
  48. package/docs/_config.yml +2 -0
  49. package/docs/index.html +960 -0
  50. package/docs-website/package.json +20 -0
  51. package/jest.config.js +21 -0
  52. package/package.json +50 -0
  53. package/scripts/init.ts +290 -0
  54. package/src/agent-audit.ts +270 -0
  55. package/src/agent-checkout.ts +227 -0
  56. package/src/agent-coordination.ts +318 -0
  57. package/src/async-queue.ts +203 -0
  58. package/src/auto-branching.ts +279 -0
  59. package/src/auto-commit.ts +166 -0
  60. package/src/cherry-pick.ts +252 -0
  61. package/src/chunked-upload.ts +224 -0
  62. package/src/cli-v2.ts +335 -0
  63. package/src/cli.ts +318 -0
  64. package/src/cliff-detection.ts +232 -0
  65. package/src/commands.ts +267 -0
  66. package/src/commit-hash-system.ts +351 -0
  67. package/src/compression.ts +176 -0
  68. package/src/conflict-resolution-ui.ts +277 -0
  69. package/src/conflict-visualization.ts +238 -0
  70. package/src/diff-formatter.ts +184 -0
  71. package/src/diff.ts +124 -0
  72. package/src/distributed-coordination.ts +273 -0
  73. package/src/git-interop.ts +316 -0
  74. package/src/index-cache.ts +88 -0
  75. package/src/index.ts +38 -0
  76. package/src/merge-engine.ts +143 -0
  77. package/src/message-search.ts +370 -0
  78. package/src/performance-monitoring.ts +236 -0
  79. package/src/rebase.ts +327 -0
  80. package/src/rollback.ts +215 -0
  81. package/src/semantic-grouping.ts +245 -0
  82. package/src/stage-area.ts +324 -0
  83. package/src/stash.ts +278 -0
  84. package/src/storage.ts +131 -0
  85. package/src/sync.ts +205 -0
  86. package/src/tags.ts +244 -0
  87. package/src/types.ts +119 -0
  88. package/src/webhooks.ts +119 -0
  89. package/src/workspace-isolation.ts +298 -0
  90. package/tests/auto-commit.test.ts +308 -0
  91. package/tests/checkout.test.ts +136 -0
  92. package/tests/commit.test.ts +118 -0
  93. package/tests/diff.test.ts +191 -0
  94. package/tests/github.test.ts +94 -0
  95. package/tests/integration.test.ts +267 -0
  96. package/tests/log.test.ts +125 -0
  97. package/tests/phase2-integration.test.ts +370 -0
  98. package/tests/storage.test.ts +167 -0
  99. package/tests/tags.test.ts +477 -0
  100. package/tests/types.test.ts +75 -0
  101. package/tests/v1.1/agent-audit.test.ts +472 -0
  102. package/tests/v1.1/agent-coordination.test.ts +308 -0
  103. package/tests/v1.1/async-queue.test.ts +253 -0
  104. package/tests/v1.1/comprehensive.test.ts +521 -0
  105. package/tests/v1.1/diff-formatter.test.ts +238 -0
  106. package/tests/v1.1/integration.test.ts +389 -0
  107. package/tests/v1.1/onboarding.test.ts +365 -0
  108. package/tests/v1.1/rollback.test.ts +370 -0
  109. package/tests/v1.1/semantic-grouping.test.ts +230 -0
  110. package/tests/v1.2/chunked-upload.test.ts +301 -0
  111. package/tests/v1.2/cliff-detection.test.ts +272 -0
  112. package/tests/v1.2/commit-hash-system.test.ts +288 -0
  113. package/tests/v1.2/compression.test.ts +220 -0
  114. package/tests/v1.2/conflict-visualization.test.ts +263 -0
  115. package/tests/v1.2/distributed.test.ts +261 -0
  116. package/tests/v1.2/performance-monitoring.test.ts +328 -0
  117. package/tests/v1.3/auto-branching.test.ts +270 -0
  118. package/tests/v1.3/message-search.test.ts +264 -0
  119. package/tests/v1.3/stage-area.test.ts +330 -0
  120. package/tests/v1.3/stash-rebase-cherry-pick.test.ts +361 -0
  121. package/tests/v1.4/cli.test.ts +171 -0
  122. package/tests/v1.4/conflict-resolution-advanced.test.ts +429 -0
  123. package/tests/v1.4/conflict-resolution-ui.test.ts +286 -0
  124. package/tests/v1.4/workspace-isolation-advanced.test.ts +382 -0
  125. package/tests/v1.4/workspace-isolation.test.ts +268 -0
  126. package/tests/v1.5/agent-coordination.real.test.ts +401 -0
  127. package/tests/v1.5/cli-v2.test.ts +354 -0
  128. package/tests/v1.5/git-interop.real.test.ts +358 -0
  129. package/tests/v1.5/integration-testing.real.test.ts +440 -0
  130. package/tsconfig.json +26 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Rollback Engine for Trace
3
+ * Reset to previous commit state (equivalent to git reset)
4
+ */
5
+
6
+ export interface RollbackPoint {
7
+ commitId: string;
8
+ agentId: string;
9
+ timestamp: number;
10
+ message: string;
11
+ fileStates: Map<string, string>; // file -> content hash
12
+ }
13
+
14
+ export interface RollbackResult {
15
+ success: boolean;
16
+ fromCommit: string;
17
+ toCommit: string;
18
+ filesRestored: string[];
19
+ filesDiscarded: string[];
20
+ timestamp: number;
21
+ }
22
+
23
+ export class RollbackEngine {
24
+ private history: RollbackPoint[] = [];
25
+ private fileSnapshots: Map<string, Map<string, string>> = new Map(); // commitId -> fileStates
26
+
27
+ /**
28
+ * Create rollback point (snapshot)
29
+ */
30
+ createSnapshot(
31
+ commitId: string,
32
+ agentId: string,
33
+ message: string,
34
+ files: Map<string, string>
35
+ ): void {
36
+ const snapshot: RollbackPoint = {
37
+ commitId,
38
+ agentId,
39
+ timestamp: Date.now(),
40
+ message,
41
+ fileStates: new Map(files),
42
+ };
43
+
44
+ this.history.push(snapshot);
45
+ this.fileSnapshots.set(commitId, new Map(files));
46
+ }
47
+
48
+ /**
49
+ * Rollback to specific commit
50
+ */
51
+ rollbackToCommit(targetCommitId: string): RollbackResult {
52
+ const targetIndex = this.history.findIndex(h => h.commitId === targetCommitId);
53
+
54
+ if (targetIndex === -1) {
55
+ throw new Error(`Commit ${targetCommitId} not found`);
56
+ }
57
+
58
+ const currentCommit = this.history[this.history.length - 1];
59
+ const targetCommit = this.history[targetIndex];
60
+
61
+ const currentFiles = this.fileSnapshots.get(currentCommit.commitId) || new Map();
62
+ const targetFiles = this.fileSnapshots.get(targetCommitId) || new Map();
63
+
64
+ // Find what needs to be restored/discarded
65
+ const filesRestored: string[] = [];
66
+ const filesDiscarded: string[] = [];
67
+
68
+ // Files that exist in target but not in current (restore)
69
+ for (const file of targetFiles.keys()) {
70
+ if (!currentFiles.has(file)) {
71
+ filesRestored.push(file);
72
+ }
73
+ }
74
+
75
+ // Files that exist in current but not in target (discard)
76
+ for (const file of currentFiles.keys()) {
77
+ if (!targetFiles.has(file)) {
78
+ filesDiscarded.push(file);
79
+ }
80
+ }
81
+
82
+ // Truncate history to target
83
+ this.history = this.history.slice(0, targetIndex + 1);
84
+
85
+ return {
86
+ success: true,
87
+ fromCommit: currentCommit.commitId,
88
+ toCommit: targetCommitId,
89
+ filesRestored,
90
+ filesDiscarded,
91
+ timestamp: Date.now(),
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Rollback N commits back
97
+ */
98
+ rollbackN(n: number): RollbackResult {
99
+ if (n < 1) throw new Error('N must be >= 1');
100
+ if (n > this.history.length) throw new Error('Cannot rollback more commits than exist');
101
+
102
+ const targetIndex = this.history.length - n - 1;
103
+ const targetCommit = this.history[targetIndex];
104
+
105
+ return this.rollbackToCommit(targetCommit.commitId);
106
+ }
107
+
108
+ /**
109
+ * Rollback to commit before agent's changes
110
+ */
111
+ rollbackBeforeAgent(agentId: string): RollbackResult {
112
+ const targetIndex = this.history.findLastIndex(h => h.agentId !== agentId);
113
+
114
+ if (targetIndex === -1) {
115
+ throw new Error(`No commits found before agent ${agentId}`);
116
+ }
117
+
118
+ const targetCommit = this.history[targetIndex];
119
+ return this.rollbackToCommit(targetCommit.commitId);
120
+ }
121
+
122
+ /**
123
+ * Get available rollback points
124
+ */
125
+ getRollbackPoints(limit: number = 10): RollbackPoint[] {
126
+ return this.history.slice(-limit).reverse();
127
+ }
128
+
129
+ /**
130
+ * Check if rollback is safe (no conflicts)
131
+ */
132
+ isRollbackSafe(targetCommitId: string): boolean {
133
+ const targetIndex = this.history.findIndex(h => h.commitId === targetCommitId);
134
+
135
+ if (targetIndex === -1) return false;
136
+
137
+ // Safe if no uncommitted changes would be lost
138
+ // (in real implementation, would check working directory)
139
+ return true;
140
+ }
141
+
142
+ /**
143
+ * Get diff between two commits
144
+ */
145
+ getDiffBetween(fromCommitId: string, toCommitId: string): Map<string, string> {
146
+ const fromFiles = this.fileSnapshots.get(fromCommitId) || new Map();
147
+ const toFiles = this.fileSnapshots.get(toCommitId) || new Map();
148
+
149
+ const diff = new Map<string, string>();
150
+
151
+ for (const [file, toHash] of toFiles) {
152
+ const fromHash = fromFiles.get(file);
153
+ if (fromHash !== toHash) {
154
+ diff.set(file, `${fromHash || 'NEW'} -> ${toHash}`);
155
+ }
156
+ }
157
+
158
+ for (const [file] of fromFiles) {
159
+ if (!toFiles.has(file)) {
160
+ diff.set(file, `DELETED`);
161
+ }
162
+ }
163
+
164
+ return diff;
165
+ }
166
+
167
+ /**
168
+ * Get history
169
+ */
170
+ getHistory(): RollbackPoint[] {
171
+ return [...this.history];
172
+ }
173
+
174
+ /**
175
+ * Get commit details
176
+ */
177
+ getCommit(commitId: string): RollbackPoint | undefined {
178
+ return this.history.find(h => h.commitId === commitId);
179
+ }
180
+
181
+ /**
182
+ * List all commits by agent
183
+ */
184
+ getCommitsByAgent(agentId: string): RollbackPoint[] {
185
+ return this.history.filter(h => h.agentId === agentId);
186
+ }
187
+
188
+ /**
189
+ * Verify rollback integrity
190
+ */
191
+ verifyIntegrity(): { valid: boolean; errors: string[] } {
192
+ const errors: string[] = [];
193
+
194
+ // Check history is chronological
195
+ for (let i = 0; i < this.history.length - 1; i++) {
196
+ if (this.history[i].timestamp > this.history[i + 1].timestamp) {
197
+ errors.push(`Timeline violation at commit ${i}`);
198
+ }
199
+ }
200
+
201
+ // Check snapshots match history
202
+ for (const commit of this.history) {
203
+ if (!this.fileSnapshots.has(commit.commitId)) {
204
+ errors.push(`Missing snapshot for commit ${commit.commitId}`);
205
+ }
206
+ }
207
+
208
+ return {
209
+ valid: errors.length === 0,
210
+ errors,
211
+ };
212
+ }
213
+ }
214
+
215
+ export default RollbackEngine;
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Semantic Grouping for Trace
3
+ * Auto-categorizes changes by type (feature, fix, docs, test, refactor, perf, security)
4
+ */
5
+
6
+ export type ChangeType = 'feature' | 'fix' | 'docs' | 'test' | 'refactor' | 'perf' | 'security' | 'chore' | 'unknown';
7
+
8
+ export interface GroupedChange {
9
+ type: ChangeType;
10
+ files: string[];
11
+ count: number;
12
+ summary: string;
13
+ }
14
+
15
+ export interface SemanticGroup {
16
+ feature: GroupedChange | null;
17
+ fix: GroupedChange | null;
18
+ docs: GroupedChange | null;
19
+ test: GroupedChange | null;
20
+ refactor: GroupedChange | null;
21
+ perf: GroupedChange | null;
22
+ security: GroupedChange | null;
23
+ chore: GroupedChange | null;
24
+ unknown: GroupedChange | null;
25
+ }
26
+
27
+ export class SemanticGrouping {
28
+ /**
29
+ * Detect change type from file path and content
30
+ */
31
+ static detectType(filePath: string, commitMessage?: string): ChangeType {
32
+ // Pattern matching for file paths
33
+ if (this.matchesPattern(filePath, /\.(test|spec)\.ts$|__tests__|\.test\./)) {
34
+ return 'test';
35
+ }
36
+
37
+ if (this.matchesPattern(filePath, /README|\.md$|docs\//)) {
38
+ return 'docs';
39
+ }
40
+
41
+ if (this.matchesPattern(filePath, /security|auth|crypt|encrypt|hash|sign/)) {
42
+ return 'security';
43
+ }
44
+
45
+ if (this.matchesPattern(filePath, /perf|bench|cache|optim|compress/)) {
46
+ return 'perf';
47
+ }
48
+
49
+ if (this.matchesPattern(filePath, /refactor|cleanup|reorganize|rename/)) {
50
+ return 'refactor';
51
+ }
52
+
53
+ // Commit message patterns
54
+ if (commitMessage) {
55
+ const msg = commitMessage.toLowerCase();
56
+
57
+ if (msg.startsWith('fix:') || msg.startsWith('bugfix')) {
58
+ return 'fix';
59
+ }
60
+
61
+ if (msg.startsWith('feat:') || msg.startsWith('feature')) {
62
+ return 'feature';
63
+ }
64
+
65
+ if (msg.startsWith('docs:')) {
66
+ return 'docs';
67
+ }
68
+
69
+ if (msg.startsWith('test:')) {
70
+ return 'test';
71
+ }
72
+
73
+ if (msg.startsWith('perf:')) {
74
+ return 'perf';
75
+ }
76
+
77
+ if (msg.startsWith('security:') || msg.includes('security')) {
78
+ return 'security';
79
+ }
80
+
81
+ if (msg.startsWith('refactor:')) {
82
+ return 'refactor';
83
+ }
84
+
85
+ if (msg.startsWith('chore:')) {
86
+ return 'chore';
87
+ }
88
+ }
89
+
90
+ // Default heuristics
91
+ if (this.matchesPattern(filePath, /src\/.*\.ts$/)) {
92
+ return 'feature'; // src/ files are usually features
93
+ }
94
+
95
+ return 'unknown';
96
+ }
97
+
98
+ /**
99
+ * Group files by change type
100
+ */
101
+ static group(files: string[], commitMessage?: string): SemanticGroup {
102
+ const groups: Record<ChangeType, string[]> = {
103
+ feature: [],
104
+ fix: [],
105
+ docs: [],
106
+ test: [],
107
+ refactor: [],
108
+ perf: [],
109
+ security: [],
110
+ chore: [],
111
+ unknown: [],
112
+ };
113
+
114
+ for (const file of files) {
115
+ const type = this.detectType(file, commitMessage);
116
+ groups[type].push(file);
117
+ }
118
+
119
+ // Build result
120
+ const result: SemanticGroup = {
121
+ feature: groups.feature.length > 0 ? {
122
+ type: 'feature',
123
+ files: groups.feature,
124
+ count: groups.feature.length,
125
+ summary: `Added ${groups.feature.length} new feature(s)`,
126
+ } : null,
127
+ fix: groups.fix.length > 0 ? {
128
+ type: 'fix',
129
+ files: groups.fix,
130
+ count: groups.fix.length,
131
+ summary: `Fixed ${groups.fix.length} issue(s)`,
132
+ } : null,
133
+ docs: groups.docs.length > 0 ? {
134
+ type: 'docs',
135
+ files: groups.docs,
136
+ count: groups.docs.length,
137
+ summary: `Updated ${groups.docs.length} doc(s)`,
138
+ } : null,
139
+ test: groups.test.length > 0 ? {
140
+ type: 'test',
141
+ files: groups.test,
142
+ count: groups.test.length,
143
+ summary: `Added ${groups.test.length} test(s)`,
144
+ } : null,
145
+ refactor: groups.refactor.length > 0 ? {
146
+ type: 'refactor',
147
+ files: groups.refactor,
148
+ count: groups.refactor.length,
149
+ summary: `Refactored ${groups.refactor.length} file(s)`,
150
+ } : null,
151
+ perf: groups.perf.length > 0 ? {
152
+ type: 'perf',
153
+ files: groups.perf,
154
+ count: groups.perf.length,
155
+ summary: `Optimized ${groups.perf.length} file(s)`,
156
+ } : null,
157
+ security: groups.security.length > 0 ? {
158
+ type: 'security',
159
+ files: groups.security,
160
+ count: groups.security.length,
161
+ summary: `Enhanced security in ${groups.security.length} file(s)`,
162
+ } : null,
163
+ chore: groups.chore.length > 0 ? {
164
+ type: 'chore',
165
+ files: groups.chore,
166
+ count: groups.chore.length,
167
+ summary: `Maintenance: ${groups.chore.length} file(s)`,
168
+ } : null,
169
+ unknown: groups.unknown.length > 0 ? {
170
+ type: 'unknown',
171
+ files: groups.unknown,
172
+ count: groups.unknown.length,
173
+ summary: `Uncategorized: ${groups.unknown.length} file(s)`,
174
+ } : null,
175
+ };
176
+
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Generate semantic commit message from grouped changes
182
+ */
183
+ static generateMessage(group: SemanticGroup): string {
184
+ const parts: string[] = [];
185
+
186
+ if (group.feature) {
187
+ parts.push(`โœจ ${group.feature.summary}`);
188
+ }
189
+ if (group.fix) {
190
+ parts.push(`๐Ÿ› ${group.fix.summary}`);
191
+ }
192
+ if (group.security) {
193
+ parts.push(`๐Ÿ”’ ${group.security.summary}`);
194
+ }
195
+ if (group.perf) {
196
+ parts.push(`โšก ${group.perf.summary}`);
197
+ }
198
+ if (group.test) {
199
+ parts.push(`โœ… ${group.test.summary}`);
200
+ }
201
+ if (group.docs) {
202
+ parts.push(`๐Ÿ“– ${group.docs.summary}`);
203
+ }
204
+ if (group.refactor) {
205
+ parts.push(`โ™ป๏ธ ${group.refactor.summary}`);
206
+ }
207
+ if (group.chore) {
208
+ parts.push(`๐Ÿงน ${group.chore.summary}`);
209
+ }
210
+
211
+ return parts.join('\n');
212
+ }
213
+
214
+ /**
215
+ * Helper: match pattern against path
216
+ */
217
+ private static matchesPattern(path: string, pattern: RegExp): boolean {
218
+ return pattern.test(path);
219
+ }
220
+
221
+ /**
222
+ * Check if files represent a "micro" commit (minimal changes)
223
+ */
224
+ static isMicroCommit(files: string[], lineChanges?: number): boolean {
225
+ // Micro = few files, few lines
226
+ const fileCount = files.length;
227
+ const lines = lineChanges || 50;
228
+
229
+ return fileCount <= 5 && lines <= 100;
230
+ }
231
+
232
+ /**
233
+ * Get dominant change type (most files)
234
+ */
235
+ static getDominantType(group: SemanticGroup): ChangeType {
236
+ const types: (ChangeType | null)[] = Object.values(group);
237
+ const withCounts = types
238
+ .filter((t): t is ChangeType => t !== null)
239
+ .sort((a, b) => (group[b]?.count || 0) - (group[a]?.count || 0));
240
+
241
+ return withCounts[0] || 'unknown';
242
+ }
243
+ }
244
+
245
+ export default SemanticGrouping;