task-summary-extractor 8.1.0 → 9.0.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/.env.example +38 -0
- package/ARCHITECTURE.md +116 -15
- package/EXPLORATION.md +164 -101
- package/QUICK_START.md +5 -2
- package/README.md +70 -18
- package/bin/taskex.js +11 -4
- package/package.json +39 -6
- package/process_and_upload.js +7 -100
- package/prompt.json +199 -131
- package/src/config.js +52 -3
- package/src/{utils → modes}/deep-dive.js +2 -2
- package/src/{utils → modes}/dynamic-mode.js +2 -2
- package/src/{utils → modes}/focused-reanalysis.js +4 -3
- package/src/{utils → modes}/progress-updater.js +2 -2
- package/src/phases/_shared.js +43 -0
- package/src/phases/compile.js +101 -0
- package/src/phases/deep-dive.js +118 -0
- package/src/phases/discover.js +178 -0
- package/src/phases/init.js +192 -0
- package/src/phases/output.js +238 -0
- package/src/phases/process-media.js +633 -0
- package/src/phases/services.js +104 -0
- package/src/phases/summary.js +86 -0
- package/src/pipeline.js +431 -1462
- package/src/renderers/docx.js +531 -0
- package/src/renderers/html.js +672 -0
- package/src/renderers/markdown.js +15 -183
- package/src/renderers/pdf.js +90 -0
- package/src/renderers/shared.js +211 -0
- package/src/schemas/analysis-compiled.schema.json +381 -0
- package/src/schemas/analysis-segment.schema.json +380 -0
- package/src/services/doc-parser.js +346 -0
- package/src/services/gemini.js +105 -48
- package/src/services/git.js +0 -29
- package/src/services/video.js +123 -8
- package/src/utils/adaptive-budget.js +6 -6
- package/src/utils/{progress.js → checkpoint.js} +2 -1
- package/src/utils/cli.js +161 -113
- package/src/utils/colors.js +83 -0
- package/src/utils/confidence-filter.js +138 -0
- package/src/utils/context-manager.js +0 -4
- package/src/utils/diff-engine.js +2 -4
- package/src/utils/global-config.js +6 -5
- package/src/utils/health-dashboard.js +11 -9
- package/src/utils/json-parser.js +5 -3
- package/src/utils/learning-loop.js +3 -3
- package/src/utils/progress-bar.js +286 -0
- package/src/utils/quality-gate.js +4 -8
- package/src/utils/retry.js +13 -5
- package/src/utils/schema-validator.js +314 -0
- package/src/utils/prompt.js +0 -32
- /package/src/{utils → modes}/change-detector.js +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// --- Services ---
|
|
7
|
+
const { initFirebase, uploadToStorage, storageExists } = require('../services/firebase');
|
|
8
|
+
const { initGemini, prepareDocsForGemini } = require('../services/gemini');
|
|
9
|
+
|
|
10
|
+
// --- Utils ---
|
|
11
|
+
const { parallelMap } = require('../utils/retry');
|
|
12
|
+
|
|
13
|
+
// --- Shared state ---
|
|
14
|
+
const { c } = require('../utils/colors');
|
|
15
|
+
const { getLog, isShuttingDown, phaseTimer } = require('./_shared');
|
|
16
|
+
|
|
17
|
+
// ======================== PHASE: SERVICES ========================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize Firebase and Gemini services, prepare context documents.
|
|
21
|
+
* Returns augmented ctx with storage, firebaseReady, ai, contextDocs.
|
|
22
|
+
*/
|
|
23
|
+
async function phaseServices(ctx) {
|
|
24
|
+
const log = getLog();
|
|
25
|
+
const timer = phaseTimer('services');
|
|
26
|
+
const { opts, allDocFiles } = ctx;
|
|
27
|
+
const callName = path.basename(ctx.targetDir);
|
|
28
|
+
|
|
29
|
+
console.log(`${c.dim('Initializing services...')}`);
|
|
30
|
+
|
|
31
|
+
let storage = null;
|
|
32
|
+
let firebaseReady = false;
|
|
33
|
+
if (!opts.skipUpload && !opts.dryRun) {
|
|
34
|
+
const fb = await initFirebase();
|
|
35
|
+
storage = fb.storage;
|
|
36
|
+
firebaseReady = fb.authenticated;
|
|
37
|
+
} else if (opts.skipUpload) {
|
|
38
|
+
console.log(` Firebase: ${c.dim('skipped (--skip-upload)')}`);
|
|
39
|
+
} else {
|
|
40
|
+
console.log(` Firebase: ${c.dim('skipped (--dry-run)')}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let ai = null;
|
|
44
|
+
if (!opts.skipGemini && !opts.dryRun) {
|
|
45
|
+
ai = await initGemini();
|
|
46
|
+
console.log(` ${c.success('Gemini AI: ready')}`);
|
|
47
|
+
} else if (opts.skipGemini) {
|
|
48
|
+
console.log(` Gemini AI: ${c.dim('skipped (--skip-gemini)')}`);
|
|
49
|
+
} else {
|
|
50
|
+
console.log(` Gemini AI: ${c.dim('skipped (--dry-run)')}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
log.step(`Services: Firebase auth=${firebaseReady}, Gemini=${ai ? 'ready' : 'skipped'}`);
|
|
54
|
+
|
|
55
|
+
// --- Prepare documents for Gemini ---
|
|
56
|
+
let contextDocs = [];
|
|
57
|
+
if (ai) {
|
|
58
|
+
contextDocs = await prepareDocsForGemini(ai, allDocFiles);
|
|
59
|
+
} else if (allDocFiles.length > 0) {
|
|
60
|
+
console.log(` ${c.warn('Skipping Gemini doc preparation (AI not active)')}`);
|
|
61
|
+
contextDocs = allDocFiles
|
|
62
|
+
.filter(({ absPath }) => ['.txt', '.md', '.vtt', '.srt', '.csv', '.json', '.xml', '.html']
|
|
63
|
+
.includes(path.extname(absPath).toLowerCase()))
|
|
64
|
+
.map(({ absPath, relPath }) => ({
|
|
65
|
+
type: 'inlineText',
|
|
66
|
+
fileName: relPath,
|
|
67
|
+
content: fs.readFileSync(absPath, 'utf8'),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Upload documents to Firebase Storage for archival ---
|
|
72
|
+
const docStorageUrls = {};
|
|
73
|
+
if (firebaseReady && !opts.skipUpload) {
|
|
74
|
+
await parallelMap(allDocFiles, async ({ absPath: docPath, relPath }) => {
|
|
75
|
+
if (isShuttingDown()) return;
|
|
76
|
+
const docStoragePath = `calls/${callName}/documents/${relPath}`;
|
|
77
|
+
try {
|
|
78
|
+
if (!opts.forceUpload) {
|
|
79
|
+
const existingUrl = await storageExists(storage, docStoragePath);
|
|
80
|
+
if (existingUrl) {
|
|
81
|
+
docStorageUrls[relPath] = existingUrl;
|
|
82
|
+
console.log(` ${c.success(`Document already in Storage \u2192 ${c.cyan(docStoragePath)}`)}`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const url = await uploadToStorage(storage, docPath, docStoragePath);
|
|
87
|
+
docStorageUrls[relPath] = url;
|
|
88
|
+
console.log(` ${c.success(`Document ${opts.forceUpload ? '(re-uploaded)' : '\u2192'} ${c.cyan(docStoragePath)}`)}`);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.warn(` ${c.warn(`Document upload failed (${relPath}): ${err.message}`)}`);
|
|
91
|
+
}
|
|
92
|
+
}, opts.parallel);
|
|
93
|
+
} else if (opts.skipUpload) {
|
|
94
|
+
console.log(` ${c.warn('Skipping document uploads (--skip-upload)')}`);
|
|
95
|
+
} else {
|
|
96
|
+
console.log(` ${c.warn('Skipping document uploads (Firebase auth not configured)')}`);
|
|
97
|
+
}
|
|
98
|
+
console.log('');
|
|
99
|
+
|
|
100
|
+
timer.end();
|
|
101
|
+
return { ...ctx, storage, firebaseReady, ai, contextDocs, docStorageUrls, callName };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = phaseServices;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// --- Config ---
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
|
|
6
|
+
// --- Utils ---
|
|
7
|
+
const { c } = require('../utils/colors');
|
|
8
|
+
|
|
9
|
+
// --- Shared state ---
|
|
10
|
+
const { getLog } = require('./_shared');
|
|
11
|
+
|
|
12
|
+
// ======================== PHASE: SUMMARY ========================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Print the final summary with timing, cost, and file locations.
|
|
16
|
+
*/
|
|
17
|
+
function phaseSummary(ctx, results, { jsonPath, mdPath, runTs, compilationRun }) {
|
|
18
|
+
const log = getLog();
|
|
19
|
+
const { opts, firebaseReady, callName, docStorageUrls, costTracker } = ctx;
|
|
20
|
+
const totalSegs = results.files.reduce((s, f) => s + f.segmentCount, 0);
|
|
21
|
+
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(c.cyan('══════════════════════════════════════════════'));
|
|
24
|
+
console.log(c.heading(' COMPLETE'));
|
|
25
|
+
console.log(c.cyan('══════════════════════════════════════════════'));
|
|
26
|
+
console.log(` Results JSON : ${c.cyan(jsonPath)}`);
|
|
27
|
+
console.log(` Results MD : ${c.cyan(mdPath)}`);
|
|
28
|
+
console.log(` Files : ${c.highlight(results.files.length)}`);
|
|
29
|
+
console.log(` Segments : ${c.highlight(totalSegs)}`);
|
|
30
|
+
console.log(` Elapsed : ${c.yellow(log.elapsed())}`);
|
|
31
|
+
if (compilationRun) {
|
|
32
|
+
console.log(` Compilation : ${c.yellow((compilationRun.durationMs / 1000).toFixed(1) + 's')} | ${c.yellow((compilationRun.tokenUsage?.totalTokens?.toLocaleString() || '?') + ' tokens')}`);
|
|
33
|
+
}
|
|
34
|
+
results.files.forEach(f => {
|
|
35
|
+
console.log(` ${c.dim(f.originalFile)}: ${c.yellow(f.originalSizeMB + ' MB')} → ${c.green(f.compressedTotalMB + ' MB')} ${c.dim(`(${f.compressionRatio})`)}`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Cost breakdown
|
|
39
|
+
const cost = costTracker.getSummary();
|
|
40
|
+
if (cost.totalTokens > 0) {
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(` ${c.heading(`Cost estimate (${config.GEMINI_MODEL}):`)}`);
|
|
43
|
+
console.log(` Input tokens : ${c.yellow(cost.inputTokens.toLocaleString())} ${c.dim(`($${cost.inputCost.toFixed(4)})`)}`);
|
|
44
|
+
console.log(` Output tokens : ${c.yellow(cost.outputTokens.toLocaleString())} ${c.dim(`($${cost.outputCost.toFixed(4)})`)}`);
|
|
45
|
+
console.log(` Thinking tokens: ${c.yellow(cost.thinkingTokens.toLocaleString())} ${c.dim(`($${cost.thinkingCost.toFixed(4)})`)}`);
|
|
46
|
+
console.log(` Total : ${c.highlight(cost.totalTokens.toLocaleString() + ' tokens')} | ${c.green('$' + cost.totalCost.toFixed(4))}`);
|
|
47
|
+
console.log(` AI time : ${c.yellow((cost.totalDurationMs / 1000).toFixed(1) + 's')}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (firebaseReady && !opts.skipUpload) {
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log(` ${c.heading('Firebase Storage:')}`);
|
|
53
|
+
console.log(` ${c.dim(`calls/${callName}/documents/`)} → ${c.yellow(Object.keys(docStorageUrls).length)} doc(s)`);
|
|
54
|
+
console.log(` ${c.dim(`calls/${callName}/segments/`)} → ${c.yellow(totalSegs)} segment(s)`);
|
|
55
|
+
console.log(` ${c.dim(`calls/${callName}/runs/${runTs}/`)} → results.json + results.md`);
|
|
56
|
+
if (results.storageUrl) {
|
|
57
|
+
console.log(` Results URL: ${c.link(results.storageUrl)}`);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(` ${c.warn('Firebase Storage: uploads skipped')}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Log summary
|
|
65
|
+
log.summary([
|
|
66
|
+
`Call: ${callName}`,
|
|
67
|
+
`Videos: ${results.files.length}`,
|
|
68
|
+
`Segments: ${totalSegs}`,
|
|
69
|
+
`Compiled: ${results.compilation ? 'Yes (AI)' : 'No (fallback merge)'}`,
|
|
70
|
+
`Firebase: ${firebaseReady && !opts.skipUpload ? 'OK' : 'skipped'}`,
|
|
71
|
+
`Documents: ${results.contextDocuments.length}`,
|
|
72
|
+
`Cost: $${cost.totalCost.toFixed(4)} (${cost.totalTokens.toLocaleString()} tokens)`,
|
|
73
|
+
`Elapsed: ${log.elapsed()}`,
|
|
74
|
+
...results.files.map(f => ` ${f.originalFile}: ${f.originalSizeMB}MB → ${f.compressedTotalMB}MB (${f.compressionRatio})`),
|
|
75
|
+
`Results JSON: ${jsonPath}`,
|
|
76
|
+
`Results MD: ${mdPath}`,
|
|
77
|
+
`Logs: ${log.detailedPath}`,
|
|
78
|
+
]);
|
|
79
|
+
log.step('DONE');
|
|
80
|
+
|
|
81
|
+
console.log(` Logs: ${c.dim(log.detailedPath)}`);
|
|
82
|
+
console.log(` ${c.dim(log.minimalPath)}`);
|
|
83
|
+
console.log('');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = phaseSummary;
|