ai-dev-analytics 2.0.0 → 2.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 (182) hide show
  1. package/README.en.md +34 -48
  2. package/README.md +52 -458
  3. package/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/dist/cli/commands/doctor.js +64 -9
  5. package/dist/cli/commands/doctor.js.map +1 -1
  6. package/dist/cli/commands/init.d.ts.map +1 -1
  7. package/dist/cli/commands/init.js +4 -0
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/cli/commands/memory.d.ts.map +1 -1
  10. package/dist/cli/commands/memory.js +3 -13
  11. package/dist/cli/commands/memory.js.map +1 -1
  12. package/dist/cli/commands/rules.d.ts +0 -10
  13. package/dist/cli/commands/rules.d.ts.map +1 -1
  14. package/dist/cli/commands/rules.js +7 -33
  15. package/dist/cli/commands/rules.js.map +1 -1
  16. package/dist/cli/commands/skills.d.ts +0 -5
  17. package/dist/cli/commands/skills.d.ts.map +1 -1
  18. package/dist/cli/commands/skills.js +4 -24
  19. package/dist/cli/commands/skills.js.map +1 -1
  20. package/dist/cli/commands/sync.d.ts.map +1 -1
  21. package/dist/cli/commands/sync.js +6 -15
  22. package/dist/cli/commands/sync.js.map +1 -1
  23. package/dist/cli/index.js +19 -90
  24. package/dist/cli/index.js.map +1 -1
  25. package/dist/{schemas/run-json.d.ts → internal/runtime/schema.d.ts} +4 -18
  26. package/dist/internal/runtime/schema.d.ts.map +1 -0
  27. package/dist/{schemas/run-json.js → internal/runtime/schema.js} +5 -9
  28. package/dist/internal/runtime/schema.js.map +1 -0
  29. package/dist/{utils/run-data.d.ts → internal/runtime/state.d.ts} +7 -8
  30. package/dist/internal/runtime/state.d.ts.map +1 -0
  31. package/dist/{utils/run-data.js → internal/runtime/state.js} +11 -12
  32. package/dist/internal/runtime/state.js.map +1 -0
  33. package/dist/{utils → internal/runtime}/summary.d.ts +2 -2
  34. package/dist/internal/runtime/summary.d.ts.map +1 -0
  35. package/dist/{utils → internal/runtime}/summary.js +4 -4
  36. package/dist/internal/runtime/summary.js.map +1 -0
  37. package/dist/internal/runtime/tokens.d.ts.map +1 -0
  38. package/dist/internal/runtime/tokens.js.map +1 -0
  39. package/dist/mcp/server.js +9 -8
  40. package/dist/mcp/server.js.map +1 -1
  41. package/dist/schemas/aida-project.d.ts +4 -0
  42. package/dist/schemas/aida-project.d.ts.map +1 -1
  43. package/dist/schemas/rules.d.ts +15 -0
  44. package/dist/schemas/rules.d.ts.map +1 -0
  45. package/dist/schemas/rules.js +5 -0
  46. package/dist/schemas/rules.js.map +1 -0
  47. package/dist/services/project-build.d.ts +44 -0
  48. package/dist/services/project-build.d.ts.map +1 -0
  49. package/dist/services/project-build.js +32 -0
  50. package/dist/services/project-build.js.map +1 -0
  51. package/dist/services/project-health.d.ts +14 -0
  52. package/dist/services/project-health.d.ts.map +1 -0
  53. package/dist/services/project-health.js +19 -0
  54. package/dist/services/project-health.js.map +1 -0
  55. package/dist/services/security-audit.d.ts +59 -0
  56. package/dist/services/security-audit.d.ts.map +1 -0
  57. package/dist/services/security-audit.js +638 -0
  58. package/dist/services/security-audit.js.map +1 -0
  59. package/dist/utils/ai-build.d.ts +0 -5
  60. package/dist/utils/ai-build.d.ts.map +1 -1
  61. package/dist/utils/ai-build.js +2 -35
  62. package/dist/utils/ai-build.js.map +1 -1
  63. package/dist/utils/guide.d.ts +3 -3
  64. package/dist/utils/guide.js +6 -6
  65. package/dist/utils/import.d.ts.map +1 -1
  66. package/dist/utils/import.js +4 -15
  67. package/dist/utils/import.js.map +1 -1
  68. package/dist/utils/paths.d.ts +0 -10
  69. package/dist/utils/paths.d.ts.map +1 -1
  70. package/dist/utils/paths.js +1 -17
  71. package/dist/utils/paths.js.map +1 -1
  72. package/dist/utils/project-health.d.ts +1 -0
  73. package/dist/utils/project-health.d.ts.map +1 -1
  74. package/dist/utils/project-health.js +26 -3
  75. package/dist/utils/project-health.js.map +1 -1
  76. package/dist/utils/rules.d.ts +11 -1
  77. package/dist/utils/rules.d.ts.map +1 -1
  78. package/dist/utils/rules.js +27 -1
  79. package/dist/utils/rules.js.map +1 -1
  80. package/dist/utils/skills.d.ts +6 -10
  81. package/dist/utils/skills.d.ts.map +1 -1
  82. package/dist/utils/skills.js +23 -67
  83. package/dist/utils/skills.js.map +1 -1
  84. package/package.json +12 -14
  85. package/dist/cli/commands/build.d.ts +0 -2
  86. package/dist/cli/commands/build.d.ts.map +0 -1
  87. package/dist/cli/commands/build.js +0 -55
  88. package/dist/cli/commands/build.js.map +0 -1
  89. package/dist/cli/commands/dashboard.d.ts +0 -2
  90. package/dist/cli/commands/dashboard.d.ts.map +0 -1
  91. package/dist/cli/commands/dashboard.js +0 -70
  92. package/dist/cli/commands/dashboard.js.map +0 -1
  93. package/dist/cli/commands/import.d.ts +0 -2
  94. package/dist/cli/commands/import.d.ts.map +0 -1
  95. package/dist/cli/commands/import.js +0 -71
  96. package/dist/cli/commands/import.js.map +0 -1
  97. package/dist/cli/commands/log.d.ts +0 -2
  98. package/dist/cli/commands/log.d.ts.map +0 -1
  99. package/dist/cli/commands/log.js +0 -440
  100. package/dist/cli/commands/log.js.map +0 -1
  101. package/dist/cli/commands/merge-data.d.ts +0 -20
  102. package/dist/cli/commands/merge-data.d.ts.map +0 -1
  103. package/dist/cli/commands/merge-data.js +0 -634
  104. package/dist/cli/commands/merge-data.js.map +0 -1
  105. package/dist/cli/commands/merge.d.ts +0 -2
  106. package/dist/cli/commands/merge.d.ts.map +0 -1
  107. package/dist/cli/commands/merge.js +0 -82
  108. package/dist/cli/commands/merge.js.map +0 -1
  109. package/dist/cli/commands/migrate-dir.d.ts +0 -6
  110. package/dist/cli/commands/migrate-dir.d.ts.map +0 -1
  111. package/dist/cli/commands/migrate-dir.js +0 -125
  112. package/dist/cli/commands/migrate-dir.js.map +0 -1
  113. package/dist/cli/commands/migrate-legacy.d.ts +0 -2
  114. package/dist/cli/commands/migrate-legacy.d.ts.map +0 -1
  115. package/dist/cli/commands/migrate-legacy.js +0 -141
  116. package/dist/cli/commands/migrate-legacy.js.map +0 -1
  117. package/dist/cli/commands/migrate.d.ts +0 -2
  118. package/dist/cli/commands/migrate.d.ts.map +0 -1
  119. package/dist/cli/commands/migrate.js +0 -303
  120. package/dist/cli/commands/migrate.js.map +0 -1
  121. package/dist/cli/commands/reindex.d.ts +0 -14
  122. package/dist/cli/commands/reindex.d.ts.map +0 -1
  123. package/dist/cli/commands/reindex.js +0 -69
  124. package/dist/cli/commands/reindex.js.map +0 -1
  125. package/dist/cli/commands/report.d.ts +0 -2
  126. package/dist/cli/commands/report.d.ts.map +0 -1
  127. package/dist/cli/commands/report.js +0 -224
  128. package/dist/cli/commands/report.js.map +0 -1
  129. package/dist/cli/commands/start.d.ts +0 -2
  130. package/dist/cli/commands/start.d.ts.map +0 -1
  131. package/dist/cli/commands/start.js +0 -155
  132. package/dist/cli/commands/start.js.map +0 -1
  133. package/dist/cli/commands/status.d.ts +0 -2
  134. package/dist/cli/commands/status.d.ts.map +0 -1
  135. package/dist/cli/commands/status.js +0 -70
  136. package/dist/cli/commands/status.js.map +0 -1
  137. package/dist/cli/commands/update.d.ts +0 -2
  138. package/dist/cli/commands/update.d.ts.map +0 -1
  139. package/dist/cli/commands/update.js +0 -74
  140. package/dist/cli/commands/update.js.map +0 -1
  141. package/dist/schemas/run-json.d.ts.map +0 -1
  142. package/dist/schemas/run-json.js.map +0 -1
  143. package/dist/server/api.d.ts +0 -30
  144. package/dist/server/api.d.ts.map +0 -1
  145. package/dist/server/api.js +0 -232
  146. package/dist/server/api.js.map +0 -1
  147. package/dist/server/index.d.ts +0 -2
  148. package/dist/server/index.d.ts.map +0 -1
  149. package/dist/server/index.js +0 -228
  150. package/dist/server/index.js.map +0 -1
  151. package/dist/utils/run-data.d.ts.map +0 -1
  152. package/dist/utils/run-data.js.map +0 -1
  153. package/dist/utils/summary.d.ts.map +0 -1
  154. package/dist/utils/summary.js.map +0 -1
  155. package/dist/utils/tokens.d.ts.map +0 -1
  156. package/dist/utils/tokens.js.map +0 -1
  157. package/src/assets/skills/audit.md +0 -98
  158. package/src/assets/skills/bug-fixer.md +0 -43
  159. package/src/assets/skills/code-generator.md +0 -71
  160. package/src/assets/skills/commit-code.md +0 -67
  161. package/src/assets/skills/dashboard-generator.md +0 -65
  162. package/src/assets/skills/dev-flower.md +0 -85
  163. package/src/assets/skills/deviation-recorder.md +0 -83
  164. package/src/assets/skills/docx-to-markdown.md +0 -69
  165. package/src/assets/skills/mcp-reviewer.md +0 -38
  166. package/src/assets/skills/requirement-analyzer.md +0 -103
  167. package/src/assets/skills/rules-evolver.md +0 -47
  168. package/src/assets/skills/self-reviewer.md +0 -49
  169. package/src/assets/skills/task-splitter.md +0 -60
  170. package/src/assets/skills/workflow-orchestrator.md +0 -209
  171. package/src/assets/templates/demo-run.json +0 -910
  172. package/src/assets/templates/run.json +0 -63
  173. package/src/dashboard/assets/index-B8QcPcg7.css +0 -1
  174. package/src/dashboard/assets/index-DcAl6lhS.js +0 -111
  175. package/src/dashboard/demo/overview.json +0 -71
  176. package/src/dashboard/demo/run.en.json +0 -1169
  177. package/src/dashboard/demo/run.json +0 -2667
  178. package/src/dashboard/demo/run.zh.json +0 -1169
  179. package/src/dashboard/demo/runs.json +0 -19
  180. package/src/dashboard/index.html +0 -13
  181. /package/dist/{utils → internal/runtime}/tokens.d.ts +0 -0
  182. /package/dist/{utils → internal/runtime}/tokens.js +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge-data.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/merge-data.ts"],"names":[],"mappings":"AAuDA,KAAK,WAAW,GAAG,QAAQ,GAAG,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC;AAElE,UAAU,gBAAgB;IACxB,MAAM,EAAE,WAAW,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AA+iBD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,gBAAgB,CAAA;IAC7B,cAAc,EAAE,gBAAgB,CAAA;IAChC,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,YAAY,EAAE,gBAAgB,CAAA;IAC9B,IAAI,EAAE,gBAAgB,CAAA;IACtB,YAAY,EAAE,OAAO,CAAA;IACrB,kBAAkB,EAAE,OAAO,CAAA;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAqEvE;AAcD,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CA2C/C"}
@@ -1,634 +0,0 @@
1
- import { readdirSync, statSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { green, red, yellow } from '../../utils/display.js';
4
- import { fileExists, readText, extractConflictSections, writeJson, } from '../../utils/fs.js';
5
- import { aidaDir, configPath, legacyModuleMemoryPath, memoryIndexPath, moduleMemoriesDir, moduleMemoryPath, runsDir, } from '../../utils/paths.js';
6
- import { buildMemoryViews, loadMemoryIndex, loadModuleMemory, normalizeModuleKey, saveModuleMemory, } from '../../utils/memory.js';
7
- import { buildIndex } from './reindex.js';
8
- import { normalizeRunData, recalcMetrics, resolveCurrentTaskId } from '../../utils/run-data.js';
9
- function uniqueStrings(values) {
10
- const result = [];
11
- const seen = new Set();
12
- for (const value of values) {
13
- const normalized = `${value || ''}`.trim();
14
- if (!normalized || seen.has(normalized))
15
- continue;
16
- seen.add(normalized);
17
- result.push(normalized);
18
- }
19
- return result;
20
- }
21
- function latestIso(a, b) {
22
- if (!a)
23
- return b || '';
24
- if (!b)
25
- return a;
26
- return new Date(a).getTime() >= new Date(b).getTime() ? a : b;
27
- }
28
- function pickLatestString(current, incoming, currentUpdatedAt, incomingUpdatedAt) {
29
- const currentValue = (current || '').trim();
30
- const incomingValue = (incoming || '').trim();
31
- if (!currentValue)
32
- return incomingValue;
33
- if (!incomingValue)
34
- return currentValue;
35
- return latestIso(currentUpdatedAt, incomingUpdatedAt) === incomingUpdatedAt ? incomingValue : currentValue;
36
- }
37
- function parseConflictJsonObject(raw) {
38
- const trimmed = raw.trim();
39
- if (!trimmed || trimmed === 'null')
40
- return null;
41
- try {
42
- return JSON.parse(trimmed);
43
- }
44
- catch {
45
- return null;
46
- }
47
- }
48
- function hasConflict(filePath) {
49
- if (!fileExists(filePath))
50
- return false;
51
- const raw = readText(filePath);
52
- return raw.includes('<<<<<<<') || raw.includes('>>>>>>>');
53
- }
54
- function mergeByKey(items, keyOf, mergeItem) {
55
- const map = new Map();
56
- for (const item of items) {
57
- const key = keyOf(item);
58
- const existing = map.get(key);
59
- map.set(key, existing ? mergeItem(existing, item) : item);
60
- }
61
- return [...map.values()];
62
- }
63
- function mergeIndexEntry(current, incoming) {
64
- const useIncoming = latestIso(current.updatedAt, incoming.updatedAt) === incoming.updatedAt;
65
- return {
66
- key: current.key || incoming.key,
67
- title: pickLatestString(current.title, incoming.title, current.updatedAt, incoming.updatedAt),
68
- summary: pickLatestString(current.summary, incoming.summary, current.updatedAt, incoming.updatedAt),
69
- keywords: uniqueStrings([...current.keywords, ...incoming.keywords]),
70
- paths: uniqueStrings([...current.paths, ...incoming.paths]),
71
- tickets: uniqueStrings([...(current.tickets || []), ...(incoming.tickets || [])]),
72
- updatedAt: useIncoming ? incoming.updatedAt : current.updatedAt,
73
- };
74
- }
75
- function mergeMemoryIndex(ours, theirs) {
76
- const mergedModules = mergeByKey([...((ours?.items || ours?.modules || [])), ...((theirs?.items || theirs?.modules || []))], (item) => normalizeModuleKey(item.key), (current, incoming) => mergeIndexEntry({ ...current, key: normalizeModuleKey(current.key) }, { ...incoming, key: normalizeModuleKey(incoming.key) })).sort((a, b) => a.key.localeCompare(b.key));
77
- return {
78
- schemaVersion: '2.0',
79
- updatedAt: latestIso(ours?.updatedAt, theirs?.updatedAt),
80
- items: mergedModules,
81
- };
82
- }
83
- function hydrateModuleMemoryFromIndex(projectRoot, entry) {
84
- const moduleKey = normalizeModuleKey(entry.key);
85
- if (!moduleKey)
86
- return;
87
- const existing = loadModuleMemory(projectRoot, moduleKey);
88
- if (existing) {
89
- saveModuleMemory(projectRoot, {
90
- ...existing,
91
- moduleKey,
92
- title: existing.title || entry.title || moduleKey,
93
- summary: existing.summary || entry.summary || '',
94
- keywords: uniqueStrings([...(existing.keywords || []), moduleKey, entry.title, ...(entry.keywords || [])]),
95
- entryFiles: uniqueStrings([...(existing.entryFiles || []), ...(entry.paths || [])]),
96
- relatedPaths: uniqueStrings([...(existing.relatedPaths || []), ...(entry.paths || [])]),
97
- updatedAt: latestIso(existing.updatedAt, entry.updatedAt) || existing.updatedAt || entry.updatedAt || new Date().toISOString(),
98
- });
99
- return;
100
- }
101
- if (fileExists(moduleMemoryPath(projectRoot, moduleKey)))
102
- return;
103
- if (fileExists(legacyModuleMemoryPath(projectRoot, moduleKey)))
104
- return;
105
- saveModuleMemory(projectRoot, {
106
- schemaVersion: '2.0',
107
- moduleKey,
108
- title: entry.title || moduleKey,
109
- summary: entry.summary || '',
110
- keywords: uniqueStrings([moduleKey, entry.title, ...(entry.keywords || [])]),
111
- entryFiles: uniqueStrings(entry.paths || []),
112
- relatedPaths: uniqueStrings(entry.paths || []),
113
- dataFlow: [],
114
- decisions: [],
115
- constraints: [],
116
- pitfalls: [],
117
- relatedRules: [],
118
- tickets: [],
119
- changes: [],
120
- updatedAt: entry.updatedAt || new Date().toISOString(),
121
- });
122
- }
123
- function hydrateMissingModuleMemoriesFromIndex(projectRoot) {
124
- const index = loadMemoryIndex(projectRoot);
125
- for (const entry of index.items) {
126
- hydrateModuleMemoryFromIndex(projectRoot, entry);
127
- }
128
- }
129
- function mergeModuleMemoryRecord(current, incoming) {
130
- const useIncoming = latestIso(current.updatedAt, incoming.updatedAt) === incoming.updatedAt;
131
- const mergedTickets = mergeByKey([...current.tickets, ...incoming.tickets], (item) => `${item.ticket || ''}|${item.branch || ''}`, (left, right) => ({
132
- ticket: left.ticket || right.ticket,
133
- branch: left.branch || right.branch,
134
- summary: pickLatestString(left.summary, right.summary, left.updatedAt, right.updatedAt),
135
- updatedAt: latestIso(left.updatedAt, right.updatedAt),
136
- }));
137
- const mergedChanges = mergeByKey([...(current.changes || []), ...(incoming.changes || [])], (item) => item.ticket || item.branch
138
- ? `${item.ticket || ''}|${item.branch || ''}`
139
- : `${item.title || ''}|${item.summary}`, (left, right) => ({
140
- ticket: left.ticket || right.ticket,
141
- branch: left.branch || right.branch,
142
- title: pickLatestString(left.title, right.title, left.updatedAt, right.updatedAt) || undefined,
143
- summary: pickLatestString(left.summary, right.summary, left.updatedAt, right.updatedAt),
144
- updatedAt: latestIso(left.updatedAt, right.updatedAt),
145
- }));
146
- return {
147
- schemaVersion: '2.0',
148
- moduleKey: current.moduleKey || incoming.moduleKey,
149
- title: pickLatestString(current.title, incoming.title, current.updatedAt, incoming.updatedAt),
150
- summary: pickLatestString(current.summary, incoming.summary, current.updatedAt, incoming.updatedAt),
151
- keywords: uniqueStrings([...current.keywords, ...incoming.keywords]),
152
- entryFiles: uniqueStrings([...current.entryFiles, ...incoming.entryFiles]),
153
- relatedPaths: uniqueStrings([...current.relatedPaths, ...incoming.relatedPaths]),
154
- dataFlow: uniqueStrings([...current.dataFlow, ...incoming.dataFlow]),
155
- decisions: uniqueStrings([...current.decisions, ...incoming.decisions]),
156
- constraints: uniqueStrings([...current.constraints, ...incoming.constraints]),
157
- pitfalls: uniqueStrings([...current.pitfalls, ...incoming.pitfalls]),
158
- relatedRules: uniqueStrings([...current.relatedRules, ...incoming.relatedRules]),
159
- tickets: mergedTickets,
160
- changes: mergedChanges,
161
- updatedAt: useIncoming ? incoming.updatedAt : current.updatedAt,
162
- };
163
- }
164
- function mergeRunContextRecord(current, incoming) {
165
- const useIncoming = latestIso(current.updatedAt, incoming.updatedAt) === incoming.updatedAt;
166
- return {
167
- branch: current.branch || incoming.branch,
168
- ticket: pickLatestString(current.ticket, incoming.ticket, current.updatedAt, incoming.updatedAt) || undefined,
169
- title: pickLatestString(current.title, incoming.title, current.updatedAt, incoming.updatedAt),
170
- summary: pickLatestString(current.summary, incoming.summary, current.updatedAt, incoming.updatedAt),
171
- currentPhase: pickLatestString(current.currentPhase, incoming.currentPhase, current.updatedAt, incoming.updatedAt),
172
- modules: uniqueStrings([...current.modules, ...incoming.modules]),
173
- completed: uniqueStrings([...current.completed, ...incoming.completed]),
174
- inProgress: uniqueStrings([...current.inProgress, ...incoming.inProgress]),
175
- next: uniqueStrings([...current.next, ...incoming.next]),
176
- decisions: uniqueStrings([...current.decisions, ...incoming.decisions]),
177
- constraints: uniqueStrings([...current.constraints, ...incoming.constraints]),
178
- keyFiles: uniqueStrings([...current.keyFiles, ...incoming.keyFiles]),
179
- risks: uniqueStrings([...current.risks, ...incoming.risks]),
180
- updatedAt: useIncoming ? incoming.updatedAt : current.updatedAt,
181
- };
182
- }
183
- function mergeRequirementModule(current, incoming) {
184
- return {
185
- id: current.id || incoming.id,
186
- name: pickLatestString(current.name, incoming.name),
187
- description: pickLatestString(current.description, incoming.description),
188
- assignee: incoming.assignee || current.assignee,
189
- };
190
- }
191
- function mergeRequirementPhase(current, incoming) {
192
- return {
193
- phase: current.phase || incoming.phase,
194
- file: current.file || incoming.file,
195
- title: pickLatestString(current.title, incoming.title),
196
- confirmedAt: latestIso(current.confirmedAt || undefined, incoming.confirmedAt || undefined) || null,
197
- };
198
- }
199
- function mergeDeveloper(current, incoming) {
200
- return {
201
- name: current.name || incoming.name,
202
- modules: uniqueStrings([...current.modules, ...incoming.modules]),
203
- tasks: Math.max(current.tasks, incoming.tasks),
204
- completedTasks: Math.max(current.completedTasks, incoming.completedTasks),
205
- bugs: Math.max(current.bugs, incoming.bugs),
206
- deviations: Math.max(current.deviations, incoming.deviations),
207
- linesAdded: Math.max(current.linesAdded, incoming.linesAdded),
208
- linesRemoved: Math.max(current.linesRemoved, incoming.linesRemoved),
209
- firstPassRate: Math.max(current.firstPassRate, incoming.firstPassRate),
210
- actualWorkSeconds: Math.max(current.actualWorkSeconds, incoming.actualWorkSeconds),
211
- totalTokens: Math.max(current.totalTokens, incoming.totalTokens),
212
- };
213
- }
214
- function mergeRequirementRecord(current, incoming) {
215
- const developers = mergeByKey([...current.developers, ...incoming.developers], (item) => item.name, mergeDeveloper).sort((a, b) => a.name.localeCompare(b.name));
216
- const modules = mergeByKey([...current.modules, ...incoming.modules], (item) => item.id || item.name, mergeRequirementModule);
217
- const prdPhases = mergeByKey([...current.prdPhases, ...incoming.prdPhases], (item) => `${item.phase}|${item.file}`, mergeRequirementPhase);
218
- const highlights = mergeByKey([...current.highlights, ...incoming.highlights], (item) => `${item.content}|${item.createdAt}`, (left) => left);
219
- return {
220
- branch: current.branch || incoming.branch,
221
- title: pickLatestString(current.title, incoming.title, current.updatedAt, incoming.updatedAt),
222
- summary: pickLatestString(current.summary, incoming.summary, current.updatedAt, incoming.updatedAt),
223
- prdPhases,
224
- modules,
225
- highlights,
226
- developers,
227
- totals: {
228
- tasks: developers.reduce((sum, item) => sum + item.tasks, 0),
229
- completedTasks: developers.reduce((sum, item) => sum + item.completedTasks, 0),
230
- bugs: developers.reduce((sum, item) => sum + item.bugs, 0),
231
- deviations: developers.reduce((sum, item) => sum + item.deviations, 0),
232
- linesAdded: developers.reduce((sum, item) => sum + item.linesAdded, 0),
233
- linesRemoved: developers.reduce((sum, item) => sum + item.linesRemoved, 0),
234
- totalTokens: developers.reduce((sum, item) => sum + item.totalTokens, 0),
235
- },
236
- createdAt: current.createdAt || incoming.createdAt,
237
- updatedAt: latestIso(current.updatedAt, incoming.updatedAt),
238
- };
239
- }
240
- const taskStatusRank = {
241
- pending: 0,
242
- 'in-progress': 1,
243
- done: 2,
244
- };
245
- function mergeTask(current, incoming) {
246
- const currentRank = taskStatusRank[current.status];
247
- const incomingRank = taskStatusRank[incoming.status];
248
- const preferred = incomingRank >= currentRank ? incoming : current;
249
- const other = preferred === incoming ? current : incoming;
250
- return {
251
- ...preferred,
252
- title: pickLatestString(current.title, incoming.title),
253
- stageName: pickLatestString(current.stageName, incoming.stageName),
254
- prdPhase: pickLatestString(current.prdPhase, incoming.prdPhase),
255
- acceptance: pickLatestString(current.acceptance, incoming.acceptance) || undefined,
256
- createdAt: current.createdAt || incoming.createdAt,
257
- startedAt: latestIso(current.startedAt, incoming.startedAt) || current.startedAt || incoming.startedAt,
258
- completedAt: latestIso(current.completedAt || undefined, incoming.completedAt || undefined) || preferred.completedAt || other.completedAt || null,
259
- status: currentRank === incomingRank ? preferred.status : (incomingRank > currentRank ? incoming.status : current.status),
260
- };
261
- }
262
- function mergeBug(current, incoming) {
263
- const status = current.status === 'fixed' || incoming.status === 'fixed' ? 'fixed' : 'open';
264
- const severityOrder = { low: 0, medium: 1, high: 2, critical: 3 };
265
- return {
266
- ...current,
267
- ...incoming,
268
- title: pickLatestString(current.title, incoming.title),
269
- severity: severityOrder[current.severity] >= severityOrder[incoming.severity] ? current.severity : incoming.severity,
270
- source: current.source || incoming.source,
271
- status,
272
- files: uniqueStrings([...current.files, ...incoming.files]),
273
- fix: pickLatestString(current.fix || undefined, incoming.fix || undefined) || null,
274
- taskId: incoming.taskId || current.taskId || null,
275
- reportedAt: current.reportedAt || incoming.reportedAt,
276
- fixedAt: latestIso(current.fixedAt || undefined, incoming.fixedAt || undefined) || null,
277
- };
278
- }
279
- function mergeDeviation(current, incoming) {
280
- return {
281
- ...current,
282
- ...incoming,
283
- title: pickLatestString(current.title, incoming.title),
284
- aiOutput: pickLatestString(current.aiOutput, incoming.aiOutput) || undefined,
285
- expectedOutput: pickLatestString(current.expectedOutput, incoming.expectedOutput) || undefined,
286
- files: uniqueStrings([...current.files, ...incoming.files]),
287
- ruleSedimented: incoming.ruleSedimented ?? current.ruleSedimented,
288
- detectedAt: current.detectedAt || incoming.detectedAt,
289
- fixedAt: latestIso(current.fixedAt || undefined, incoming.fixedAt || undefined) || null,
290
- };
291
- }
292
- function mergeReview(current, incoming) {
293
- const preferred = latestIso(current.reviewedAt, incoming.reviewedAt) === incoming.reviewedAt ? incoming : current;
294
- return {
295
- ...preferred,
296
- taskId: incoming.taskId || current.taskId || null,
297
- scope: pickLatestString(current.scope, incoming.scope),
298
- result: preferred.result,
299
- issueCount: Math.max(current.issueCount, incoming.issueCount),
300
- issues: uniqueStrings([...(current.issues || []), ...(incoming.issues || [])]),
301
- reviewedAt: preferred.reviewedAt,
302
- };
303
- }
304
- function mergeRuleItem(current, incoming) {
305
- const preferred = latestIso(current.sedimentedAt || undefined, incoming.sedimentedAt || undefined) === incoming.sedimentedAt ? incoming : current;
306
- return {
307
- ...preferred,
308
- content: pickLatestString(current.content, incoming.content),
309
- category: pickLatestString(current.category, incoming.category) || undefined,
310
- sourceDeviation: incoming.sourceDeviation || current.sourceDeviation || null,
311
- sedimentedAt: latestIso(current.sedimentedAt || undefined, incoming.sedimentedAt || undefined) || null,
312
- file: pickLatestString(current.file, incoming.file),
313
- status: incoming.status || current.status,
314
- };
315
- }
316
- function mergeFileItem(current, incoming) {
317
- const changeType = current.changeType === incoming.changeType
318
- ? current.changeType
319
- : (current.changeType === 'deleted' || incoming.changeType === 'deleted' ? 'deleted' : 'modified');
320
- return {
321
- path: current.path || incoming.path,
322
- changeType,
323
- linesAdded: Math.max(current.linesAdded, incoming.linesAdded),
324
- linesRemoved: Math.max(current.linesRemoved, incoming.linesRemoved),
325
- changeCount: Math.max(current.changeCount, incoming.changeCount),
326
- lastModified: latestIso(current.lastModified, incoming.lastModified) || undefined,
327
- };
328
- }
329
- function mergeWorkflow(current, incoming) {
330
- const statusRank = {
331
- pending: 0,
332
- in_progress: 1,
333
- completed: 2,
334
- failed: 3,
335
- };
336
- const preferred = statusRank[incoming.status] >= statusRank[current.status] ? incoming : current;
337
- return {
338
- ...preferred,
339
- stage: pickLatestString(current.stage, incoming.stage),
340
- prdPhase: pickLatestString(current.prdPhase, incoming.prdPhase) || undefined,
341
- startTime: current.startTime || incoming.startTime,
342
- endTime: latestIso(current.endTime, incoming.endTime) || undefined,
343
- };
344
- }
345
- function recalcRunSummary(data) {
346
- data.summary.totalTasks = data.tasks.length;
347
- data.summary.completedTasks = data.tasks.filter((item) => item.status === 'done').length;
348
- data.summary.bugCount = data.bugs.length;
349
- data.summary.deviationCount = data.deviations.length;
350
- data.summary.reviewCount = data.reviews.length;
351
- data.summary.reviewPassCount = data.reviews.filter((item) => item.result === 'pass').length;
352
- data.summary.reviewFailCount = data.reviews.filter((item) => item.result === 'fail').length;
353
- data.summary.rulesSedimented = data.rules.filter((item) => item.status !== 'pending').length;
354
- data.summary.prdPhaseCount = uniqueStrings(data.tasks.map((item) => item.prdPhase)).length;
355
- data.summary.filesChanged = data.files.length;
356
- data.summary.linesAdded = data.files.reduce((sum, item) => sum + (item.linesAdded || 0), 0);
357
- data.summary.linesRemoved = data.files.reduce((sum, item) => sum + (item.linesRemoved || 0), 0);
358
- }
359
- function mergeRunDataRecord(currentRaw, incomingRaw) {
360
- const current = normalizeRunData(currentRaw);
361
- const incoming = normalizeRunData(incomingRaw);
362
- const merged = normalizeRunData({
363
- ...current,
364
- ...incoming,
365
- meta: {
366
- ...current.meta,
367
- ...incoming.meta,
368
- schemaVersion: incoming.meta.schemaVersion || current.meta.schemaVersion,
369
- branch: current.meta.branch || incoming.meta.branch,
370
- developer: current.meta.developer || incoming.meta.developer,
371
- project: pickLatestString(current.meta.project, incoming.meta.project),
372
- aiModel: pickLatestString(current.meta.aiModel, incoming.meta.aiModel),
373
- aiTool: pickLatestString(current.meta.aiTool, incoming.meta.aiTool),
374
- startTime: current.meta.startTime || incoming.meta.startTime,
375
- endTime: latestIso(current.meta.endTime, incoming.meta.endTime) || undefined,
376
- status: pickLatestString(current.meta.status, incoming.meta.status),
377
- prdPhases: uniqueStrings([...(current.meta.prdPhases || []), ...(incoming.meta.prdPhases || [])]),
378
- },
379
- summary: { ...current.summary, ...incoming.summary },
380
- metrics: { ...current.metrics, ...incoming.metrics },
381
- context: {
382
- ...current.context,
383
- ...incoming.context,
384
- currentStage: pickLatestString(current.context.currentStage, incoming.context.currentStage) || undefined,
385
- currentPrdPhase: pickLatestString(current.context.currentPrdPhase, incoming.context.currentPrdPhase) || undefined,
386
- currentTaskId: undefined,
387
- lastUpdated: latestIso(current.context.lastUpdated, incoming.context.lastUpdated) || undefined,
388
- },
389
- tasks: mergeByKey([...current.tasks, ...incoming.tasks], (item) => item.taskId, mergeTask),
390
- bugs: mergeByKey([...current.bugs, ...incoming.bugs], (item) => item.bugId, mergeBug),
391
- deviations: mergeByKey([...current.deviations, ...incoming.deviations], (item) => item.deviationId, mergeDeviation),
392
- reviews: mergeByKey([...current.reviews, ...incoming.reviews], (item) => item.reviewId, mergeReview),
393
- rules: mergeByKey([...current.rules, ...incoming.rules], (item) => item.ruleId, mergeRuleItem),
394
- files: mergeByKey([...current.files, ...incoming.files], (item) => item.path, mergeFileItem),
395
- timeline: mergeByKey([...current.timeline, ...incoming.timeline], (item) => `${item.type}|${item.title}|${item.timestamp}`, (left) => left)
396
- .sort((a, b) => a.timestamp.localeCompare(b.timestamp)),
397
- workflow: mergeByKey([...current.workflow, ...incoming.workflow], (item) => `${item.stage}|${item.prdPhase || ''}`, mergeWorkflow),
398
- events: mergeByKey([...current.events, ...incoming.events], (item) => `${item.type}|${item.time}|${JSON.stringify(item.data)}`, (left) => left)
399
- .sort((a, b) => a.time.localeCompare(b.time)),
400
- cost: {
401
- ...current.cost,
402
- ...incoming.cost,
403
- totalTokens: Math.max(current.cost.totalTokens || 0, incoming.cost.totalTokens || 0),
404
- estimatedManualHours: Math.max(current.cost.estimatedManualHours || 0, incoming.cost.estimatedManualHours || 0),
405
- actualHours: Math.max(current.cost.actualHours || 0, incoming.cost.actualHours || 0),
406
- tokenBreakdown: mergeByKey([...(current.cost.tokenBreakdown || []), ...(incoming.cost.tokenBreakdown || [])], (item) => item.stage, (left, right) => ({ stage: left.stage || right.stage, tokens: Math.max(left.tokens, right.tokens) })),
407
- tokenDetail: current.cost.tokenDetail || incoming.cost.tokenDetail
408
- ? {
409
- inputTokens: Math.max(current.cost.tokenDetail?.inputTokens || 0, incoming.cost.tokenDetail?.inputTokens || 0),
410
- outputTokens: Math.max(current.cost.tokenDetail?.outputTokens || 0, incoming.cost.tokenDetail?.outputTokens || 0),
411
- cacheCreationTokens: Math.max(current.cost.tokenDetail?.cacheCreationTokens || 0, incoming.cost.tokenDetail?.cacheCreationTokens || 0),
412
- cacheReadTokens: Math.max(current.cost.tokenDetail?.cacheReadTokens || 0, incoming.cost.tokenDetail?.cacheReadTokens || 0),
413
- }
414
- : undefined,
415
- },
416
- highlights: mergeByKey([...current.highlights, ...incoming.highlights], (item) => `${item.content}|${item.createdAt}`, (left) => left),
417
- });
418
- recalcRunSummary(merged);
419
- merged.context.currentTaskId = resolveCurrentTaskId(merged.tasks);
420
- const activeTask = merged.tasks.find((item) => item.taskId === merged.context.currentTaskId);
421
- merged.context.currentStage = activeTask?.stageName;
422
- merged.context.currentPrdPhase = activeTask?.prdPhase;
423
- recalcMetrics(merged);
424
- return merged;
425
- }
426
- function mergeConflictFile(filePath, mergeObjects) {
427
- if (!fileExists(filePath))
428
- return 'missing';
429
- const raw = readText(filePath);
430
- if (!raw.includes('<<<<<<<') && !raw.includes('>>>>>>>'))
431
- return 'no-conflict';
432
- const sections = extractConflictSections(raw);
433
- if (!sections)
434
- return 'error';
435
- const ours = parseConflictJsonObject(sections.ours);
436
- const theirs = parseConflictJsonObject(sections.theirs);
437
- const merged = mergeObjects(ours, theirs);
438
- writeJson(filePath, merged);
439
- return 'merged';
440
- }
441
- function walkJsonTargets(rootDir, targetName) {
442
- if (!fileExists(rootDir))
443
- return [];
444
- const results = [];
445
- const stack = [rootDir];
446
- while (stack.length > 0) {
447
- const current = stack.pop();
448
- for (const name of readdirSync(current)) {
449
- const fullPath = resolve(current, name);
450
- const stat = statSync(fullPath);
451
- if (stat.isDirectory())
452
- stack.push(fullPath);
453
- else if (name === targetName)
454
- results.push(fullPath);
455
- }
456
- }
457
- return results.sort();
458
- }
459
- function walkMemoryModuleFiles(rootDir) {
460
- if (!fileExists(rootDir))
461
- return [];
462
- const results = [];
463
- const stack = [rootDir];
464
- while (stack.length > 0) {
465
- const current = stack.pop();
466
- for (const name of readdirSync(current)) {
467
- const fullPath = resolve(current, name);
468
- const stat = statSync(fullPath);
469
- if (stat.isDirectory())
470
- stack.push(fullPath);
471
- else if (name.endsWith('.json'))
472
- results.push(fullPath);
473
- }
474
- }
475
- return results.sort();
476
- }
477
- function mergeFixedFile(filePath, merger, counters) {
478
- const status = mergeConflictFile(filePath, merger);
479
- if (status === 'merged')
480
- counters.merged++;
481
- else if (status === 'missing')
482
- counters.missing++;
483
- else if (status === 'error')
484
- counters.errors++;
485
- }
486
- function mergeModuleFiles(files, merger) {
487
- const counters = { status: 'no-conflict', merged: 0, missing: 0, errors: 0 };
488
- for (const filePath of files) {
489
- if (!hasConflict(filePath))
490
- continue;
491
- const status = mergeConflictFile(filePath, (ours, theirs) => merger(ours, theirs));
492
- if (status === 'merged')
493
- counters.merged++;
494
- else if (status === 'error')
495
- counters.errors++;
496
- }
497
- counters.status = counters.errors > 0 ? 'error' : counters.merged > 0 ? 'merged' : 'no-conflict';
498
- return counters;
499
- }
500
- function mergeRunFiles(files) {
501
- const counters = { status: 'no-conflict', merged: 0, missing: 0, errors: 0 };
502
- for (const filePath of files) {
503
- if (!hasConflict(filePath))
504
- continue;
505
- const status = mergeConflictFile(filePath, (ours, theirs) => mergeRunDataRecord(ours, theirs));
506
- if (status === 'merged')
507
- counters.merged++;
508
- else if (status === 'error')
509
- counters.errors++;
510
- }
511
- counters.status = counters.errors > 0 ? 'error' : counters.merged > 0 ? 'merged' : 'no-conflict';
512
- return counters;
513
- }
514
- export function mergeAidaJsonData(projectRoot) {
515
- const summary = {
516
- memoryIndex: { status: 'missing', merged: 0, missing: 1, errors: 0 },
517
- moduleMemories: { status: 'missing', merged: 0, missing: 0, errors: 0 },
518
- contexts: { status: 'missing', merged: 0, missing: 0, errors: 0 },
519
- requirements: { status: 'missing', merged: 0, missing: 0, errors: 0 },
520
- runs: { status: 'missing', merged: 0, missing: 0, errors: 0 },
521
- rebuiltIndex: false,
522
- rebuiltMemoryViews: false,
523
- };
524
- if (!fileExists(configPath(projectRoot)) || !fileExists(aidaDir(projectRoot))) {
525
- return summary;
526
- }
527
- const memoryIndex = memoryIndexPath(projectRoot);
528
- if (fileExists(memoryIndex)) {
529
- summary.memoryIndex = { status: 'no-conflict', merged: 0, missing: 0, errors: 0 };
530
- mergeFixedFile(memoryIndex, mergeMemoryIndex, summary.memoryIndex);
531
- summary.memoryIndex.status = summary.memoryIndex.errors > 0 ? 'error' : summary.memoryIndex.merged > 0 ? 'merged' : 'no-conflict';
532
- }
533
- const moduleFiles = walkMemoryModuleFiles(moduleMemoriesDir(projectRoot));
534
- summary.moduleMemories = moduleFiles.length === 0
535
- ? { status: 'missing', merged: 0, missing: 0, errors: 0 }
536
- : mergeModuleFiles(moduleFiles, mergeModuleMemoryRecord);
537
- const contextFiles = walkJsonTargets(runsDir(projectRoot), 'context.json');
538
- summary.contexts = { status: contextFiles.length === 0 ? 'missing' : 'no-conflict', merged: 0, missing: 0, errors: 0 };
539
- for (const filePath of contextFiles) {
540
- const status = mergeConflictFile(filePath, (ours, theirs) => mergeRunContextRecord(ours, theirs));
541
- if (status === 'merged')
542
- summary.contexts.merged++;
543
- else if (status === 'error')
544
- summary.contexts.errors++;
545
- }
546
- if (summary.contexts.status !== 'missing') {
547
- summary.contexts.status = summary.contexts.errors > 0 ? 'error' : summary.contexts.merged > 0 ? 'merged' : 'no-conflict';
548
- }
549
- const requirementFiles = walkJsonTargets(runsDir(projectRoot), 'requirement.json');
550
- summary.requirements = { status: requirementFiles.length === 0 ? 'missing' : 'no-conflict', merged: 0, missing: 0, errors: 0 };
551
- for (const filePath of requirementFiles) {
552
- const status = mergeConflictFile(filePath, (ours, theirs) => mergeRequirementRecord(ours, theirs));
553
- if (status === 'merged')
554
- summary.requirements.merged++;
555
- else if (status === 'error')
556
- summary.requirements.errors++;
557
- }
558
- if (summary.requirements.status !== 'missing') {
559
- summary.requirements.status = summary.requirements.errors > 0 ? 'error' : summary.requirements.merged > 0 ? 'merged' : 'no-conflict';
560
- }
561
- const runFiles = walkJsonTargets(runsDir(projectRoot), 'run.json');
562
- summary.runs = runFiles.length === 0 ? { status: 'missing', merged: 0, missing: 0, errors: 0 } : mergeRunFiles(runFiles);
563
- const hasAnyMerge = [
564
- summary.memoryIndex,
565
- summary.moduleMemories,
566
- summary.contexts,
567
- summary.requirements,
568
- summary.runs,
569
- ].some((item) => item.status === 'merged');
570
- if (hasAnyMerge) {
571
- hydrateMissingModuleMemoriesFromIndex(projectRoot);
572
- buildMemoryViews(projectRoot);
573
- summary.rebuiltMemoryViews = true;
574
- buildIndex(projectRoot);
575
- summary.rebuiltIndex = true;
576
- }
577
- return summary;
578
- }
579
- function printLine(label, result) {
580
- if (result.status === 'merged') {
581
- console.log(` ${label}: merged, ${result.merged} file(s)`);
582
- }
583
- else if (result.status === 'no-conflict') {
584
- console.log(` ${label}: no conflict`);
585
- }
586
- else if (result.status === 'missing') {
587
- console.log(` ${label}: missing`);
588
- }
589
- else {
590
- console.log(` ${label}: parse error`);
591
- }
592
- }
593
- export async function mergeData() {
594
- const projectRoot = process.cwd();
595
- if (!fileExists(configPath(projectRoot))) {
596
- console.log(red('\n AIDA not initialized. Run `npx aida init` first.\n'));
597
- return;
598
- }
599
- const summary = mergeAidaJsonData(projectRoot);
600
- const hasError = [
601
- summary.memoryIndex,
602
- summary.moduleMemories,
603
- summary.contexts,
604
- summary.requirements,
605
- summary.runs,
606
- ].some((item) => item.status === 'error');
607
- if (hasError) {
608
- console.log(red('\n AIDA data merge finished with parse errors. Resolve the remaining conflicted JSON manually.\n'));
609
- return;
610
- }
611
- const changed = [
612
- summary.memoryIndex,
613
- summary.moduleMemories,
614
- summary.contexts,
615
- summary.requirements,
616
- summary.runs,
617
- ].some((item) => item.status === 'merged');
618
- if (!changed) {
619
- console.log(yellow('\n No AIDA JSON conflicts detected.\n'));
620
- return;
621
- }
622
- console.log(green('\n ✓ AIDA data merge completed\n'));
623
- printLine('memory index', summary.memoryIndex);
624
- printLine('module memories', summary.moduleMemories);
625
- printLine('branch contexts', summary.contexts);
626
- printLine('requirements', summary.requirements);
627
- printLine('run.json', summary.runs);
628
- if (summary.rebuiltMemoryViews)
629
- console.log(' memory views: rebuilt');
630
- if (summary.rebuiltIndex)
631
- console.log(' project index: rebuilt');
632
- console.log('');
633
- }
634
- //# sourceMappingURL=merge-data.js.map