task-summary-extractor 8.1.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.
- package/ARCHITECTURE.md +605 -0
- package/EXPLORATION.md +451 -0
- package/QUICK_START.md +272 -0
- package/README.md +544 -0
- package/bin/taskex.js +64 -0
- package/package.json +63 -0
- package/process_and_upload.js +107 -0
- package/prompt.json +265 -0
- package/setup.js +505 -0
- package/src/config.js +327 -0
- package/src/logger.js +355 -0
- package/src/pipeline.js +2006 -0
- package/src/renderers/markdown.js +968 -0
- package/src/services/firebase.js +106 -0
- package/src/services/gemini.js +779 -0
- package/src/services/git.js +329 -0
- package/src/services/video.js +305 -0
- package/src/utils/adaptive-budget.js +266 -0
- package/src/utils/change-detector.js +466 -0
- package/src/utils/cli.js +415 -0
- package/src/utils/context-manager.js +499 -0
- package/src/utils/cost-tracker.js +156 -0
- package/src/utils/deep-dive.js +549 -0
- package/src/utils/diff-engine.js +315 -0
- package/src/utils/dynamic-mode.js +567 -0
- package/src/utils/focused-reanalysis.js +317 -0
- package/src/utils/format.js +32 -0
- package/src/utils/fs.js +39 -0
- package/src/utils/global-config.js +315 -0
- package/src/utils/health-dashboard.js +216 -0
- package/src/utils/inject-cli-flags.js +58 -0
- package/src/utils/json-parser.js +245 -0
- package/src/utils/learning-loop.js +301 -0
- package/src/utils/progress-updater.js +451 -0
- package/src/utils/progress.js +166 -0
- package/src/utils/prompt.js +32 -0
- package/src/utils/quality-gate.js +429 -0
- package/src/utils/retry.js +129 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff Engine — compares current compilation against previous runs
|
|
3
|
+
* to generate a delta report: what's new, what's resolved, what changed.
|
|
4
|
+
*
|
|
5
|
+
* This enables incremental awareness — users can see what changed between
|
|
6
|
+
* successive analyses of the same call (or across calls in a series).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// ======================== PREVIOUS RUN LOADING ========================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Find and load the most recent previous compilation for comparison.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} targetDir - The call folder (e.g., "call 1/")
|
|
20
|
+
* @param {string} [currentRunTs] - Current run timestamp to exclude
|
|
21
|
+
* @returns {object|null} Previous compiled analysis, or null if none found
|
|
22
|
+
*/
|
|
23
|
+
function loadPreviousCompilation(targetDir, currentRunTs = null) {
|
|
24
|
+
const runsDir = path.join(targetDir, 'runs');
|
|
25
|
+
if (!fs.existsSync(runsDir)) return null;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const runDirs = fs.readdirSync(runsDir)
|
|
29
|
+
.filter(d => {
|
|
30
|
+
const full = path.join(runsDir, d);
|
|
31
|
+
return fs.statSync(full).isDirectory() && d !== currentRunTs;
|
|
32
|
+
})
|
|
33
|
+
.sort()
|
|
34
|
+
.reverse(); // Most recent first
|
|
35
|
+
|
|
36
|
+
for (const dir of runDirs) {
|
|
37
|
+
const compilationPath = path.join(runsDir, dir, 'compilation.json');
|
|
38
|
+
if (fs.existsSync(compilationPath)) {
|
|
39
|
+
const data = JSON.parse(fs.readFileSync(compilationPath, 'utf8'));
|
|
40
|
+
const parsed = data.output?.parsed || data.compiled || null;
|
|
41
|
+
if (parsed) {
|
|
42
|
+
return {
|
|
43
|
+
timestamp: dir,
|
|
44
|
+
compiled: parsed,
|
|
45
|
+
runPath: path.join(runsDir, dir),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fallback: try results.json
|
|
51
|
+
const resultsPath = path.join(runsDir, dir, 'results.json');
|
|
52
|
+
if (fs.existsSync(resultsPath)) {
|
|
53
|
+
const results = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));
|
|
54
|
+
if (results.compilation) {
|
|
55
|
+
return {
|
|
56
|
+
timestamp: dir,
|
|
57
|
+
compiled: null, // No compiled data in results.json directly
|
|
58
|
+
runPath: path.join(runsDir, dir),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.warn(` ⚠ Could not load previous compilation: ${err.message}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ======================== DIFF GENERATION ========================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Compare two compiled analyses and produce a diff.
|
|
74
|
+
*
|
|
75
|
+
* @param {object} current - Current compiled analysis
|
|
76
|
+
* @param {object} previous - Previous compiled analysis
|
|
77
|
+
* @returns {object} Diff report
|
|
78
|
+
*/
|
|
79
|
+
function generateDiff(current, previous) {
|
|
80
|
+
if (!current || !previous) {
|
|
81
|
+
return { hasDiff: false, reason: !previous ? 'no_previous_run' : 'no_current_data' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const diff = {
|
|
85
|
+
hasDiff: true,
|
|
86
|
+
previousTimestamp: previous.timestamp || 'unknown',
|
|
87
|
+
tickets: diffArray(current.tickets || [], previous.tickets || [], 'ticket_id'),
|
|
88
|
+
changeRequests: diffArray(current.change_requests || [], previous.change_requests || [], 'id'),
|
|
89
|
+
actionItems: diffArray(current.action_items || [], previous.action_items || [], 'id'),
|
|
90
|
+
blockers: diffArray(current.blockers || [], previous.blockers || [], 'id'),
|
|
91
|
+
scopeChanges: diffArray(current.scope_changes || [], previous.scope_changes || [], 'id'),
|
|
92
|
+
summary: {
|
|
93
|
+
current: current.summary || '',
|
|
94
|
+
previous: previous.summary || '',
|
|
95
|
+
changed: (current.summary || '') !== (previous.summary || ''),
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Calculate totals
|
|
100
|
+
diff.totals = {
|
|
101
|
+
newItems: (diff.tickets.added?.length || 0) +
|
|
102
|
+
(diff.changeRequests.added?.length || 0) +
|
|
103
|
+
(diff.actionItems.added?.length || 0) +
|
|
104
|
+
(diff.blockers.added?.length || 0) +
|
|
105
|
+
(diff.scopeChanges.added?.length || 0),
|
|
106
|
+
removedItems: (diff.tickets.removed?.length || 0) +
|
|
107
|
+
(diff.changeRequests.removed?.length || 0) +
|
|
108
|
+
(diff.actionItems.removed?.length || 0) +
|
|
109
|
+
(diff.blockers.removed?.length || 0) +
|
|
110
|
+
(diff.scopeChanges.removed?.length || 0),
|
|
111
|
+
changedItems: (diff.tickets.changed?.length || 0) +
|
|
112
|
+
(diff.changeRequests.changed?.length || 0) +
|
|
113
|
+
(diff.actionItems.changed?.length || 0) +
|
|
114
|
+
(diff.blockers.changed?.length || 0) +
|
|
115
|
+
(diff.scopeChanges.changed?.length || 0),
|
|
116
|
+
unchangedItems: (diff.tickets.unchanged?.length || 0) +
|
|
117
|
+
(diff.changeRequests.unchanged?.length || 0) +
|
|
118
|
+
(diff.actionItems.unchanged?.length || 0) +
|
|
119
|
+
(diff.blockers.unchanged?.length || 0) +
|
|
120
|
+
(diff.scopeChanges.unchanged?.length || 0),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return diff;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Diff two arrays of items by ID field.
|
|
128
|
+
*
|
|
129
|
+
* @param {Array} currentArr - Current items
|
|
130
|
+
* @param {Array} previousArr - Previous items
|
|
131
|
+
* @param {string} idField - Field name to use as ID
|
|
132
|
+
* @returns {{ added: Array, removed: Array, changed: Array, unchanged: Array }}
|
|
133
|
+
*/
|
|
134
|
+
function diffArray(currentArr, previousArr, idField) {
|
|
135
|
+
const prevMap = new Map();
|
|
136
|
+
for (const item of previousArr) {
|
|
137
|
+
const id = item[idField];
|
|
138
|
+
if (id) prevMap.set(id, item);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const currMap = new Map();
|
|
142
|
+
for (const item of currentArr) {
|
|
143
|
+
const id = item[idField];
|
|
144
|
+
if (id) currMap.set(id, item);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const added = [];
|
|
148
|
+
const changed = [];
|
|
149
|
+
const unchanged = [];
|
|
150
|
+
const removed = [];
|
|
151
|
+
|
|
152
|
+
// Find added and changed
|
|
153
|
+
for (const [id, currItem] of currMap) {
|
|
154
|
+
if (!prevMap.has(id)) {
|
|
155
|
+
added.push({ id, item: currItem, _diffStatus: 'new' });
|
|
156
|
+
} else {
|
|
157
|
+
const prevItem = prevMap.get(id);
|
|
158
|
+
const changes = detectFieldChanges(currItem, prevItem, idField);
|
|
159
|
+
if (changes.length > 0) {
|
|
160
|
+
changed.push({ id, item: currItem, changes, _diffStatus: 'changed' });
|
|
161
|
+
} else {
|
|
162
|
+
unchanged.push({ id, _diffStatus: 'unchanged' });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Find removed
|
|
168
|
+
for (const [id, prevItem] of prevMap) {
|
|
169
|
+
if (!currMap.has(id)) {
|
|
170
|
+
removed.push({ id, item: prevItem, _diffStatus: 'removed' });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { added, removed, changed, unchanged };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Detect specific field changes between two items.
|
|
179
|
+
*
|
|
180
|
+
* @param {object} current
|
|
181
|
+
* @param {object} previous
|
|
182
|
+
* @param {string} idField - Skip the ID field itself
|
|
183
|
+
* @returns {Array<{field: string, from: any, to: any}>}
|
|
184
|
+
*/
|
|
185
|
+
function detectFieldChanges(current, previous, idField) {
|
|
186
|
+
const changes = [];
|
|
187
|
+
const importantFields = ['status', 'priority', 'assigned_to', 'assignee', 'owner', 'confidence'];
|
|
188
|
+
|
|
189
|
+
for (const field of importantFields) {
|
|
190
|
+
const curr = current[field];
|
|
191
|
+
const prev = previous[field];
|
|
192
|
+
if (curr !== prev && (curr || prev)) {
|
|
193
|
+
changes.push({ field, from: prev || null, to: curr || null });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return changes;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ======================== MARKDOWN RENDERING ========================
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Render the diff as a Markdown section to append to the main report.
|
|
204
|
+
*
|
|
205
|
+
* @param {object} diff - From generateDiff()
|
|
206
|
+
* @returns {string} Markdown section
|
|
207
|
+
*/
|
|
208
|
+
function renderDiffMarkdown(diff) {
|
|
209
|
+
if (!diff || !diff.hasDiff) return '';
|
|
210
|
+
|
|
211
|
+
const lines = [];
|
|
212
|
+
const ln = (...args) => lines.push(args.join(''));
|
|
213
|
+
|
|
214
|
+
ln('## 🔄 Changes Since Previous Run');
|
|
215
|
+
ln('');
|
|
216
|
+
ln(`> Compared against run from: \`${diff.previousTimestamp}\``);
|
|
217
|
+
ln('');
|
|
218
|
+
|
|
219
|
+
const t = diff.totals;
|
|
220
|
+
if (t.newItems === 0 && t.removedItems === 0 && t.changedItems === 0) {
|
|
221
|
+
ln('No changes detected since the previous run.');
|
|
222
|
+
ln('');
|
|
223
|
+
return lines.join('\n');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
ln(`| Category | New | Removed | Changed | Unchanged |`);
|
|
227
|
+
ln(`| --- | --- | --- | --- | --- |`);
|
|
228
|
+
|
|
229
|
+
const categories = [
|
|
230
|
+
{ name: 'Tickets', d: diff.tickets },
|
|
231
|
+
{ name: 'Change Requests', d: diff.changeRequests },
|
|
232
|
+
{ name: 'Action Items', d: diff.actionItems },
|
|
233
|
+
{ name: 'Blockers', d: diff.blockers },
|
|
234
|
+
{ name: 'Scope Changes', d: diff.scopeChanges },
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
for (const { name, d } of categories) {
|
|
238
|
+
const a = d.added?.length || 0;
|
|
239
|
+
const r = d.removed?.length || 0;
|
|
240
|
+
const c = d.changed?.length || 0;
|
|
241
|
+
const u = d.unchanged?.length || 0;
|
|
242
|
+
if (a + r + c > 0) {
|
|
243
|
+
ln(`| ${name} | ${a > 0 ? `+${a}` : '-'} | ${r > 0 ? `-${r}` : '-'} | ${c > 0 ? `~${c}` : '-'} | ${u} |`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
ln('');
|
|
247
|
+
|
|
248
|
+
// Detail new items
|
|
249
|
+
const allNew = [
|
|
250
|
+
...diff.tickets.added.map(i => ({ type: 'Ticket', ...i })),
|
|
251
|
+
...diff.changeRequests.added.map(i => ({ type: 'CR', ...i })),
|
|
252
|
+
...diff.actionItems.added.map(i => ({ type: 'Action', ...i })),
|
|
253
|
+
...diff.blockers.added.map(i => ({ type: 'Blocker', ...i })),
|
|
254
|
+
...diff.scopeChanges.added.map(i => ({ type: 'Scope', ...i })),
|
|
255
|
+
];
|
|
256
|
+
if (allNew.length > 0) {
|
|
257
|
+
ln('### ➕ New Items');
|
|
258
|
+
ln('');
|
|
259
|
+
for (const n of allNew) {
|
|
260
|
+
const title = n.item.title || n.item.description || n.item.ticket_id || n.id;
|
|
261
|
+
ln(`- **[${n.type}]** ${n.id}: ${title}`);
|
|
262
|
+
}
|
|
263
|
+
ln('');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Detail removed items
|
|
267
|
+
const allRemoved = [
|
|
268
|
+
...diff.tickets.removed.map(i => ({ type: 'Ticket', ...i })),
|
|
269
|
+
...diff.changeRequests.removed.map(i => ({ type: 'CR', ...i })),
|
|
270
|
+
...diff.actionItems.removed.map(i => ({ type: 'Action', ...i })),
|
|
271
|
+
...diff.blockers.removed.map(i => ({ type: 'Blocker', ...i })),
|
|
272
|
+
...diff.scopeChanges.removed.map(i => ({ type: 'Scope', ...i })),
|
|
273
|
+
];
|
|
274
|
+
if (allRemoved.length > 0) {
|
|
275
|
+
ln('### ➖ Removed Items');
|
|
276
|
+
ln('');
|
|
277
|
+
for (const r of allRemoved) {
|
|
278
|
+
const title = r.item.title || r.item.description || r.item.ticket_id || r.id;
|
|
279
|
+
ln(`- **[${r.type}]** ${r.id}: ${title}`);
|
|
280
|
+
}
|
|
281
|
+
ln('');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Detail changed items
|
|
285
|
+
const allChanged = [
|
|
286
|
+
...diff.tickets.changed.map(i => ({ type: 'Ticket', ...i })),
|
|
287
|
+
...diff.changeRequests.changed.map(i => ({ type: 'CR', ...i })),
|
|
288
|
+
...diff.actionItems.changed.map(i => ({ type: 'Action', ...i })),
|
|
289
|
+
...diff.blockers.changed.map(i => ({ type: 'Blocker', ...i })),
|
|
290
|
+
...diff.scopeChanges.changed.map(i => ({ type: 'Scope', ...i })),
|
|
291
|
+
];
|
|
292
|
+
if (allChanged.length > 0) {
|
|
293
|
+
ln('### 🔀 Changed Items');
|
|
294
|
+
ln('');
|
|
295
|
+
for (const c of allChanged) {
|
|
296
|
+
const title = c.item.title || c.item.description || c.item.ticket_id || c.id;
|
|
297
|
+
ln(`- **[${c.type}]** ${c.id}: ${title}`);
|
|
298
|
+
for (const ch of c.changes) {
|
|
299
|
+
ln(` - \`${ch.field}\`: ${ch.from || '_empty_'} → **${ch.to || '_empty_'}**`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
ln('');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return lines.join('\n');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = {
|
|
309
|
+
loadPreviousCompilation,
|
|
310
|
+
generateDiff,
|
|
311
|
+
renderDiffMarkdown,
|
|
312
|
+
// Expose for testing
|
|
313
|
+
diffArray,
|
|
314
|
+
detectFieldChanges,
|
|
315
|
+
};
|