task-summary-extractor 9.2.2 → 9.3.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.
package/.env.example CHANGED
@@ -12,8 +12,8 @@ GEMINI_API_KEY=your_gemini_api_key
12
12
  GEMINI_MODEL=gemini-2.5-flash
13
13
 
14
14
  # ======================== VIDEO PROCESSING ========================
15
- # Speed multiplier (default: 1.5)
16
- VIDEO_SPEED=1.5
15
+ # Speed multiplier (default: 1.6)
16
+ VIDEO_SPEED=1.6
17
17
  # Segment duration in seconds (default: 280)
18
18
  VIDEO_SEGMENT_TIME=280
19
19
  # ffmpeg preset: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
@@ -36,3 +36,7 @@ THINKING_BUDGET=24576
36
36
  COMPILATION_THINKING_BUDGET=10240
37
37
  # Max polling time for Gemini File API processing in ms (default: 300000 = 5 min)
38
38
  GEMINI_POLL_TIMEOUT_MS=300000
39
+
40
+ # ======================== NPM PUBLISHING ========================
41
+ # Automation token for npm publish (optional — if not set, browser sign-in is used)
42
+ # NPM_TOKEN=npm_your_token_here
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-summary-extractor",
3
- "version": "9.2.2",
3
+ "version": "9.3.1",
4
4
  "description": "AI-powered meeting analysis & document generation CLI — video + document processing, deep dive docs, dynamic mode, interactive CLI with model selection, confidence scoring, learning loop, git progress tracking",
5
5
  "main": "process_and_upload.js",
6
6
  "bin": {
@@ -15,8 +15,7 @@
15
15
  ".env.example",
16
16
  "README.md",
17
17
  "QUICK_START.md",
18
- "ARCHITECTURE.md",
19
- "EXPLORATION.md"
18
+ "ARCHITECTURE.md"
20
19
  ],
21
20
  "scripts": {
22
21
  "setup": "node setup.js",
package/src/config.js CHANGED
@@ -220,7 +220,7 @@ function getMaxThinkingBudget() {
220
220
 
221
221
  // ======================== VIDEO PROCESSING ========================
222
222
 
223
- const SPEED = envFloat('VIDEO_SPEED', 1.5);
223
+ const SPEED = envFloat('VIDEO_SPEED', 1.6);
224
224
  const SEG_TIME = envInt('VIDEO_SEGMENT_TIME', 280); // seconds — produces segments < 5 min
225
225
  const PRESET = env('VIDEO_PRESET', 'slow');
226
226
  const VIDEO_EXTS = ['.mp4', '.mkv', '.avi', '.mov', '.webm'];
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Deep Summary — pre-summarizes context documents before segment analysis
3
+ * to dramatically reduce input tokens per segment.
4
+ *
5
+ * Instead of sending full document content (potentially 500K+ tokens) to
6
+ * every segment, this module:
7
+ * 1. Groups documents by priority tier
8
+ * 2. Sends each group to Gemini for intelligent condensation
9
+ * 3. Replaces full content with condensed summaries
10
+ * 4. Preserves "excluded" docs at full fidelity (user-chosen focus docs)
11
+ * 5. Ensures summaries capture all ticket IDs, action items, statuses
12
+ *
13
+ * The user can pick specific docs to EXCLUDE from summarization — these stay
14
+ * full. The summary pass receives extra instructions to focus on extracting
15
+ * information related to these excluded docs' topics.
16
+ *
17
+ * Token savings: typically 60-80% reduction in per-segment context tokens.
18
+ */
19
+
20
+ 'use strict';
21
+
22
+ const { extractJson } = require('../utils/json-parser');
23
+ const { withRetry } = require('../utils/retry');
24
+ const { estimateTokens } = require('../utils/context-manager');
25
+ const { c } = require('../utils/colors');
26
+ const config = require('../config');
27
+
28
+ // ======================== CONSTANTS ========================
29
+
30
+ /** Max tokens for a single summarization call output */
31
+ const SUMMARY_MAX_OUTPUT = 16384;
32
+
33
+ /** Max input chars to send in one summarization batch (~200K tokens @ 0.3 tok/char) */
34
+ const BATCH_MAX_CHARS = 600000;
35
+
36
+ /** Minimum content length (chars) to bother summarizing — below this, keep full */
37
+ const MIN_SUMMARIZE_LENGTH = 500;
38
+
39
+ // ======================== BATCH BUILDER ========================
40
+
41
+ /**
42
+ * Group documents into batches that fit within the batch char limit.
43
+ * Each batch will be summarized in a single Gemini call.
44
+ *
45
+ * @param {Array} docs - Context docs to batch [{type, fileName, content}]
46
+ * @param {number} [maxChars=BATCH_MAX_CHARS] - Max chars per batch
47
+ * @returns {Array<Array>} Batches of docs
48
+ */
49
+ function buildBatches(docs, maxChars = BATCH_MAX_CHARS) {
50
+ const batches = [];
51
+ let currentBatch = [];
52
+ let currentChars = 0;
53
+
54
+ for (const doc of docs) {
55
+ const docChars = doc.content ? doc.content.length : 0;
56
+
57
+ // If this single doc exceeds the batch limit, it gets its own batch
58
+ if (docChars > maxChars) {
59
+ if (currentBatch.length > 0) {
60
+ batches.push(currentBatch);
61
+ currentBatch = [];
62
+ currentChars = 0;
63
+ }
64
+ batches.push([doc]);
65
+ continue;
66
+ }
67
+
68
+ if (currentChars + docChars > maxChars && currentBatch.length > 0) {
69
+ batches.push(currentBatch);
70
+ currentBatch = [];
71
+ currentChars = 0;
72
+ }
73
+
74
+ currentBatch.push(doc);
75
+ currentChars += docChars;
76
+ }
77
+
78
+ if (currentBatch.length > 0) {
79
+ batches.push(currentBatch);
80
+ }
81
+
82
+ return batches;
83
+ }
84
+
85
+ // ======================== SUMMARIZE ONE BATCH ========================
86
+
87
+ /**
88
+ * Summarize a batch of documents into a condensed representation.
89
+ *
90
+ * @param {object} ai - Gemini AI instance
91
+ * @param {Array} docs - Documents in this batch
92
+ * @param {object} [opts]
93
+ * @param {string[]} [opts.focusTopics=[]] - Topics to focus on (from excluded docs)
94
+ * @param {number} [opts.thinkingBudget=8192] - Thinking token budget
95
+ * @param {number} [opts.batchIndex=0] - Batch number for logging
96
+ * @param {number} [opts.totalBatches=1] - Total batches for logging
97
+ * @returns {Promise<{summaries: Map<string, string>, tokenUsage: object}|null>}
98
+ */
99
+ async function summarizeBatch(ai, docs, opts = {}) {
100
+ const {
101
+ focusTopics = [],
102
+ thinkingBudget = 8192,
103
+ batchIndex = 0,
104
+ totalBatches = 1,
105
+ } = opts;
106
+
107
+ const docEntries = docs
108
+ .filter(d => d.type === 'inlineText' && d.content)
109
+ .map(d => `=== DOCUMENT: ${d.fileName} ===\n${d.content}`);
110
+
111
+ if (docEntries.length === 0) return null;
112
+
113
+ const focusSection = focusTopics.length > 0
114
+ ? `\n\nFOCUS AREAS — The user has selected certain documents to keep at full fidelity. ` +
115
+ `Your summaries must be especially thorough about information related to these topics:\n` +
116
+ focusTopics.map((t, i) => ` ${i + 1}. ${t}`).join('\n') +
117
+ `\n\nFor every ticket ID, action item, blocker, or status mentioned in relation to these ` +
118
+ `focus areas, include them verbatim in the summary. Do NOT omit any IDs or assignments.`
119
+ : '';
120
+
121
+ const promptText = `You are a precision document summarizer for a meeting analysis pipeline.
122
+
123
+ Your job: read ALL documents below and produce a CONDENSED version of each that preserves:
124
+ - Every ticket ID, task ID, CR number, or reference number (verbatim)
125
+ - All assignees, reviewers, and responsible parties
126
+ - All statuses (open, closed, in_progress, blocked, etc.)
127
+ - All action items and their owners
128
+ - All blockers, dependencies, and deadlines
129
+ - Key decisions and their rationale
130
+ - File paths and code references
131
+ - Numerical data (percentages, counts, dates, versions)
132
+
133
+ What to remove:
134
+ - Verbose explanations of well-known concepts
135
+ - Redundant phrasing and filler text
136
+ - Formatting-only content (decorative headers, dividers)
137
+ - Boilerplate/template text that adds no information
138
+ ${focusSection}
139
+
140
+ OUTPUT FORMAT:
141
+ Return valid JSON with this structure:
142
+ {
143
+ "summaries": {
144
+ "<fileName>": "<condensed text — plain text, preserving all key info>",
145
+ ...
146
+ },
147
+ "metadata": {
148
+ "originalTokensEstimate": <number>,
149
+ "summaryTokensEstimate": <number>,
150
+ "compressionRatio": <number between 0 and 1>
151
+ }
152
+ }
153
+
154
+ Aim for 70-80% size reduction while preserving ALL actionable information.
155
+ Every ID, every name, every status must survive the summarization.
156
+
157
+ DOCUMENTS TO SUMMARIZE (${docEntries.length} documents):
158
+
159
+ ${docEntries.join('\n\n')}`;
160
+
161
+ const requestPayload = {
162
+ model: config.GEMINI_MODEL,
163
+ contents: [{ role: 'user', parts: [{ text: promptText }] }],
164
+ config: {
165
+ systemInstruction: 'You are a lossless information compressor. Preserve every ID, name, status, assignment, and actionable detail. Output valid JSON only.',
166
+ maxOutputTokens: SUMMARY_MAX_OUTPUT,
167
+ temperature: 0,
168
+ thinkingConfig: { thinkingBudget },
169
+ },
170
+ };
171
+
172
+ try {
173
+ const label = totalBatches > 1
174
+ ? `Deep summary batch ${batchIndex + 1}/${totalBatches}`
175
+ : 'Deep summary';
176
+
177
+ const response = await withRetry(
178
+ () => ai.models.generateContent(requestPayload),
179
+ { label, maxRetries: 2, baseDelay: 3000 }
180
+ );
181
+
182
+ const rawText = response.text;
183
+ const parsed = extractJson(rawText);
184
+
185
+ if (!parsed || !parsed.summaries) return null;
186
+
187
+ const usage = response.usageMetadata || {};
188
+ const tokenUsage = {
189
+ inputTokens: usage.promptTokenCount || 0,
190
+ outputTokens: usage.candidatesTokenCount || 0,
191
+ totalTokens: usage.totalTokenCount || 0,
192
+ thoughtTokens: usage.thoughtsTokenCount || 0,
193
+ };
194
+
195
+ return { summaries: parsed.summaries, metadata: parsed.metadata || {}, tokenUsage };
196
+ } catch (err) {
197
+ console.warn(` ${c.warn(`Deep summary batch ${batchIndex + 1} failed: ${err.message}`)}`);
198
+ return null;
199
+ }
200
+ }
201
+
202
+ // ======================== MAIN ENTRY POINT ========================
203
+
204
+ /**
205
+ * Run deep summarization on context documents.
206
+ *
207
+ * @param {object} ai - Gemini AI instance
208
+ * @param {Array} contextDocs - All prepared context docs
209
+ * @param {object} [opts]
210
+ * @param {string[]} [opts.excludeFileNames=[]] - Doc fileNames to keep at full fidelity
211
+ * @param {number} [opts.thinkingBudget=8192] - Thinking budget per batch
212
+ * @param {Function} [opts.onProgress] - Callback(done, total) for progress
213
+ * @returns {Promise<{docs: Array, stats: object}>}
214
+ */
215
+ async function deepSummarize(ai, contextDocs, opts = {}) {
216
+ const {
217
+ excludeFileNames = [],
218
+ thinkingBudget = 8192,
219
+ onProgress = null,
220
+ } = opts;
221
+
222
+ const excludeSet = new Set(excludeFileNames.map(n => n.toLowerCase()));
223
+
224
+ // Partition: docs to summarize vs docs to keep full
225
+ const toSummarize = [];
226
+ const keepFull = [];
227
+
228
+ for (const doc of contextDocs) {
229
+ // Keep non-text docs (fileData = PDF etc.) as-is
230
+ if (doc.type !== 'inlineText') {
231
+ keepFull.push(doc);
232
+ continue;
233
+ }
234
+
235
+ // Keep excluded docs at full fidelity
236
+ if (excludeSet.has(doc.fileName.toLowerCase())) {
237
+ keepFull.push(doc);
238
+ continue;
239
+ }
240
+
241
+ // Skip tiny docs — not worth summarizing
242
+ if (!doc.content || doc.content.length < MIN_SUMMARIZE_LENGTH) {
243
+ keepFull.push(doc);
244
+ continue;
245
+ }
246
+
247
+ toSummarize.push(doc);
248
+ }
249
+
250
+ if (toSummarize.length === 0) {
251
+ return {
252
+ docs: contextDocs,
253
+ stats: {
254
+ summarized: 0,
255
+ keptFull: keepFull.length,
256
+ originalTokens: 0,
257
+ summaryTokens: 0,
258
+ savedTokens: 0,
259
+ savingsPercent: 0,
260
+ totalInputTokens: 0,
261
+ totalOutputTokens: 0,
262
+ },
263
+ };
264
+ }
265
+
266
+ // Build focus topics from excluded docs (tell summarizer what to prioritize)
267
+ const focusTopics = keepFull
268
+ .filter(d => d.type === 'inlineText' && excludeSet.has(d.fileName.toLowerCase()))
269
+ .map(d => d.fileName);
270
+
271
+ // Batch documents
272
+ const batches = buildBatches(toSummarize);
273
+
274
+ console.log(` Batched ${c.highlight(toSummarize.length)} doc(s) into ${c.highlight(batches.length)} summarization batch(es)`);
275
+ if (focusTopics.length > 0) {
276
+ console.log(` Focus topics from ${c.highlight(focusTopics.length)} excluded doc(s):`);
277
+ focusTopics.forEach(t => console.log(` ${c.dim('•')} ${c.cyan(t)}`));
278
+ }
279
+
280
+ // Process batches (sequential for now; can add parallelization later)
281
+ const allSummaries = new Map();
282
+ let totalInput = 0;
283
+ let totalOutput = 0;
284
+ let batchesDone = 0;
285
+
286
+ for (let i = 0; i < batches.length; i++) {
287
+ const result = await summarizeBatch(ai, batches[i], {
288
+ focusTopics,
289
+ thinkingBudget,
290
+ batchIndex: i,
291
+ totalBatches: batches.length,
292
+ });
293
+
294
+ batchesDone++;
295
+ if (onProgress) onProgress(batchesDone, batches.length);
296
+
297
+ if (result && result.summaries) {
298
+ for (const [fileName, summary] of Object.entries(result.summaries)) {
299
+ allSummaries.set(fileName.toLowerCase(), summary);
300
+ }
301
+ totalInput += result.tokenUsage.inputTokens;
302
+ totalOutput += result.tokenUsage.outputTokens;
303
+ }
304
+ }
305
+
306
+ // Replace doc content with summaries
307
+ let originalTokens = 0;
308
+ let summaryTokens = 0;
309
+ const resultDocs = [];
310
+
311
+ for (const doc of contextDocs) {
312
+ if (doc.type !== 'inlineText') {
313
+ resultDocs.push(doc);
314
+ continue;
315
+ }
316
+
317
+ // Check if this doc was excluded (kept full)
318
+ if (excludeSet.has(doc.fileName.toLowerCase())) {
319
+ resultDocs.push(doc);
320
+ continue;
321
+ }
322
+
323
+ // Check if we have a summary for this doc
324
+ const summaryKey = doc.fileName.toLowerCase();
325
+ const summary = allSummaries.get(summaryKey);
326
+
327
+ if (summary && summary.length > 0) {
328
+ const origTokens = estimateTokens(doc.content);
329
+ const sumTokens = estimateTokens(summary);
330
+ originalTokens += origTokens;
331
+ summaryTokens += sumTokens;
332
+
333
+ resultDocs.push({
334
+ ...doc,
335
+ content: `[Deep Summary — original: ~${origTokens.toLocaleString()} tokens → condensed: ~${sumTokens.toLocaleString()} tokens]\n\n${summary}`,
336
+ _originalLength: doc.content.length,
337
+ _summaryLength: summary.length,
338
+ _deepSummarized: true,
339
+ });
340
+ } else {
341
+ // No summary returned — keep original
342
+ resultDocs.push(doc);
343
+ }
344
+ }
345
+
346
+ const savedTokens = originalTokens - summaryTokens;
347
+ const savingsPercent = originalTokens > 0
348
+ ? parseFloat(((savedTokens / originalTokens) * 100).toFixed(1))
349
+ : 0;
350
+
351
+ return {
352
+ docs: resultDocs,
353
+ stats: {
354
+ summarized: allSummaries.size,
355
+ keptFull: keepFull.length,
356
+ originalTokens,
357
+ summaryTokens,
358
+ savedTokens,
359
+ savingsPercent,
360
+ totalInputTokens: totalInput,
361
+ totalOutputTokens: totalOutput,
362
+ },
363
+ };
364
+ }
365
+
366
+ // ======================== EXPORTS ========================
367
+
368
+ module.exports = {
369
+ deepSummarize,
370
+ summarizeBatch,
371
+ buildBatches,
372
+ SUMMARY_MAX_OUTPUT,
373
+ BATCH_MAX_CHARS,
374
+ MIN_SUMMARIZE_LENGTH,
375
+ };
@@ -85,6 +85,7 @@ async function phaseDiscover(ctx) {
85
85
  if (opts.resume) activeFlags.push('resume');
86
86
  if (opts.reanalyze) activeFlags.push('reanalyze');
87
87
  if (opts.dryRun) activeFlags.push('dry-run');
88
+ if (opts.deepSummary) activeFlags.push('deep-summary');
88
89
  if (activeFlags.length > 0) {
89
90
  console.log(` Flags: ${c.yellow(activeFlags.join(', '))}`);
90
91
  }
@@ -67,6 +67,10 @@ async function phaseInit() {
67
67
  disableDiff: !!flags['no-diff'],
68
68
  noHtml: !!flags['no-html'],
69
69
  deepDive: !!flags['deep-dive'],
70
+ deepSummary: !!flags['deep-summary'],
71
+ deepSummaryExclude: typeof flags['exclude-docs'] === 'string'
72
+ ? flags['exclude-docs'].split(',').map(s => s.trim()).filter(Boolean)
73
+ : [], // populated by CLI flag, interactive picker, or kept empty
70
74
  dynamic: !!flags.dynamic,
71
75
  request: typeof flags.request === 'string' ? flags.request : null,
72
76
  updateProgress: !!flags['update-progress'],
@@ -94,36 +98,10 @@ async function phaseInit() {
94
98
  opts.runMode = mode;
95
99
 
96
100
  if (mode !== 'custom') {
97
- // Apply preset overrides
98
- const { selectRunMode: _ignore, ...cliModule } = require('../utils/cli');
99
- // Access RUN_PRESETS from the module
100
- const presetOverrides = {
101
- fast: {
102
- disableFocusedPass: true,
103
- disableLearning: true,
104
- disableDiff: true,
105
- format: 'md,json',
106
- formats: new Set(['md', 'json']),
107
- modelTier: 'economy',
108
- },
109
- balanced: {
110
- disableFocusedPass: false,
111
- disableLearning: false,
112
- disableDiff: false,
113
- format: 'all',
114
- formats: new Set(['md', 'html', 'json', 'pdf', 'docx']),
115
- modelTier: 'balanced',
116
- },
117
- detailed: {
118
- disableFocusedPass: false,
119
- disableLearning: false,
120
- disableDiff: false,
121
- format: 'all',
122
- formats: new Set(['md', 'html', 'json', 'pdf', 'docx']),
123
- modelTier: 'premium',
124
- },
125
- };
126
- const preset = presetOverrides[mode];
101
+ // Apply preset overrides from the shared RUN_PRESETS definition
102
+ const { RUN_PRESETS } = require('../utils/cli');
103
+ const presetDef = RUN_PRESETS[mode];
104
+ const preset = presetDef ? presetDef.overrides : null;
127
105
  if (preset) {
128
106
  opts.disableFocusedPass = preset.disableFocusedPass;
129
107
  opts.disableLearning = preset.disableLearning;
@@ -322,6 +300,7 @@ function _printRunSummary(opts, modelId, models, targetDir) {
322
300
  if (!opts.disableLearning) features.push(c.green('learning'));
323
301
  if (!opts.disableDiff) features.push(c.green('diff'));
324
302
  if (opts.deepDive) features.push(c.cyan('deep-dive'));
303
+ if (opts.deepSummary) features.push(c.cyan('deep-summary'));
325
304
  if (opts.dynamic) features.push(c.cyan('dynamic'));
326
305
  if (opts.resume) features.push(c.yellow('resume'));
327
306
  if (opts.dryRun) features.push(c.yellow('dry-run'));
@@ -7,6 +7,9 @@ const path = require('path');
7
7
  const { initFirebase, uploadToStorage, storageExists } = require('../services/firebase');
8
8
  const { initGemini, prepareDocsForGemini } = require('../services/gemini');
9
9
 
10
+ // --- Modes ---
11
+ const { deepSummarize } = require('../modes/deep-summary');
12
+
10
13
  // --- Utils ---
11
14
  const { parallelMap } = require('../utils/retry');
12
15
 
@@ -101,4 +104,61 @@ async function phaseServices(ctx) {
101
104
  return { ...ctx, storage, firebaseReady, ai, contextDocs, docStorageUrls, callName };
102
105
  }
103
106
 
104
- module.exports = phaseServices;
107
+ // ======================== PHASE: DEEP SUMMARY ========================
108
+
109
+ /**
110
+ * Pre-summarize context documents to save input tokens per segment.
111
+ * Runs only when --deep-summary flag is active.
112
+ *
113
+ * @param {object} ctx - Pipeline context with ai, contextDocs, opts
114
+ * @returns {object} Updated ctx with summarized contextDocs and deepSummaryStats
115
+ */
116
+ async function phaseDeepSummary(ctx) {
117
+ const log = getLog();
118
+ const { opts, ai, contextDocs } = ctx;
119
+
120
+ if (!opts.deepSummary || !ai || contextDocs.length === 0) {
121
+ return { ...ctx, deepSummaryStats: null };
122
+ }
123
+
124
+ console.log('');
125
+ console.log(c.cyan(' ── Deep Summary — Pre-summarizing context documents ──'));
126
+ log.step('Deep summary: starting context document pre-summarization');
127
+ if (log && log.phaseStart) log.phaseStart('deep_summary');
128
+
129
+ const excludeNames = opts.deepSummaryExclude || [];
130
+ let updatedDocs = contextDocs;
131
+ let deepSummaryStats = null;
132
+
133
+ try {
134
+ const result = await deepSummarize(ai, contextDocs, {
135
+ excludeFileNames: excludeNames,
136
+ thinkingBudget: Math.min(8192, opts.thinkingBudget),
137
+ });
138
+
139
+ updatedDocs = result.docs;
140
+ deepSummaryStats = result.stats;
141
+
142
+ if (deepSummaryStats.summarized > 0) {
143
+ console.log(` ${c.success(`Summarized ${c.highlight(deepSummaryStats.summarized)} doc(s) — saved ~${c.highlight(deepSummaryStats.savedTokens.toLocaleString())} tokens (${c.yellow(deepSummaryStats.savingsPercent + '%')} reduction)`)}`);
144
+ console.log(` ${c.dim('Original:')} ~${deepSummaryStats.originalTokens.toLocaleString()} tokens → ${c.dim('Condensed:')} ~${deepSummaryStats.summaryTokens.toLocaleString()} tokens`);
145
+ if (deepSummaryStats.keptFull > 0) {
146
+ console.log(` ${c.dim('Kept full:')} ${deepSummaryStats.keptFull} doc(s) (excluded from summary)`);
147
+ }
148
+ log.step(`Deep summary: ${deepSummaryStats.summarized} docs summarized, ${deepSummaryStats.savedTokens} tokens saved (${deepSummaryStats.savingsPercent}%)`);
149
+ log.metric('deep_summary', deepSummaryStats);
150
+ } else {
151
+ console.log(` ${c.dim('No documents needed summarization')}`);
152
+ }
153
+ } catch (err) {
154
+ console.warn(` ${c.warn(`Deep summary failed (continuing with full docs): ${err.message}`)}`);
155
+ log.warn(`Deep summary failed: ${err.message}`);
156
+ }
157
+
158
+ if (log && log.phaseEnd) log.phaseEnd({ stats: deepSummaryStats });
159
+ console.log('');
160
+
161
+ return { ...ctx, contextDocs: updatedDocs, deepSummaryStats };
162
+ }
163
+
164
+ module.exports = { phaseServices, phaseDeepSummary };
package/src/pipeline.js CHANGED
@@ -32,7 +32,7 @@ const { getLog, isShuttingDown, PKG_ROOT, PROJECT_ROOT } = require('./phases/_sh
32
32
  // --- Pipeline phases ---
33
33
  const phaseInit = require('./phases/init');
34
34
  const phaseDiscover = require('./phases/discover');
35
- const phaseServices = require('./phases/services');
35
+ const { phaseServices, phaseDeepSummary } = require('./phases/services');
36
36
  const phaseProcessVideo = require('./phases/process-media');
37
37
  const phaseCompile = require('./phases/compile');
38
38
  const phaseOutput = require('./phases/output');
@@ -46,7 +46,7 @@ const phaseDeepDive = require('./phases/deep-dive');
46
46
  // --- Utils (for run orchestration + alt modes) ---
47
47
  const { c } = require('./utils/colors');
48
48
  const { findDocsRecursive } = require('./utils/fs');
49
- const { promptUserText } = require('./utils/cli');
49
+ const { promptUserText, selectDocsToExclude } = require('./utils/cli');
50
50
  const { createProgressBar } = require('./utils/progress-bar');
51
51
  const { buildHealthReport, printHealthDashboard } = require('./utils/health-dashboard');
52
52
  const { saveHistory, buildHistoryEntry } = require('./utils/learning-loop');
@@ -92,9 +92,21 @@ async function run() {
92
92
 
93
93
  // Phase 3: Services
94
94
  bar.setPhase('services');
95
- const fullCtx = await phaseServices(ctx);
95
+ let fullCtx = await phaseServices(ctx);
96
96
  bar.tick('Services ready');
97
97
 
98
+ // Phase 3.5 (optional): Deep Summary — pre-summarize context docs
99
+ if (fullCtx.opts.deepSummary && fullCtx.ai && fullCtx.contextDocs.length > 0) {
100
+ // Interactive picker: let user choose docs to keep at full fidelity
101
+ if (process.stdin.isTTY && fullCtx.opts.deepSummaryExclude.length === 0) {
102
+ const excluded = await selectDocsToExclude(fullCtx.contextDocs);
103
+ fullCtx.opts.deepSummaryExclude = excluded;
104
+ }
105
+ bar.setPhase('deep-summary', 1);
106
+ fullCtx = await phaseDeepSummary(fullCtx);
107
+ bar.tick('Docs summarized');
108
+ }
109
+
98
110
  // Phase 4: Process each media file (video or audio)
99
111
  const allSegmentAnalyses = [];
100
112
  const allSegmentReports = [];
@@ -117,6 +129,7 @@ async function run() {
117
129
  contextDocuments: fullCtx.contextDocs.map(d => d.fileName),
118
130
  documentStorageUrls: fullCtx.docStorageUrls,
119
131
  firebaseAuthenticated: fullCtx.firebaseReady,
132
+ deepSummary: fullCtx.deepSummaryStats || null,
120
133
  files: [],
121
134
  };
122
135
 
@@ -90,7 +90,7 @@ async function prepareDocsForGemini(ai, docFileList) {
90
90
  const pollStart = Date.now();
91
91
  while (file.state === 'PROCESSING') {
92
92
  if (Date.now() - pollStart > GEMINI_POLL_TIMEOUT_MS) {
93
- console.warn(` ${c.warn(`${name} — polling timed out after ${(GEMINI_POLL_TIMEOUT_MS / 1000).toFixed(0)}s, skipping`)}`);
93
+ console.warn(` ${c.warn(`${name} — file is still processing after ${(GEMINI_POLL_TIMEOUT_MS / 1000).toFixed(0)}s, skipping (you can increase the wait time with GEMINI_POLL_TIMEOUT_MS in .env)`)}`);
94
94
  file = null;
95
95
  break;
96
96
  }
@@ -287,7 +287,7 @@ async function processWithGemini(ai, filePath, displayName, contextDocs = [], pr
287
287
  const pollStart = Date.now();
288
288
  while (uploaded.state === 'PROCESSING') {
289
289
  if (Date.now() - pollStart > GEMINI_POLL_TIMEOUT_MS) {
290
- throw new Error(`Gemini file processing timed out after ${(GEMINI_POLL_TIMEOUT_MS / 1000).toFixed(0)}s for ${displayName}. Try again or increase GEMINI_POLL_TIMEOUT_MS.`);
290
+ throw new Error(`File "${displayName}" is still processing after ${(GEMINI_POLL_TIMEOUT_MS / 1000).toFixed(0)}s. Try again or increase the wait time by setting GEMINI_POLL_TIMEOUT_MS in your .env file.`);
291
291
  }
292
292
  process.stdout.write(` Processing${'.'.repeat((waited % 3) + 1)} \r`);
293
293
  await new Promise(r => setTimeout(r, 5000));
@@ -343,7 +343,7 @@ async function processWithGemini(ai, filePath, displayName, contextDocs = [], pr
343
343
  buildProgressiveContext(previousAnalyses, userName) || ''
344
344
  );
345
345
  const docBudget = Math.max(100000, config.GEMINI_CONTEXT_WINDOW - 350000 - prevContextEstimate);
346
- console.log(` Context budget: ${(docBudget / 1000).toFixed(0)}K tokens for docs (${contextDocs.length} available)`);
346
+ console.log(` Reference docs budget: ${(docBudget / 1000).toFixed(0)}K (${contextDocs.length} doc${contextDocs.length !== 1 ? 's' : ''} available)`);
347
347
 
348
348
  const { selected: selectedDocs, excluded, stats } = selectDocsByBudget(
349
349
  contextDocs, docBudget, { segmentIndex }
package/src/utils/cli.js CHANGED
@@ -35,7 +35,7 @@ function parseArgs(argv) {
35
35
  'skip-upload', 'force-upload', 'no-storage-url',
36
36
  'skip-compression', 'skip-gemini',
37
37
  'resume', 'reanalyze', 'dry-run',
38
- 'dynamic', 'deep-dive', 'update-progress',
38
+ 'dynamic', 'deep-dive', 'deep-summary', 'update-progress',
39
39
  'no-focused-pass', 'no-learning', 'no-diff',
40
40
  'no-html',
41
41
  ]);
@@ -371,6 +371,8 @@ ${f('(default)', 'Video/audio analysis — compress, analyze, compile')}
371
371
  ${f('--dynamic', 'Document generation — no media required')}
372
372
  ${f('--update-progress', 'Track item completion via git changes')}
373
373
  ${f('--deep-dive', 'Generate explanatory docs per topic')}
374
+ ${f('--deep-summary', 'Pre-summarize context docs (saves ~60-80% input tokens)')}
375
+ ${f('--exclude-docs <list>', 'Comma-separated doc names to keep full (use with --deep-summary)')}
374
376
 
375
377
  ${h('CORE OPTIONS')}
376
378
  ${f('--name <name>', 'Your name (skip interactive prompt)')}
@@ -435,6 +437,8 @@ ${f('--version, -v', 'Show version')}
435
437
  ${c.dim('$')} taskex --format pdf "call 1" ${c.dim('# PDF report')}
436
438
  ${c.dim('$')} taskex --format docx "call 1" ${c.dim('# Word document')}
437
439
  ${c.dim('$')} taskex --resume "call 1" ${c.dim('# Resume interrupted run')}
440
+ ${c.dim('$')} taskex --deep-summary "call 1" ${c.dim('# Pre-summarize docs, save tokens')}
441
+ ${c.dim('$')} taskex --deep-summary --exclude-docs "board.md,spec.md" "call 1" ${c.dim('# Keep specific docs full')}
438
442
  ${c.dim('$')} taskex --update-progress --repo ./my-project "call 1"
439
443
  `);
440
444
  // Signal early exit — pipeline checks for help flag before calling this
@@ -444,6 +448,7 @@ ${f('--version, -v', 'Show version')}
444
448
  module.exports = {
445
449
  parseArgs, showHelp, discoverFolders, selectFolder, selectModel,
446
450
  promptUser, promptUserText, selectRunMode, selectFormats, selectConfidence,
451
+ selectDocsToExclude,
447
452
  };
448
453
 
449
454
  // ======================== INTERACTIVE PROMPTS ========================
@@ -527,6 +532,9 @@ const RUN_PRESETS = {
527
532
  },
528
533
  };
529
534
 
535
+ // Attach RUN_PRESETS to exports (defined after module.exports due to const ordering)
536
+ module.exports.RUN_PRESETS = RUN_PRESETS;
537
+
530
538
  /**
531
539
  * Interactive run-mode selector. Shows preset options and returns the chosen
532
540
  * preset key. The caller applies overrides to opts.
@@ -721,3 +729,83 @@ async function selectConfidence() {
721
729
  });
722
730
  });
723
731
  }
732
+
733
+ // ======================== DEEP SUMMARY DOC EXCLUSION PICKER ========================
734
+
735
+ /**
736
+ * Interactive picker: let user select documents to EXCLUDE from deep summary.
737
+ * Excluded docs stay at full fidelity; the summary pass focuses on their topics.
738
+ *
739
+ * @param {Array<{fileName: string, type: string, content?: string}>} contextDocs - Prepared docs
740
+ * @returns {Promise<string[]>} Array of excluded fileName strings
741
+ */
742
+ async function selectDocsToExclude(contextDocs) {
743
+ const readline = require('readline');
744
+
745
+ // Only show inlineText docs with actual content
746
+ const eligible = contextDocs
747
+ .filter(d => d.type === 'inlineText' && d.content && d.content.length > 0)
748
+ .map(d => ({
749
+ fileName: d.fileName,
750
+ chars: d.content.length,
751
+ tokensEst: Math.ceil(d.content.length * 0.3),
752
+ }));
753
+
754
+ if (eligible.length === 0) return [];
755
+
756
+ console.log('');
757
+ console.log(` ${c.bold('📋 Deep Summary — Choose What to Keep in Full')}`);
758
+ console.log(c.dim(' ' + '─'.repeat(60)));
759
+ console.log('');
760
+ console.log(` ${c.dim('To save processing time, we can create short summaries of your')}`);
761
+ console.log(` ${c.dim('reference documents. The AI will still read them — just faster.')}`);
762
+ console.log('');
763
+ console.log(` ${c.bold('If a document is especially important to you, select it below')}`);
764
+ console.log(` ${c.bold('to keep it in full.')} The rest will be smartly condensed.`);
765
+ console.log('');
766
+
767
+ eligible.forEach((d, i) => {
768
+ const num = c.cyan(`[${i + 1}]`);
769
+ const size = d.tokensEst >= 1000
770
+ ? c.dim(`~${(d.tokensEst / 1000).toFixed(0)}K words`)
771
+ : c.dim(`~${d.tokensEst} words`);
772
+ console.log(` ${num} ${c.bold(d.fileName)} ${size}`);
773
+ });
774
+ console.log('');
775
+ console.log(c.dim(' Tip: Enter = condense all · Type numbers to keep full (e.g. 1,3)'));
776
+ console.log('');
777
+
778
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
779
+ return new Promise(resolve => {
780
+ rl.question(' Keep full (e.g. 1,3 or Enter = condense all): ', answer => {
781
+ rl.close();
782
+ const trimmed = (answer || '').trim();
783
+
784
+ if (!trimmed) {
785
+ console.log(c.success('Got it — all documents will be condensed for faster processing'));
786
+ resolve([]);
787
+ return;
788
+ }
789
+
790
+ const parts = trimmed.split(/[\s,]+/).filter(Boolean);
791
+ const excluded = [];
792
+
793
+ for (const p of parts) {
794
+ const num = parseInt(p, 10);
795
+ if (num >= 1 && num <= eligible.length) {
796
+ excluded.push(eligible[num - 1].fileName);
797
+ }
798
+ }
799
+
800
+ if (excluded.length === 0) {
801
+ console.log(c.warn('No valid selections — condensing all documents'));
802
+ resolve([]);
803
+ return;
804
+ }
805
+
806
+ console.log(c.success(`Keeping ${excluded.length} doc(s) in full — the rest will be condensed:`));
807
+ excluded.forEach(f => console.log(` ${c.dim('•')} ${c.cyan(f)}`));
808
+ resolve(excluded);
809
+ });
810
+ });
811
+ }
package/EXPLORATION.md DELETED
@@ -1,514 +0,0 @@
1
- # Task Summary Extractor — Where We Are & Where We Can Go
2
-
3
- > **Version 9.0.0** — March 2026
4
- > Module map, codebase stats, and future roadmap.
5
- > For setup and CLI reference, see [README.md](README.md) · [Quick Start](QUICK_START.md)
6
- > For architecture diagrams and algorithms, see [ARCHITECTURE.md](ARCHITECTURE.md)
7
-
8
- ---
9
-
10
- ## Part 1: Where We Are Now
11
-
12
- ### Architecture Overview
13
-
14
- ```text
15
- ┌─────────────────────────────────────────────────────────────────────┐
16
- │ taskex (bin/taskex.js) or process_and_upload.js │
17
- │ (Entry Points) │
18
- └─────────────────────────┬───────────────────────────────────────────┘
19
-
20
- ┌─────────────────────────▼───────────────────────────────────────────┐
21
- │ pipeline.js (~920 lines) │
22
- │ 9-Phase Orchestrator + src/phases/ │
23
- │ │
24
- │ Init ──────► Discover ──► Services ──► ProcessVideo ──► Compile │
25
- │ │ learning │ │ │ │
26
- │ │ insights ┌─────▼────▼──┐ Diff │
27
- │ │ loaded │ For each │ Engine │
28
- │ │ │ segment: │ │ │
29
- │ │ │ ┌─────────┐ │ Output │
30
- │ │ │ │Compress │ │ (MD+HTML) │
31
- │ │ │ │Upload │ │ │ │
32
- │ │ │ │Analyze ◄──┼── Quality Gate │
33
- │ │ │ │ ↻Retry │ │ + Confidence│
34
- │ │ │ │ 🔍Focus │ │ Scoring │
35
- │ │ │ └─────────┘ │ │
36
- │ │ └─────────────┘ Summary │
37
- │ │ │ │
38
- │ └──────────────────── learning history saved ◄──────────┘ │
39
- └────┬──────────┬──────────┬──────────┬──────┬─────────┬─────────────┘
40
- │ │ │ │ │ │
41
- ┌────▼───┐ ┌───▼──────┐ ┌▼────────┐ ┌▼─────┐ ┌▼─────┐┌▼─────────┐
42
- │Services│ │ Utils │ │Renderers│ │Logger│ │Schema││ Config │
43
- │ │ │ │ │ │ │ │ │ ││ │
44
- │gemini │ │quality │ │markdown │ │JSONL │ │seg ││dotenv │
45
- │firebase│ │-gate │ │(801 ln) │ │struct│ │comp ││validation│
46
- │video │ │colors │ │html.js │ │spans │ │ ││env helper│
47
- │git │ │progress │ │(673 ln) │ │phases│ │ ││model reg │
48
- │doc- │ │-bar │ │pdf.js │ │metric│ │ ││ │
49
- │parser │ │confidence│ │docx.js │ │ │ │ ││ │
50
- │ │ │-filter │ │shared │ │ │ │ ││ │
51
- │ │ │ │ │(212 ln) │ │ │ │ ││ │
52
- │ │ │schema- │ │ │ │ │ │ ││ │
53
- │ │ │validator │ │ │ │ │ │ ││ │
54
- │ │ │+15 more │ │ │ │ │ │ ││ │
55
- └────────┘ └──────────┘ └─────────┘ └──────┘ └──────┘└──────────┘
56
- ```
57
-
58
- ### Codebase Stats
59
-
60
- | Category | Files | Lines |
61
- | ---------- | ------- | ------- |
62
- | Pipeline orchestrator | 1 | ~920 |
63
- | Pipeline phases (`src/phases/`) | 9 | ~1,800 |
64
- | Services (Gemini, Firebase, Video, Git, Doc-Parser) | 5 | ~1,650 |
65
- | Modes (AI pipeline phases) | 5 | 2,054 |
66
- | Utilities (19 modules) | 19 | ~4,100 |
67
- | Renderers (markdown, html, pdf, docx, shared) | 5 | ~2,100 |
68
- | Config + Logger | 2 | 597 |
69
- | Schemas (JSON) | 2 | ~400 |
70
- | Entry points (taskex + legacy) | 2 | 79 |
71
- | Setup script | 1 | 418 |
72
- | Prompt (JSON) | 1 | 333 |
73
- | Tests (vitest) | 13 | ~3,200 |
74
- | **Total** | **~63 files** | **~13,000+ lines** |
75
-
76
- ### Version History
77
-
78
- | Version | Theme | Key Additions |
79
- | --------- | ------- | --------------- |
80
- | **v3** | Core Improvements | dotenv, logger, retry logic, CLI args, graceful shutdown, config validation, progress persistence, error handling, video fixes, parallel uploads |
81
- | **v4** | Architecture & Orchestration | Phase decomposition (7 phases), CostTracker, configurable thinking budget, poll timeouts, dead code cleanup, no `process.exit()`, CLI enhancements |
82
- | **v5** | Smart & Accurate | Quality Gate (4-dimension scoring), auto-retry with corrective hints, adaptive thinking budget, smart boundary detection, health dashboard, enhanced prompt engineering, compilation quality assessment |
83
- | **v6** | Self-Improving Intelligence | Confidence scoring per item, focused re-analysis for weak areas, learning loop (historical auto-tuning), diff-aware compilation (cross-run deltas), structured JSONL logging with phase spans, confidence badges in Markdown |
84
- | **v6.1** | Smart Change Detection | Git-based progress tracking, AI-powered change correlation, automatic item status assessment (DONE/IN_PROGRESS/NOT_STARTED), progress markdown reports, `--update-progress` mode |
85
- | **v6.2** | Deep Dive | `--deep-dive` generates explanatory docs per topic, 8 content categories |
86
- | **v7.0** | Dynamic Mode | `--dynamic` doc-only mode, interactive folder selection, fully flexible pipeline |
87
- | **v7.1** | Dynamic + Video | `--dynamic` now processes videos: compress, segment, analyze — works with any content |
88
- | **v7.2** | Model Selection | Interactive model selector, `--model` flag, 5-model registry with pricing, runtime model switching |
89
- | **v7.2.1** | Storage URL + Audit | Firebase Storage URLs as Gemini External URLs (skip File API upload), 3-strategy file resolution, URI reuse for retry/focused pass, Gemini file cleanup, confidence % fix, logger/firebase/git/version fixes |
90
- | **v7.2.2** | Upload Control | `--force-upload` to re-upload existing files, `--no-storage-url` to force Gemini File API, production-ready docs |
91
- | **v7.2.3** | Production Hardening | Cross-platform ffmpeg detection, shell injection fix (spawnSync), auto git init for `--update-progress`, `runs/` excluded from doc discovery, entry point docs updated |
92
- | **v8.0.0** | npm Package | Global CLI (`taskex`), `--gemini-key`/`--firebase-*` config flags, CWD-based path resolution, CWD-first `.env`, `bin/taskex.js` entry point, npm publish-ready `package.json` |
93
- | **v8.1.0** | Smart Global Config | Persistent `~/.taskexrc` config, `taskex config` subcommand, first-run API key prompting, 5-level config resolution, production audit (14 fixes), shared CLI flag injection, boolean flag parser fix |
94
- | **v8.2.0** | Architecture Cleanup | `src/modes/` for AI pipeline phases, `retry.js` self-contained defaults, dead code removal, export trimming, `process_and_upload.js` slim shim, `progress.js` → `checkpoint.js`, merged `prompt.js` into `cli.js` |
95
- | **v8.3.0** | Universal Content Analysis | prompt.json v4.0.0 — input type auto-detection (video/audio/document/mixed), timestamps conditional, domain-adaptive extraction for any content source, gemini.js bridge text generalized |
96
- | **v9.0.0** | CLI UX + Pipeline Decomposition | Colors & progress bar, HTML reports (`results.html`), PDF & DOCX output (`results.pdf`, `results.docx`), JSON Schema validation (`src/schemas/`), confidence filter (`--min-confidence`), pipeline decomposed into `src/phases/` (9 modules), test suite (285 tests via vitest), multi-format output (`--format`: md/html/json/pdf/docx/all), doc-parser service, shared renderer utilities |
97
-
98
- ### What v6 Delivers
99
-
100
- #### 1. Confidence Scoring Per Extracted Item
101
-
102
- Every ticket, action item, CR, blocker, and scope change now carries a `confidence` field (HIGH / MEDIUM / LOW) and a `confidence_reason` explaining the evidence basis.
103
-
104
- | Confidence | Meaning | Example |
105
- | ------------ | --------- | --------- |
106
- | **HIGH** | Explicitly stated + corroborated by docs/context | "Mentioned by name with ticket ID in VTT and Azure DevOps" |
107
- | **MEDIUM** | Partially stated or single-source | "Discussed verbally but no written reference" |
108
- | **LOW** | Inferred from context, not directly stated | "Implied from related discussion, not explicitly assigned" |
109
-
110
- **Where it shows up:**
111
-
112
- - **Quality Gate** (`quality-gate.js` — 366 lines): New 15-point confidence coverage dimension in density scoring. Flags missing confidence fields and suspicious uniformity (all HIGH = likely not calibrated). Generates retry hints for poor confidence.
113
- - **Markdown Renderer** (`markdown.js` — 879 lines): Confidence badges (🟢 🟡 🔴) on every ticket header, action item row, CR row, blocker, scope change, and todo item. "📊 Confidence Distribution" summary table near report header.
114
- - **Prompt** (`prompt.json` — 333 lines): Explicit confidence scoring instructions injected into extraction prompt. Self-verification checklist updated.
115
-
116
- #### 2. Focused Re-Analysis (`focused-reanalysis.js` — 268 lines)
117
-
118
- When the quality gate identifies specific weak dimensions (score <60, ≥2 weak areas), a **targeted second pass** runs instead of a full re-analysis.
119
-
120
- | Component | What It Does |
121
- | ----------- | -------------- |
122
- | `identifyWeaknesses()` | Analyzes quality dimensions + confidence coverage to find gaps (missing tickets, sparse assignees, low confidence items, broken cross-refs) |
123
- | `runFocusedPass()` | Sends a focused Gemini prompt targeting ONLY the weak areas, with reduced thinking budget (12K tokens) |
124
- | `mergeFocusedResults()` | Intelligent merge: updates existing items by ID, appends new items, marks `_enhanced_by_focused_pass` / `_from_focused_pass` |
125
-
126
- **Pipeline integration**: Runs after the quality gate + retry cycle for each segment. Controlled by `--no-focused-pass` flag. Costs tracked separately in cost tracker.
127
-
128
- #### 3. Learning & Improvement Loop (`learning-loop.js` — 269 lines)
129
-
130
- The pipeline remembers its past performance and auto-tunes for the future.
131
-
132
- **How it works:**
133
-
134
- 1. **Before processing**: `loadHistory()` reads `history.json` (up to 50 past runs), `analyzeHistory()` computes trends and budget adjustments
135
- 2. **Budget auto-tuning**: If avg quality <45 across recent runs → boost thinking budget +4096 tokens. If >80 → reduce by 2048 to save cost.
136
- 3. **Retry effectiveness**: Tracks whether retries actually improve quality. If retry success rate <30%, recommends increasing base budget instead.
137
- 4. **After processing**: `saveHistory()` persists compact metrics (quality scores, extraction counts, costs, budgets, retry stats) for the next run.
138
-
139
- ```text
140
- 📈 Learning Insights:
141
- Historical runs : 12
142
- Quality trend : improving (avg: 74/100)
143
- Budget adjust : -2048 tokens (analysis)
144
- Recommendations :
145
- • High average quality (74/100) — reducing thinking budget by 2048 tokens to save cost
146
- • Focused re-analysis was used in 3/10 runs — system is self-correcting effectively
147
- ```
148
-
149
- #### 4. Diff-Aware Compilation (`diff-engine.js` — 277 lines)
150
-
151
- Compares the current run's compiled analysis against the previous run to produce a delta report.
152
-
153
- | Diff Category | What's Detected |
154
- | --------------- | ----------------- |
155
- | **New items** | Tickets, CRs, action items, blockers, scope changes that didn't exist before |
156
- | **Removed items** | Items from the previous run that no longer appear |
157
- | **Changed items** | Status, priority, assignee, or confidence changes on existing items |
158
- | **Unchanged** | Items that remain identical |
159
-
160
- **Output**: Appended to `results.md` as a "🔄 Changes Since Previous Run" section with summary table + detailed new/removed/changed listings. Also saved as `diff.json` in the run folder.
161
-
162
- #### 5. Structured Logging & Observability (`logger.js` — 306 lines)
163
-
164
- The logger now writes **three parallel outputs**:
165
-
166
- | Output | Format | Purpose |
167
- | -------- | -------- | --------- |
168
- | `*_detailed.log` | Human-readable | Full debug/info/warn/error messages |
169
- | `*_minimal.log` | Compact steps | Steps + timestamps only |
170
- | `*_structured.jsonl` | Machine-readable JSONL | Every event as a JSON object with level, timestamp, context, phase |
171
-
172
- **New capabilities:**
173
-
174
- - **Phase spans**: `phaseStart(name)` / `phaseEnd(meta)` track timing per pipeline phase with structured records
175
- - **Operation context**: `setContext()` / `clearContext()` stack for enriching log entries with segment/operation metadata
176
- - **Structured metrics**: `metric(name, value)` for recording quantitative data (confidence coverage, token counts, etc.)
177
- - All phase timers auto-emit structured span events
178
-
179
- #### 6. Enhanced Quality Gate (`quality-gate.js` — 366 lines)
180
-
181
- **New in v6:** Confidence coverage is now a scoring dimension within density (15 points):
182
-
183
- - Checks percentage of items with valid confidence fields
184
- - Detects suspicious uniformity (all same confidence = likely not calibrated)
185
- - New `getConfidenceStats(analysis)` export returns `{total, high, medium, low, missing, coverage}`
186
- - Two new retry hint generators for missing/uniform confidence
187
-
188
- ### All v5 Features Retained
189
-
190
- | Feature | Module | Description |
191
- | --------- | -------- | ------------- |
192
- | Quality Gate | `quality-gate.js` | 4-dimension scoring (structure, density, integrity, cross-refs), auto-retry on FAIL |
193
- | Adaptive Thinking Budget | `adaptive-budget.js` | Segment position, complexity, context docs → dynamic 8K–32K range |
194
- | Smart Boundary Detection | `context-manager.js` | Mid-conversation detection, open ticket carry-forward, continuity hints |
195
- | Health Dashboard | `health-dashboard.js` | Quality scores, extraction density bars, retry stats, efficiency metrics |
196
- | Enhanced Prompt | `prompt.json` | Universal content analysis (v4.0.0): input type detection, timestamps conditional on content type, domain-adaptive extraction, self-verification checklist |
197
-
198
- ### Current Capabilities
199
-
200
- | Capability | Status | Description |
201
- | ------------ | -------- | ------------- |
202
- | Video compression | ✅ Mature | ffmpeg-based, CRF, configurable speed/preset |
203
- | Video segmentation | ✅ Mature | Time-based splitting, segment pre-validation |
204
- | Firebase upload | ✅ Mature | Parallel, retry, skip-existing, anonymous auth, async I/O, `--force-upload` re-upload |
205
- | Storage URL optimization | ✅ v7.2.1 New | Firebase download URLs used as Gemini External URLs — skips File API upload, `--no-storage-url` to disable |
206
- | Gemini segment analysis | ✅ Premium | 1M context, VTT slicing, progressive context, adaptive budget, 3-strategy file resolution |
207
- | Gemini file cleanup | ✅ v7.2.1 New | Auto-delete File API uploads after all passes complete |
208
- | Quality gate + retry | ✅ Enhanced | 4-dimension scoring + confidence coverage dimension, auto-retry with hints |
209
- | Confidence scoring | ✅ v6 New | HIGH/MEDIUM/LOW per item with evidence reasoning |
210
- | Focused re-analysis | ✅ v6 New | Targeted second pass for weak quality dimensions |
211
- | Learning loop | ✅ v6 New | Historical auto-tuning of budgets/thresholds across runs |
212
- | Diff engine | ✅ v6 New | Cross-run delta reports (new/removed/changed items) |
213
- | Structured logging | ✅ v6 New | JSONL structured log, phase spans, operation contexts, metrics |
214
- | Cross-segment continuity | ✅ Premium | Progressive context compression, boundary detection, focus instructions |
215
- | AI compilation | ✅ Premium | Dedup, name normalization, adaptive compilation budget |
216
- | Markdown rendering | ✅ Enhanced | Name clustering, ID dedup, confidence badges, diff section |
217
- | Cost tracking | ✅ Mature | Per-segment + compilation + focused passes, long-context tier pricing |
218
- | Progress persistence | ✅ Mature | Checkpoint/resume after crashes |
219
- | CLI | ✅ Complete | 18 flags, help, version, output dir |
220
- | Logging | ✅ v6 Rewritten | Triple output: detailed + minimal + structured JSONL |
221
- | Health dashboard | ✅ Mature | Quality, density, retries, efficiency |
222
-
223
- ### CLI Reference
224
-
225
- ```text
226
- Usage: taskex [options] [folder]
227
-
228
- Install: npm i -g task-summary-extractor
229
-
230
- If no folder is specified, shows an interactive folder selector.
231
-
232
- Configuration (override .env):
233
- --gemini-key <key> Gemini API key
234
- --firebase-key <key> Firebase API key
235
- --firebase-project <id> Firebase project ID
236
- --firebase-bucket <bucket> Firebase storage bucket
237
- --firebase-domain <domain> Firebase auth domain
238
-
239
- Modes:
240
- (default) Video analysis — compress, analyze, extract, compile
241
- --dynamic Document-only mode — no video required
242
- --update-progress Track item completion via git
243
- --deep-dive Generate explanatory docs per topic discussed
244
-
245
- Core Options:
246
- --name <name> Your name (skips interactive prompt)
247
- --model <id> Gemini model to use (skips interactive selector)
248
- --skip-upload Skip all Firebase Storage uploads
249
- --force-upload Re-upload files even if they already exist in Storage
250
- --no-storage-url Disable Storage URL optimization (force Gemini File API)
251
- --skip-compression Skip video compression (use existing segments)
252
- --skip-gemini Skip Gemini AI analysis
253
- --resume Resume from last checkpoint
254
- --reanalyze Force re-analysis of all segments
255
- --dry-run Show what would be done without executing
256
-
257
- Dynamic Mode:
258
- --dynamic Enable document-only mode
259
- --request <text> What to generate (prompted if omitted)
260
-
261
- Progress Tracking:
262
- --repo <path> Path to the project git repo
263
-
264
- Tuning:
265
- --parallel <n> Max parallel uploads (default: 3)
266
- --parallel-analysis <n> Concurrent segment analysis batches (default: 2)
267
- --thinking-budget <n> Thinking tokens per segment (default: 24576)
268
- --compilation-thinking-budget <n> Thinking tokens for compilation (default: 10240)
269
- --log-level <level> debug | info | warn | error (default: info)
270
- --output <dir> Custom output directory for results
271
- --no-focused-pass Disable focused re-analysis
272
- --no-learning Disable learning loop
273
- --no-diff Disable diff comparison
274
-
275
- Output:
276
- --format <type> Output format: md, html, json, pdf, docx, all (default: md)
277
- --min-confidence <level> Filter by confidence: high, medium, low
278
- --no-html Suppress HTML report generation
279
-
280
- Info:
281
- --help, -h Show help
282
- --version, -v Show version
283
- ```
284
-
285
- ### Full Module Map
286
-
287
- ```text
288
- bin/
289
- └── taskex.js 65 ln ★ v8.0.0 — Global CLI entry point, config flag injection
290
-
291
- src/
292
- ├── config.js 291 ln Central config, env vars, model registry, validation
293
- ├── logger.js 306 ln ★ v6 — Triple output: detailed + minimal + structured JSONL, phase spans, metrics
294
- ├── pipeline.js ~920 ln Multi-mode orchestrator with lazy phase imports, Storage URL optimization, upload control flags, learning loop, diff engine
295
- ├── phases/ ★ v9.0.0 — Decomposed pipeline phase modules
296
- │ ├── _shared.js Shared phase utilities (logging, error helpers)
297
- │ ├── init.js Phase 1: CLI parsing, config validation, logger setup
298
- │ ├── discover.js Phase 2: Find videos/audio, discover docs, resolve name
299
- │ ├── services.js Phase 3: Firebase auth, Gemini init, doc prep
300
- │ ├── process-media.js Phase 4: Compress, upload, analyze, quality gate
301
- │ ├── compile.js Phase 5: Cross-segment compilation, diff engine
302
- │ ├── output.js Phase 6: Write JSON, render MD + HTML
303
- │ ├── summary.js Phase 8: Save learning history, print summary
304
- │ └── deep-dive.js Phase 9: Optional deep-dive generation
305
- ├── modes/ ★ v8.2.0 — AI-heavy pipeline phase modules
306
- │ ├── change-detector.js 417 ln Git-based change correlation engine
307
- │ ├── deep-dive.js 473 ln ★ v6.2 — Topic discovery, parallel doc generation, index builder
308
- │ ├── dynamic-mode.js 494 ln ★ v7.0 — Context-only doc generation, topic planning, parallel writing
309
- │ ├── focused-reanalysis.js 268 ln ★ v6 — Weakness detection, targeted second pass, intelligent merge
310
- │ └── progress-updater.js 402 ln ★ v6.1 — AI-powered progress assessment, status report generation
311
- ├── renderers/
312
- │ ├── markdown.js 801 ln ★ v6 — Confidence badges (🟢🟡🔴), confidence distribution table, diff section
313
- │ ├── html.js 673 ln ★ v9.0.0 — Self-contained HTML report: collapsible sections, confidence badges, filtering, dark mode
314
- │ ├── pdf.js ★ v9.0.0 — PDF report renderer: HTML → PDF conversion via puppeteer
315
- │ ├── docx.js ★ v9.0.0 — DOCX report renderer: programmatic Word document via docx npm package
316
- │ └── shared.js 212 ln ★ v9.0.0 — Shared renderer utilities (name clustering, dedup, formatting)
317
- ├── services/
318
- │ ├── firebase.js 92 ln Init, upload, exists check (with retry, async I/O)
319
- │ ├── gemini.js 677 ln ★ v7.2.1 — 3-strategy file resolution, External URL support, cleanup, doc prep, analysis, compilation
320
- │ ├── git.js 264 ln ★ v7.2.3 — Git CLI wrapper: log, diff, status, changed files, auto-init
321
- │ ├── video.js 273 ln ★ v7.2.3 — ffmpeg compress, segment, probe (cross-platform, spawnSync)
322
- │ └── doc-parser.js 346 ln ★ v9.0.0 — Document text extraction (DOCX, XLSX, PPTX, HTML, ODT, RTF, EPUB)
323
- └── utils/ Pure utilities — parsing, retry, budget, config
324
- ├── adaptive-budget.js 230 ln ★ v5 — Transcript complexity → dynamic budget
325
- ├── checkpoint.js 145 ln Checkpoint/resume persistence (renamed from progress.js in v8.2.0)
326
- ├── cli.js 391 ln ★ v8.0.0 — Interactive prompts, model selector, folder picker, config flags, taskex help
327
- ├── context-manager.js 420 ln 4-tier priority, VTT slicing, progressive context, boundary detection
328
- ├── cost-tracker.js 140 ln Token counting, USD cost estimation (+ focused pass tracking)
329
- ├── diff-engine.js 277 ln ★ v6 — Cross-run delta: new/removed/changed items, Markdown rendering
330
- ├── format.js 27 ln Duration, bytes formatting
331
- ├── fs.js 34 ln Recursive doc finder (skips runs/)
332
- ├── global-config.js 274 ln ★ v8.1.0 — ~/.taskexrc persistent config, interactive setup
333
- ├── health-dashboard.js 191 ln ★ v5 — Quality report, density bars, efficiency metrics
334
- ├── inject-cli-flags.js 49 ln ★ v8.1.0 — CLI flag → env var injection
335
- ├── json-parser.js 216 ln 5-strategy JSON extraction + repair
336
- ├── learning-loop.js 269 ln ★ v6 — History I/O, trend analysis, budget auto-tuning, recommendations
337
- ├── quality-gate.js 366 ln ★ v6 — 4+1 dimension scoring (+ confidence coverage), retry hints
338
- ├── retry.js 118 ln Exponential backoff, parallel map (self-contained defaults)
339
- ├── colors.js 84 ln ★ v9.0.0 — Zero-dep ANSI color utility (bold, red, green, yellow, cyan, dim, reset)
340
- ├── progress-bar.js 287 ln ★ v9.0.0 — Visual progress bar with phase tracking, ETA, cost display, TTY-aware
341
- ├── confidence-filter.js 130 ln ★ v9.0.0 — Filter extracted items by confidence level (--min-confidence flag)
342
- └── schema-validator.js 260 ln ★ v9.0.0 — JSON Schema validation using ajv (segment + compiled schemas)
343
-
344
- schemas/
345
- ├── analysis-segment.schema.json ★ v9.0.0 — JSON Schema for segment analysis output
346
- └── analysis-compiled.schema.json ★ v9.0.0 — JSON Schema for compiled analysis output
347
-
348
- prompt.json 333 ln ★ v4.0.0 — Universal content analysis: video, audio, documents, mixed input; auto-detects input type + domain
349
- process_and_upload.js 14 ln Backward-compatible shim — delegates to bin/taskex.js
350
- setup.js 418 ln Automated first-time setup & environment validation (v8.0.0)
351
- ```
352
-
353
- ---
354
-
355
- ## Part 2: Where We Can Go
356
-
357
- ### Already Implemented (v6)
358
-
359
- The following features from the original exploration have been **fully implemented**:
360
-
361
- | Feature | Status | Implemented In |
362
- | --------- | -------- | ---------------- |
363
- | 📊 Confidence Scoring Per Extracted Item | ✅ Done | `prompt.json`, `quality-gate.js`, `markdown.js` |
364
- | 🔄 Multi-Pass Analysis (Focused Re-extraction) | ✅ Done | `modes/focused-reanalysis.js` (268 ln), pipeline integration |
365
- | 🧠 Learning & Improvement Loop | ✅ Done | `learning-loop.js` (270 ln), pipeline init + save |
366
- | 📝 Diff-Aware Compilation | ✅ Done | `diff-engine.js` (280 ln), pipeline output + MD |
367
- | 🔍 Structured Logging & Observability | ✅ Done | `logger.js` rewritten (303 ln), JSONL + spans + metrics |
368
- | Parallel segment analysis (via CLI) | ✅ Done | `--parallel-analysis` flag, pipeline batching |
369
- | 🔎 Smart Change Detection & Progress Tracking | ✅ Done | `git.js` (310 ln), `modes/change-detector.js` (417 ln), `modes/progress-updater.js` (402 ln), pipeline `--update-progress` mode |
370
- | 🗓️ Deep Dive Document Generation | ✅ Done | `modes/deep-dive.js` (473 ln), pipeline phase 9 |
371
- | 📝 Dynamic Mode (doc-only generation) | ✅ Done | `modes/dynamic-mode.js` (494 ln), pipeline `--dynamic` route |
372
- | 🤖 Runtime Model Selection | ✅ Done | `config.js` model registry, `cli.js` selector, `--model` flag |
373
- | 📊 Progress Bar | ✅ Done | `progress-bar.js` (287 ln), pipeline integration, TTY-aware |
374
- | 🌐 HTML Report Viewer | ✅ Done | `renderers/html.js` (673 ln), self-contained HTML with filtering, dark mode |
375
- | 🔧 JSON Schema Validation | ✅ Done | `schema-validator.js` (260 ln), `schemas/` (2 files), ajv-based |
376
- | 🎯 Confidence Filter | ✅ Done | `confidence-filter.js` (130 ln), `--min-confidence` flag |
377
- | 🏗️ Pipeline Decomposition | ✅ Done | `src/phases/` (9 modules), `pipeline.js` reduced to ~920 lines |
378
- | 🧪 Test Suite | ✅ Done | 285 tests across 13 files using vitest |
379
- | 🎨 ANSI Colors | ✅ Done | `colors.js` (84 ln), zero-dep ANSI color utility wired throughout CLI |
380
- | 📄 Doc Parser Service | ✅ Done | `doc-parser.js` (346 ln), DOCX/XLSX/PPTX/HTML/ODT/RTF/EPUB extraction |
381
-
382
- ---
383
-
384
- ### Tier 1: High-Impact, Medium Effort
385
-
386
- #### 🔊 Speaker Diarization & Attribution
387
-
388
- **What**: Automatically identify who is speaking at each moment in the video.
389
- **How**: Use Gemini's audio understanding or integrate a dedicated diarization API (e.g., AssemblyAI, Deepgram) as a preprocessing step. Map speaker segments to VTT timestamps.
390
- **Impact**: Dramatically improves action item attribution ("Mohamed said X" vs. "someone said X"). Currently relies on Gemini inferring speakers from VTT voice tags or contextual clues.
391
- **Modules affected**: New `services/diarization.js`, updates to `gemini.js` content building, `context-manager.js` for speaker-aware slicing.
392
-
393
- #### 🌐 ~~Web Dashboard / Viewer~~ ✅ Done (v9.0.0)
394
-
395
- **Status**: Implemented as `src/renderers/html.js` — self-contained HTML report with collapsible sections, confidence badges, filtering, dark mode, and print-friendly styling. Generated as `results.html` alongside `results.md`.
396
- **Next step**: Build a hosted React/Next.js viewer that reads from Firebase for team-wide access.
397
-
398
- ---
399
-
400
- ### Tier 2: Differentiation Features
401
-
402
- #### 🎯 Task Board Integration (Azure DevOps / Jira / Linear)
403
-
404
- **What**: Push extracted action items and tickets directly to your project management tool.
405
- **How**: After compilation, map extracted items to work item templates. Use Azure DevOps REST API / Jira API / Linear API to create/update items. Cross-reference extracted CR numbers with existing work items.
406
- **Impact**: Closes the loop — call discussions automatically become tracked work items. No manual "meeting notes → task creation" step.
407
- **Modules affected**: New `services/task-board.js`, integration config in `config.js`, new CLI flags (`--push-to-jira`, `--sync-devops`).
408
-
409
- #### 🎙️ Real-Time / Live Analysis Mode
410
-
411
- **What**: Analyze calls as they happen, producing running analysis instead of post-call batch processing.
412
- **How**: Stream audio/video chunks to Gemini in real-time using Live API. Maintain a rolling context window. Produce incremental analysis updates.
413
- **Impact**: During the call, participants see extracted items appearing in real-time. Post-call report is instant.
414
- **Modules affected**: New `services/live-stream.js`, new `pipeline-live.js`, WebSocket output to a dashboard.
415
-
416
- ---
417
-
418
- ### Tier 3: Platform Evolution
419
-
420
- #### 🏗️ Plugin Architecture
421
-
422
- **What**: Allow custom plugins for different output formats, analysis types, and integrations.
423
- **How**: Define hook points: `onSegmentAnalyzed`, `onCompiled`, `onOutput`. Plugins register handlers. Ship with built-in plugins (markdown, json, firebase). Community can add: Slack notifications, email summaries, PDF reports, custom prompts per team.
424
- **Impact**: Transforms from a single-purpose tool to a platform. Different teams customize for their workflow.
425
-
426
- #### 🤖 Multi-Model Support
427
-
428
- **What**: Support different AI models beyond Gemini — OpenAI GPT-4o, Claude, Llama local models.
429
- **Status**: *Partially implemented in v7.2* — runtime model selection across 5 Gemini models with `--model` flag and interactive selector. Full multi-provider abstraction (OpenAI, Claude, local) remains a future enhancement.
430
- **Next step**: Abstract the AI service behind a provider interface. Each provider implements: `upload()`, `analyze()`, `compile()`. Config selects the active provider.
431
- **Impact**: Users choose the best model for their budget/accuracy needs. Can run local models for sensitive content. Enables A/B testing between models.
432
-
433
- #### 📱 Mobile App / Bot
434
-
435
- **What**: Telegram/Teams/Slack bot that accepts video links and returns analysis.
436
- **How**: Bot receives a shared video/meeting link → triggers pipeline → sends back the compiled Markdown or a link to the web dashboard.
437
- **Impact**: Zero-friction usage — share a link, get a task summary. No CLI needed.
438
-
439
- #### 🔐 Multi-Tenant SaaS
440
-
441
- **What**: Hosted version where teams sign up, configure their projects, and get analysis as a service.
442
- **How**: Next.js frontend, Node.js API (reusing current pipeline), per-team Firebase/S3 storage, Stripe billing, queue-based processing.
443
- **Impact**: Commercial product. Teams pay per call analyzed. Revenue model.
444
-
445
- ---
446
-
447
- ### Tier 4: Polish & Reliability
448
-
449
- #### 🧪 ~~Test Suite~~ ✅ Done (v9.0.0)
450
-
451
- **Status**: 285 tests across 13 files using vitest. Covers: quality-gate, adaptive-budget, json-parser, confidence-filter, context-manager, diff-engine, format, progress-bar, retry, schema-validator, cli, and renderers (html, markdown).
452
- **Commands**: `npm test`, `npm run test:watch`, `npm run test:coverage`.
453
-
454
- #### 📦 ~~Packaging & Distribution~~ ✅ Done (v8.0.0)
455
-
456
- **Status**: Published as `task-summary-extractor` on npm. Global CLI: `taskex`. Install: `npm i -g task-summary-extractor`.
457
- **What was done**: `bin/taskex.js` entry point, `--gemini-key`/`--firebase-*` CLI config flags, CWD-based `.env` resolution, `PKG_ROOT`/`PROJECT_ROOT` path split for global compatibility.
458
-
459
- #### 🔍 Advanced Observability (OpenTelemetry)
460
-
461
- **What**: Extend the existing structured JSONL logging with OpenTelemetry trace export for external monitoring.
462
- **How**: Wrap existing `phaseStart`/`phaseEnd` spans with OTel SDK. Export traces to Jaeger/Grafana. Add alert rules on quality metric degradation.
463
- **Impact**: Production monitoring. Performance profiling across runs. Alert on quality regression trends.
464
- **Note**: Basic structured logging is already done in v6. This extends it to distributed tracing systems.
465
-
466
- #### 🌍 i18n Prompt Library
467
-
468
- **What**: Support different language pairs beyond Arabic+English. Ship prompt templates per domain.
469
- **How**: Move prompt.json to a `prompts/` directory with variants: `arabic-english-dotnet.json`, `spanish-english-react.json`, `french-english-java.json`. CLI flag: `--prompt-template react-english`.
470
- **Impact**: Anyone can use this tool regardless of their team's language or tech stack.
471
-
472
- ---
473
-
474
- ### Quick Wins (< 1 day each)
475
-
476
- | Feature | Effort | Impact |
477
- | --------- | -------- | -------- |
478
- | **Email summary** — send compiled MD via SMTP after processing | ~2 hrs | Users get results in inbox |
479
- | **Slack webhook** — post summary to a channel | ~1 hr | Team-wide visibility |
480
- | **Segment preview** — show first 3 VTT lines per segment before analyzing | ~30 min | Better UX during processing |
481
- | **Custom output templates** — Handlebars/Mustache for MD output | ~4 hrs | Teams customize report format |
482
- | **~~Audio-only mode~~** | ~~Done~~ | prompt.json v4.0.0 supports audio/doc/mixed — pipeline video requirement is next |
483
- | **Watch mode** — monitor a folder and auto-process new recordings | ~3 hrs | Hands-free automation |
484
- | **Git integration** — auto-commit results to repo | ~1 hr | Version-controlled meeting history |
485
- | **Confidence threshold filter** | ~~Done~~ | ★ v9.0.0 `--min-confidence` flag implemented in `confidence-filter.js` |
486
- | **History viewer** — CLI command to print learning loop trends without running pipeline | ~2 hrs | Introspect past performance |
487
-
488
- ---
489
-
490
- ### Recommended Next Sprint
491
-
492
- Based on impact vs. effort, here's a suggested 5-item sprint building on v7.2:
493
-
494
- 1. **~~Test suite foundation~~** — ✅ Done (v9.0.0) — 285 tests across 13 files using vitest
495
- 2. **~~Web dashboard / viewer~~** — ✅ Done (v9.0.0) — Self-contained HTML report with filtering and collapsible sections
496
- 3. **Speaker diarization** — Gemini audio understanding for speaker attribution (1.5 days)
497
- 4. **Task board integration** — Push tickets/CRs to Azure DevOps or Jira (1.5 days)
498
- 5. **Slack/email notification** — Post compiled results automatically (half day)
499
-
500
- These five deliver: reliability (tests), accessibility (dashboard), accuracy (speakers), workflow integration (task board), and team visibility (notifications).
501
-
502
- ---
503
-
504
- *Generated from the v9.0.0 codebase — ~63 files, ~13,000+ lines of self-improving pipeline intelligence. npm: `task-summary-extractor` · CLI: `taskex`*
505
-
506
- ---
507
-
508
- ## See Also
509
-
510
- | Doc | What's In It |
511
- | ----- | ------------- |
512
- | 📖 [README.md](README.md) | Setup, CLI flags, configuration, features |
513
- | 📖 [QUICK_START.md](QUICK_START.md) | Step-by-step first-time walkthrough |
514
- | 🏗️ [ARCHITECTURE.md](ARCHITECTURE.md) | Pipeline phases, processing flows, Mermaid diagrams |