@sonnechasser/ntrp 0.1.4 → 0.1.7
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/dist/index.js +2377 -1214
- package/dist/index.js.map +1 -1
- package/dist/investigation/verbosity-cli.js +1523 -557
- package/dist/investigation/verbosity-cli.js.map +1 -1
- package/dist/mcp/server.js +2128 -1162
- package/dist/mcp/server.js.map +1 -1
- package/dist/whimsy/time-bank-smoke.js +24161 -0
- package/dist/whimsy/time-bank-smoke.js.map +1 -0
- package/package.json +2 -2
package/dist/mcp/server.js
CHANGED
|
@@ -1272,989 +1272,1990 @@ CREATE INDEX IF NOT EXISTS idx_health_readings_batch ON health_readings(upload_b
|
|
|
1272
1272
|
}
|
|
1273
1273
|
});
|
|
1274
1274
|
|
|
1275
|
-
// src/
|
|
1276
|
-
var
|
|
1277
|
-
__export(
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
getRecentSessions: () => getRecentSessions,
|
|
1291
|
-
getSessionsDir: () => getSessionsDir,
|
|
1292
|
-
getUnfinishedSessions: () => getUnfinishedSessions,
|
|
1293
|
-
hydrateAnalysisFromPersistedState: () => hydrateAnalysisFromPersistedState,
|
|
1294
|
-
initContext: () => initContext,
|
|
1295
|
-
initHeadlessAgentContext: () => initHeadlessAgentContext,
|
|
1296
|
-
isAnalysisReady: () => isAnalysisReady,
|
|
1297
|
-
isSessionInProgress: () => isSessionInProgress,
|
|
1298
|
-
lensBadgeLabel: () => lensBadgeLabel,
|
|
1299
|
-
listSessions: () => listSessions,
|
|
1300
|
-
loadSessionFile: () => loadSessionFile,
|
|
1301
|
-
makeSessionId: () => makeSessionId,
|
|
1302
|
-
markLensCompleted: () => markLensCompleted,
|
|
1303
|
-
prefersMetricsFirstContext: () => prefersMetricsFirstContext,
|
|
1304
|
-
recordMessage: () => recordMessage,
|
|
1305
|
-
resetContextForSwitch: () => resetContextForSwitch,
|
|
1306
|
-
resolveSessionByToken: () => resolveSessionByToken,
|
|
1307
|
-
rotateToFreshSession: () => rotateToFreshSession,
|
|
1308
|
-
saveSessionState: () => saveSessionState,
|
|
1309
|
-
setPrimaryLens: () => setPrimaryLens
|
|
1275
|
+
// src/config/store.ts
|
|
1276
|
+
var store_exports = {};
|
|
1277
|
+
__export(store_exports, {
|
|
1278
|
+
deleteConfigValue: () => deleteConfigValue,
|
|
1279
|
+
getConfigValue: () => getConfigValue,
|
|
1280
|
+
getExportsDir: () => getExportsDir,
|
|
1281
|
+
getKnowledgeDir: () => getKnowledgeDir,
|
|
1282
|
+
getMemoryDir: () => getMemoryDir,
|
|
1283
|
+
getStrategiesDir: () => getStrategiesDir,
|
|
1284
|
+
getWinsDir: () => getWinsDir,
|
|
1285
|
+
loadConfig: () => loadConfig,
|
|
1286
|
+
ntrpHome: () => ntrpHome,
|
|
1287
|
+
resetConfigCache: () => resetConfigCache,
|
|
1288
|
+
saveConfig: () => saveConfig,
|
|
1289
|
+
setConfigValue: () => setConfigValue
|
|
1310
1290
|
});
|
|
1311
|
-
import {
|
|
1312
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync, readFileSync, readdirSync, statSync, rmSync as rmSync2 } from "fs";
|
|
1291
|
+
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1313
1292
|
import { homedir } from "os";
|
|
1314
|
-
import {
|
|
1315
|
-
function
|
|
1316
|
-
|
|
1317
|
-
if (!ctx.dataset) return false;
|
|
1318
|
-
const counts = ctx.dataset.counts ?? {};
|
|
1319
|
-
return Object.values(counts).some((n) => n > 0);
|
|
1293
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
1294
|
+
function ntrpHome() {
|
|
1295
|
+
return NTRP_DIR2;
|
|
1320
1296
|
}
|
|
1321
|
-
function
|
|
1322
|
-
|
|
1297
|
+
function ensureDir2() {
|
|
1298
|
+
if (!existsSync2(NTRP_DIR2)) {
|
|
1299
|
+
mkdirSync2(NTRP_DIR2, { recursive: true });
|
|
1300
|
+
}
|
|
1323
1301
|
}
|
|
1324
|
-
function
|
|
1325
|
-
|
|
1302
|
+
function loadConfig() {
|
|
1303
|
+
if (cachedConfig) return cachedConfig;
|
|
1304
|
+
ensureDir2();
|
|
1305
|
+
if (!existsSync2(CONFIG_PATH)) {
|
|
1306
|
+
cachedConfig = {};
|
|
1307
|
+
return cachedConfig;
|
|
1308
|
+
}
|
|
1309
|
+
try {
|
|
1310
|
+
cachedConfig = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1311
|
+
} catch {
|
|
1312
|
+
cachedConfig = {};
|
|
1313
|
+
}
|
|
1314
|
+
return cachedConfig;
|
|
1315
|
+
}
|
|
1316
|
+
function saveConfig(config) {
|
|
1317
|
+
ensureDir2();
|
|
1318
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
1319
|
+
cachedConfig = config;
|
|
1320
|
+
}
|
|
1321
|
+
function resetConfigCache() {
|
|
1322
|
+
cachedConfig = null;
|
|
1323
|
+
}
|
|
1324
|
+
function getConfigValue(key) {
|
|
1325
|
+
if (key === "api-key") return loadConfig()["api-key"];
|
|
1326
|
+
if (key === "license-key") return process.env.NTRP_LICENSE_KEY ?? loadConfig()["license-key"];
|
|
1327
|
+
const config = loadConfig();
|
|
1328
|
+
return config[key];
|
|
1329
|
+
}
|
|
1330
|
+
function setConfigValue(key, value) {
|
|
1331
|
+
const config = loadConfig();
|
|
1332
|
+
config[key] = value;
|
|
1333
|
+
saveConfig(config);
|
|
1334
|
+
}
|
|
1335
|
+
function deleteConfigValue(key) {
|
|
1336
|
+
const config = loadConfig();
|
|
1337
|
+
delete config[key];
|
|
1338
|
+
saveConfig(config);
|
|
1339
|
+
}
|
|
1340
|
+
function getExportsDir() {
|
|
1341
|
+
const config = loadConfig();
|
|
1342
|
+
const dir = resolve2(config["export-dir"] ?? join2(NTRP_DIR2, "exports"));
|
|
1326
1343
|
if (!existsSync2(dir)) {
|
|
1327
1344
|
mkdirSync2(dir, { recursive: true });
|
|
1328
1345
|
}
|
|
1329
1346
|
return dir;
|
|
1330
1347
|
}
|
|
1331
|
-
function
|
|
1332
|
-
const dir = join2(
|
|
1348
|
+
function getStrategiesDir() {
|
|
1349
|
+
const dir = join2(NTRP_DIR2, "strategies");
|
|
1333
1350
|
if (!existsSync2(dir)) {
|
|
1334
1351
|
mkdirSync2(dir, { recursive: true });
|
|
1352
|
+
writeFileSync(join2(dir, "README.md"), `# Strategies
|
|
1353
|
+
|
|
1354
|
+
This directory holds your GTM strategy files. Each file describes a strategy you're executing.
|
|
1355
|
+
|
|
1356
|
+
## How to use
|
|
1357
|
+
|
|
1358
|
+
1. Create a markdown file for each active strategy (e.g., \`multi-thread-q2.md\`)
|
|
1359
|
+
2. Describe the goal, target segment, and success criteria
|
|
1360
|
+
3. Reference playbook plays that support this strategy
|
|
1361
|
+
4. After diagnosis, check if vital signs improved in the targeted area
|
|
1362
|
+
|
|
1363
|
+
## Example
|
|
1364
|
+
|
|
1365
|
+
\`\`\`markdown
|
|
1366
|
+
# Multi-Thread Enterprise Deals \u2014 Q2
|
|
1367
|
+
|
|
1368
|
+
**Goal:** Reduce single-threaded deals from 65% to under 30%
|
|
1369
|
+
**Segment:** Enterprise accounts > $100K
|
|
1370
|
+
**Play:** Multi-Thread Your Deals
|
|
1371
|
+
**Success metric:** Thread depth score > 70
|
|
1372
|
+
\`\`\`
|
|
1373
|
+
`);
|
|
1335
1374
|
}
|
|
1336
1375
|
return dir;
|
|
1337
1376
|
}
|
|
1338
|
-
function
|
|
1339
|
-
|
|
1377
|
+
function getMemoryDir() {
|
|
1378
|
+
const dir = join2(NTRP_DIR2, "memory");
|
|
1379
|
+
if (!existsSync2(dir)) {
|
|
1380
|
+
mkdirSync2(dir, { recursive: true });
|
|
1381
|
+
}
|
|
1382
|
+
return dir;
|
|
1340
1383
|
}
|
|
1341
|
-
function
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1384
|
+
function getKnowledgeDir() {
|
|
1385
|
+
const dir = join2(NTRP_DIR2, "knowledge");
|
|
1386
|
+
if (!existsSync2(dir)) {
|
|
1387
|
+
mkdirSync2(dir, { recursive: true });
|
|
1388
|
+
writeFileSync(join2(dir, "README.md"), `# Knowledge Packs
|
|
1389
|
+
|
|
1390
|
+
Drop case studies, GTM frameworks, benchmark reports, or playbooks here as
|
|
1391
|
+
markdown, text, or PDF. NTRP ingests them with \`/knowledge add <file>\` and
|
|
1392
|
+
references the most relevant passages during analysis \u2014 so the agent can learn
|
|
1393
|
+
from work done outside this platform.
|
|
1394
|
+
|
|
1395
|
+
## How to use
|
|
1396
|
+
|
|
1397
|
+
1. Add a file: \`/knowledge add ~/Downloads/plg-benchmarks-2026.pdf\`
|
|
1398
|
+
2. List what's indexed: \`/knowledge list\`
|
|
1399
|
+
3. Ask a question \u2014 relevant passages are pulled in automatically.
|
|
1400
|
+
`);
|
|
1401
|
+
}
|
|
1402
|
+
return dir;
|
|
1346
1403
|
}
|
|
1347
|
-
function
|
|
1348
|
-
|
|
1404
|
+
function getWinsDir() {
|
|
1405
|
+
const dir = join2(NTRP_DIR2, "wins");
|
|
1406
|
+
if (!existsSync2(dir)) {
|
|
1407
|
+
mkdirSync2(dir, { recursive: true });
|
|
1408
|
+
writeFileSync(join2(dir, "README.md"), `# Wins
|
|
1409
|
+
|
|
1410
|
+
This directory logs outcomes when a strategy or play succeeds. Each win creates a record that future diagnoses can reference.
|
|
1411
|
+
|
|
1412
|
+
## How to use
|
|
1413
|
+
|
|
1414
|
+
1. After executing a play, log the result here (e.g., \`2026-04-clean-pipeline.md\`)
|
|
1415
|
+
2. Include: what you did, what changed, before/after scores
|
|
1416
|
+
3. Future AI findings will reference wins to track improvement over time
|
|
1417
|
+
|
|
1418
|
+
## Example
|
|
1419
|
+
|
|
1420
|
+
\`\`\`markdown
|
|
1421
|
+
# Pipeline Cleanup \u2014 April 2026
|
|
1422
|
+
|
|
1423
|
+
**Play:** Clean Dead Pipeline
|
|
1424
|
+
**Before:** Freshness 29/100, $3.1M stale pipeline
|
|
1425
|
+
**After:** Freshness 72/100, removed 45 zombie deals
|
|
1426
|
+
**Impact:** Forecast accuracy improved from 62% to 84%
|
|
1427
|
+
\`\`\`
|
|
1428
|
+
`);
|
|
1429
|
+
}
|
|
1430
|
+
return dir;
|
|
1349
1431
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1432
|
+
var NTRP_DIR2, CONFIG_PATH, cachedConfig;
|
|
1433
|
+
var init_store = __esm({
|
|
1434
|
+
"src/config/store.ts"() {
|
|
1435
|
+
"use strict";
|
|
1436
|
+
NTRP_DIR2 = process.env.NTRP_HOME ? resolve2(process.env.NTRP_HOME) : join2(homedir(), ".ntrp");
|
|
1437
|
+
CONFIG_PATH = join2(NTRP_DIR2, "config.json");
|
|
1438
|
+
cachedConfig = null;
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
// src/config/install.ts
|
|
1443
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1444
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1445
|
+
import { join as join3 } from "path";
|
|
1446
|
+
function installPath() {
|
|
1447
|
+
return join3(ntrpHome(), "install.json");
|
|
1356
1448
|
}
|
|
1357
|
-
function
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
sessionFile,
|
|
1363
|
-
oneShot,
|
|
1364
|
-
execution: buildExecutionOptions({
|
|
1365
|
-
mode: oneShot ? "one_shot" : "interactive",
|
|
1366
|
-
...execution
|
|
1367
|
-
}),
|
|
1368
|
-
snapshot: { computeResult: null, divergences: [] },
|
|
1369
|
-
messages: [],
|
|
1370
|
-
conversation: [],
|
|
1371
|
-
stage: "new",
|
|
1372
|
-
deliverables: [],
|
|
1373
|
-
analysis: defaultSessionAnalysis(),
|
|
1374
|
-
wizardDepth: 0,
|
|
1375
|
-
attachments: [],
|
|
1376
|
-
deliverIntent: false,
|
|
1377
|
-
computeInProgress: false
|
|
1378
|
-
};
|
|
1449
|
+
function ensureDir3() {
|
|
1450
|
+
const dir = ntrpHome();
|
|
1451
|
+
if (!existsSync3(dir)) {
|
|
1452
|
+
mkdirSync3(dir, { recursive: true });
|
|
1453
|
+
}
|
|
1379
1454
|
}
|
|
1380
|
-
function
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1455
|
+
function isValidInstall(value) {
|
|
1456
|
+
if (!value || typeof value !== "object") return false;
|
|
1457
|
+
const r = value;
|
|
1458
|
+
return r.schema_version === 1 && typeof r.install_id === "string" && r.install_id.length > 0 && typeof r.created_at === "string";
|
|
1459
|
+
}
|
|
1460
|
+
function ensureInstall() {
|
|
1461
|
+
if (cachedInstall) return cachedInstall;
|
|
1462
|
+
const path = installPath();
|
|
1463
|
+
if (existsSync3(path)) {
|
|
1464
|
+
try {
|
|
1465
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
1466
|
+
if (isValidInstall(parsed)) {
|
|
1467
|
+
cachedInstall = parsed;
|
|
1468
|
+
return parsed;
|
|
1469
|
+
}
|
|
1470
|
+
} catch {
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
const record = {
|
|
1474
|
+
schema_version: 1,
|
|
1475
|
+
install_id: randomUUID2(),
|
|
1476
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1386
1477
|
};
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
if (ctx.resumedFromId) file.resumed_from = ctx.resumedFromId;
|
|
1392
|
-
if (ctx.analysis) file.analysis = ctx.analysis;
|
|
1393
|
-
if (ctx.scope) file.scope = ctx.scope;
|
|
1394
|
-
if (ctx.attachments && ctx.attachments.length > 0) file.attachments = ctx.attachments;
|
|
1395
|
-
if (ctx.llm && Object.keys(ctx.llm).length > 0) file.llm = ctx.llm;
|
|
1396
|
-
return file;
|
|
1478
|
+
ensureDir3();
|
|
1479
|
+
writeFileSync2(path, JSON.stringify(record, null, 2) + "\n");
|
|
1480
|
+
cachedInstall = record;
|
|
1481
|
+
return record;
|
|
1397
1482
|
}
|
|
1398
|
-
function
|
|
1399
|
-
return
|
|
1483
|
+
function getInstallId() {
|
|
1484
|
+
return ensureInstall().install_id;
|
|
1400
1485
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1486
|
+
var cachedInstall;
|
|
1487
|
+
var init_install = __esm({
|
|
1488
|
+
"src/config/install.ts"() {
|
|
1489
|
+
"use strict";
|
|
1490
|
+
init_store();
|
|
1491
|
+
cachedInstall = null;
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
// src/config/progress-migrate.ts
|
|
1496
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
1497
|
+
import { join as join4 } from "path";
|
|
1498
|
+
function legacyStatePath() {
|
|
1499
|
+
return join4(ntrpHome(), "state.json");
|
|
1403
1500
|
}
|
|
1404
|
-
function
|
|
1405
|
-
|
|
1406
|
-
ctx.analysis = { ...ctx.analysis, completed };
|
|
1501
|
+
function legacyStateBackupPath() {
|
|
1502
|
+
return join4(ntrpHome(), "state.json.bak");
|
|
1407
1503
|
}
|
|
1408
|
-
function
|
|
1409
|
-
|
|
1410
|
-
const hasHealth = analysis.completed.includes("gtm_health") || analysis.primary === "gtm_health";
|
|
1411
|
-
const hasMetrics = analysis.completed.includes("revenue_metrics") || analysis.primary === "revenue_metrics";
|
|
1412
|
-
if (hasHealth && hasMetrics) return "both";
|
|
1413
|
-
if (hasMetrics) return "metrics";
|
|
1414
|
-
return "health";
|
|
1415
|
-
}
|
|
1416
|
-
function buildAnalysisBlock(ctx) {
|
|
1417
|
-
return [
|
|
1418
|
-
`Primary lens: ${ctx.analysis.primary}`,
|
|
1419
|
-
`Completed: ${ctx.analysis.completed.join(", ") || "none"}`,
|
|
1420
|
-
`Badge: ${lensBadgeLabel(ctx.analysis)}`,
|
|
1421
|
-
ctx.analysis.coverage ? `Coverage: ${ctx.analysis.coverage.distinct_months} months, ${ctx.analysis.coverage.recommended_cadence} cadence` : null,
|
|
1422
|
-
ctx.analysis.data_source_type ? `Data source: ${ctx.analysis.data_source_type}` : null
|
|
1423
|
-
].filter(Boolean).join("\n");
|
|
1504
|
+
function progressPath() {
|
|
1505
|
+
return join4(ntrpHome(), "progress.json");
|
|
1424
1506
|
}
|
|
1425
|
-
function
|
|
1426
|
-
|
|
1507
|
+
function isValidLegacyState(value) {
|
|
1508
|
+
if (!value || typeof value !== "object") return false;
|
|
1509
|
+
const s = value;
|
|
1510
|
+
return s.schema_version === 1 && typeof s.total_minutes_saved === "number" && Array.isArray(s.credits) && Array.isArray(s.milestones_unlocked);
|
|
1427
1511
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
const
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1512
|
+
function migrateLegacyStateIfNeeded(installId) {
|
|
1513
|
+
if (existsSync4(progressPath())) return null;
|
|
1514
|
+
const legacyPath = legacyStatePath();
|
|
1515
|
+
if (!existsSync4(legacyPath)) return null;
|
|
1516
|
+
try {
|
|
1517
|
+
const parsed = JSON.parse(readFileSync3(legacyPath, "utf-8"));
|
|
1518
|
+
if (!isValidLegacyState(parsed)) return null;
|
|
1519
|
+
const { schema_version: _v, ...rest } = parsed;
|
|
1520
|
+
const progress = {
|
|
1521
|
+
...rest,
|
|
1522
|
+
schema_version: 2,
|
|
1523
|
+
install_id: installId
|
|
1438
1524
|
};
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1525
|
+
writeFileSync3(progressPath(), JSON.stringify(progress, null, 2) + "\n");
|
|
1526
|
+
try {
|
|
1527
|
+
renameSync(legacyPath, legacyStateBackupPath());
|
|
1528
|
+
} catch {
|
|
1529
|
+
}
|
|
1530
|
+
return progress;
|
|
1531
|
+
} catch {
|
|
1532
|
+
return null;
|
|
1442
1533
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const completed = [];
|
|
1449
|
-
if (diagnosis) completed.push("gtm_health");
|
|
1450
|
-
if (metrics?.metrics.length) completed.push("revenue_metrics");
|
|
1451
|
-
if (completed.length === 0) return;
|
|
1452
|
-
let primary = ctx.analysis.primary;
|
|
1453
|
-
if (completed.includes("revenue_metrics") && !completed.includes("gtm_health")) {
|
|
1454
|
-
primary = "revenue_metrics";
|
|
1455
|
-
} else if (completed.includes("gtm_health") && !completed.includes("revenue_metrics")) {
|
|
1456
|
-
primary = "gtm_health";
|
|
1534
|
+
}
|
|
1535
|
+
var init_progress_migrate = __esm({
|
|
1536
|
+
"src/config/progress-migrate.ts"() {
|
|
1537
|
+
"use strict";
|
|
1538
|
+
init_store();
|
|
1457
1539
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
// src/whimsy/usage-backfill.ts
|
|
1543
|
+
function isoWeekKey(d) {
|
|
1544
|
+
const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
|
|
1545
|
+
const day = date.getUTCDay() || 7;
|
|
1546
|
+
date.setUTCDate(date.getUTCDate() + 4 - day);
|
|
1547
|
+
const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
|
|
1548
|
+
const weekNo = Math.ceil(((date.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
1549
|
+
return `${date.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
|
|
1550
|
+
}
|
|
1551
|
+
function actionBase(action) {
|
|
1552
|
+
return action.split(":")[0] ?? action;
|
|
1553
|
+
}
|
|
1554
|
+
function bumpWeekly(weekly, week, patch) {
|
|
1555
|
+
const idx = weekly.findIndex((w) => w.week === week);
|
|
1556
|
+
const row = idx >= 0 ? { ...weekly[idx] } : { week, minutes_saved: 0, llm_calls: 0, actions: 0 };
|
|
1557
|
+
if (patch.minutes_saved) row.minutes_saved += patch.minutes_saved;
|
|
1558
|
+
if (patch.actions) row.actions += patch.actions;
|
|
1559
|
+
return idx >= 0 ? weekly.map((w, i) => i === idx ? row : w) : [...weekly, row];
|
|
1560
|
+
}
|
|
1561
|
+
function rebuildFromCredits(credits) {
|
|
1562
|
+
let diagnoses = 0;
|
|
1563
|
+
let metrics_runs = 0;
|
|
1564
|
+
let deliverables = 0;
|
|
1565
|
+
let nl_exchanges = 0;
|
|
1566
|
+
let weekly = [];
|
|
1567
|
+
let first_active_at;
|
|
1568
|
+
let last_active_at;
|
|
1569
|
+
for (const credit of credits) {
|
|
1570
|
+
if (!first_active_at || credit.at < first_active_at) first_active_at = credit.at;
|
|
1571
|
+
if (!last_active_at || credit.at > last_active_at) last_active_at = credit.at;
|
|
1572
|
+
const base = actionBase(credit.action);
|
|
1573
|
+
if (base === "diagnose" || base === "diagnose_findings") diagnoses++;
|
|
1574
|
+
if (base === "metrics" || base === "metrics_findings") metrics_runs++;
|
|
1575
|
+
if (base === "deliverable" || base === "deliverable_deck") deliverables++;
|
|
1576
|
+
if (base === "nl_answer") nl_exchanges++;
|
|
1577
|
+
const week = isoWeekKey(new Date(credit.at));
|
|
1578
|
+
weekly = bumpWeekly(weekly, week, { minutes_saved: credit.minutes, actions: 1 });
|
|
1579
|
+
}
|
|
1580
|
+
return {
|
|
1581
|
+
first_active_at,
|
|
1582
|
+
last_active_at,
|
|
1583
|
+
diagnoses,
|
|
1584
|
+
metrics_runs,
|
|
1585
|
+
deliverables,
|
|
1586
|
+
nl_exchanges,
|
|
1587
|
+
weekly
|
|
1588
|
+
};
|
|
1460
1589
|
}
|
|
1461
|
-
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1590
|
+
function mergeWeekly(existing, fromCredits) {
|
|
1591
|
+
const byWeek = /* @__PURE__ */ new Map();
|
|
1592
|
+
for (const row of fromCredits) {
|
|
1593
|
+
byWeek.set(row.week, { ...row });
|
|
1594
|
+
}
|
|
1595
|
+
for (const row of existing) {
|
|
1596
|
+
const prior = byWeek.get(row.week);
|
|
1597
|
+
if (prior) {
|
|
1598
|
+
byWeek.set(row.week, {
|
|
1599
|
+
week: row.week,
|
|
1600
|
+
minutes_saved: Math.max(prior.minutes_saved, row.minutes_saved),
|
|
1601
|
+
actions: Math.max(prior.actions, row.actions),
|
|
1602
|
+
llm_calls: row.llm_calls
|
|
1603
|
+
});
|
|
1604
|
+
} else {
|
|
1605
|
+
byWeek.set(row.week, { ...row });
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return [...byWeek.values()].sort((a, b) => a.week.localeCompare(b.week));
|
|
1609
|
+
}
|
|
1610
|
+
function migrateUsageIfNeeded(state) {
|
|
1611
|
+
if (state.credits.length === 0) return { state, changed: false };
|
|
1612
|
+
if (state.usage?.first_active_at) return { state, changed: false };
|
|
1613
|
+
const fromCredits = rebuildFromCredits(state.credits);
|
|
1614
|
+
const prior = state.usage;
|
|
1615
|
+
const usage = {
|
|
1616
|
+
sessions_closed: prior?.sessions_closed ?? 0,
|
|
1617
|
+
llm_calls: prior?.llm_calls ?? 0,
|
|
1618
|
+
input_tokens: prior?.input_tokens ?? 0,
|
|
1619
|
+
output_tokens: prior?.output_tokens ?? 0,
|
|
1620
|
+
...fromCredits,
|
|
1621
|
+
weekly: mergeWeekly(prior?.weekly ?? [], fromCredits.weekly)
|
|
1622
|
+
};
|
|
1623
|
+
return { state: { ...state, usage }, changed: true };
|
|
1465
1624
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
if (ctx.oneShot) return;
|
|
1470
|
-
try {
|
|
1471
|
-
writeFileSync(ctx.sessionFile, JSON.stringify(buildSessionFile(ctx), null, 2) + "\n");
|
|
1472
|
-
} catch {
|
|
1625
|
+
var init_usage_backfill = __esm({
|
|
1626
|
+
"src/whimsy/usage-backfill.ts"() {
|
|
1627
|
+
"use strict";
|
|
1473
1628
|
}
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
// src/config/progress.ts
|
|
1632
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
1633
|
+
import { join as join5 } from "path";
|
|
1634
|
+
function progressPath2() {
|
|
1635
|
+
return join5(ntrpHome(), "progress.json");
|
|
1474
1636
|
}
|
|
1475
|
-
function
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
} catch {
|
|
1637
|
+
function ensureDir4() {
|
|
1638
|
+
const dir = ntrpHome();
|
|
1639
|
+
if (!existsSync5(dir)) {
|
|
1640
|
+
mkdirSync4(dir, { recursive: true });
|
|
1480
1641
|
}
|
|
1481
1642
|
}
|
|
1482
|
-
function
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
}
|
|
1491
|
-
} catch {
|
|
1492
|
-
return null;
|
|
1493
|
-
}
|
|
1494
|
-
if (mostRecent === 0) return null;
|
|
1495
|
-
return formatRelativeTime(new Date(mostRecent));
|
|
1643
|
+
function emptyProgress(installId) {
|
|
1644
|
+
return {
|
|
1645
|
+
schema_version: 2,
|
|
1646
|
+
install_id: installId,
|
|
1647
|
+
total_minutes_saved: 0,
|
|
1648
|
+
credits: [],
|
|
1649
|
+
milestones_unlocked: []
|
|
1650
|
+
};
|
|
1496
1651
|
}
|
|
1497
|
-
function
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
if (
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1652
|
+
function isValidProgress(value) {
|
|
1653
|
+
if (!value || typeof value !== "object") return false;
|
|
1654
|
+
const s = value;
|
|
1655
|
+
return s.schema_version === 2 && typeof s.install_id === "string" && typeof s.total_minutes_saved === "number" && Array.isArray(s.credits) && Array.isArray(s.milestones_unlocked);
|
|
1656
|
+
}
|
|
1657
|
+
function reconcileInstallId(state) {
|
|
1658
|
+
const localId = getInstallId();
|
|
1659
|
+
if (state.install_id === localId) return { state, changed: false };
|
|
1660
|
+
if (!installMismatchWarned) {
|
|
1661
|
+
installMismatchWarned = true;
|
|
1662
|
+
console.warn(
|
|
1663
|
+
" progress.json install_id did not match this machine \u2014 rebound to local install."
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
return { state: { ...state, install_id: localId }, changed: true };
|
|
1509
1667
|
}
|
|
1510
|
-
function
|
|
1511
|
-
const
|
|
1512
|
-
if (!
|
|
1668
|
+
function readProgressFile() {
|
|
1669
|
+
const path = progressPath2();
|
|
1670
|
+
if (!existsSync5(path)) return { state: null, changed: false };
|
|
1513
1671
|
try {
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
session.thread = normalizeThread(session.thread);
|
|
1518
|
-
}
|
|
1519
|
-
return session;
|
|
1672
|
+
const parsed = JSON.parse(readFileSync4(path, "utf-8"));
|
|
1673
|
+
if (!isValidProgress(parsed)) return { state: null, changed: false };
|
|
1674
|
+
return reconcileInstallId(parsed);
|
|
1520
1675
|
} catch {
|
|
1521
|
-
return null;
|
|
1676
|
+
return { state: null, changed: false };
|
|
1522
1677
|
}
|
|
1523
1678
|
}
|
|
1524
|
-
function
|
|
1525
|
-
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
entries.push({
|
|
1540
|
-
id,
|
|
1541
|
-
created_at: session.created_at,
|
|
1542
|
-
ended_at: session.ended_at,
|
|
1543
|
-
exchange_count: session.exchange_count ?? Math.floor(session.messages.length / 2),
|
|
1544
|
-
summary: session.summary,
|
|
1545
|
-
name: session.name,
|
|
1546
|
-
stage: session.stage,
|
|
1547
|
-
dataset: session.dataset,
|
|
1548
|
-
deliverables: session.deliverables,
|
|
1549
|
-
analysis: session.analysis,
|
|
1550
|
-
mtime
|
|
1551
|
-
});
|
|
1552
|
-
} catch {
|
|
1553
|
-
}
|
|
1679
|
+
function loadProgress() {
|
|
1680
|
+
ensureInstall();
|
|
1681
|
+
const installId = getInstallId();
|
|
1682
|
+
let state = null;
|
|
1683
|
+
let changed = false;
|
|
1684
|
+
const fromFile = readProgressFile();
|
|
1685
|
+
if (fromFile.state) {
|
|
1686
|
+
state = fromFile.state;
|
|
1687
|
+
changed = fromFile.changed;
|
|
1688
|
+
}
|
|
1689
|
+
if (!state) {
|
|
1690
|
+
const migrated2 = migrateLegacyStateIfNeeded(installId);
|
|
1691
|
+
if (migrated2) {
|
|
1692
|
+
state = migrated2;
|
|
1693
|
+
changed = true;
|
|
1554
1694
|
}
|
|
1555
|
-
} catch {
|
|
1556
|
-
return [];
|
|
1557
1695
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
return
|
|
1567
|
-
|
|
1568
|
-
|
|
1696
|
+
if (!state) {
|
|
1697
|
+
state = emptyProgress(installId);
|
|
1698
|
+
changed = true;
|
|
1699
|
+
}
|
|
1700
|
+
const { state: usageMigrated, changed: usageChanged } = migrateUsageIfNeeded(state);
|
|
1701
|
+
state = usageMigrated;
|
|
1702
|
+
if (usageChanged) changed = true;
|
|
1703
|
+
if (changed) saveProgress(state);
|
|
1704
|
+
return state;
|
|
1705
|
+
}
|
|
1706
|
+
function saveProgress(state) {
|
|
1707
|
+
ensureDir4();
|
|
1708
|
+
const next = {
|
|
1709
|
+
...state,
|
|
1710
|
+
schema_version: 2,
|
|
1711
|
+
install_id: getInstallId()
|
|
1712
|
+
};
|
|
1713
|
+
writeFileSync4(progressPath2(), JSON.stringify(next, null, 2) + "\n");
|
|
1569
1714
|
}
|
|
1570
|
-
function
|
|
1571
|
-
|
|
1572
|
-
if (
|
|
1573
|
-
|
|
1574
|
-
|
|
1715
|
+
function appendCredit(state, credit) {
|
|
1716
|
+
const credits = [...state.credits, credit];
|
|
1717
|
+
if (credits.length > CREDIT_HISTORY_CAP) {
|
|
1718
|
+
credits.splice(0, credits.length - CREDIT_HISTORY_CAP);
|
|
1719
|
+
}
|
|
1720
|
+
return {
|
|
1721
|
+
...state,
|
|
1722
|
+
total_minutes_saved: state.total_minutes_saved + credit.minutes,
|
|
1723
|
+
credits
|
|
1724
|
+
};
|
|
1575
1725
|
}
|
|
1576
|
-
function
|
|
1577
|
-
return
|
|
1726
|
+
function hasCreditAction(state, action) {
|
|
1727
|
+
return state.credits.some((c) => c.action === action);
|
|
1578
1728
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
continue;
|
|
1590
|
-
}
|
|
1591
|
-
file.stage = "ended";
|
|
1592
|
-
file.ended_at = endedAt;
|
|
1593
|
-
const filePath = sessionPathForId(s.id);
|
|
1594
|
-
if (!filePath) {
|
|
1595
|
-
skipped.push(s.id);
|
|
1596
|
-
continue;
|
|
1597
|
-
}
|
|
1598
|
-
writeFileSync(filePath, JSON.stringify(file, null, 2) + "\n");
|
|
1599
|
-
closed.push(s.id);
|
|
1600
|
-
}
|
|
1601
|
-
const currentActive = active.some((s) => s.id === ctx.sessionId);
|
|
1602
|
-
if (currentActive) {
|
|
1603
|
-
const alreadyClosed = ctx.stage === "delivered" || ctx.stage === "ended";
|
|
1604
|
-
const hasWork = ctx.stage === "analyzed" || !!ctx.dataset || ctx.messages.length > 0 || ctx.deliverables.length > 0;
|
|
1605
|
-
if (!alreadyClosed && hasWork) {
|
|
1606
|
-
await endSession(ctx);
|
|
1607
|
-
if (!closed.includes(ctx.sessionId)) closed.push(ctx.sessionId);
|
|
1608
|
-
} else if (!alreadyClosed && (ctx.messages.length > 0 || !!ctx.dataset?.label)) {
|
|
1609
|
-
await endSession(ctx);
|
|
1610
|
-
if (!closed.includes(ctx.sessionId)) closed.push(ctx.sessionId);
|
|
1611
|
-
}
|
|
1729
|
+
var CREDIT_HISTORY_CAP, installMismatchWarned;
|
|
1730
|
+
var init_progress = __esm({
|
|
1731
|
+
"src/config/progress.ts"() {
|
|
1732
|
+
"use strict";
|
|
1733
|
+
init_install();
|
|
1734
|
+
init_progress_migrate();
|
|
1735
|
+
init_store();
|
|
1736
|
+
init_usage_backfill();
|
|
1737
|
+
CREDIT_HISTORY_CAP = 100;
|
|
1738
|
+
installMismatchWarned = false;
|
|
1612
1739
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
// src/ui/theme.ts
|
|
1743
|
+
import chalk from "chalk";
|
|
1744
|
+
function paint(token, text) {
|
|
1745
|
+
if (token === "dim") return chalk.dim(text);
|
|
1746
|
+
return chalk.hex(TOKENS[token])(text);
|
|
1617
1747
|
}
|
|
1618
|
-
function
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1748
|
+
function bold(text) {
|
|
1749
|
+
return chalk.bold(text);
|
|
1750
|
+
}
|
|
1751
|
+
function badge(label, tone = "muted") {
|
|
1752
|
+
const normalized = ` ${label.toUpperCase()} `;
|
|
1753
|
+
switch (tone) {
|
|
1754
|
+
case "success":
|
|
1755
|
+
return chalk.hex(TOKENS.success)(normalized);
|
|
1756
|
+
case "warning":
|
|
1757
|
+
return chalk.hex(TOKENS.warning)(normalized);
|
|
1758
|
+
case "error":
|
|
1759
|
+
return chalk.hex(TOKENS.error)(normalized);
|
|
1760
|
+
case "info":
|
|
1761
|
+
return chalk.hex(TOKENS.info)(normalized);
|
|
1762
|
+
case "accent":
|
|
1763
|
+
return chalk.hex(TOKENS.accent)(normalized);
|
|
1764
|
+
case "muted":
|
|
1765
|
+
return chalk.dim(normalized);
|
|
1623
1766
|
}
|
|
1624
|
-
return null;
|
|
1625
1767
|
}
|
|
1626
|
-
|
|
1627
|
-
return
|
|
1768
|
+
function sectionHeading(label) {
|
|
1769
|
+
return `${paint("accent", "\u25B8")} ${paint("accent", bold(label))}`;
|
|
1628
1770
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1771
|
+
function actionHint(label, command, detail) {
|
|
1772
|
+
const suffix = detail ? chalk.dim(` ${detail}`) : "";
|
|
1773
|
+
return `${chalk.dim(label)} ${paint("accent", command)}${suffix}`;
|
|
1631
1774
|
}
|
|
1632
|
-
|
|
1633
|
-
const
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
llm: void 0
|
|
1642
|
-
});
|
|
1643
|
-
ctx.datasetPath = datasetPathForSession(newId);
|
|
1644
|
-
await setActiveDbPath2(ctx.datasetPath);
|
|
1775
|
+
function scoreBar(score, status, width = 14) {
|
|
1776
|
+
const filled = Math.round(score / 100 * width);
|
|
1777
|
+
const color = chalk.hex(STATUS_COLORS[status]);
|
|
1778
|
+
let filledPart = "";
|
|
1779
|
+
for (let i = 0; i < filled; i++) {
|
|
1780
|
+
filledPart += i % 2 === 0 ? "\u2588" : "\u2593";
|
|
1781
|
+
}
|
|
1782
|
+
const emptyPart = "\u2591".repeat(width - filled);
|
|
1783
|
+
return color(filledPart) + chalk.dim(emptyPart);
|
|
1645
1784
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1785
|
+
var STATUS_COLORS, TOKENS;
|
|
1786
|
+
var init_theme = __esm({
|
|
1787
|
+
"src/ui/theme.ts"() {
|
|
1788
|
+
"use strict";
|
|
1789
|
+
init_formatters();
|
|
1790
|
+
STATUS_COLORS = {
|
|
1791
|
+
green: "#22c55e",
|
|
1792
|
+
yellow: "#eab308",
|
|
1793
|
+
red: "#ef4444"
|
|
1794
|
+
};
|
|
1795
|
+
TOKENS = {
|
|
1796
|
+
accent: "#14b8a6",
|
|
1797
|
+
accentBright: "#2dd4bf",
|
|
1798
|
+
border: "#334155",
|
|
1799
|
+
borderMuted: "#1e293b",
|
|
1800
|
+
dim: "#64748b",
|
|
1801
|
+
text: "#e2e8f0",
|
|
1802
|
+
error: "#ef4444",
|
|
1803
|
+
warning: "#eab308",
|
|
1804
|
+
success: "#22c55e",
|
|
1805
|
+
info: "#3b82f6"
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
});
|
|
1809
|
+
|
|
1810
|
+
// src/whimsy/time-milestones.ts
|
|
1811
|
+
function getMilestoneById(id) {
|
|
1812
|
+
return TIME_MILESTONES.find((m) => m.id === id);
|
|
1813
|
+
}
|
|
1814
|
+
function nextMilestone(totalHours, unlocked) {
|
|
1815
|
+
for (const m of TIME_MILESTONES) {
|
|
1816
|
+
if (!unlocked.includes(m.id) && totalHours < m.hours) {
|
|
1817
|
+
return m;
|
|
1663
1818
|
}
|
|
1664
|
-
return void 0;
|
|
1665
1819
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1820
|
+
return null;
|
|
1821
|
+
}
|
|
1822
|
+
function newlyUnlockedMilestones(previousMinutes, newMinutes, unlocked) {
|
|
1823
|
+
const prevHours = previousMinutes / 60;
|
|
1824
|
+
const newHours = newMinutes / 60;
|
|
1825
|
+
return TIME_MILESTONES.filter(
|
|
1826
|
+
(m) => !unlocked.includes(m.id) && newHours >= m.hours && prevHours < m.hours
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
var TIME_MILESTONES;
|
|
1830
|
+
var init_time_milestones = __esm({
|
|
1831
|
+
"src/whimsy/time-milestones.ts"() {
|
|
1832
|
+
"use strict";
|
|
1833
|
+
TIME_MILESTONES = [
|
|
1834
|
+
{
|
|
1835
|
+
id: "first_hour",
|
|
1836
|
+
hours: 1,
|
|
1837
|
+
title: "First hour back",
|
|
1838
|
+
message: "First hour back. That's one pipeline standup you didn't have to sit through."
|
|
1839
|
+
},
|
|
1840
|
+
{
|
|
1841
|
+
id: "half_day",
|
|
1842
|
+
hours: 4,
|
|
1843
|
+
title: "Half day",
|
|
1844
|
+
message: "4 hours saved \u2014 a half-day an analyst would've billed you for."
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
id: "analyst_day",
|
|
1848
|
+
hours: 8,
|
|
1849
|
+
title: "Analyst day",
|
|
1850
|
+
message: "A full analyst day, reclaimed."
|
|
1851
|
+
},
|
|
1852
|
+
{
|
|
1853
|
+
id: "long_weekend",
|
|
1854
|
+
hours: 24,
|
|
1855
|
+
title: "Three days",
|
|
1856
|
+
message: "Three analyst days. You could've been in spreadsheets."
|
|
1857
|
+
},
|
|
1858
|
+
{
|
|
1859
|
+
id: "analyst_week",
|
|
1860
|
+
hours: 40,
|
|
1861
|
+
title: "Analyst week",
|
|
1862
|
+
message: "A week of analyst time. Your calendar thanks you."
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
id: "analyst_fortnight",
|
|
1866
|
+
hours: 80,
|
|
1867
|
+
title: "Two weeks",
|
|
1868
|
+
message: "Two weeks of manual pipeline archaeology \u2014 skipped."
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
id: "analyst_month",
|
|
1872
|
+
hours: 160,
|
|
1873
|
+
title: "Analyst month",
|
|
1874
|
+
message: "A month of analyst hours. That's a hiring conversation you didn't need."
|
|
1875
|
+
},
|
|
1876
|
+
{
|
|
1877
|
+
id: "quarter_fte",
|
|
1878
|
+
hours: 500,
|
|
1879
|
+
title: "Quarter FTE",
|
|
1880
|
+
message: "500 hours. That's a quarter of a full-time analyst year."
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
id: "two_quarters",
|
|
1884
|
+
hours: 600,
|
|
1885
|
+
title: "Two quarters",
|
|
1886
|
+
message: "600 hours \u2014 half a fiscal year of analyst time, back in your calendar."
|
|
1887
|
+
},
|
|
1888
|
+
{
|
|
1889
|
+
id: "nine_months",
|
|
1890
|
+
hours: 720,
|
|
1891
|
+
title: "Nine months",
|
|
1892
|
+
message: "720 hours. Three quarters of a year \u2014 most teams never get this much outside help."
|
|
1893
|
+
},
|
|
1894
|
+
{
|
|
1895
|
+
id: "eleven_months",
|
|
1896
|
+
hours: 840,
|
|
1897
|
+
title: "Eleven months",
|
|
1898
|
+
message: "840 hours saved. You're one month shy of a full annual arc."
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
id: "annual_arc",
|
|
1902
|
+
hours: 960,
|
|
1903
|
+
title: "Annual arc",
|
|
1904
|
+
message: "960 hours \u2014 a year of normal use, banked. The subscription paid for itself."
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
id: "subscription_year",
|
|
1908
|
+
hours: 1100,
|
|
1909
|
+
title: "Subscription year",
|
|
1910
|
+
message: "1,100 hours. A full year plus wiggle room \u2014 even power users rarely climb higher."
|
|
1911
|
+
}
|
|
1912
|
+
];
|
|
1676
1913
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1916
|
+
// src/whimsy/time-perspectives.ts
|
|
1917
|
+
function getPerspectiveById(id) {
|
|
1918
|
+
return TIME_PERSPECTIVES.find((p) => p.id === id);
|
|
1919
|
+
}
|
|
1920
|
+
function ratioInBand(perspective, totalHours) {
|
|
1921
|
+
const ratio = totalHours / perspective.reference_hours;
|
|
1922
|
+
const min = perspective.min_ratio ?? 0.3;
|
|
1923
|
+
const max = perspective.max_ratio ?? 300;
|
|
1924
|
+
return ratio >= min && ratio <= max;
|
|
1925
|
+
}
|
|
1926
|
+
function pickPerspective(totalHours, options = {}) {
|
|
1927
|
+
if (totalHours <= 0) return null;
|
|
1928
|
+
const exclude = new Set(options.excludeIds ?? []);
|
|
1929
|
+
let candidates = TIME_PERSPECTIVES.filter((p) => !exclude.has(p.id) && ratioInBand(p, totalHours));
|
|
1930
|
+
if (candidates.length === 0) {
|
|
1931
|
+
candidates = TIME_PERSPECTIVES.filter((p) => !exclude.has(p.id));
|
|
1932
|
+
}
|
|
1933
|
+
if (candidates.length === 0) return TIME_PERSPECTIVES[0] ?? null;
|
|
1934
|
+
const otherCategories = candidates.filter((p) => p.category !== options.lastCategory);
|
|
1935
|
+
const pool = otherCategories.length > 0 ? otherCategories : candidates;
|
|
1936
|
+
const seed = options.seed ?? Date.now();
|
|
1937
|
+
return pool[Math.abs(seed) % pool.length] ?? null;
|
|
1938
|
+
}
|
|
1939
|
+
function formatRatio(ratio) {
|
|
1940
|
+
if (ratio >= 100) return Math.round(ratio).toString();
|
|
1941
|
+
if (ratio >= 10) return ratio.toFixed(0);
|
|
1942
|
+
if (ratio >= 1) return ratio.toFixed(1);
|
|
1943
|
+
return ratio.toFixed(2);
|
|
1944
|
+
}
|
|
1945
|
+
function formatPct(pct) {
|
|
1946
|
+
if (pct >= 10) return Math.round(pct).toString();
|
|
1947
|
+
if (pct >= 1) return pct.toFixed(1);
|
|
1948
|
+
return pct.toFixed(2);
|
|
1949
|
+
}
|
|
1950
|
+
function formatPerspectiveLine(perspective, totalHours) {
|
|
1951
|
+
const ratio = totalHours / perspective.reference_hours;
|
|
1952
|
+
const pct = ratio * 100;
|
|
1953
|
+
return perspective.template.replace("{ratio}", formatRatio(ratio)).replace("{pct}", formatPct(pct)).replace("{label}", perspective.label);
|
|
1954
|
+
}
|
|
1955
|
+
var TIME_PERSPECTIVES;
|
|
1956
|
+
var init_time_perspectives = __esm({
|
|
1957
|
+
"src/whimsy/time-perspectives.ts"() {
|
|
1958
|
+
"use strict";
|
|
1959
|
+
TIME_PERSPECTIVES = [
|
|
1960
|
+
{ id: "dsotm", category: "music", reference_hours: 0.74, label: "Dark Side of the Moon", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 1, max_ratio: 200 },
|
|
1961
|
+
{ id: "rush_2112", category: "music", reference_hours: 0.33, label: "2112", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 2, max_ratio: 200 },
|
|
1962
|
+
{ id: "bohemian_rhapsody", category: "music", reference_hours: 0.1, label: "Bohemian Rhapsody", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 5, max_ratio: 500 },
|
|
1963
|
+
{ id: "stairway", category: "music", reference_hours: 0.13, label: "Stairway to Heaven", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 5, max_ratio: 400 },
|
|
1964
|
+
{ id: "podcast_binge", category: "music", reference_hours: 0.75, label: "hour-long podcast episodes", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 300 },
|
|
1965
|
+
{ id: "abbey_road", category: "music", reference_hours: 0.8, label: "Abbey Road", template: "\u2248 {ratio}\xD7 through {label}", min_ratio: 1, max_ratio: 200 },
|
|
1966
|
+
{ id: "iron_maiden_set", category: "music", reference_hours: 2, label: "an Iron Maiden marathon set", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 50 },
|
|
1967
|
+
{ id: "festival_set", category: "music", reference_hours: 1.5, label: "main-stage festival sets", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 100 },
|
|
1968
|
+
{ id: "jazz_club", category: "music", reference_hours: 3, label: "late-night jazz sets", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 80 },
|
|
1969
|
+
{ id: "ring_cycle", category: "music", reference_hours: 15, label: "Wagner's Ring Cycle", template: "\u2248 {pct}% of {label}", min_ratio: 0.1, max_ratio: 2 },
|
|
1970
|
+
{ id: "shrek", category: "film", reference_hours: 1.5, label: "Shrek (the first one)", template: "\u2248 {ratio}\xD7 watching {label}", min_ratio: 1, max_ratio: 150 },
|
|
1971
|
+
{ id: "blockbuster", category: "film", reference_hours: 2.1, label: "average blockbusters", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 100 },
|
|
1972
|
+
{ id: "dune_two", category: "film", reference_hours: 2.75, label: "Dune: Part Two", template: "\u2248 {ratio}\xD7 in theater for {label}", min_ratio: 1, max_ratio: 100 },
|
|
1973
|
+
{ id: "scorsese", category: "film", reference_hours: 3.5, label: "Goodfellas", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 80 },
|
|
1974
|
+
{ id: "godfather", category: "film", reference_hours: 6.5, label: "the Godfather saga", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 20 },
|
|
1975
|
+
{ id: "lotr_extended", category: "film", reference_hours: 11.4, label: "the LOTR extended trilogy", template: "Longer than all of {label}", min_ratio: 1, max_ratio: 50 },
|
|
1976
|
+
{ id: "cooking_brisket", category: "film", reference_hours: 12, label: "low-and-slow brisket cooks", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
|
|
1977
|
+
{ id: "the_office", category: "film", reference_hours: 68, label: "The Office (full series)", template: "\u2248 {ratio}\xD7 bingeing {label}", min_ratio: 5, max_ratio: 200 },
|
|
1978
|
+
{ id: "marvel_marathon", category: "film", reference_hours: 50, label: "an MCU Phase One marathon", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
|
|
1979
|
+
{ id: "around_world", category: "film", reference_hours: 1920, label: "Around the World in 80 Days (fictionally)", template: "\u2248 {pct}% of {label}", min_ratio: 0.3, max_ratio: 1 },
|
|
1980
|
+
{ id: "soccer_match", category: "sports", reference_hours: 1.75, label: "Premier League matches", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 150 },
|
|
1981
|
+
{ id: "marathon", category: "sports", reference_hours: 2, label: "marathons at world-record pace", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 80 },
|
|
1982
|
+
{ id: "baseball_game", category: "sports", reference_hours: 3, label: "nine-inning baseball games", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 80 },
|
|
1983
|
+
{ id: "superbowl", category: "sports", reference_hours: 3.5, label: "Super Bowls", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 80 },
|
|
1984
|
+
{ id: "nfl_game", category: "sports", reference_hours: 3.25, label: "NFL games (with commercials)", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 50 },
|
|
1985
|
+
{ id: "wimbledon", category: "sports", reference_hours: 5, label: "Wimbledon finals", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
|
|
1986
|
+
{ id: "tour_stage", category: "sports", reference_hours: 4.5, label: "Tour de France stages", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 60 },
|
|
1987
|
+
{ id: "olympics", category: "sports", reference_hours: 250, label: "Summer Olympics broadcast hours", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 10 },
|
|
1988
|
+
{ id: "moon_light", category: "cosmos", reference_hours: 1.3 / 3600, label: "a beam of light Earth \u2192 Moon", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1e3, max_ratio: 1e6 },
|
|
1989
|
+
{ id: "iss_orbit", category: "cosmos", reference_hours: 1.5, label: "ISS orbits", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 200 },
|
|
1990
|
+
{ id: "light_sun", category: "cosmos", reference_hours: 8.3, label: "solar light crossing to Earth", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 200 },
|
|
1991
|
+
{ id: "sleep_cycle", category: "cosmos", reference_hours: 8, label: "full nights of sleep", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 150 },
|
|
1992
|
+
{ id: "red_eye", category: "cosmos", reference_hours: 5.5, label: "transcontinental red-eyes", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 100 },
|
|
1993
|
+
{ id: "mayfly", category: "cosmos", reference_hours: 24, label: "a mayfly's entire adult life", template: "\u2248 {pct}% of {label}", min_ratio: 0.1, max_ratio: 2 },
|
|
1994
|
+
{ id: "earth_rotation", category: "cosmos", reference_hours: 24, label: "Earth rotations", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.1, max_ratio: 50 },
|
|
1995
|
+
{ id: "jupiter_storm", category: "cosmos", reference_hours: 150, label: "Jupiter's Great Red Spot rotation", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 15 },
|
|
1996
|
+
{ id: "lunar_month", category: "cosmos", reference_hours: 708, label: "a lunar cycle", template: "\u2248 {pct}% of {label}", min_ratio: 0.05, max_ratio: 2 },
|
|
1997
|
+
{ id: "mars_transit", category: "cosmos", reference_hours: 5110, label: "a one-way Mars transit (optimistic)", template: "\u2248 {pct}% of {label}", min_ratio: 1e-3, max_ratio: 5 },
|
|
1998
|
+
{ id: "calendar_year", category: "cosmos", reference_hours: 8760, label: "all the hours in a calendar year", template: "\u2248 {pct}% of {label}", min_ratio: 0.05, max_ratio: 0.2 },
|
|
1999
|
+
{ id: "standup", category: "gtm", reference_hours: 0.25, label: "daily standups", template: "\u2248 {ratio}\xD7 skipped {label}", min_ratio: 4, max_ratio: 500 },
|
|
2000
|
+
{ id: "quick_sync", category: "gtm", reference_hours: 0.5, label: "avoided 'quick syncs'", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 2, max_ratio: 200 },
|
|
2001
|
+
{ id: "pipeline_review", category: "gtm", reference_hours: 1, label: "weekly pipeline reviews", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 200 },
|
|
2002
|
+
{ id: "forecast_call", category: "gtm", reference_hours: 1.5, label: "forecast calls", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 150 },
|
|
2003
|
+
{ id: "pivot_spiral", category: "gtm", reference_hours: 2, label: "spreadsheet pivot-table spirals", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 100 },
|
|
2004
|
+
{ id: "win_loss", category: "gtm", reference_hours: 4, label: "win/loss interview blocks", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 50 },
|
|
2005
|
+
{ id: "crm_cleanup", category: "gtm", reference_hours: 6, label: "CRM hygiene sprints", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.5, max_ratio: 40 },
|
|
2006
|
+
{ id: "qbr_prep", category: "gtm", reference_hours: 8, label: "QBR prep blocks", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 20 },
|
|
2007
|
+
{ id: "board_deck", category: "gtm", reference_hours: 12, label: "board deck builds", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 0.3, max_ratio: 30 },
|
|
2008
|
+
{ id: "semester", category: "gtm", reference_hours: 400, label: "a college semester of analyst coverage", template: "\u2248 {ratio}\xD7 {label}", min_ratio: 1, max_ratio: 5 },
|
|
2009
|
+
{ id: "business_year", category: "gtm", reference_hours: 2e3, label: "a full-time analyst year", template: "\u2248 {pct}% of {label}", min_ratio: 0.2, max_ratio: 1 }
|
|
2010
|
+
];
|
|
1679
2011
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
2012
|
+
});
|
|
2013
|
+
|
|
2014
|
+
// src/whimsy/time-bank-whimsy.ts
|
|
2015
|
+
function formatHours(h) {
|
|
2016
|
+
if (h < 1) return `${Math.round(h * 60)}m`;
|
|
2017
|
+
if (h < 10) return `${h.toFixed(1)}h`;
|
|
2018
|
+
return `${Math.round(h)}h`;
|
|
2019
|
+
}
|
|
2020
|
+
function randomNearMilestoneGoodbye(hoursSaved, hoursToNext, nextTitle) {
|
|
2021
|
+
const pool = NEAR_MILESTONE_GOODBYES;
|
|
2022
|
+
const fn = pool[Math.floor(Math.random() * pool.length)] ?? pool[0];
|
|
2023
|
+
return fn(hoursSaved, hoursToNext, nextTitle);
|
|
2024
|
+
}
|
|
2025
|
+
var NEAR_MILESTONE_GOODBYES;
|
|
2026
|
+
var init_time_bank_whimsy = __esm({
|
|
2027
|
+
"src/whimsy/time-bank-whimsy.ts"() {
|
|
2028
|
+
"use strict";
|
|
2029
|
+
NEAR_MILESTONE_GOODBYES = [
|
|
2030
|
+
(saved, toGo, next) => `${formatHours(saved)} saved \u2014 ${formatHours(toGo)} from ${next}. Almost there.`,
|
|
2031
|
+
(saved, toGo, next) => `${formatHours(saved)} in the bank. One more push hits ${next}.`,
|
|
2032
|
+
(saved, _toGo, next) => `You're at ${formatHours(saved)}. ${next} is right around the corner.`,
|
|
2033
|
+
(saved, toGo, next) => `${formatHours(toGo)} to ${next}. You've already banked ${formatHours(saved)}.`,
|
|
2034
|
+
(saved, _toGo, next) => `Close \u2014 ${formatHours(saved)} saved and ${next} is within reach.`
|
|
2035
|
+
];
|
|
1682
2036
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
2037
|
+
});
|
|
2038
|
+
|
|
2039
|
+
// src/whimsy/perspective-rotation.ts
|
|
2040
|
+
function perspectiveRotationDue(state, now2 = Date.now()) {
|
|
2041
|
+
const perspectiveId = state.perspective_id ?? state.last_perspective_id;
|
|
2042
|
+
if (!perspectiveId) return true;
|
|
2043
|
+
const rotatedAt = state.perspective_rotated_at ? Date.parse(state.perspective_rotated_at) : 0;
|
|
2044
|
+
const minutesAtRotation = state.perspective_minutes_at_rotation ?? 0;
|
|
2045
|
+
const creditedSince = state.total_minutes_saved - minutesAtRotation;
|
|
2046
|
+
const msSince = rotatedAt > 0 ? now2 - rotatedAt : PERSPECTIVE_ROTATE_CALENDAR_MS;
|
|
2047
|
+
return creditedSince >= PERSPECTIVE_ROTATE_CREDIT_MINUTES || msSince >= PERSPECTIVE_ROTATE_CALENDAR_MS;
|
|
2048
|
+
}
|
|
2049
|
+
function rotationSeed(state) {
|
|
2050
|
+
const epoch = state.perspective_minutes_at_rotation ?? state.total_minutes_saved;
|
|
2051
|
+
const count = state.perspective_rotation_count ?? 0;
|
|
2052
|
+
return epoch * 31 + count * 17;
|
|
2053
|
+
}
|
|
2054
|
+
function bumpRecentPerspectiveIds(recent, id) {
|
|
2055
|
+
const next = [...(recent ?? []).filter((x) => x !== id), id];
|
|
2056
|
+
if (next.length > PERSPECTIVE_EXCLUDE_RECENT) {
|
|
2057
|
+
next.splice(0, next.length - PERSPECTIVE_EXCLUDE_RECENT);
|
|
1685
2058
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
2059
|
+
return next;
|
|
2060
|
+
}
|
|
2061
|
+
var PERSPECTIVE_ROTATE_CREDIT_MINUTES, PERSPECTIVE_ROTATE_CALENDAR_MS, PERSPECTIVE_EXCLUDE_RECENT;
|
|
2062
|
+
var init_perspective_rotation = __esm({
|
|
2063
|
+
"src/whimsy/perspective-rotation.ts"() {
|
|
2064
|
+
"use strict";
|
|
2065
|
+
PERSPECTIVE_ROTATE_CREDIT_MINUTES = 180;
|
|
2066
|
+
PERSPECTIVE_ROTATE_CALENDAR_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2067
|
+
PERSPECTIVE_EXCLUDE_RECENT = 6;
|
|
1688
2068
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
2069
|
+
});
|
|
2070
|
+
|
|
2071
|
+
// src/whimsy/usage-stats.ts
|
|
2072
|
+
var usage_stats_exports = {};
|
|
2073
|
+
__export(usage_stats_exports, {
|
|
2074
|
+
buildUsageSummary: () => buildUsageSummary,
|
|
2075
|
+
getUsageStats: () => getUsageStats,
|
|
2076
|
+
isoWeekKey: () => isoWeekKey2,
|
|
2077
|
+
recordLlmUsage: () => recordLlmUsage,
|
|
2078
|
+
recordSessionClosed: () => recordSessionClosed,
|
|
2079
|
+
recordUsageFromCredit: () => recordUsageFromCredit
|
|
2080
|
+
});
|
|
2081
|
+
function emptyUsage() {
|
|
2082
|
+
return {
|
|
2083
|
+
sessions_closed: 0,
|
|
2084
|
+
diagnoses: 0,
|
|
2085
|
+
metrics_runs: 0,
|
|
2086
|
+
deliverables: 0,
|
|
2087
|
+
nl_exchanges: 0,
|
|
2088
|
+
llm_calls: 0,
|
|
2089
|
+
input_tokens: 0,
|
|
2090
|
+
output_tokens: 0,
|
|
2091
|
+
weekly: []
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function ensureUsage(state) {
|
|
2095
|
+
return state.usage ?? emptyUsage();
|
|
2096
|
+
}
|
|
2097
|
+
function isoWeekKey2(d = /* @__PURE__ */ new Date()) {
|
|
2098
|
+
const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
|
|
2099
|
+
const day = date.getUTCDay() || 7;
|
|
2100
|
+
date.setUTCDate(date.getUTCDate() + 4 - day);
|
|
2101
|
+
const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
|
|
2102
|
+
const weekNo = Math.ceil(((date.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
2103
|
+
return `${date.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
|
|
2104
|
+
}
|
|
2105
|
+
function bumpWeekly2(weekly, patch) {
|
|
2106
|
+
const week = patch.week ?? isoWeekKey2();
|
|
2107
|
+
const idx = weekly.findIndex((w) => w.week === week);
|
|
2108
|
+
const row = idx >= 0 ? { ...weekly[idx] } : { week, minutes_saved: 0, llm_calls: 0, actions: 0 };
|
|
2109
|
+
if (patch.minutes_saved) row.minutes_saved += patch.minutes_saved;
|
|
2110
|
+
if (patch.llm_calls) row.llm_calls += patch.llm_calls;
|
|
2111
|
+
if (patch.actions) row.actions += patch.actions;
|
|
2112
|
+
const next = idx >= 0 ? weekly.map((w, i) => i === idx ? row : w) : [...weekly, row];
|
|
2113
|
+
if (next.length > WEEKLY_CAP) next.splice(0, next.length - WEEKLY_CAP);
|
|
2114
|
+
return next;
|
|
2115
|
+
}
|
|
2116
|
+
function touchUsage(state, patch) {
|
|
2117
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
2118
|
+
const usage = ensureUsage(state);
|
|
2119
|
+
return {
|
|
2120
|
+
...state,
|
|
2121
|
+
usage: {
|
|
2122
|
+
...usage,
|
|
2123
|
+
...patch,
|
|
2124
|
+
first_active_at: usage.first_active_at ?? now2,
|
|
2125
|
+
last_active_at: now2,
|
|
2126
|
+
weekly: patch.weekly ?? usage.weekly
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
function recordUsageFromCredit(action, minutes) {
|
|
2131
|
+
if (minutes <= 0) return;
|
|
2132
|
+
let state = loadProgress();
|
|
2133
|
+
const usage = ensureUsage(state);
|
|
2134
|
+
const weekly = bumpWeekly2(usage.weekly, { minutes_saved: minutes, actions: 1 });
|
|
2135
|
+
const counters = { weekly };
|
|
2136
|
+
if (action === "diagnose" || action === "diagnose_findings") counters.diagnoses = usage.diagnoses + 1;
|
|
2137
|
+
if (action === "metrics" || action === "metrics_findings") counters.metrics_runs = usage.metrics_runs + 1;
|
|
2138
|
+
if (action === "deliverable" || action === "deliverable_deck") counters.deliverables = usage.deliverables + 1;
|
|
2139
|
+
if (action === "nl_answer") counters.nl_exchanges = usage.nl_exchanges + 1;
|
|
2140
|
+
state = touchUsage(state, counters);
|
|
2141
|
+
saveProgress(state);
|
|
2142
|
+
}
|
|
2143
|
+
function recordSessionClosed() {
|
|
2144
|
+
let state = loadProgress();
|
|
2145
|
+
const usage = ensureUsage(state);
|
|
2146
|
+
state = touchUsage(state, {
|
|
2147
|
+
sessions_closed: usage.sessions_closed + 1,
|
|
2148
|
+
weekly: bumpWeekly2(usage.weekly, { actions: 1 })
|
|
2149
|
+
});
|
|
2150
|
+
saveProgress(state);
|
|
2151
|
+
}
|
|
2152
|
+
function recordLlmUsage(tokenUsage) {
|
|
2153
|
+
let state = loadProgress();
|
|
2154
|
+
const usage = ensureUsage(state);
|
|
2155
|
+
const weekly = bumpWeekly2(usage.weekly, { llm_calls: 1 });
|
|
2156
|
+
state = touchUsage(state, {
|
|
2157
|
+
llm_calls: usage.llm_calls + 1,
|
|
2158
|
+
input_tokens: usage.input_tokens + (tokenUsage?.input_tokens ?? 0),
|
|
2159
|
+
output_tokens: usage.output_tokens + (tokenUsage?.output_tokens ?? 0),
|
|
2160
|
+
weekly
|
|
2161
|
+
});
|
|
2162
|
+
saveProgress(state);
|
|
2163
|
+
}
|
|
2164
|
+
function getUsageStats() {
|
|
2165
|
+
return ensureUsage(loadProgress());
|
|
2166
|
+
}
|
|
2167
|
+
function buildUsageSummary(sessionCounts, totalHours, milestonesUnlocked, milestoneTotal) {
|
|
2168
|
+
return {
|
|
2169
|
+
usage: getUsageStats(),
|
|
2170
|
+
total_sessions_on_disk: sessionCounts.total,
|
|
2171
|
+
sessions_with_work: sessionCounts.withWork,
|
|
2172
|
+
total_hours_saved: totalHours,
|
|
2173
|
+
milestones_unlocked: milestonesUnlocked,
|
|
2174
|
+
milestone_total: milestoneTotal
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
var WEEKLY_CAP;
|
|
2178
|
+
var init_usage_stats = __esm({
|
|
2179
|
+
"src/whimsy/usage-stats.ts"() {
|
|
2180
|
+
"use strict";
|
|
2181
|
+
init_progress();
|
|
2182
|
+
WEEKLY_CAP = 52;
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
|
|
2186
|
+
// src/whimsy/time-bank.ts
|
|
2187
|
+
var time_bank_exports = {};
|
|
2188
|
+
__export(time_bank_exports, {
|
|
2189
|
+
creditDeliverable: () => creditDeliverable,
|
|
2190
|
+
creditDiagnoseComplete: () => creditDiagnoseComplete,
|
|
2191
|
+
creditGapCompute: () => creditGapCompute,
|
|
2192
|
+
creditMetricsComplete: () => creditMetricsComplete,
|
|
2193
|
+
creditNlAnswer: () => creditNlAnswer,
|
|
2194
|
+
creditOnboardComplete: () => creditOnboardComplete,
|
|
2195
|
+
creditSessionDeliverableWrapup: () => creditSessionDeliverableWrapup,
|
|
2196
|
+
formatHoursLabel: () => formatHoursLabel,
|
|
2197
|
+
getTimeBankSummary: () => getTimeBankSummary,
|
|
2198
|
+
isNearNextMilestone: () => isNearNextMilestone,
|
|
2199
|
+
loadTimeBankState: () => loadTimeBankState,
|
|
2200
|
+
pickGoodbyeWithTimeBank: () => pickGoodbyeWithTimeBank,
|
|
2201
|
+
printTimeBankCelebration: () => printTimeBankCelebration,
|
|
2202
|
+
recordTimeCredit: () => recordTimeCredit
|
|
2203
|
+
});
|
|
2204
|
+
import chalk2 from "chalk";
|
|
2205
|
+
function actionKey(action, ctx, suffix) {
|
|
2206
|
+
const sessionScoped = /* @__PURE__ */ new Set([
|
|
2207
|
+
"gap_compute",
|
|
2208
|
+
"diagnose",
|
|
2209
|
+
"diagnose_findings",
|
|
2210
|
+
"metrics",
|
|
2211
|
+
"metrics_findings",
|
|
2212
|
+
"deliverable",
|
|
2213
|
+
"deliverable_deck",
|
|
2214
|
+
"session_deliverable_wrapup",
|
|
2215
|
+
"nl_answer"
|
|
2216
|
+
]);
|
|
2217
|
+
if (sessionScoped.has(action) && ctx?.sessionId) {
|
|
2218
|
+
return suffix ? `${action}:${ctx.sessionId}:${suffix}` : `${action}:${ctx.sessionId}`;
|
|
2219
|
+
}
|
|
2220
|
+
return action;
|
|
2221
|
+
}
|
|
2222
|
+
function shouldSkip(ctx) {
|
|
2223
|
+
return !ctx || ctx.oneShot;
|
|
2224
|
+
}
|
|
2225
|
+
function recordTimeCredit(action, ctx, opts) {
|
|
2226
|
+
if (shouldSkip(ctx)) return null;
|
|
2227
|
+
const minutes = ACTION_MINUTES[action];
|
|
2228
|
+
if (!minutes || minutes <= 0) return null;
|
|
2229
|
+
const key = actionKey(action, ctx, opts?.suffix);
|
|
2230
|
+
let state = loadProgress();
|
|
2231
|
+
if (hasCreditAction(state, key)) {
|
|
2232
|
+
return { credited_minutes: 0, new_milestones: [], total_minutes: state.total_minutes_saved };
|
|
2233
|
+
}
|
|
2234
|
+
const previousMinutes = state.total_minutes_saved;
|
|
2235
|
+
const credit = {
|
|
2236
|
+
action: key,
|
|
2237
|
+
minutes,
|
|
2238
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2239
|
+
session_id: ctx?.sessionId
|
|
2240
|
+
};
|
|
2241
|
+
state = appendCredit(state, credit);
|
|
2242
|
+
const unlocked = newlyUnlockedMilestones(
|
|
2243
|
+
previousMinutes,
|
|
2244
|
+
state.total_minutes_saved,
|
|
2245
|
+
state.milestones_unlocked
|
|
2246
|
+
);
|
|
2247
|
+
if (unlocked.length > 0) {
|
|
2248
|
+
state = {
|
|
2249
|
+
...state,
|
|
2250
|
+
milestones_unlocked: [...state.milestones_unlocked, ...unlocked.map((m) => m.id)]
|
|
2251
|
+
};
|
|
1691
2252
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
2253
|
+
saveProgress(state);
|
|
2254
|
+
if (minutes > 0) {
|
|
2255
|
+
recordUsageFromCredit(action, minutes);
|
|
2256
|
+
state = maybeRotatePerspective(state, state.total_minutes_saved / 60);
|
|
2257
|
+
saveProgress(state);
|
|
1694
2258
|
}
|
|
1695
|
-
if (
|
|
1696
|
-
|
|
2259
|
+
if (!opts?.silent && unlocked.length > 0) {
|
|
2260
|
+
for (const m of unlocked) {
|
|
2261
|
+
printTimeBankCelebration(m, state.total_minutes_saved);
|
|
2262
|
+
}
|
|
1697
2263
|
}
|
|
1698
|
-
|
|
1699
|
-
|
|
2264
|
+
return {
|
|
2265
|
+
credited_minutes: minutes,
|
|
2266
|
+
new_milestones: unlocked,
|
|
2267
|
+
total_minutes: state.total_minutes_saved
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
function creditGapCompute(ctx) {
|
|
2271
|
+
recordTimeCredit("gap_compute", ctx);
|
|
2272
|
+
if (!hasCreditAction(loadProgress(), "gap_compute_first_ever")) {
|
|
2273
|
+
recordTimeCredit("gap_compute_first_ever", ctx);
|
|
1700
2274
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2275
|
+
}
|
|
2276
|
+
function creditDiagnoseComplete(ctx, withFindings) {
|
|
2277
|
+
recordTimeCredit("diagnose", ctx);
|
|
2278
|
+
if (withFindings) {
|
|
2279
|
+
recordTimeCredit("diagnose_findings", ctx);
|
|
1704
2280
|
}
|
|
1705
|
-
return void 0;
|
|
1706
2281
|
}
|
|
1707
|
-
function
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
let matches = all2.filter((s) => s.id === idArg);
|
|
1712
|
-
if (matches.length === 0) matches = all2.filter((s) => s.name?.toLowerCase() === lower);
|
|
1713
|
-
if (matches.length === 0 && idArg.length >= 4) {
|
|
1714
|
-
matches = all2.filter((s) => s.id.endsWith(idArg));
|
|
2282
|
+
function creditMetricsComplete(ctx, withFindings) {
|
|
2283
|
+
recordTimeCredit("metrics", ctx);
|
|
2284
|
+
if (withFindings) {
|
|
2285
|
+
recordTimeCredit("metrics_findings", ctx);
|
|
1715
2286
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
);
|
|
1722
|
-
}
|
|
1723
|
-
return null;
|
|
2287
|
+
}
|
|
2288
|
+
function creditDeliverable(ctx, target) {
|
|
2289
|
+
recordTimeCredit("deliverable", ctx);
|
|
2290
|
+
if (target === "deck") {
|
|
2291
|
+
recordTimeCredit("deliverable_deck", ctx);
|
|
1724
2292
|
}
|
|
1725
|
-
return matches[0];
|
|
1726
2293
|
}
|
|
1727
|
-
function
|
|
1728
|
-
|
|
1729
|
-
const all2 = listSessions();
|
|
1730
|
-
return all2.find((s) => s.name?.toLowerCase() === lower) ?? null;
|
|
2294
|
+
function creditNlAnswer(ctx, exchangeIndex) {
|
|
2295
|
+
recordTimeCredit("nl_answer", ctx, { suffix: String(exchangeIndex) });
|
|
1731
2296
|
}
|
|
1732
|
-
function
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2297
|
+
function creditOnboardComplete(ctx) {
|
|
2298
|
+
recordTimeCredit("onboard", ctx);
|
|
2299
|
+
}
|
|
2300
|
+
function creditSessionDeliverableWrapup(ctx) {
|
|
2301
|
+
recordTimeCredit("session_deliverable_wrapup", ctx);
|
|
2302
|
+
}
|
|
2303
|
+
function activePerspectiveId(state) {
|
|
2304
|
+
return state.perspective_id ?? state.last_perspective_id;
|
|
2305
|
+
}
|
|
2306
|
+
function maybeRotatePerspective(state, totalHours) {
|
|
2307
|
+
const currentId = activePerspectiveId(state);
|
|
2308
|
+
const current = currentId ? getPerspectiveById(currentId) : void 0;
|
|
2309
|
+
const staleBand = current && !ratioInBand2(current, totalHours);
|
|
2310
|
+
if (!perspectiveRotationDue(state) && current && !staleBand) {
|
|
2311
|
+
return state;
|
|
1738
2312
|
}
|
|
1739
|
-
|
|
2313
|
+
const lastCategory = current?.category;
|
|
2314
|
+
const picked = pickPerspective(totalHours, {
|
|
2315
|
+
excludeIds: state.recent_perspective_ids ?? [],
|
|
2316
|
+
lastCategory,
|
|
2317
|
+
seed: rotationSeed(state)
|
|
2318
|
+
});
|
|
2319
|
+
if (!picked) return state;
|
|
2320
|
+
return {
|
|
2321
|
+
...state,
|
|
2322
|
+
perspective_id: picked.id,
|
|
2323
|
+
last_perspective_id: picked.id,
|
|
2324
|
+
perspective_rotated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2325
|
+
perspective_minutes_at_rotation: state.total_minutes_saved,
|
|
2326
|
+
perspective_rotation_count: (state.perspective_rotation_count ?? 0) + 1,
|
|
2327
|
+
recent_perspective_ids: bumpRecentPerspectiveIds(state.recent_perspective_ids, picked.id)
|
|
2328
|
+
};
|
|
1740
2329
|
}
|
|
1741
|
-
function
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
2330
|
+
function ratioInBand2(perspective, totalHours) {
|
|
2331
|
+
const ratio = totalHours / perspective.reference_hours;
|
|
2332
|
+
const min = perspective.min_ratio ?? 0.3;
|
|
2333
|
+
const max = perspective.max_ratio ?? 300;
|
|
2334
|
+
return ratio >= min && ratio <= max;
|
|
2335
|
+
}
|
|
2336
|
+
function getTimeBankSummary() {
|
|
2337
|
+
let state = loadProgress();
|
|
2338
|
+
const total_minutes = state.total_minutes_saved;
|
|
2339
|
+
const total_hours = total_minutes / 60;
|
|
2340
|
+
if (total_minutes > 0) {
|
|
2341
|
+
state = maybeRotatePerspective(state, total_hours);
|
|
2342
|
+
saveProgress(state);
|
|
2343
|
+
}
|
|
2344
|
+
const next = nextMilestone(total_hours, state.milestones_unlocked);
|
|
2345
|
+
let progress_pct = 100;
|
|
2346
|
+
if (next) {
|
|
2347
|
+
const prevMilestone = state.milestones_unlocked.length > 0 ? getMilestoneById(state.milestones_unlocked[state.milestones_unlocked.length - 1]) : void 0;
|
|
2348
|
+
const prevHours = prevMilestone?.hours ?? 0;
|
|
2349
|
+
const span = next.hours - prevHours;
|
|
2350
|
+
progress_pct = span > 0 ? Math.min(100, (total_hours - prevHours) / span * 100) : 0;
|
|
2351
|
+
}
|
|
2352
|
+
const perspectiveId = activePerspectiveId(state);
|
|
2353
|
+
const perspective = perspectiveId ? getPerspectiveById(perspectiveId) : null;
|
|
2354
|
+
const perspective_line = perspective ? formatPerspectiveLine(perspective, total_hours) : null;
|
|
2355
|
+
return {
|
|
2356
|
+
total_hours,
|
|
2357
|
+
total_minutes,
|
|
2358
|
+
next_milestone: next,
|
|
2359
|
+
progress_pct,
|
|
2360
|
+
perspective_line
|
|
2361
|
+
};
|
|
1761
2362
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
2363
|
+
function printTimeBankCelebration(milestone, totalMinutes) {
|
|
2364
|
+
const totalHours = totalMinutes / 60;
|
|
2365
|
+
const state = loadProgress();
|
|
2366
|
+
const perspective = pickPerspective(totalHours, {
|
|
2367
|
+
excludeIds: state.recent_perspective_ids ?? [],
|
|
2368
|
+
seed: rotationSeed(state) + 1
|
|
2369
|
+
});
|
|
2370
|
+
console.log();
|
|
2371
|
+
console.log(" " + paint("accent", `\u2726 ${milestone.title}`) + chalk2.dim(` \u2014 ${formatHoursLabel(totalHours)} saved`));
|
|
2372
|
+
console.log(" " + chalk2.dim(milestone.message));
|
|
2373
|
+
if (perspective) {
|
|
2374
|
+
console.log(" " + chalk2.dim.italic(formatPerspectiveLine(perspective, totalHours)));
|
|
2375
|
+
}
|
|
2376
|
+
console.log();
|
|
2377
|
+
}
|
|
2378
|
+
function formatHoursLabel(hours) {
|
|
2379
|
+
if (hours < 1) return `${Math.round(hours * 60)}m`;
|
|
2380
|
+
if (hours < 10) return `${hours.toFixed(1)}h`;
|
|
2381
|
+
if (hours >= 1e3) return `${Math.round(hours).toLocaleString("en-US")}h`;
|
|
2382
|
+
return `${Math.round(hours)}h`;
|
|
2383
|
+
}
|
|
2384
|
+
function isNearNextMilestone(threshold = 0.15) {
|
|
2385
|
+
const state = loadProgress();
|
|
2386
|
+
if (state.total_minutes_saved <= 0) return false;
|
|
2387
|
+
const totalHours = state.total_minutes_saved / 60;
|
|
2388
|
+
const next = nextMilestone(totalHours, state.milestones_unlocked);
|
|
2389
|
+
if (!next) return false;
|
|
2390
|
+
const prev = state.milestones_unlocked.map((id) => getMilestoneById(id)).filter((m) => !!m).sort((a, b) => b.hours - a.hours)[0];
|
|
2391
|
+
const prevHours = prev?.hours ?? 0;
|
|
2392
|
+
const span = next.hours - prevHours;
|
|
2393
|
+
if (span <= 0) return false;
|
|
2394
|
+
const progress = (totalHours - prevHours) / span;
|
|
2395
|
+
return progress >= 1 - threshold;
|
|
2396
|
+
}
|
|
2397
|
+
function pickGoodbyeWithTimeBank() {
|
|
2398
|
+
if (Math.random() > 0.25) return null;
|
|
2399
|
+
if (!isNearNextMilestone()) return null;
|
|
2400
|
+
const state = loadProgress();
|
|
2401
|
+
const totalHours = state.total_minutes_saved / 60;
|
|
2402
|
+
const next = nextMilestone(totalHours, state.milestones_unlocked);
|
|
2403
|
+
if (!next) return null;
|
|
2404
|
+
const hoursToNext = Math.max(0, next.hours - totalHours);
|
|
2405
|
+
return randomNearMilestoneGoodbye(totalHours, hoursToNext, next.title);
|
|
2406
|
+
}
|
|
2407
|
+
function loadTimeBankState() {
|
|
2408
|
+
return loadProgress();
|
|
2409
|
+
}
|
|
2410
|
+
var ACTION_MINUTES;
|
|
2411
|
+
var init_time_bank = __esm({
|
|
2412
|
+
"src/whimsy/time-bank.ts"() {
|
|
1765
2413
|
"use strict";
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
2414
|
+
init_progress();
|
|
2415
|
+
init_theme();
|
|
2416
|
+
init_time_milestones();
|
|
2417
|
+
init_time_perspectives();
|
|
2418
|
+
init_time_bank_whimsy();
|
|
2419
|
+
init_perspective_rotation();
|
|
2420
|
+
init_usage_stats();
|
|
2421
|
+
ACTION_MINUTES = {
|
|
2422
|
+
gap_compute: 30,
|
|
2423
|
+
gap_compute_first_ever: 30,
|
|
2424
|
+
diagnose: 180,
|
|
2425
|
+
diagnose_findings: 60,
|
|
2426
|
+
metrics: 120,
|
|
2427
|
+
metrics_findings: 60,
|
|
2428
|
+
deliverable: 240,
|
|
2429
|
+
deliverable_deck: 120,
|
|
2430
|
+
nl_answer: 15,
|
|
2431
|
+
onboard: 30,
|
|
2432
|
+
session_deliverable_wrapup: 30
|
|
2433
|
+
};
|
|
1769
2434
|
}
|
|
1770
2435
|
});
|
|
1771
2436
|
|
|
1772
|
-
// src/
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2437
|
+
// src/cli/context.ts
|
|
2438
|
+
var context_exports = {};
|
|
2439
|
+
__export(context_exports, {
|
|
2440
|
+
buildAnalysisBlock: () => buildAnalysisBlock,
|
|
2441
|
+
buildSwitchContext: () => buildSwitchContext,
|
|
2442
|
+
closeAllActiveSessions: () => closeAllActiveSessions,
|
|
2443
|
+
closeSession: () => closeSession,
|
|
2444
|
+
datasetPathForSession: () => datasetPathForSession,
|
|
2445
|
+
defaultSessionAnalysis: () => defaultSessionAnalysis,
|
|
2446
|
+
endSession: () => endSession,
|
|
2447
|
+
findSessionByName: () => findSessionByName,
|
|
2448
|
+
getActiveSessions: () => getActiveSessions,
|
|
2449
|
+
getDatasetsDir: () => getDatasetsDir,
|
|
2450
|
+
getLastActivityRelative: () => getLastActivityRelative,
|
|
2451
|
+
getLastWorkedSession: () => getLastWorkedSession,
|
|
2452
|
+
getRecentSessions: () => getRecentSessions,
|
|
2453
|
+
getSessionsDir: () => getSessionsDir,
|
|
2454
|
+
getUnfinishedSessions: () => getUnfinishedSessions,
|
|
2455
|
+
hydrateAnalysisFromPersistedState: () => hydrateAnalysisFromPersistedState,
|
|
2456
|
+
initContext: () => initContext,
|
|
2457
|
+
initHeadlessAgentContext: () => initHeadlessAgentContext,
|
|
2458
|
+
isAnalysisReady: () => isAnalysisReady,
|
|
2459
|
+
isSessionInProgress: () => isSessionInProgress,
|
|
2460
|
+
lensBadgeLabel: () => lensBadgeLabel,
|
|
2461
|
+
listSessions: () => listSessions,
|
|
2462
|
+
loadSessionFile: () => loadSessionFile,
|
|
2463
|
+
makeSessionId: () => makeSessionId,
|
|
2464
|
+
markLensCompleted: () => markLensCompleted,
|
|
2465
|
+
prefersMetricsFirstContext: () => prefersMetricsFirstContext,
|
|
2466
|
+
recordMessage: () => recordMessage,
|
|
2467
|
+
resetContextForSwitch: () => resetContextForSwitch,
|
|
2468
|
+
resolveSessionByToken: () => resolveSessionByToken,
|
|
2469
|
+
rotateToFreshSession: () => rotateToFreshSession,
|
|
2470
|
+
saveSessionState: () => saveSessionState,
|
|
2471
|
+
setPrimaryLens: () => setPrimaryLens
|
|
2472
|
+
});
|
|
2473
|
+
import { basename, join as join6, resolve as resolve3, sep } from "path";
|
|
2474
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, readFileSync as readFileSync5, readdirSync, statSync, rmSync as rmSync2 } from "fs";
|
|
2475
|
+
import { homedir as homedir2 } from "os";
|
|
2476
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2477
|
+
function isAnalysisReady(ctx) {
|
|
2478
|
+
if (ctx.stage !== "analyzed" || ctx.analysis.completed.length === 0) return false;
|
|
2479
|
+
if (!ctx.dataset) return false;
|
|
2480
|
+
const counts = ctx.dataset.counts ?? {};
|
|
2481
|
+
return Object.values(counts).some((n) => n > 0);
|
|
2482
|
+
}
|
|
2483
|
+
function ntrpHomeDir() {
|
|
2484
|
+
return process.env.NTRP_HOME ? resolve3(process.env.NTRP_HOME) : join6(homedir2(), ".ntrp");
|
|
2485
|
+
}
|
|
2486
|
+
function getSessionsDir() {
|
|
2487
|
+
const dir = join6(ntrpHomeDir(), "sessions");
|
|
2488
|
+
if (!existsSync6(dir)) {
|
|
2489
|
+
mkdirSync5(dir, { recursive: true });
|
|
2490
|
+
}
|
|
2491
|
+
return dir;
|
|
2492
|
+
}
|
|
2493
|
+
function getDatasetsDir() {
|
|
2494
|
+
const dir = join6(ntrpHomeDir(), "datasets");
|
|
2495
|
+
if (!existsSync6(dir)) {
|
|
2496
|
+
mkdirSync5(dir, { recursive: true });
|
|
2497
|
+
}
|
|
2498
|
+
return dir;
|
|
2499
|
+
}
|
|
2500
|
+
function datasetPathForSession(id) {
|
|
2501
|
+
return join6(getDatasetsDir(), `${id}.duckdb`);
|
|
2502
|
+
}
|
|
2503
|
+
function makeSessionId() {
|
|
2504
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
2505
|
+
const date = now2.toISOString().slice(0, 10);
|
|
2506
|
+
const uuid2 = randomUUID3().slice(0, 4);
|
|
2507
|
+
return `${date}-${uuid2}`;
|
|
2508
|
+
}
|
|
2509
|
+
function isValidSessionId(id) {
|
|
2510
|
+
return SESSION_ID_RE.test(id);
|
|
2511
|
+
}
|
|
2512
|
+
function sessionPathForId(id) {
|
|
2513
|
+
if (!isValidSessionId(id)) return null;
|
|
2514
|
+
const dir = resolve3(getSessionsDir());
|
|
2515
|
+
const filePath = resolve3(dir, `${id}.json`);
|
|
2516
|
+
if (filePath !== dir && !filePath.startsWith(dir + sep)) return null;
|
|
2517
|
+
return filePath;
|
|
2518
|
+
}
|
|
2519
|
+
function initContext(oneShot, execution) {
|
|
2520
|
+
const sessionId = makeSessionId();
|
|
2521
|
+
const sessionFile = join6(getSessionsDir(), `${sessionId}.json`);
|
|
2522
|
+
return {
|
|
2523
|
+
sessionId,
|
|
2524
|
+
sessionFile,
|
|
2525
|
+
oneShot,
|
|
2526
|
+
execution: buildExecutionOptions({
|
|
2527
|
+
mode: oneShot ? "one_shot" : "interactive",
|
|
2528
|
+
...execution
|
|
2529
|
+
}),
|
|
2530
|
+
snapshot: { computeResult: null, divergences: [] },
|
|
2531
|
+
messages: [],
|
|
2532
|
+
conversation: [],
|
|
2533
|
+
stage: "new",
|
|
2534
|
+
deliverables: [],
|
|
2535
|
+
analysis: defaultSessionAnalysis(),
|
|
2536
|
+
wizardDepth: 0,
|
|
2537
|
+
attachments: [],
|
|
2538
|
+
deliverIntent: false,
|
|
2539
|
+
computeInProgress: false
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
function buildSessionFile(ctx) {
|
|
2543
|
+
const file = {
|
|
2544
|
+
id: ctx.sessionId,
|
|
2545
|
+
created_at: ctx.messages[0]?.at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2546
|
+
messages: ctx.messages,
|
|
2547
|
+
stage: ctx.stage
|
|
2548
|
+
};
|
|
2549
|
+
if (ctx.sessionName) file.name = ctx.sessionName;
|
|
2550
|
+
if (ctx.dataset) file.dataset = ctx.dataset;
|
|
2551
|
+
if (ctx.deliverables.length > 0) file.deliverables = ctx.deliverables;
|
|
2552
|
+
if (ctx.conversation.length > 0) file.thread = ctx.conversation;
|
|
2553
|
+
if (ctx.resumedFromId) file.resumed_from = ctx.resumedFromId;
|
|
2554
|
+
if (ctx.analysis) file.analysis = ctx.analysis;
|
|
2555
|
+
if (ctx.scope) file.scope = ctx.scope;
|
|
2556
|
+
if (ctx.attachments && ctx.attachments.length > 0) file.attachments = ctx.attachments;
|
|
2557
|
+
if (ctx.llm && Object.keys(ctx.llm).length > 0) file.llm = ctx.llm;
|
|
2558
|
+
return file;
|
|
2559
|
+
}
|
|
2560
|
+
function defaultSessionAnalysis(primary = "gtm_health") {
|
|
2561
|
+
return { primary, completed: [] };
|
|
2562
|
+
}
|
|
2563
|
+
function setPrimaryLens(ctx, lens) {
|
|
2564
|
+
ctx.analysis = { ...ctx.analysis, primary: lens };
|
|
2565
|
+
}
|
|
2566
|
+
function markLensCompleted(ctx, lens) {
|
|
2567
|
+
const completed = ctx.analysis.completed.includes(lens) ? ctx.analysis.completed : [...ctx.analysis.completed, lens];
|
|
2568
|
+
ctx.analysis = { ...ctx.analysis, completed };
|
|
2569
|
+
}
|
|
2570
|
+
function lensBadgeLabel(analysis) {
|
|
2571
|
+
if (!analysis) return "health";
|
|
2572
|
+
const hasHealth = analysis.completed.includes("gtm_health") || analysis.primary === "gtm_health";
|
|
2573
|
+
const hasMetrics = analysis.completed.includes("revenue_metrics") || analysis.primary === "revenue_metrics";
|
|
2574
|
+
if (hasHealth && hasMetrics) return "both";
|
|
2575
|
+
if (hasMetrics) return "metrics";
|
|
2576
|
+
return "health";
|
|
2577
|
+
}
|
|
2578
|
+
function buildAnalysisBlock(ctx) {
|
|
2579
|
+
return [
|
|
2580
|
+
`Primary lens: ${ctx.analysis.primary}`,
|
|
2581
|
+
`Completed: ${ctx.analysis.completed.join(", ") || "none"}`,
|
|
2582
|
+
`Badge: ${lensBadgeLabel(ctx.analysis)}`,
|
|
2583
|
+
ctx.analysis.coverage ? `Coverage: ${ctx.analysis.coverage.distinct_months} months, ${ctx.analysis.coverage.recommended_cadence} cadence` : null,
|
|
2584
|
+
ctx.analysis.data_source_type ? `Data source: ${ctx.analysis.data_source_type}` : null
|
|
2585
|
+
].filter(Boolean).join("\n");
|
|
2586
|
+
}
|
|
2587
|
+
function prefersMetricsFirstContext(ctx) {
|
|
2588
|
+
return ctx.analysis.primary === "revenue_metrics" && ctx.analysis.completed.includes("revenue_metrics") && !ctx.analysis.completed.includes("gtm_health");
|
|
2589
|
+
}
|
|
2590
|
+
async function hydrateAnalysisFromPersistedState(ctx) {
|
|
2591
|
+
const sessions = listSessions({ limit: 10 });
|
|
2592
|
+
const withAnalysis = sessions.find(
|
|
2593
|
+
(s) => s.analysis && (s.analysis.completed.length > 0 || s.analysis.primary !== "gtm_health" || s.stage === "analyzed")
|
|
2594
|
+
);
|
|
2595
|
+
if (withAnalysis?.analysis) {
|
|
2596
|
+
ctx.analysis = {
|
|
2597
|
+
...defaultSessionAnalysis(withAnalysis.analysis.primary),
|
|
2598
|
+
...withAnalysis.analysis,
|
|
2599
|
+
completed: [...withAnalysis.analysis.completed]
|
|
2600
|
+
};
|
|
2601
|
+
if (withAnalysis.stage) ctx.stage = withAnalysis.stage;
|
|
2602
|
+
if (withAnalysis.dataset) ctx.dataset = withAnalysis.dataset;
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
const { loadLatestDiagnosis: loadLatestDiagnosis2, loadLatestMetricsAnalysis: loadLatestMetricsAnalysis2 } = await Promise.resolve().then(() => (init_queries(), queries_exports));
|
|
2606
|
+
const [diagnosis, metrics] = await Promise.all([
|
|
2607
|
+
loadLatestDiagnosis2(),
|
|
2608
|
+
loadLatestMetricsAnalysis2()
|
|
2609
|
+
]);
|
|
2610
|
+
const completed = [];
|
|
2611
|
+
if (diagnosis) completed.push("gtm_health");
|
|
2612
|
+
if (metrics?.metrics.length) completed.push("revenue_metrics");
|
|
2613
|
+
if (completed.length === 0) return;
|
|
2614
|
+
let primary = ctx.analysis.primary;
|
|
2615
|
+
if (completed.includes("revenue_metrics") && !completed.includes("gtm_health")) {
|
|
2616
|
+
primary = "revenue_metrics";
|
|
2617
|
+
} else if (completed.includes("gtm_health") && !completed.includes("revenue_metrics")) {
|
|
2618
|
+
primary = "gtm_health";
|
|
2619
|
+
}
|
|
2620
|
+
ctx.analysis = { ...ctx.analysis, primary, completed };
|
|
2621
|
+
if (ctx.stage === "new") ctx.stage = "analyzed";
|
|
2622
|
+
}
|
|
2623
|
+
async function initHeadlessAgentContext() {
|
|
2624
|
+
const ctx = initContext(true, { mode: "headless", output: "json" });
|
|
2625
|
+
await hydrateAnalysisFromPersistedState(ctx);
|
|
2626
|
+
return ctx;
|
|
2627
|
+
}
|
|
2628
|
+
function recordMessage(ctx, role, content) {
|
|
2629
|
+
const msg = { role, content, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2630
|
+
ctx.messages.push(msg);
|
|
2631
|
+
if (ctx.oneShot) return;
|
|
2632
|
+
try {
|
|
2633
|
+
writeFileSync5(ctx.sessionFile, JSON.stringify(buildSessionFile(ctx), null, 2) + "\n");
|
|
2634
|
+
} catch {
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
function saveSessionState(ctx) {
|
|
2638
|
+
if (ctx.oneShot) return;
|
|
2639
|
+
try {
|
|
2640
|
+
writeFileSync5(ctx.sessionFile, JSON.stringify(buildSessionFile(ctx), null, 2) + "\n");
|
|
2641
|
+
} catch {
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
function getLastActivityRelative() {
|
|
2645
|
+
const dir = getSessionsDir();
|
|
2646
|
+
let mostRecent = 0;
|
|
2647
|
+
try {
|
|
2648
|
+
for (const name of readdirSync(dir)) {
|
|
2649
|
+
if (!name.endsWith(".json")) continue;
|
|
2650
|
+
const m = statSync(join6(dir, name)).mtimeMs;
|
|
2651
|
+
if (m > mostRecent) mostRecent = m;
|
|
1803
2652
|
}
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
2653
|
+
} catch {
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
if (mostRecent === 0) return null;
|
|
2657
|
+
return formatRelativeTime(new Date(mostRecent));
|
|
2658
|
+
}
|
|
2659
|
+
function formatRelativeTime(then) {
|
|
2660
|
+
const diffMs = Date.now() - then.getTime();
|
|
2661
|
+
if (diffMs < 0) return "just now";
|
|
2662
|
+
const s = Math.floor(diffMs / 1e3);
|
|
2663
|
+
if (s < 60) return "just now";
|
|
2664
|
+
const m = Math.floor(s / 60);
|
|
2665
|
+
if (m < 60) return `${m}m ago`;
|
|
2666
|
+
const h = Math.floor(m / 60);
|
|
2667
|
+
if (h < 24) return `${h}h ago`;
|
|
2668
|
+
const d = Math.floor(h / 24);
|
|
2669
|
+
if (d < 7) return `${d}d ago`;
|
|
2670
|
+
return then.toISOString().slice(0, 10);
|
|
2671
|
+
}
|
|
2672
|
+
function loadSessionFile(id) {
|
|
2673
|
+
const filePath = sessionPathForId(id);
|
|
2674
|
+
if (!filePath) return null;
|
|
2675
|
+
try {
|
|
2676
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
2677
|
+
const session = JSON.parse(raw);
|
|
2678
|
+
if (session.thread?.length) {
|
|
2679
|
+
session.thread = normalizeThread(session.thread);
|
|
1810
2680
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2681
|
+
return session;
|
|
2682
|
+
} catch {
|
|
2683
|
+
return null;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
function listSessions(opts) {
|
|
2687
|
+
const dir = getSessionsDir();
|
|
2688
|
+
const entries = [];
|
|
2689
|
+
try {
|
|
2690
|
+
const files = readdirSync(dir).filter((name) => name.endsWith(".json")).map((name) => {
|
|
2691
|
+
const filePath = join6(dir, name);
|
|
2692
|
+
return { name, filePath, mtime: statSync(filePath).mtimeMs };
|
|
2693
|
+
}).sort((a, b) => b.mtime - a.mtime);
|
|
2694
|
+
const filesToRead = opts?.limit ? files.slice(0, opts.limit) : files;
|
|
2695
|
+
for (const { name, filePath, mtime } of filesToRead) {
|
|
2696
|
+
const id = basename(name, ".json");
|
|
2697
|
+
if (!isValidSessionId(id)) continue;
|
|
2698
|
+
try {
|
|
2699
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
2700
|
+
const session = JSON.parse(raw);
|
|
2701
|
+
entries.push({
|
|
2702
|
+
id,
|
|
2703
|
+
created_at: session.created_at,
|
|
2704
|
+
ended_at: session.ended_at,
|
|
2705
|
+
exchange_count: session.exchange_count ?? Math.floor(session.messages.length / 2),
|
|
2706
|
+
summary: session.summary,
|
|
2707
|
+
name: session.name,
|
|
2708
|
+
stage: session.stage,
|
|
2709
|
+
dataset: session.dataset,
|
|
2710
|
+
deliverables: session.deliverables,
|
|
2711
|
+
analysis: session.analysis,
|
|
2712
|
+
mtime
|
|
2713
|
+
});
|
|
2714
|
+
} catch {
|
|
1815
2715
|
}
|
|
1816
2716
|
}
|
|
2717
|
+
} catch {
|
|
2718
|
+
return [];
|
|
1817
2719
|
}
|
|
1818
|
-
|
|
2720
|
+
entries.sort((a, b) => b.mtime - a.mtime);
|
|
2721
|
+
if (opts?.limit) return entries.slice(0, opts.limit);
|
|
2722
|
+
return entries;
|
|
1819
2723
|
}
|
|
1820
|
-
function
|
|
1821
|
-
return
|
|
1822
|
-
|
|
2724
|
+
function getRecentSessions(n) {
|
|
2725
|
+
return listSessions({ limit: n });
|
|
2726
|
+
}
|
|
2727
|
+
function getUnfinishedSessions(excludeId) {
|
|
2728
|
+
return listSessions().filter(
|
|
2729
|
+
(s) => s.id !== excludeId && s.stage === "analyzed" && (s.deliverables?.length ?? 0) === 0
|
|
1823
2730
|
);
|
|
1824
2731
|
}
|
|
1825
|
-
function
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
2732
|
+
function isSessionInProgress(s) {
|
|
2733
|
+
if (s.stage === "ended") return false;
|
|
2734
|
+
if ((s.deliverables?.length ?? 0) > 0 || s.stage === "delivered") return false;
|
|
2735
|
+
if (s.stage === "analyzed") return true;
|
|
2736
|
+
return (s.exchange_count ?? 0) > 0 || !!s.dataset?.label;
|
|
2737
|
+
}
|
|
2738
|
+
function getActiveSessions() {
|
|
2739
|
+
return listSessions().filter(isSessionInProgress);
|
|
2740
|
+
}
|
|
2741
|
+
async function closeAllActiveSessions(ctx) {
|
|
2742
|
+
const active = getActiveSessions();
|
|
2743
|
+
const closed = [];
|
|
2744
|
+
const skipped = [];
|
|
2745
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2746
|
+
for (const s of active) {
|
|
2747
|
+
if (s.id === ctx.sessionId) continue;
|
|
2748
|
+
const file = loadSessionFile(s.id);
|
|
2749
|
+
if (!file) {
|
|
2750
|
+
skipped.push(s.id);
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
file.stage = "ended";
|
|
2754
|
+
file.ended_at = endedAt;
|
|
2755
|
+
const filePath = sessionPathForId(s.id);
|
|
2756
|
+
if (!filePath) {
|
|
2757
|
+
skipped.push(s.id);
|
|
2758
|
+
continue;
|
|
2759
|
+
}
|
|
2760
|
+
writeFileSync5(filePath, JSON.stringify(file, null, 2) + "\n");
|
|
2761
|
+
closed.push(s.id);
|
|
1831
2762
|
}
|
|
1832
|
-
|
|
2763
|
+
const currentActive = active.some((s) => s.id === ctx.sessionId);
|
|
2764
|
+
if (currentActive) {
|
|
2765
|
+
const alreadyClosed = ctx.stage === "delivered" || ctx.stage === "ended";
|
|
2766
|
+
const hasWork = ctx.stage === "analyzed" || !!ctx.dataset || ctx.messages.length > 0 || ctx.deliverables.length > 0;
|
|
2767
|
+
if (!alreadyClosed && hasWork) {
|
|
2768
|
+
await endSession(ctx);
|
|
2769
|
+
if (!closed.includes(ctx.sessionId)) closed.push(ctx.sessionId);
|
|
2770
|
+
} else if (!alreadyClosed && (ctx.messages.length > 0 || !!ctx.dataset?.label)) {
|
|
2771
|
+
await endSession(ctx);
|
|
2772
|
+
if (!closed.includes(ctx.sessionId)) closed.push(ctx.sessionId);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
await rotateToFreshSession(ctx);
|
|
2776
|
+
const { initSchema: initSchema2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
2777
|
+
await initSchema2();
|
|
2778
|
+
return { closed, skipped };
|
|
1833
2779
|
}
|
|
1834
|
-
function
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
case "not_equals":
|
|
1840
|
-
return String(value) !== String(filter.value);
|
|
1841
|
-
case "greater_than":
|
|
1842
|
-
return Number(value) > Number(filter.value);
|
|
1843
|
-
case "less_than":
|
|
1844
|
-
return Number(value) < Number(filter.value);
|
|
1845
|
-
case "contains":
|
|
1846
|
-
return String(value).toLowerCase().includes(String(filter.value).toLowerCase());
|
|
1847
|
-
case "in":
|
|
1848
|
-
return Array.isArray(filter.value) && filter.value.includes(String(value));
|
|
1849
|
-
default:
|
|
1850
|
-
return false;
|
|
2780
|
+
function getLastWorkedSession() {
|
|
2781
|
+
for (const s of listSessions()) {
|
|
2782
|
+
if ((s.exchange_count ?? 0) > 0 || s.stage === "analyzed" || s.stage === "delivered" || !!s.dataset?.label) {
|
|
2783
|
+
return s;
|
|
2784
|
+
}
|
|
1851
2785
|
}
|
|
2786
|
+
return null;
|
|
1852
2787
|
}
|
|
1853
|
-
async function
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
2788
|
+
async function closeSession(ctx) {
|
|
2789
|
+
return finalizeSession(ctx, ctx.stage);
|
|
2790
|
+
}
|
|
2791
|
+
async function endSession(ctx) {
|
|
2792
|
+
return finalizeSession(ctx, "ended");
|
|
2793
|
+
}
|
|
2794
|
+
async function rotateToFreshSession(ctx) {
|
|
2795
|
+
const { setActiveDbPath: setActiveDbPath2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
|
|
2796
|
+
const newId = makeSessionId();
|
|
2797
|
+
resetContextForSwitch(ctx, {
|
|
2798
|
+
sessionId: newId,
|
|
2799
|
+
sessionFile: join6(getSessionsDir(), `${newId}.json`),
|
|
2800
|
+
messages: [],
|
|
2801
|
+
stage: "new",
|
|
2802
|
+
analysis: defaultSessionAnalysis(),
|
|
2803
|
+
llm: void 0
|
|
2804
|
+
});
|
|
2805
|
+
ctx.datasetPath = datasetPathForSession(newId);
|
|
2806
|
+
await setActiveDbPath2(ctx.datasetPath);
|
|
2807
|
+
}
|
|
2808
|
+
async function finalizeSession(ctx, stage) {
|
|
2809
|
+
if (ctx.oneShot) return void 0;
|
|
2810
|
+
const exchangeCount = Math.floor(ctx.messages.length / 2);
|
|
2811
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2812
|
+
if (ctx.messages.length === 0 && ctx.stage === "new" && !ctx.dataset && ctx.deliverables.length === 0) {
|
|
2813
|
+
if (ctx.datasetPath) {
|
|
2814
|
+
try {
|
|
2815
|
+
const { close: close2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
|
|
2816
|
+
await close2();
|
|
2817
|
+
} catch {
|
|
2818
|
+
}
|
|
2819
|
+
for (const path of [ctx.datasetPath, `${ctx.datasetPath}.wal`]) {
|
|
2820
|
+
try {
|
|
2821
|
+
rmSync2(path, { force: true });
|
|
2822
|
+
} catch {
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
return void 0;
|
|
1872
2827
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
WHERE json_extract_string(metadata, '$.size') IS NOT NULL
|
|
1877
|
-
GROUP BY size
|
|
1878
|
-
HAVING cnt >= 5
|
|
1879
|
-
ORDER BY cnt DESC`
|
|
1880
|
-
);
|
|
1881
|
-
for (const row of sizes) {
|
|
1882
|
-
await insertSegment({
|
|
1883
|
-
name: `Size: ${row.size}`,
|
|
1884
|
-
entity_type: "organizations",
|
|
1885
|
-
filters: [{ field: "metadata.size", operator: "equals", value: row.size }],
|
|
1886
|
-
is_auto_generated: true
|
|
1887
|
-
});
|
|
1888
|
-
count++;
|
|
2828
|
+
if (ctx.deliverables.length > 0) {
|
|
2829
|
+
const { creditSessionDeliverableWrapup: creditSessionDeliverableWrapup2 } = await Promise.resolve().then(() => (init_time_bank(), time_bank_exports));
|
|
2830
|
+
creditSessionDeliverableWrapup2(ctx);
|
|
1889
2831
|
}
|
|
1890
|
-
const
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
filters: [{ field: "amount", operator: "less_than", value: 100001 }],
|
|
1903
|
-
is_auto_generated: true
|
|
1904
|
-
});
|
|
1905
|
-
count++;
|
|
2832
|
+
const { recordSessionClosed: recordSessionClosed2 } = await Promise.resolve().then(() => (init_usage_stats(), usage_stats_exports));
|
|
2833
|
+
recordSessionClosed2();
|
|
2834
|
+
const file = {
|
|
2835
|
+
id: ctx.sessionId,
|
|
2836
|
+
created_at: ctx.messages[0]?.at ?? endedAt,
|
|
2837
|
+
messages: ctx.messages,
|
|
2838
|
+
ended_at: endedAt,
|
|
2839
|
+
exchange_count: exchangeCount,
|
|
2840
|
+
stage
|
|
2841
|
+
};
|
|
2842
|
+
if (ctx.resumedFromId) {
|
|
2843
|
+
file.resumed_from = ctx.resumedFromId;
|
|
1906
2844
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
FROM opportunities o
|
|
1910
|
-
JOIN people p ON o.owner_id = p.id
|
|
1911
|
-
WHERE o.owner_id IS NOT NULL
|
|
1912
|
-
GROUP BY o.owner_id, p.canonical_name
|
|
1913
|
-
HAVING cnt >= 3
|
|
1914
|
-
ORDER BY cnt DESC`
|
|
1915
|
-
);
|
|
1916
|
-
for (const row of owners) {
|
|
1917
|
-
await insertSegment({
|
|
1918
|
-
name: `Rep: ${row.owner_name}`,
|
|
1919
|
-
entity_type: "opportunities",
|
|
1920
|
-
filters: [{ field: "owner_id", operator: "equals", value: row.owner_id }],
|
|
1921
|
-
is_auto_generated: true
|
|
1922
|
-
});
|
|
1923
|
-
count++;
|
|
2845
|
+
if (ctx.sessionName) {
|
|
2846
|
+
file.name = ctx.sessionName;
|
|
1924
2847
|
}
|
|
1925
|
-
|
|
2848
|
+
if (ctx.dataset) {
|
|
2849
|
+
file.dataset = ctx.dataset;
|
|
2850
|
+
}
|
|
2851
|
+
if (ctx.deliverables.length > 0) {
|
|
2852
|
+
file.deliverables = ctx.deliverables;
|
|
2853
|
+
}
|
|
2854
|
+
if (ctx.conversation.length > 0) {
|
|
2855
|
+
file.thread = ctx.conversation;
|
|
2856
|
+
}
|
|
2857
|
+
if (ctx.analysis) {
|
|
2858
|
+
file.analysis = ctx.analysis;
|
|
2859
|
+
}
|
|
2860
|
+
if (ctx.scope) {
|
|
2861
|
+
file.scope = ctx.scope;
|
|
2862
|
+
}
|
|
2863
|
+
if (ctx.attachments && ctx.attachments.length > 0) {
|
|
2864
|
+
file.attachments = ctx.attachments;
|
|
2865
|
+
}
|
|
2866
|
+
if (ctx.llm && Object.keys(ctx.llm).length > 0) {
|
|
2867
|
+
file.llm = ctx.llm;
|
|
2868
|
+
}
|
|
2869
|
+
try {
|
|
2870
|
+
writeFileSync5(ctx.sessionFile, JSON.stringify(file, null, 2) + "\n");
|
|
2871
|
+
} catch {
|
|
2872
|
+
}
|
|
2873
|
+
return void 0;
|
|
1926
2874
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2875
|
+
function resolveSessionByToken(idArg, options) {
|
|
2876
|
+
const printErrors = options?.printErrors !== false;
|
|
2877
|
+
const all2 = listSessions();
|
|
2878
|
+
const lower = idArg.toLowerCase();
|
|
2879
|
+
let matches = all2.filter((s) => s.id === idArg);
|
|
2880
|
+
if (matches.length === 0) matches = all2.filter((s) => s.name?.toLowerCase() === lower);
|
|
2881
|
+
if (matches.length === 0 && idArg.length >= 4) {
|
|
2882
|
+
matches = all2.filter((s) => s.id.endsWith(idArg));
|
|
1932
2883
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
const aggMap = new Map(aggregate.vital_signs.map((v) => [v.vital_sign, v]));
|
|
1940
|
-
for (const seg of segments) {
|
|
1941
|
-
for (const vs of seg.result.vital_signs) {
|
|
1942
|
-
const agg = aggMap.get(vs.vital_sign);
|
|
1943
|
-
if (!agg) continue;
|
|
1944
|
-
const delta = vs.score - agg.score;
|
|
1945
|
-
const statusDiffers = vs.status !== agg.status;
|
|
1946
|
-
const scoreDiverges = Math.abs(delta) >= SCORE_THRESHOLD;
|
|
1947
|
-
if (statusDiffers || scoreDiverges) {
|
|
1948
|
-
divergences.push({
|
|
1949
|
-
segmentId: seg.segmentId,
|
|
1950
|
-
segmentName: seg.segmentName,
|
|
1951
|
-
vitalSign: vs.vital_sign,
|
|
1952
|
-
segmentScore: vs.score,
|
|
1953
|
-
aggregateScore: agg.score,
|
|
1954
|
-
delta,
|
|
1955
|
-
segmentStatus: vs.status,
|
|
1956
|
-
aggregateStatus: agg.status
|
|
1957
|
-
});
|
|
1958
|
-
}
|
|
2884
|
+
if (matches.length === 0) return void 0;
|
|
2885
|
+
if (matches.length > 1) {
|
|
2886
|
+
if (printErrors) {
|
|
2887
|
+
console.log(
|
|
2888
|
+
` Ambiguous "${idArg}" \u2014 matches ${matches.length} sessions. Use a longer id.`
|
|
2889
|
+
);
|
|
1959
2890
|
}
|
|
2891
|
+
return null;
|
|
1960
2892
|
}
|
|
1961
|
-
|
|
1962
|
-
return { divergences };
|
|
2893
|
+
return matches[0];
|
|
1963
2894
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2895
|
+
function findSessionByName(name) {
|
|
2896
|
+
const lower = name.toLowerCase();
|
|
2897
|
+
const all2 = listSessions();
|
|
2898
|
+
return all2.find((s) => s.name?.toLowerCase() === lower) ?? null;
|
|
2899
|
+
}
|
|
2900
|
+
function buildSwitchContext(session) {
|
|
2901
|
+
const parts = [];
|
|
2902
|
+
if (session.summary) parts.push(session.summary);
|
|
2903
|
+
const userMsgs = session.messages.filter((m) => m.role === "user").slice(-3);
|
|
2904
|
+
for (const m of userMsgs) {
|
|
2905
|
+
parts.push(m.content.slice(0, 300));
|
|
1967
2906
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2907
|
+
return parts.join("\n");
|
|
2908
|
+
}
|
|
2909
|
+
function resetContextForSwitch(ctx, opts) {
|
|
2910
|
+
ctx.sessionId = opts.sessionId;
|
|
2911
|
+
ctx.sessionFile = opts.sessionFile;
|
|
2912
|
+
ctx.sessionName = opts.sessionName;
|
|
2913
|
+
ctx.messages = opts.messages;
|
|
2914
|
+
ctx.conversation = opts.conversation ?? [];
|
|
2915
|
+
ctx.resumedFromId = opts.resumedFromId;
|
|
2916
|
+
ctx.resumedSessionSummary = opts.resumedSessionSummary;
|
|
2917
|
+
ctx.stage = opts.stage ?? "new";
|
|
2918
|
+
ctx.dataset = opts.dataset;
|
|
2919
|
+
ctx.deliverables = opts.deliverables ?? [];
|
|
2920
|
+
ctx.analysis = opts.analysis ?? defaultSessionAnalysis();
|
|
2921
|
+
ctx.scope = opts.scope;
|
|
2922
|
+
ctx.attachments = opts.attachments ?? [];
|
|
2923
|
+
ctx.llm = opts.llm;
|
|
2924
|
+
ctx.gapAudit = void 0;
|
|
2925
|
+
ctx.deliverIntent = false;
|
|
2926
|
+
ctx.computeInProgress = false;
|
|
2927
|
+
ctx.wizardDepth = 0;
|
|
2928
|
+
ctx.snapshot = { computeResult: null, divergences: [] };
|
|
2929
|
+
}
|
|
2930
|
+
var SESSION_ID_RE;
|
|
2931
|
+
var init_context2 = __esm({
|
|
2932
|
+
"src/cli/context.ts"() {
|
|
1974
2933
|
"use strict";
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
org_window_days: 90,
|
|
1979
|
-
opp_window_days: 30,
|
|
1980
|
-
red_below: 60,
|
|
1981
|
-
green_above: 80,
|
|
1982
|
-
weights: { people: 0.35, organizations: 0.3, opportunities: 0.35 }
|
|
1983
|
-
},
|
|
1984
|
-
flow_rate: {
|
|
1985
|
-
green_days: 45,
|
|
1986
|
-
yellow_days: 90,
|
|
1987
|
-
stuck_days: 60,
|
|
1988
|
-
max_days: 120
|
|
1989
|
-
},
|
|
1990
|
-
drop_rate: {
|
|
1991
|
-
marketing_systems: ["hubspot"],
|
|
1992
|
-
sales_systems: ["salesforce"],
|
|
1993
|
-
recency_days: 30,
|
|
1994
|
-
red_below: 60,
|
|
1995
|
-
green_above: 80,
|
|
1996
|
-
weights: { cross_system: 0.6, abandoned: 0.4 }
|
|
1997
|
-
},
|
|
1998
|
-
signal_to_noise: {
|
|
1999
|
-
lookback_days: 90,
|
|
2000
|
-
red_below: 40,
|
|
2001
|
-
green_above: 65
|
|
2002
|
-
},
|
|
2003
|
-
thread_depth: {
|
|
2004
|
-
activity_window_days: 90,
|
|
2005
|
-
multi_thread_threshold: 2,
|
|
2006
|
-
red_below: 40,
|
|
2007
|
-
green_above: 65
|
|
2008
|
-
}
|
|
2009
|
-
};
|
|
2934
|
+
init_thread_compat();
|
|
2935
|
+
init_context();
|
|
2936
|
+
SESSION_ID_RE = /^\d{4}-\d{2}-\d{2}-[a-f0-9]{4}$/i;
|
|
2010
2937
|
}
|
|
2011
2938
|
});
|
|
2012
2939
|
|
|
2013
|
-
// src/
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
flow_rate: {
|
|
2026
|
-
green_days: 21,
|
|
2027
|
-
yellow_days: 45,
|
|
2028
|
-
stuck_days: 30,
|
|
2029
|
-
max_days: 60
|
|
2030
|
-
},
|
|
2031
|
-
signal_to_noise: {
|
|
2032
|
-
lookback_days: 60,
|
|
2033
|
-
red_below: 25,
|
|
2034
|
-
green_above: 50
|
|
2035
|
-
},
|
|
2036
|
-
thread_depth: {
|
|
2037
|
-
activity_window_days: 60,
|
|
2038
|
-
red_below: 30,
|
|
2039
|
-
green_above: 55
|
|
2040
|
-
}
|
|
2041
|
-
},
|
|
2042
|
-
smb_velocity: {
|
|
2043
|
-
freshness: {
|
|
2044
|
-
people_window_days: 60,
|
|
2045
|
-
org_window_days: 60
|
|
2046
|
-
},
|
|
2047
|
-
flow_rate: {
|
|
2048
|
-
green_days: 30,
|
|
2049
|
-
yellow_days: 60,
|
|
2050
|
-
stuck_days: 45,
|
|
2051
|
-
max_days: 90
|
|
2052
|
-
},
|
|
2053
|
-
signal_to_noise: {
|
|
2054
|
-
lookback_days: 60
|
|
2055
|
-
},
|
|
2056
|
-
thread_depth: {
|
|
2057
|
-
activity_window_days: 60
|
|
2058
|
-
}
|
|
2059
|
-
},
|
|
2060
|
-
mid_market: {
|
|
2061
|
-
flow_rate: {
|
|
2062
|
-
green_days: 60,
|
|
2063
|
-
yellow_days: 120,
|
|
2064
|
-
stuck_days: 75,
|
|
2065
|
-
max_days: 150
|
|
2066
|
-
}
|
|
2067
|
-
},
|
|
2068
|
-
enterprise: {
|
|
2069
|
-
freshness: {
|
|
2070
|
-
people_window_days: 120,
|
|
2071
|
-
org_window_days: 120,
|
|
2072
|
-
opp_window_days: 45
|
|
2073
|
-
},
|
|
2074
|
-
flow_rate: {
|
|
2075
|
-
green_days: 90,
|
|
2076
|
-
yellow_days: 180,
|
|
2077
|
-
stuck_days: 90,
|
|
2078
|
-
max_days: 240
|
|
2079
|
-
},
|
|
2080
|
-
signal_to_noise: {
|
|
2081
|
-
lookback_days: 120
|
|
2082
|
-
},
|
|
2083
|
-
thread_depth: {
|
|
2084
|
-
activity_window_days: 120,
|
|
2085
|
-
multi_thread_threshold: 3,
|
|
2086
|
-
red_below: 50,
|
|
2087
|
-
green_above: 75
|
|
2088
|
-
}
|
|
2940
|
+
// src/pipeline/segments.ts
|
|
2941
|
+
function resolveSegmentScopeFromSnapshot(segment, snapshot) {
|
|
2942
|
+
const orgIds = [];
|
|
2943
|
+
const peopleIds = [];
|
|
2944
|
+
const oppIds = [];
|
|
2945
|
+
if (segment.entity_type === "organizations") {
|
|
2946
|
+
const filtered = filterEntities(snapshot.organizations, segment.filters);
|
|
2947
|
+
const orgIdSet = new Set(filtered.map((o) => o.id));
|
|
2948
|
+
orgIds.push(...orgIdSet);
|
|
2949
|
+
for (const p of snapshot.people) {
|
|
2950
|
+
if (p.organization_id && orgIdSet.has(p.organization_id)) {
|
|
2951
|
+
peopleIds.push(p.id);
|
|
2089
2952
|
}
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
})
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
if (cachedConfig) return cachedConfig;
|
|
2123
|
-
ensureDir2();
|
|
2124
|
-
if (!existsSync3(CONFIG_PATH)) {
|
|
2125
|
-
cachedConfig = {};
|
|
2126
|
-
return cachedConfig;
|
|
2127
|
-
}
|
|
2128
|
-
try {
|
|
2129
|
-
cachedConfig = JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
|
|
2130
|
-
} catch {
|
|
2131
|
-
cachedConfig = {};
|
|
2953
|
+
}
|
|
2954
|
+
for (const o of snapshot.opportunities) {
|
|
2955
|
+
if (o.organization_id && orgIdSet.has(o.organization_id)) {
|
|
2956
|
+
oppIds.push(o.id);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
} else if (segment.entity_type === "opportunities") {
|
|
2960
|
+
const filtered = filterEntities(snapshot.opportunities, segment.filters);
|
|
2961
|
+
oppIds.push(...filtered.map((o) => o.id));
|
|
2962
|
+
const orgIdSet = /* @__PURE__ */ new Set();
|
|
2963
|
+
for (const o of filtered) {
|
|
2964
|
+
if (o.organization_id) orgIdSet.add(o.organization_id);
|
|
2965
|
+
}
|
|
2966
|
+
orgIds.push(...orgIdSet);
|
|
2967
|
+
for (const p of snapshot.people) {
|
|
2968
|
+
if (p.organization_id && orgIdSet.has(p.organization_id)) {
|
|
2969
|
+
peopleIds.push(p.id);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
} else if (segment.entity_type === "people") {
|
|
2973
|
+
const filtered = filterEntities(snapshot.people, segment.filters);
|
|
2974
|
+
peopleIds.push(...filtered.map((p) => p.id));
|
|
2975
|
+
const orgIdSet = /* @__PURE__ */ new Set();
|
|
2976
|
+
for (const p of filtered) {
|
|
2977
|
+
if (p.organization_id) orgIdSet.add(p.organization_id);
|
|
2978
|
+
}
|
|
2979
|
+
orgIds.push(...orgIdSet);
|
|
2980
|
+
for (const o of snapshot.opportunities) {
|
|
2981
|
+
if (o.organization_id && orgIdSet.has(o.organization_id)) {
|
|
2982
|
+
oppIds.push(o.id);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2132
2985
|
}
|
|
2133
|
-
return
|
|
2134
|
-
}
|
|
2135
|
-
function saveConfig(config) {
|
|
2136
|
-
ensureDir2();
|
|
2137
|
-
writeFileSync2(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
2138
|
-
cachedConfig = config;
|
|
2139
|
-
}
|
|
2140
|
-
function resetConfigCache() {
|
|
2141
|
-
cachedConfig = null;
|
|
2142
|
-
}
|
|
2143
|
-
function getConfigValue(key) {
|
|
2144
|
-
if (key === "api-key") return loadConfig()["api-key"];
|
|
2145
|
-
if (key === "license-key") return process.env.NTRP_LICENSE_KEY ?? loadConfig()["license-key"];
|
|
2146
|
-
const config = loadConfig();
|
|
2147
|
-
return config[key];
|
|
2148
|
-
}
|
|
2149
|
-
function setConfigValue(key, value) {
|
|
2150
|
-
const config = loadConfig();
|
|
2151
|
-
config[key] = value;
|
|
2152
|
-
saveConfig(config);
|
|
2986
|
+
return { orgIds, peopleIds, oppIds };
|
|
2153
2987
|
}
|
|
2154
|
-
function
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2988
|
+
function filterEntities(entities, filters) {
|
|
2989
|
+
return entities.filter(
|
|
2990
|
+
(entity) => filters.every((f) => matchFilter(entity, f))
|
|
2991
|
+
);
|
|
2158
2992
|
}
|
|
2159
|
-
function
|
|
2160
|
-
const
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2993
|
+
function getNestedValue(obj, path) {
|
|
2994
|
+
const parts = path.split(".");
|
|
2995
|
+
let current = obj;
|
|
2996
|
+
for (const part of parts) {
|
|
2997
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
2998
|
+
current = current[part];
|
|
2164
2999
|
}
|
|
2165
|
-
return
|
|
3000
|
+
return current;
|
|
2166
3001
|
}
|
|
2167
|
-
function
|
|
2168
|
-
const
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
\`\`\`markdown
|
|
2185
|
-
# Multi-Thread Enterprise Deals \u2014 Q2
|
|
2186
|
-
|
|
2187
|
-
**Goal:** Reduce single-threaded deals from 65% to under 30%
|
|
2188
|
-
**Segment:** Enterprise accounts > $100K
|
|
2189
|
-
**Play:** Multi-Thread Your Deals
|
|
2190
|
-
**Success metric:** Thread depth score > 70
|
|
2191
|
-
\`\`\`
|
|
2192
|
-
`);
|
|
3002
|
+
function matchFilter(entity, filter) {
|
|
3003
|
+
const value = getNestedValue(entity, filter.field);
|
|
3004
|
+
switch (filter.operator) {
|
|
3005
|
+
case "equals":
|
|
3006
|
+
return String(value) === String(filter.value);
|
|
3007
|
+
case "not_equals":
|
|
3008
|
+
return String(value) !== String(filter.value);
|
|
3009
|
+
case "greater_than":
|
|
3010
|
+
return Number(value) > Number(filter.value);
|
|
3011
|
+
case "less_than":
|
|
3012
|
+
return Number(value) < Number(filter.value);
|
|
3013
|
+
case "contains":
|
|
3014
|
+
return String(value).toLowerCase().includes(String(filter.value).toLowerCase());
|
|
3015
|
+
case "in":
|
|
3016
|
+
return Array.isArray(filter.value) && filter.value.includes(String(value));
|
|
3017
|
+
default:
|
|
3018
|
+
return false;
|
|
2193
3019
|
}
|
|
2194
|
-
return dir;
|
|
2195
3020
|
}
|
|
2196
|
-
function
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
3021
|
+
async function autoGenerateSegments() {
|
|
3022
|
+
await all("DELETE FROM segments WHERE is_auto_generated = true");
|
|
3023
|
+
let count = 0;
|
|
3024
|
+
const industries = await all(
|
|
3025
|
+
`SELECT json_extract_string(metadata, '$.industry') as industry, COUNT(*) as cnt
|
|
3026
|
+
FROM organizations
|
|
3027
|
+
WHERE json_extract_string(metadata, '$.industry') IS NOT NULL
|
|
3028
|
+
GROUP BY industry
|
|
3029
|
+
HAVING cnt >= 5
|
|
3030
|
+
ORDER BY cnt DESC`
|
|
3031
|
+
);
|
|
3032
|
+
for (const row of industries) {
|
|
3033
|
+
await insertSegment({
|
|
3034
|
+
name: `Industry: ${row.industry}`,
|
|
3035
|
+
entity_type: "organizations",
|
|
3036
|
+
filters: [{ field: "metadata.industry", operator: "equals", value: row.industry }],
|
|
3037
|
+
is_auto_generated: true
|
|
3038
|
+
});
|
|
3039
|
+
count++;
|
|
2200
3040
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
2. List what's indexed: \`/knowledge list\`
|
|
2218
|
-
3. Ask a question \u2014 relevant passages are pulled in automatically.
|
|
2219
|
-
`);
|
|
3041
|
+
const sizes = await all(
|
|
3042
|
+
`SELECT json_extract_string(metadata, '$.size') as size, COUNT(*) as cnt
|
|
3043
|
+
FROM organizations
|
|
3044
|
+
WHERE json_extract_string(metadata, '$.size') IS NOT NULL
|
|
3045
|
+
GROUP BY size
|
|
3046
|
+
HAVING cnt >= 5
|
|
3047
|
+
ORDER BY cnt DESC`
|
|
3048
|
+
);
|
|
3049
|
+
for (const row of sizes) {
|
|
3050
|
+
await insertSegment({
|
|
3051
|
+
name: `Size: ${row.size}`,
|
|
3052
|
+
entity_type: "organizations",
|
|
3053
|
+
filters: [{ field: "metadata.size", operator: "equals", value: row.size }],
|
|
3054
|
+
is_auto_generated: true
|
|
3055
|
+
});
|
|
3056
|
+
count++;
|
|
2220
3057
|
}
|
|
2221
|
-
|
|
3058
|
+
const oppCount = await all("SELECT COUNT(*) as cnt FROM opportunities");
|
|
3059
|
+
if ((oppCount[0]?.cnt ?? 0) >= 10) {
|
|
3060
|
+
await insertSegment({
|
|
3061
|
+
name: "Enterprise Deals (>$100K)",
|
|
3062
|
+
entity_type: "opportunities",
|
|
3063
|
+
filters: [{ field: "amount", operator: "greater_than", value: 1e5 }],
|
|
3064
|
+
is_auto_generated: true
|
|
3065
|
+
});
|
|
3066
|
+
count++;
|
|
3067
|
+
await insertSegment({
|
|
3068
|
+
name: "SMB Deals (\u2264$100K)",
|
|
3069
|
+
entity_type: "opportunities",
|
|
3070
|
+
filters: [{ field: "amount", operator: "less_than", value: 100001 }],
|
|
3071
|
+
is_auto_generated: true
|
|
3072
|
+
});
|
|
3073
|
+
count++;
|
|
3074
|
+
}
|
|
3075
|
+
const owners = await all(
|
|
3076
|
+
`SELECT o.owner_id, p.canonical_name as owner_name, COUNT(*) as cnt
|
|
3077
|
+
FROM opportunities o
|
|
3078
|
+
JOIN people p ON o.owner_id = p.id
|
|
3079
|
+
WHERE o.owner_id IS NOT NULL
|
|
3080
|
+
GROUP BY o.owner_id, p.canonical_name
|
|
3081
|
+
HAVING cnt >= 3
|
|
3082
|
+
ORDER BY cnt DESC`
|
|
3083
|
+
);
|
|
3084
|
+
for (const row of owners) {
|
|
3085
|
+
await insertSegment({
|
|
3086
|
+
name: `Rep: ${row.owner_name}`,
|
|
3087
|
+
entity_type: "opportunities",
|
|
3088
|
+
filters: [{ field: "owner_id", operator: "equals", value: row.owner_id }],
|
|
3089
|
+
is_auto_generated: true
|
|
3090
|
+
});
|
|
3091
|
+
count++;
|
|
3092
|
+
}
|
|
3093
|
+
return count;
|
|
2222
3094
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
## How to use
|
|
2232
|
-
|
|
2233
|
-
1. After executing a play, log the result here (e.g., \`2026-04-clean-pipeline.md\`)
|
|
2234
|
-
2. Include: what you did, what changed, before/after scores
|
|
2235
|
-
3. Future AI findings will reference wins to track improvement over time
|
|
2236
|
-
|
|
2237
|
-
## Example
|
|
2238
|
-
|
|
2239
|
-
\`\`\`markdown
|
|
2240
|
-
# Pipeline Cleanup \u2014 April 2026
|
|
3095
|
+
var init_segments = __esm({
|
|
3096
|
+
"src/pipeline/segments.ts"() {
|
|
3097
|
+
"use strict";
|
|
3098
|
+
init_connection();
|
|
3099
|
+
init_queries();
|
|
3100
|
+
}
|
|
3101
|
+
});
|
|
2241
3102
|
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
3103
|
+
// src/pipeline/divergence.ts
|
|
3104
|
+
function detectDivergences(aggregate, segments) {
|
|
3105
|
+
const divergences = [];
|
|
3106
|
+
const SCORE_THRESHOLD = 15;
|
|
3107
|
+
const aggMap = new Map(aggregate.vital_signs.map((v) => [v.vital_sign, v]));
|
|
3108
|
+
for (const seg of segments) {
|
|
3109
|
+
for (const vs of seg.result.vital_signs) {
|
|
3110
|
+
const agg = aggMap.get(vs.vital_sign);
|
|
3111
|
+
if (!agg) continue;
|
|
3112
|
+
const delta = vs.score - agg.score;
|
|
3113
|
+
const statusDiffers = vs.status !== agg.status;
|
|
3114
|
+
const scoreDiverges = Math.abs(delta) >= SCORE_THRESHOLD;
|
|
3115
|
+
if (statusDiffers || scoreDiverges) {
|
|
3116
|
+
divergences.push({
|
|
3117
|
+
segmentId: seg.segmentId,
|
|
3118
|
+
segmentName: seg.segmentName,
|
|
3119
|
+
vitalSign: vs.vital_sign,
|
|
3120
|
+
segmentScore: vs.score,
|
|
3121
|
+
aggregateScore: agg.score,
|
|
3122
|
+
delta,
|
|
3123
|
+
segmentStatus: vs.status,
|
|
3124
|
+
aggregateStatus: agg.status
|
|
3125
|
+
});
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
2248
3128
|
}
|
|
2249
|
-
|
|
3129
|
+
divergences.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
|
|
3130
|
+
return { divergences };
|
|
2250
3131
|
}
|
|
2251
|
-
var
|
|
2252
|
-
|
|
2253
|
-
"src/config/store.ts"() {
|
|
3132
|
+
var init_divergence = __esm({
|
|
3133
|
+
"src/pipeline/divergence.ts"() {
|
|
2254
3134
|
"use strict";
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
3135
|
+
}
|
|
3136
|
+
});
|
|
3137
|
+
|
|
3138
|
+
// src/baselines/defaults.ts
|
|
3139
|
+
var DEFAULT_THRESHOLDS;
|
|
3140
|
+
var init_defaults = __esm({
|
|
3141
|
+
"src/baselines/defaults.ts"() {
|
|
3142
|
+
"use strict";
|
|
3143
|
+
DEFAULT_THRESHOLDS = {
|
|
3144
|
+
freshness: {
|
|
3145
|
+
people_window_days: 90,
|
|
3146
|
+
org_window_days: 90,
|
|
3147
|
+
opp_window_days: 30,
|
|
3148
|
+
red_below: 60,
|
|
3149
|
+
green_above: 80,
|
|
3150
|
+
weights: { people: 0.35, organizations: 0.3, opportunities: 0.35 }
|
|
3151
|
+
},
|
|
3152
|
+
flow_rate: {
|
|
3153
|
+
green_days: 45,
|
|
3154
|
+
yellow_days: 90,
|
|
3155
|
+
stuck_days: 60,
|
|
3156
|
+
max_days: 120
|
|
3157
|
+
},
|
|
3158
|
+
drop_rate: {
|
|
3159
|
+
marketing_systems: ["hubspot"],
|
|
3160
|
+
sales_systems: ["salesforce"],
|
|
3161
|
+
recency_days: 30,
|
|
3162
|
+
red_below: 60,
|
|
3163
|
+
green_above: 80,
|
|
3164
|
+
weights: { cross_system: 0.6, abandoned: 0.4 }
|
|
3165
|
+
},
|
|
3166
|
+
signal_to_noise: {
|
|
3167
|
+
lookback_days: 90,
|
|
3168
|
+
red_below: 40,
|
|
3169
|
+
green_above: 65
|
|
3170
|
+
},
|
|
3171
|
+
thread_depth: {
|
|
3172
|
+
activity_window_days: 90,
|
|
3173
|
+
multi_thread_threshold: 2,
|
|
3174
|
+
red_below: 40,
|
|
3175
|
+
green_above: 65
|
|
3176
|
+
}
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
});
|
|
3180
|
+
|
|
3181
|
+
// src/baselines/profile-presets.ts
|
|
3182
|
+
var PROFILE_PRESETS;
|
|
3183
|
+
var init_profile_presets = __esm({
|
|
3184
|
+
"src/baselines/profile-presets.ts"() {
|
|
3185
|
+
"use strict";
|
|
3186
|
+
PROFILE_PRESETS = {
|
|
3187
|
+
plg: {
|
|
3188
|
+
freshness: {
|
|
3189
|
+
people_window_days: 60,
|
|
3190
|
+
org_window_days: 60,
|
|
3191
|
+
opp_window_days: 21
|
|
3192
|
+
},
|
|
3193
|
+
flow_rate: {
|
|
3194
|
+
green_days: 21,
|
|
3195
|
+
yellow_days: 45,
|
|
3196
|
+
stuck_days: 30,
|
|
3197
|
+
max_days: 60
|
|
3198
|
+
},
|
|
3199
|
+
signal_to_noise: {
|
|
3200
|
+
lookback_days: 60,
|
|
3201
|
+
red_below: 25,
|
|
3202
|
+
green_above: 50
|
|
3203
|
+
},
|
|
3204
|
+
thread_depth: {
|
|
3205
|
+
activity_window_days: 60,
|
|
3206
|
+
red_below: 30,
|
|
3207
|
+
green_above: 55
|
|
3208
|
+
}
|
|
3209
|
+
},
|
|
3210
|
+
smb_velocity: {
|
|
3211
|
+
freshness: {
|
|
3212
|
+
people_window_days: 60,
|
|
3213
|
+
org_window_days: 60
|
|
3214
|
+
},
|
|
3215
|
+
flow_rate: {
|
|
3216
|
+
green_days: 30,
|
|
3217
|
+
yellow_days: 60,
|
|
3218
|
+
stuck_days: 45,
|
|
3219
|
+
max_days: 90
|
|
3220
|
+
},
|
|
3221
|
+
signal_to_noise: {
|
|
3222
|
+
lookback_days: 60
|
|
3223
|
+
},
|
|
3224
|
+
thread_depth: {
|
|
3225
|
+
activity_window_days: 60
|
|
3226
|
+
}
|
|
3227
|
+
},
|
|
3228
|
+
mid_market: {
|
|
3229
|
+
flow_rate: {
|
|
3230
|
+
green_days: 60,
|
|
3231
|
+
yellow_days: 120,
|
|
3232
|
+
stuck_days: 75,
|
|
3233
|
+
max_days: 150
|
|
3234
|
+
}
|
|
3235
|
+
},
|
|
3236
|
+
enterprise: {
|
|
3237
|
+
freshness: {
|
|
3238
|
+
people_window_days: 120,
|
|
3239
|
+
org_window_days: 120,
|
|
3240
|
+
opp_window_days: 45
|
|
3241
|
+
},
|
|
3242
|
+
flow_rate: {
|
|
3243
|
+
green_days: 90,
|
|
3244
|
+
yellow_days: 180,
|
|
3245
|
+
stuck_days: 90,
|
|
3246
|
+
max_days: 240
|
|
3247
|
+
},
|
|
3248
|
+
signal_to_noise: {
|
|
3249
|
+
lookback_days: 120
|
|
3250
|
+
},
|
|
3251
|
+
thread_depth: {
|
|
3252
|
+
activity_window_days: 120,
|
|
3253
|
+
multi_thread_threshold: 3,
|
|
3254
|
+
red_below: 50,
|
|
3255
|
+
green_above: 75
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
};
|
|
2258
3259
|
}
|
|
2259
3260
|
});
|
|
2260
3261
|
|
|
@@ -3348,7 +4349,14 @@ async function anthropicComplete(apiKey, model, req) {
|
|
|
3348
4349
|
...req.tools && req.tools.length > 0 ? { tools: toAnthropicTools(req.tools) } : {},
|
|
3349
4350
|
messages: toAnthropicMessages(req.messages)
|
|
3350
4351
|
});
|
|
3351
|
-
|
|
4352
|
+
const parsed = parseResponse(response.content);
|
|
4353
|
+
if (response.usage) {
|
|
4354
|
+
parsed.token_usage = {
|
|
4355
|
+
input_tokens: response.usage.input_tokens,
|
|
4356
|
+
output_tokens: response.usage.output_tokens
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
return parsed;
|
|
3352
4360
|
} catch (err) {
|
|
3353
4361
|
throw mapAnthropicError(err, provider);
|
|
3354
4362
|
}
|
|
@@ -3463,7 +4471,14 @@ async function openaiComplete(apiKey, model, req) {
|
|
|
3463
4471
|
if (!choice?.message) {
|
|
3464
4472
|
throw new Error("OpenAI returned no message");
|
|
3465
4473
|
}
|
|
3466
|
-
|
|
4474
|
+
const parsed = parseResponse2(choice.message);
|
|
4475
|
+
if (response.usage) {
|
|
4476
|
+
parsed.token_usage = {
|
|
4477
|
+
input_tokens: response.usage.prompt_tokens ?? 0,
|
|
4478
|
+
output_tokens: response.usage.completion_tokens ?? 0
|
|
4479
|
+
};
|
|
4480
|
+
}
|
|
4481
|
+
return parsed;
|
|
3467
4482
|
} catch (err) {
|
|
3468
4483
|
if (err instanceof OpenAI.APIError) {
|
|
3469
4484
|
throw mapOpenAiError(err, provider);
|
|
@@ -3776,8 +4791,10 @@ async function completeWithFailover(req, opts = {}) {
|
|
|
3776
4791
|
const meta = {
|
|
3777
4792
|
provider_used: provider,
|
|
3778
4793
|
model_used: model,
|
|
4794
|
+
...response.token_usage ?? {},
|
|
3779
4795
|
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {}
|
|
3780
4796
|
};
|
|
4797
|
+
recordLlmUsage(response.token_usage);
|
|
3781
4798
|
return { response, meta };
|
|
3782
4799
|
} catch (err) {
|
|
3783
4800
|
const llmErr = err;
|
|
@@ -3790,8 +4807,10 @@ async function completeWithFailover(req, opts = {}) {
|
|
|
3790
4807
|
const meta = {
|
|
3791
4808
|
provider_used: provider,
|
|
3792
4809
|
model_used: model,
|
|
4810
|
+
...response.token_usage ?? {},
|
|
3793
4811
|
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {}
|
|
3794
4812
|
};
|
|
4813
|
+
recordLlmUsage(response.token_usage);
|
|
3795
4814
|
return { response, meta };
|
|
3796
4815
|
} catch (retryErr) {
|
|
3797
4816
|
const retryLlm = retryErr;
|
|
@@ -3838,9 +4857,13 @@ async function* streamWithFailover(req, opts = {}) {
|
|
|
3838
4857
|
yield event;
|
|
3839
4858
|
}
|
|
3840
4859
|
}
|
|
4860
|
+
const estimatedOut = Math.ceil(fullText.length / 4);
|
|
4861
|
+
recordLlmUsage({ input_tokens: 0, output_tokens: estimatedOut });
|
|
3841
4862
|
const meta = {
|
|
3842
4863
|
provider_used: provider,
|
|
3843
4864
|
model_used: model,
|
|
4865
|
+
input_tokens: 0,
|
|
4866
|
+
output_tokens: estimatedOut,
|
|
3844
4867
|
...failoverFrom ? { failover: true, failover_from: failoverFrom } : {}
|
|
3845
4868
|
};
|
|
3846
4869
|
yield {
|
|
@@ -3873,6 +4896,7 @@ async function* streamWithFailover(req, opts = {}) {
|
|
|
3873
4896
|
var init_failover = __esm({
|
|
3874
4897
|
"src/ai/llm/failover.ts"() {
|
|
3875
4898
|
"use strict";
|
|
4899
|
+
init_usage_stats();
|
|
3876
4900
|
init_anthropic();
|
|
3877
4901
|
init_openai();
|
|
3878
4902
|
init_catalog();
|
|
@@ -3882,22 +4906,22 @@ var init_failover = __esm({
|
|
|
3882
4906
|
});
|
|
3883
4907
|
|
|
3884
4908
|
// src/config/profile.ts
|
|
3885
|
-
import { readFileSync as
|
|
3886
|
-
import { join as
|
|
4909
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
4910
|
+
import { join as join7 } from "path";
|
|
3887
4911
|
function profilePath() {
|
|
3888
4912
|
return PROFILE_PATH;
|
|
3889
4913
|
}
|
|
3890
4914
|
function profileExists() {
|
|
3891
|
-
return
|
|
4915
|
+
return existsSync7(PROFILE_PATH);
|
|
3892
4916
|
}
|
|
3893
4917
|
function isProfileConfigured(profile = loadProfile()) {
|
|
3894
4918
|
if (!profile) return false;
|
|
3895
4919
|
return profile.company_name.trim().length > 0;
|
|
3896
4920
|
}
|
|
3897
4921
|
function loadProfile() {
|
|
3898
|
-
if (!
|
|
4922
|
+
if (!existsSync7(PROFILE_PATH)) return null;
|
|
3899
4923
|
try {
|
|
3900
|
-
const parsed = JSON.parse(
|
|
4924
|
+
const parsed = JSON.parse(readFileSync6(PROFILE_PATH, "utf-8"));
|
|
3901
4925
|
if (!parsed || typeof parsed !== "object") return null;
|
|
3902
4926
|
return parsed;
|
|
3903
4927
|
} catch {
|
|
@@ -3910,21 +4934,21 @@ var init_profile = __esm({
|
|
|
3910
4934
|
"use strict";
|
|
3911
4935
|
init_store();
|
|
3912
4936
|
NTRP_DIR3 = ntrpHome();
|
|
3913
|
-
PROFILE_PATH =
|
|
4937
|
+
PROFILE_PATH = join7(NTRP_DIR3, "profile.json");
|
|
3914
4938
|
}
|
|
3915
4939
|
});
|
|
3916
4940
|
|
|
3917
4941
|
// src/data/playbook.ts
|
|
3918
|
-
import { existsSync as
|
|
3919
|
-
import { join as
|
|
4942
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, appendFileSync } from "fs";
|
|
4943
|
+
import { join as join8 } from "path";
|
|
3920
4944
|
function playsPath() {
|
|
3921
|
-
return
|
|
4945
|
+
return join8(getMemoryDir(), PLAYS_FILE);
|
|
3922
4946
|
}
|
|
3923
4947
|
function getCustomPlays() {
|
|
3924
4948
|
const path = playsPath();
|
|
3925
|
-
if (!
|
|
4949
|
+
if (!existsSync8(path)) return [];
|
|
3926
4950
|
const out = [];
|
|
3927
|
-
for (const line of
|
|
4951
|
+
for (const line of readFileSync7(path, "utf-8").split("\n")) {
|
|
3928
4952
|
const trimmed = line.trim();
|
|
3929
4953
|
if (!trimmed) continue;
|
|
3930
4954
|
try {
|
|
@@ -4672,9 +5696,9 @@ var init_tool_schemas = __esm({
|
|
|
4672
5696
|
});
|
|
4673
5697
|
|
|
4674
5698
|
// src/ai/privacy.ts
|
|
4675
|
-
import { existsSync as
|
|
5699
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, appendFileSync as appendFileSync2 } from "fs";
|
|
4676
5700
|
import { homedir as homedir3 } from "os";
|
|
4677
|
-
import { join as
|
|
5701
|
+
import { join as join9 } from "path";
|
|
4678
5702
|
function stripPII(obj) {
|
|
4679
5703
|
if (obj === null || obj === void 0) return obj;
|
|
4680
5704
|
if (typeof obj !== "object") return obj;
|
|
@@ -4689,14 +5713,14 @@ function stripPII(obj) {
|
|
|
4689
5713
|
return out;
|
|
4690
5714
|
}
|
|
4691
5715
|
function ensureAuditDir() {
|
|
4692
|
-
if (!
|
|
4693
|
-
|
|
5716
|
+
if (!existsSync9(AUDIT_DIR)) {
|
|
5717
|
+
mkdirSync7(AUDIT_DIR, { recursive: true });
|
|
4694
5718
|
}
|
|
4695
5719
|
}
|
|
4696
5720
|
function logToolCall(entry) {
|
|
4697
5721
|
ensureAuditDir();
|
|
4698
5722
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4699
|
-
const path =
|
|
5723
|
+
const path = join9(AUDIT_DIR, `agentic-${date}.jsonl`);
|
|
4700
5724
|
appendFileSync2(path, JSON.stringify(entry) + "\n");
|
|
4701
5725
|
}
|
|
4702
5726
|
var PII_FIELDS, AUDIT_DIR;
|
|
@@ -4720,7 +5744,7 @@ var init_privacy = __esm({
|
|
|
4720
5744
|
"raw_data",
|
|
4721
5745
|
"metadata"
|
|
4722
5746
|
]);
|
|
4723
|
-
AUDIT_DIR =
|
|
5747
|
+
AUDIT_DIR = join9(homedir3(), ".ntrp", "audit");
|
|
4724
5748
|
}
|
|
4725
5749
|
});
|
|
4726
5750
|
|
|
@@ -6153,76 +7177,8 @@ var init_insights = __esm({
|
|
|
6153
7177
|
}
|
|
6154
7178
|
});
|
|
6155
7179
|
|
|
6156
|
-
// src/ui/theme.ts
|
|
6157
|
-
import chalk from "chalk";
|
|
6158
|
-
function paint(token, text) {
|
|
6159
|
-
if (token === "dim") return chalk.dim(text);
|
|
6160
|
-
return chalk.hex(TOKENS[token])(text);
|
|
6161
|
-
}
|
|
6162
|
-
function bold(text) {
|
|
6163
|
-
return chalk.bold(text);
|
|
6164
|
-
}
|
|
6165
|
-
function badge(label, tone = "muted") {
|
|
6166
|
-
const normalized = ` ${label.toUpperCase()} `;
|
|
6167
|
-
switch (tone) {
|
|
6168
|
-
case "success":
|
|
6169
|
-
return chalk.hex(TOKENS.success)(normalized);
|
|
6170
|
-
case "warning":
|
|
6171
|
-
return chalk.hex(TOKENS.warning)(normalized);
|
|
6172
|
-
case "error":
|
|
6173
|
-
return chalk.hex(TOKENS.error)(normalized);
|
|
6174
|
-
case "info":
|
|
6175
|
-
return chalk.hex(TOKENS.info)(normalized);
|
|
6176
|
-
case "accent":
|
|
6177
|
-
return chalk.hex(TOKENS.accent)(normalized);
|
|
6178
|
-
case "muted":
|
|
6179
|
-
return chalk.dim(normalized);
|
|
6180
|
-
}
|
|
6181
|
-
}
|
|
6182
|
-
function sectionHeading(label) {
|
|
6183
|
-
return `${paint("accent", "\u25B8")} ${paint("accent", bold(label))}`;
|
|
6184
|
-
}
|
|
6185
|
-
function actionHint(label, command, detail) {
|
|
6186
|
-
const suffix = detail ? chalk.dim(` ${detail}`) : "";
|
|
6187
|
-
return `${chalk.dim(label)} ${paint("accent", command)}${suffix}`;
|
|
6188
|
-
}
|
|
6189
|
-
function scoreBar(score, status, width = 14) {
|
|
6190
|
-
const filled = Math.round(score / 100 * width);
|
|
6191
|
-
const color = chalk.hex(STATUS_COLORS[status]);
|
|
6192
|
-
let filledPart = "";
|
|
6193
|
-
for (let i = 0; i < filled; i++) {
|
|
6194
|
-
filledPart += i % 2 === 0 ? "\u2588" : "\u2593";
|
|
6195
|
-
}
|
|
6196
|
-
const emptyPart = "\u2591".repeat(width - filled);
|
|
6197
|
-
return color(filledPart) + chalk.dim(emptyPart);
|
|
6198
|
-
}
|
|
6199
|
-
var STATUS_COLORS, TOKENS;
|
|
6200
|
-
var init_theme = __esm({
|
|
6201
|
-
"src/ui/theme.ts"() {
|
|
6202
|
-
"use strict";
|
|
6203
|
-
init_formatters();
|
|
6204
|
-
STATUS_COLORS = {
|
|
6205
|
-
green: "#22c55e",
|
|
6206
|
-
yellow: "#eab308",
|
|
6207
|
-
red: "#ef4444"
|
|
6208
|
-
};
|
|
6209
|
-
TOKENS = {
|
|
6210
|
-
accent: "#14b8a6",
|
|
6211
|
-
accentBright: "#2dd4bf",
|
|
6212
|
-
border: "#334155",
|
|
6213
|
-
borderMuted: "#1e293b",
|
|
6214
|
-
dim: "#64748b",
|
|
6215
|
-
text: "#e2e8f0",
|
|
6216
|
-
error: "#ef4444",
|
|
6217
|
-
warning: "#eab308",
|
|
6218
|
-
success: "#22c55e",
|
|
6219
|
-
info: "#3b82f6"
|
|
6220
|
-
};
|
|
6221
|
-
}
|
|
6222
|
-
});
|
|
6223
|
-
|
|
6224
7180
|
// src/conversation/phase.ts
|
|
6225
|
-
import
|
|
7181
|
+
import chalk3 from "chalk";
|
|
6226
7182
|
function sessionHasData(ctx) {
|
|
6227
7183
|
const counts = ctx.dataset?.counts ?? {};
|
|
6228
7184
|
return Object.values(counts).some((n) => (n ?? 0) > 0);
|
|
@@ -6517,12 +7473,12 @@ var init_repl_globals = __esm({
|
|
|
6517
7473
|
// src/cli/prompts.ts
|
|
6518
7474
|
import { createInterface } from "readline/promises";
|
|
6519
7475
|
import { clearLine, cursorTo } from "readline";
|
|
6520
|
-
import
|
|
7476
|
+
import chalk4 from "chalk";
|
|
6521
7477
|
function marker() {
|
|
6522
7478
|
return paint("accent", "ntrp \u203A ");
|
|
6523
7479
|
}
|
|
6524
7480
|
function secretPromptLine(question) {
|
|
6525
|
-
return ` ${paint("accent", "\u25B8")} ${bold(question)} ${
|
|
7481
|
+
return ` ${paint("accent", "\u25B8")} ${bold(question)} ${chalk4.dim("(hidden \u2014 paste once, Enter)")} `;
|
|
6526
7482
|
}
|
|
6527
7483
|
function stripTerminalArtifacts(input) {
|
|
6528
7484
|
return input.replace(/\x1b\[[0-9;]*[a-zA-Z~]/g, "").replace(/\x1b\][^\x07]*(\x07|\x1b\\)/g, "").replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
|
|
@@ -6530,7 +7486,7 @@ function stripTerminalArtifacts(input) {
|
|
|
6530
7486
|
function renderQuestion(question, defaultValue) {
|
|
6531
7487
|
const base = ` ${marker()}${bold(question)}`;
|
|
6532
7488
|
if (defaultValue !== void 0 && defaultValue !== "") {
|
|
6533
|
-
return `${base} ${
|
|
7489
|
+
return `${base} ${chalk4.dim(`[${defaultValue}]`)} `;
|
|
6534
7490
|
}
|
|
6535
7491
|
return `${base} `;
|
|
6536
7492
|
}
|
|
@@ -6555,7 +7511,7 @@ function createPromptSession(existing, ctx) {
|
|
|
6555
7511
|
const raw = (await rl2.question(renderQuestion(question))).trim();
|
|
6556
7512
|
assertNotGlobalReplCommand(raw);
|
|
6557
7513
|
if (raw) return raw;
|
|
6558
|
-
console.log(" " +
|
|
7514
|
+
console.log(" " + chalk4.red("This one is required."));
|
|
6559
7515
|
}
|
|
6560
7516
|
}
|
|
6561
7517
|
async function confirm(question, defaultYes = false) {
|
|
@@ -6573,13 +7529,13 @@ function createPromptSession(existing, ctx) {
|
|
|
6573
7529
|
const defaultIdx = opts.default ? choices.findIndex((c) => c.value === opts.default) : -1;
|
|
6574
7530
|
choices.forEach((c, i) => {
|
|
6575
7531
|
const num = paint("accent", `${i + 1}.`);
|
|
6576
|
-
const active = i === defaultIdx ?
|
|
7532
|
+
const active = i === defaultIdx ? chalk4.dim(" \u2190 default") : "";
|
|
6577
7533
|
console.log(` ${num} ${c.label}${active}`);
|
|
6578
|
-
if (c.description) console.log(` ${
|
|
7534
|
+
if (c.description) console.log(` ${chalk4.dim(c.description)}`);
|
|
6579
7535
|
});
|
|
6580
7536
|
const defaultLabel = defaultIdx >= 0 ? String(defaultIdx + 1) : void 0;
|
|
6581
7537
|
console.log();
|
|
6582
|
-
console.log(" " +
|
|
7538
|
+
console.log(" " + chalk4.dim("\u2500".repeat(40)));
|
|
6583
7539
|
for (; ; ) {
|
|
6584
7540
|
const raw = (await rl2.question(renderQuestion(`Your pick [1-${choices.length}]`, defaultLabel))).trim();
|
|
6585
7541
|
assertNotGlobalReplCommand(raw);
|
|
@@ -6588,7 +7544,7 @@ function createPromptSession(existing, ctx) {
|
|
|
6588
7544
|
if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
|
|
6589
7545
|
return choices[n - 1].value;
|
|
6590
7546
|
}
|
|
6591
|
-
console.log(" " +
|
|
7547
|
+
console.log(" " + chalk4.red(`Enter a number from 1 to ${choices.length}.`));
|
|
6592
7548
|
}
|
|
6593
7549
|
}
|
|
6594
7550
|
async function askMulti(question, options) {
|
|
@@ -6598,7 +7554,7 @@ function createPromptSession(existing, ctx) {
|
|
|
6598
7554
|
options.forEach((o, i) => {
|
|
6599
7555
|
const num = paint("accent", `${i + 1}.`);
|
|
6600
7556
|
console.log(` ${num} ${o.label}`);
|
|
6601
|
-
if (o.description) console.log(` ${
|
|
7557
|
+
if (o.description) console.log(` ${chalk4.dim(o.description)}`);
|
|
6602
7558
|
});
|
|
6603
7559
|
const hint = `Choose [1-${options.length}], type your own, or enter to skip`;
|
|
6604
7560
|
const raw = (await rl2.question(renderQuestion(hint))).trim();
|
|
@@ -6701,20 +7657,20 @@ function createPromptSession(existing, ctx) {
|
|
|
6701
7657
|
for (; ; ) {
|
|
6702
7658
|
const value = await readMaskedLine(secretPromptLine(question), maskChar);
|
|
6703
7659
|
if (!value) {
|
|
6704
|
-
console.log(" " +
|
|
7660
|
+
console.log(" " + chalk4.red("This one is required."));
|
|
6705
7661
|
continue;
|
|
6706
7662
|
}
|
|
6707
7663
|
if (opts.confirm === false) return value;
|
|
6708
7664
|
const preview = value.length <= 14 ? `${value.slice(0, 4)}\u2026` : `${value.slice(0, 10)}\u2026`;
|
|
6709
|
-
console.log(" " +
|
|
7665
|
+
console.log(" " + chalk4.dim(`Captured ${value.length} characters (${preview})`));
|
|
6710
7666
|
const ok = await confirm("Save this key?", false);
|
|
6711
7667
|
if (ok) return value;
|
|
6712
|
-
console.log(" " +
|
|
7668
|
+
console.log(" " + chalk4.dim("Try again \u2014 paste the key once, then Enter."));
|
|
6713
7669
|
}
|
|
6714
7670
|
}
|
|
6715
7671
|
async function askPressEnter(message) {
|
|
6716
7672
|
await rl2.question(
|
|
6717
|
-
` ${paint("accent", "\u25B8")} ${bold(message)} ${
|
|
7673
|
+
` ${paint("accent", "\u25B8")} ${bold(message)} ${chalk4.dim("(Enter)")} `
|
|
6718
7674
|
);
|
|
6719
7675
|
}
|
|
6720
7676
|
return {
|
|
@@ -6742,30 +7698,30 @@ var init_prompts = __esm({
|
|
|
6742
7698
|
});
|
|
6743
7699
|
|
|
6744
7700
|
// src/conversation/gap-card.ts
|
|
6745
|
-
import
|
|
7701
|
+
import chalk5 from "chalk";
|
|
6746
7702
|
function printGapCard(audit) {
|
|
6747
7703
|
console.log();
|
|
6748
|
-
console.log(" " +
|
|
7704
|
+
console.log(" " + chalk5.bold("Data check"));
|
|
6749
7705
|
if (audit.satisfied.length > 0) {
|
|
6750
7706
|
for (const item of audit.satisfied) {
|
|
6751
|
-
console.log(" " +
|
|
7707
|
+
console.log(" " + chalk5.green("\u2713") + " " + chalk5.dim(`${item.label}: ${item.detail}`));
|
|
6752
7708
|
}
|
|
6753
7709
|
}
|
|
6754
7710
|
for (const item of audit.missing) {
|
|
6755
|
-
console.log(" " +
|
|
6756
|
-
console.log(" " +
|
|
7711
|
+
console.log(" " + chalk5.red("\u2717") + " " + item.label + chalk5.dim(` \u2014 ${item.why}`));
|
|
7712
|
+
console.log(" " + chalk5.dim(item.suggestion));
|
|
6757
7713
|
}
|
|
6758
7714
|
for (const item of audit.optional) {
|
|
6759
|
-
console.log(" " +
|
|
7715
|
+
console.log(" " + chalk5.yellow("~") + " " + chalk5.dim(`${item.label}: ${item.detail}`));
|
|
6760
7716
|
}
|
|
6761
7717
|
console.log();
|
|
6762
7718
|
if (audit.can_compute) {
|
|
6763
7719
|
console.log(
|
|
6764
|
-
" " +
|
|
7720
|
+
" " + chalk5.dim("Ready to compute \u2014 say ") + chalk5.cyan('"go ahead"') + chalk5.dim(" or ") + chalk5.cyan('"run analysis"')
|
|
6765
7721
|
);
|
|
6766
7722
|
} else if (audit.missing.length > 0) {
|
|
6767
7723
|
console.log(
|
|
6768
|
-
" " +
|
|
7724
|
+
" " + chalk5.dim("Load data (paste a CSV path or say ") + chalk5.cyan("use demo data") + chalk5.dim(")")
|
|
6769
7725
|
);
|
|
6770
7726
|
}
|
|
6771
7727
|
console.log();
|
|
@@ -6777,7 +7733,7 @@ var init_gap_card = __esm({
|
|
|
6777
7733
|
});
|
|
6778
7734
|
|
|
6779
7735
|
// src/metrics/companion.ts
|
|
6780
|
-
import
|
|
7736
|
+
import chalk6 from "chalk";
|
|
6781
7737
|
function getCompanionRecommendation(input) {
|
|
6782
7738
|
const { analysis, coverage, opportunityCount, activityCount, sourceType } = input;
|
|
6783
7739
|
const completed = new Set(analysis.completed);
|
|
@@ -6797,14 +7753,14 @@ function getCompanionRecommendation(input) {
|
|
|
6797
7753
|
function printAnalysisComplete(lens) {
|
|
6798
7754
|
const label = lens === "revenue_metrics" ? "SaaS metrics" : "Pipeline health";
|
|
6799
7755
|
console.log(
|
|
6800
|
-
" " +
|
|
7756
|
+
" " + chalk6.green(`\u2713 ${label} ready`) + chalk6.dim(" \u2014 type a question below ") + paint("accent", "(no slash needed)")
|
|
6801
7757
|
);
|
|
6802
7758
|
}
|
|
6803
7759
|
function printCompanionBanner(invoked, primary) {
|
|
6804
7760
|
if (invoked === "diagnose" && primary === "revenue_metrics") {
|
|
6805
7761
|
console.log();
|
|
6806
7762
|
console.log(
|
|
6807
|
-
" " +
|
|
7763
|
+
" " + chalk6.dim("Other view \u2014 ") + chalk6.bold("pipeline health") + chalk6.dim(" (this session started with SaaS metrics)")
|
|
6808
7764
|
);
|
|
6809
7765
|
console.log();
|
|
6810
7766
|
return;
|
|
@@ -6812,7 +7768,7 @@ function printCompanionBanner(invoked, primary) {
|
|
|
6812
7768
|
if (invoked === "metrics" && primary === "gtm_health") {
|
|
6813
7769
|
console.log();
|
|
6814
7770
|
console.log(
|
|
6815
|
-
" " +
|
|
7771
|
+
" " + chalk6.dim("Other view \u2014 ") + chalk6.bold("SaaS metrics") + chalk6.dim(" (this session started with pipeline health)")
|
|
6816
7772
|
);
|
|
6817
7773
|
console.log();
|
|
6818
7774
|
}
|
|
@@ -6822,7 +7778,7 @@ function printCompanionFooter(ctx, companion, options = { justCompleted: "gtm_he
|
|
|
6822
7778
|
const completed = new Set(ctx.analysis.completed);
|
|
6823
7779
|
printAnalysisComplete(justCompleted);
|
|
6824
7780
|
console.log();
|
|
6825
|
-
console.log(" " +
|
|
7781
|
+
console.log(" " + chalk6.bold("Try asking"));
|
|
6826
7782
|
const defaults = justCompleted === "revenue_metrics" ? [
|
|
6827
7783
|
"Why is NRR showing 100%?",
|
|
6828
7784
|
"Which deals drove ARR this quarter?",
|
|
@@ -6834,7 +7790,7 @@ function printCompanionFooter(ctx, companion, options = { justCompleted: "gtm_he
|
|
|
6834
7790
|
];
|
|
6835
7791
|
const asks = (suggestedAsks?.length ? suggestedAsks : defaults).slice(0, 3);
|
|
6836
7792
|
for (const q of asks) {
|
|
6837
|
-
console.log(
|
|
7793
|
+
console.log(chalk6.dim(` "${q}"`));
|
|
6838
7794
|
}
|
|
6839
7795
|
console.log();
|
|
6840
7796
|
const extras = [];
|
|
@@ -6849,7 +7805,7 @@ function printCompanionFooter(ctx, companion, options = { justCompleted: "gtm_he
|
|
|
6849
7805
|
} else {
|
|
6850
7806
|
extras.push(`${paint("accent", "/diagnose --findings")} for AI depth`);
|
|
6851
7807
|
}
|
|
6852
|
-
console.log(" " +
|
|
7808
|
+
console.log(" " + chalk6.dim("Also: ") + chalk6.dim(extras.join(" \xB7 ")));
|
|
6853
7809
|
console.log();
|
|
6854
7810
|
}
|
|
6855
7811
|
async function resolveCompanionRecommendation(ctx) {
|
|
@@ -7151,7 +8107,7 @@ var init_layout = __esm({
|
|
|
7151
8107
|
});
|
|
7152
8108
|
|
|
7153
8109
|
// src/ui/markdown.ts
|
|
7154
|
-
import
|
|
8110
|
+
import chalk7 from "chalk";
|
|
7155
8111
|
import Table from "cli-table3";
|
|
7156
8112
|
function renderMarkdown(text, opts = {}) {
|
|
7157
8113
|
const indent = opts.indent ?? 2;
|
|
@@ -7287,13 +8243,13 @@ function renderBlock(block, width) {
|
|
|
7287
8243
|
case "paragraph":
|
|
7288
8244
|
return wrapWords(inline(block.text), width).join("\n");
|
|
7289
8245
|
case "heading": {
|
|
7290
|
-
const styled =
|
|
8246
|
+
const styled = chalk7.bold(inline(block.text));
|
|
7291
8247
|
const barWidth = Math.min(width, Math.max(8, visibleWidth(styled)));
|
|
7292
8248
|
return `${styled}
|
|
7293
|
-
${
|
|
8249
|
+
${chalk7.dim(hr(barWidth))}`;
|
|
7294
8250
|
}
|
|
7295
8251
|
case "hr":
|
|
7296
|
-
return
|
|
8252
|
+
return chalk7.dim(hr(width));
|
|
7297
8253
|
case "ul":
|
|
7298
8254
|
return block.items.map((item) => {
|
|
7299
8255
|
const lines = wrapWords(inline(item), Math.max(1, width - 4));
|
|
@@ -7311,10 +8267,10 @@ ${chalk6.dim(hr(barWidth))}`;
|
|
|
7311
8267
|
}
|
|
7312
8268
|
case "blockquote": {
|
|
7313
8269
|
const lines = wrapWords(inline(block.text), Math.max(1, width - 2));
|
|
7314
|
-
return lines.map((l) =>
|
|
8270
|
+
return lines.map((l) => chalk7.dim("\u2502 ") + chalk7.italic(l)).join("\n");
|
|
7315
8271
|
}
|
|
7316
8272
|
case "code":
|
|
7317
|
-
return block.lines.map((l) =>
|
|
8273
|
+
return block.lines.map((l) => chalk7.cyan(` ${l}`)).join("\n");
|
|
7318
8274
|
case "table":
|
|
7319
8275
|
return renderTable(block.header, block.rows, width);
|
|
7320
8276
|
}
|
|
@@ -7355,11 +8311,11 @@ function inline(text) {
|
|
|
7355
8311
|
codeSpans.push(code);
|
|
7356
8312
|
return `\0CODE${idx}\0`;
|
|
7357
8313
|
});
|
|
7358
|
-
out = out.replace(/\*\*([^*\n]+?)\*\*/g, (_m, inner) =>
|
|
7359
|
-
out = out.replace(/(^|[^*\w])\*([^*\n]+?)\*(?!\*)/g, (_m, pre, inner) => `${pre}${
|
|
7360
|
-
out = out.replace(/(^|[^_\w])_([^_\n]+?)_(?!\w)/g, (_m, pre, inner) => `${pre}${
|
|
8314
|
+
out = out.replace(/\*\*([^*\n]+?)\*\*/g, (_m, inner) => chalk7.bold(inner));
|
|
8315
|
+
out = out.replace(/(^|[^*\w])\*([^*\n]+?)\*(?!\*)/g, (_m, pre, inner) => `${pre}${chalk7.italic(inner)}`);
|
|
8316
|
+
out = out.replace(/(^|[^_\w])_([^_\n]+?)_(?!\w)/g, (_m, pre, inner) => `${pre}${chalk7.italic(inner)}`);
|
|
7361
8317
|
out = out.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
7362
|
-
out = out.replace(/\u0000CODE(\d+)\u0000/g, (_m, idx) =>
|
|
8318
|
+
out = out.replace(/\u0000CODE(\d+)\u0000/g, (_m, idx) => chalk7.cyan(codeSpans[Number(idx)]));
|
|
7363
8319
|
return out;
|
|
7364
8320
|
}
|
|
7365
8321
|
var init_markdown = __esm({
|
|
@@ -7370,7 +8326,7 @@ var init_markdown = __esm({
|
|
|
7370
8326
|
});
|
|
7371
8327
|
|
|
7372
8328
|
// src/output/llm-attribution.ts
|
|
7373
|
-
import
|
|
8329
|
+
import chalk8 from "chalk";
|
|
7374
8330
|
function formatLlmAttribution(meta) {
|
|
7375
8331
|
if (!meta.model_used) return null;
|
|
7376
8332
|
const provider = meta.provider_used ?? "anthropic";
|
|
@@ -7382,7 +8338,7 @@ function formatLlmAttribution(meta) {
|
|
|
7382
8338
|
}
|
|
7383
8339
|
function printLlmAttribution(meta) {
|
|
7384
8340
|
const line = formatLlmAttribution(meta);
|
|
7385
|
-
if (line) console.log(
|
|
8341
|
+
if (line) console.log(chalk8.dim(` ${line}`));
|
|
7386
8342
|
}
|
|
7387
8343
|
var init_llm_attribution = __esm({
|
|
7388
8344
|
"src/output/llm-attribution.ts"() {
|
|
@@ -7392,7 +8348,7 @@ var init_llm_attribution = __esm({
|
|
|
7392
8348
|
});
|
|
7393
8349
|
|
|
7394
8350
|
// src/output/terminal.ts
|
|
7395
|
-
import
|
|
8351
|
+
import chalk9 from "chalk";
|
|
7396
8352
|
import ora from "ora";
|
|
7397
8353
|
import Table2 from "cli-table3";
|
|
7398
8354
|
function centerPad(text, width) {
|
|
@@ -7404,11 +8360,11 @@ function centerPad(text, width) {
|
|
|
7404
8360
|
function statusColor(status) {
|
|
7405
8361
|
switch (status) {
|
|
7406
8362
|
case "green":
|
|
7407
|
-
return
|
|
8363
|
+
return chalk9.green;
|
|
7408
8364
|
case "yellow":
|
|
7409
|
-
return
|
|
8365
|
+
return chalk9.yellow;
|
|
7410
8366
|
case "red":
|
|
7411
|
-
return
|
|
8367
|
+
return chalk9.red;
|
|
7412
8368
|
}
|
|
7413
8369
|
}
|
|
7414
8370
|
function statusDot(status) {
|
|
@@ -7425,12 +8381,12 @@ function statusBadge(status) {
|
|
|
7425
8381
|
}
|
|
7426
8382
|
}
|
|
7427
8383
|
function printHeading(label, detail) {
|
|
7428
|
-
console.log(` ${sectionHeading(label)}${detail ?
|
|
8384
|
+
console.log(` ${sectionHeading(label)}${detail ? chalk9.dim(` ${detail}`) : ""}`);
|
|
7429
8385
|
}
|
|
7430
8386
|
function printResultCard(title, rows) {
|
|
7431
8387
|
const width = 70;
|
|
7432
8388
|
const inner = width - 4;
|
|
7433
|
-
const border =
|
|
8389
|
+
const border = chalk9.dim;
|
|
7434
8390
|
console.log();
|
|
7435
8391
|
console.log(` ${border(`\u256D${"\u2500".repeat(width - 2)}\u256E`)}`);
|
|
7436
8392
|
console.log(` ${border("\u2502 ")}${padRight(sectionHeading(title), inner)}${border(" \u2502")}`);
|
|
@@ -7446,17 +8402,17 @@ function printVitalSignRow(vs) {
|
|
|
7446
8402
|
const label = VITAL_SIGN_LABELS[vs.vital_sign].padEnd(18);
|
|
7447
8403
|
const bar = scoreBar(vs.score, vs.status);
|
|
7448
8404
|
const score = String(Math.round(vs.score)).padStart(4);
|
|
7449
|
-
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${
|
|
7450
|
-
console.log(` ${dot} ${label} ${bar} ${
|
|
8405
|
+
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${chalk9.green(formatCurrency(vs.dollar_value))} ${chalk9.dim(vs.dollar_label ?? "")}` : chalk9.dim("\u2014");
|
|
8406
|
+
console.log(` ${dot} ${label} ${bar} ${chalk9.bold(score)} ${chalk9.dim("\u2502")} ${impact}`);
|
|
7451
8407
|
}
|
|
7452
8408
|
function printHealthSummary(result, _pipelineMetrics) {
|
|
7453
|
-
const scoreStr = `${
|
|
7454
|
-
const impact = result.total_value_at_risk != null && result.total_value_at_risk > 0 ? `${
|
|
8409
|
+
const scoreStr = `${chalk9.bold(String(Math.round(result.overall_score)))}${chalk9.dim("/100")}`;
|
|
8410
|
+
const impact = result.total_value_at_risk != null && result.total_value_at_risk > 0 ? `${chalk9.green(formatCurrency(result.total_value_at_risk))} ${chalk9.dim("total at risk")}` : chalk9.dim("No dollar-weighted risk detected");
|
|
7455
8411
|
const next = result.overall_status === "red" ? actionHint("Next:", "/playbook", "review recommended plays") : result.overall_status === "yellow" ? actionHint("Next:", "/diagnose --deep", "investigate the weak signal") : actionHint("Next:", "/report", "export the clean snapshot");
|
|
7456
8412
|
printResultCard("Overall Health", [
|
|
7457
|
-
`${
|
|
7458
|
-
`${
|
|
7459
|
-
`${
|
|
8413
|
+
`${chalk9.dim("Score")} ${scoreStr} ${statusBadge(result.overall_status)}`,
|
|
8414
|
+
`${chalk9.dim("Gated by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`,
|
|
8415
|
+
`${chalk9.dim("Revenue")} ${impact}`,
|
|
7460
8416
|
next
|
|
7461
8417
|
]);
|
|
7462
8418
|
}
|
|
@@ -7477,8 +8433,8 @@ function printSegmentSummary(segments) {
|
|
|
7477
8433
|
const dot = statusDot(seg.result.overall_status);
|
|
7478
8434
|
const name = seg.segment.name.padEnd(24);
|
|
7479
8435
|
const score = String(Math.round(seg.result.overall_score)).padStart(4);
|
|
7480
|
-
const gating =
|
|
7481
|
-
console.log(` ${dot} ${name} ${
|
|
8436
|
+
const gating = chalk9.dim(VITAL_SIGN_LABELS[seg.result.gating_vital_sign]);
|
|
8437
|
+
console.log(` ${dot} ${name} ${chalk9.bold(score)} ${chalk9.dim("\u2502")} ${gating}`);
|
|
7482
8438
|
}
|
|
7483
8439
|
console.log();
|
|
7484
8440
|
}
|
|
@@ -7502,7 +8458,7 @@ function printTopProblems(segments, limit = 7) {
|
|
|
7502
8458
|
if (problems.length === 0) {
|
|
7503
8459
|
printHeading("Top Problems");
|
|
7504
8460
|
console.log();
|
|
7505
|
-
console.log(" " +
|
|
8461
|
+
console.log(" " + chalk9.dim("No dollar-weighted problems found across segments."));
|
|
7506
8462
|
console.log();
|
|
7507
8463
|
return;
|
|
7508
8464
|
}
|
|
@@ -7515,45 +8471,45 @@ function printTopProblems(segments, limit = 7) {
|
|
|
7515
8471
|
const labelW = Math.max("".length, ...top.map((p) => p.dollarLabel.length));
|
|
7516
8472
|
const impactW = dollarW + 2 + labelW;
|
|
7517
8473
|
console.log(
|
|
7518
|
-
` ${sectionHeading("Top Problems")}` +
|
|
8474
|
+
` ${sectionHeading("Top Problems")}` + chalk9.dim(` (${top.length} of ${problems.length})`)
|
|
7519
8475
|
);
|
|
7520
8476
|
console.log();
|
|
7521
8477
|
const segColW = 2 + segW;
|
|
7522
8478
|
const hSeg = centerPad("Segment", segColW);
|
|
7523
8479
|
const hVital = centerPad("Vital Sign", vitalW);
|
|
7524
8480
|
const hImpact = centerPad("Revenue Impact", Math.max(impactW, "Revenue Impact".length));
|
|
7525
|
-
console.log(` ${
|
|
8481
|
+
console.log(` ${chalk9.dim(hSeg)} ${chalk9.dim(hVital)} ${chalk9.dim(hImpact)}`);
|
|
7526
8482
|
console.log();
|
|
7527
8483
|
for (let i = 0; i < top.length; i++) {
|
|
7528
8484
|
const p = top[i];
|
|
7529
8485
|
const dot = statusDot(p.status);
|
|
7530
8486
|
const seg = p.segment.padEnd(segW);
|
|
7531
8487
|
const vital = p.vitalSignLabel.padEnd(vitalW);
|
|
7532
|
-
const dollar =
|
|
7533
|
-
const label =
|
|
8488
|
+
const dollar = chalk9.green(dollarStrs[i].padStart(dollarW));
|
|
8489
|
+
const label = chalk9.dim(p.dollarLabel);
|
|
7534
8490
|
console.log(` ${dot} ${seg} ${vital} ${dollar} ${label}`);
|
|
7535
8491
|
}
|
|
7536
8492
|
if (problems.length > top.length) {
|
|
7537
8493
|
console.log();
|
|
7538
|
-
console.log(` ${
|
|
8494
|
+
console.log(` ${chalk9.dim("Run /diagnose --segment <name> to drill in")}`);
|
|
7539
8495
|
}
|
|
7540
8496
|
console.log();
|
|
7541
8497
|
}
|
|
7542
8498
|
function printFindings(findings) {
|
|
7543
8499
|
if (findings.length === 0) {
|
|
7544
|
-
console.log(
|
|
8500
|
+
console.log(chalk9.dim(" No findings generated."));
|
|
7545
8501
|
return;
|
|
7546
8502
|
}
|
|
7547
8503
|
for (const finding of findings) {
|
|
7548
|
-
const sevColor = finding.severity === "critical" ?
|
|
8504
|
+
const sevColor = finding.severity === "critical" ? chalk9.red : finding.severity === "warning" ? chalk9.yellow : chalk9.blue;
|
|
7549
8505
|
const dot = sevColor("\u25CF");
|
|
7550
|
-
const dollarTag = finding.dollar_value != null && finding.dollar_value > 0 ? ` ${
|
|
7551
|
-
console.log(` ${dot} ${
|
|
8506
|
+
const dollarTag = finding.dollar_value != null && finding.dollar_value > 0 ? ` ${chalk9.dim("\xB7")} ${chalk9.green(formatDollarValue(finding.dollar_value))}` : "";
|
|
8507
|
+
console.log(` ${dot} ${chalk9.bold(finding.segment)}${dollarTag}`);
|
|
7552
8508
|
printMarkdown(finding.finding, { indent: 2 });
|
|
7553
8509
|
if (finding.recommended_plays && finding.recommended_plays.length > 0) {
|
|
7554
8510
|
for (const play of finding.recommended_plays) {
|
|
7555
8511
|
console.log(
|
|
7556
|
-
` ${
|
|
8512
|
+
` ${chalk9.dim("Consider:")} ${paint("accent", play.play_name)} ${chalk9.dim("\u2192")} ${chalk9.dim("/playbook " + play.play_id)}`
|
|
7557
8513
|
);
|
|
7558
8514
|
}
|
|
7559
8515
|
}
|
|
@@ -7562,7 +8518,7 @@ function printFindings(findings) {
|
|
|
7562
8518
|
}
|
|
7563
8519
|
function printEntityCounts(counts) {
|
|
7564
8520
|
const table = new Table2({
|
|
7565
|
-
head: [
|
|
8521
|
+
head: [chalk9.dim("Entity"), chalk9.dim("Count")],
|
|
7566
8522
|
colWidths: [20, 12],
|
|
7567
8523
|
style: { head: [], border: [] }
|
|
7568
8524
|
});
|
|
@@ -7577,19 +8533,19 @@ function printSegmentDetail(seg, aggregate) {
|
|
|
7577
8533
|
console.log();
|
|
7578
8534
|
printHeading(seg.segment.name);
|
|
7579
8535
|
console.log(
|
|
7580
|
-
` ${statusDot(seg.result.overall_status)} ${color(
|
|
8536
|
+
` ${statusDot(seg.result.overall_status)} ${color(chalk9.bold(formatScore(seg.result.overall_score)))}${chalk9.dim("/100")} ${chalk9.dim("Gated by:")} ${paint("accent", VITAL_SIGN_LABELS[seg.result.gating_vital_sign])}`
|
|
7581
8537
|
);
|
|
7582
8538
|
console.log();
|
|
7583
8539
|
for (const vs of seg.result.vital_signs) {
|
|
7584
8540
|
const aggVs = aggregate.vital_signs.find((a) => a.vital_sign === vs.vital_sign);
|
|
7585
8541
|
const delta = aggVs ? vs.score - aggVs.score : 0;
|
|
7586
|
-
const deltaStr = delta >= 0 ?
|
|
8542
|
+
const deltaStr = delta >= 0 ? chalk9.green(`+${Math.round(delta)}`) : chalk9.red(`${Math.round(delta)}`);
|
|
7587
8543
|
const dot = statusDot(vs.status);
|
|
7588
8544
|
const label = VITAL_SIGN_LABELS[vs.vital_sign].padEnd(18);
|
|
7589
8545
|
const bar = scoreBar(vs.score, vs.status);
|
|
7590
8546
|
const score = String(Math.round(vs.score)).padStart(4);
|
|
7591
|
-
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${
|
|
7592
|
-
console.log(` ${dot} ${label} ${bar} ${
|
|
8547
|
+
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${chalk9.green(formatCurrency(vs.dollar_value))} ${chalk9.dim(vs.dollar_label ?? "")}` : chalk9.dim("\u2014");
|
|
8548
|
+
console.log(` ${dot} ${label} ${bar} ${chalk9.bold(score)} ${deltaStr.padStart(12)} ${chalk9.dim("\u2502")} ${impact}`);
|
|
7593
8549
|
}
|
|
7594
8550
|
console.log();
|
|
7595
8551
|
}
|
|
@@ -7673,7 +8629,7 @@ async function renderDiagnoseStream(options) {
|
|
|
7673
8629
|
}
|
|
7674
8630
|
findingsSpinner.stop();
|
|
7675
8631
|
console.log(
|
|
7676
|
-
|
|
8632
|
+
chalk9.dim(` Investigating... called ${toolCalls} tools across ${iterations} iterations`)
|
|
7677
8633
|
);
|
|
7678
8634
|
console.log();
|
|
7679
8635
|
if (storeFindings) {
|
|
@@ -7695,7 +8651,7 @@ async function renderDiagnoseStream(options) {
|
|
|
7695
8651
|
});
|
|
7696
8652
|
} catch (err) {
|
|
7697
8653
|
findingsSpinner.fail(deep ? "Agentic investigation failed" : "AI findings failed");
|
|
7698
|
-
console.error(
|
|
8654
|
+
console.error(chalk9.dim(String(err)));
|
|
7699
8655
|
}
|
|
7700
8656
|
}
|
|
7701
8657
|
return { fullResult, findings: collectedFindings };
|
|
@@ -7703,13 +8659,13 @@ async function renderDiagnoseStream(options) {
|
|
|
7703
8659
|
function metricStatusDot(status) {
|
|
7704
8660
|
switch (status) {
|
|
7705
8661
|
case "green":
|
|
7706
|
-
return
|
|
8662
|
+
return chalk9.green("\u25CF");
|
|
7707
8663
|
case "yellow":
|
|
7708
|
-
return
|
|
8664
|
+
return chalk9.yellow("\u25CF");
|
|
7709
8665
|
case "red":
|
|
7710
|
-
return
|
|
8666
|
+
return chalk9.red("\u25CF");
|
|
7711
8667
|
case "neutral":
|
|
7712
|
-
return
|
|
8668
|
+
return chalk9.dim("\u25CB");
|
|
7713
8669
|
}
|
|
7714
8670
|
}
|
|
7715
8671
|
function printMetricsTable(metrics, groupOrder) {
|
|
@@ -7719,16 +8675,16 @@ function printMetricsTable(metrics, groupOrder) {
|
|
|
7719
8675
|
printHeading(group);
|
|
7720
8676
|
console.log();
|
|
7721
8677
|
for (const m of groupMetrics) {
|
|
7722
|
-
const dot = m.unavailable_reason ?
|
|
8678
|
+
const dot = m.unavailable_reason ? chalk9.dim("\u25CB") : metricStatusDot(m.status);
|
|
7723
8679
|
const label = m.label.padEnd(28);
|
|
7724
|
-
const valueStr = m.unavailable_reason ?
|
|
7725
|
-
const confTag = m.confidence != null && m.confidence < 80 && m.confidence_label ?
|
|
7726
|
-
const note = m.unavailable_reason ?
|
|
8680
|
+
const valueStr = m.unavailable_reason ? chalk9.dim("--") : chalk9.bold(m.formatted);
|
|
8681
|
+
const confTag = m.confidence != null && m.confidence < 80 && m.confidence_label ? chalk9.yellow(` ${m.confidence_label} (${m.confidence})`) : m.confidence != null && m.confidence_label === "high" ? chalk9.dim(" \u2713") : "";
|
|
8682
|
+
const note = m.unavailable_reason ? chalk9.dim(m.unavailable_reason) : m.benchmark_note ? chalk9.dim(m.benchmark_note) : "";
|
|
7727
8683
|
console.log(` ${dot} ${label} ${valueStr}${confTag}${note ? " " + note : ""}`);
|
|
7728
8684
|
if (m.reliability_gate?.requirements?.length && (m.confidence ?? 100) < 80) {
|
|
7729
8685
|
const gate = m.reliability_gate.requirements[0];
|
|
7730
8686
|
if (gate) {
|
|
7731
|
-
console.log(
|
|
8687
|
+
console.log(chalk9.dim(` \u2514 Gate: ${gate}`));
|
|
7732
8688
|
}
|
|
7733
8689
|
}
|
|
7734
8690
|
}
|
|
@@ -7752,15 +8708,15 @@ __export(metrics_report_exports, {
|
|
|
7752
8708
|
printMetricsNextSteps: () => printMetricsNextSteps,
|
|
7753
8709
|
renderMetricsReport: () => renderMetricsReport
|
|
7754
8710
|
});
|
|
7755
|
-
import
|
|
8711
|
+
import chalk10 from "chalk";
|
|
7756
8712
|
function renderMetricsReport(ctx, metrics, coverage, sourceType, options = {}) {
|
|
7757
8713
|
const title = options.title ?? "SaaS Metrics Analysis";
|
|
7758
8714
|
const tier = coverageTier(coverage);
|
|
7759
8715
|
const deterministic = buildDeterministicInsights(metrics, coverage, sourceType);
|
|
7760
8716
|
const headline = pickHeadlineInsight(deterministic);
|
|
7761
8717
|
console.log();
|
|
7762
|
-
console.log(
|
|
7763
|
-
console.log(" " +
|
|
8718
|
+
console.log(chalk10.bold(` ${title}`));
|
|
8719
|
+
console.log(" " + chalk10.dim(formatCoverageHeader(sourceType, coverage)));
|
|
7764
8720
|
console.log();
|
|
7765
8721
|
printDataQualityPanel(coverage, sourceType, tier);
|
|
7766
8722
|
if (options.snapshot && coverage.distinct_quarters >= 2) {
|
|
@@ -7768,7 +8724,7 @@ function renderMetricsReport(ctx, metrics, coverage, sourceType, options = {}) {
|
|
|
7768
8724
|
}
|
|
7769
8725
|
if (headline) {
|
|
7770
8726
|
console.log(" " + paint("warning", "\u25B8 Headline"));
|
|
7771
|
-
console.log(" " +
|
|
8727
|
+
console.log(" " + chalk10.white(wrapInsight(headline)));
|
|
7772
8728
|
console.log();
|
|
7773
8729
|
}
|
|
7774
8730
|
printMetricsTable(metrics, GROUP_ORDER);
|
|
@@ -7776,9 +8732,9 @@ function renderMetricsReport(ctx, metrics, coverage, sourceType, options = {}) {
|
|
|
7776
8732
|
console.log(" " + bold("Pattern checks"));
|
|
7777
8733
|
console.log();
|
|
7778
8734
|
for (const insight of deterministic.slice(0, 5)) {
|
|
7779
|
-
const dot = insight.severity === "warning" ?
|
|
8735
|
+
const dot = insight.severity === "warning" ? chalk10.yellow("\u25CF") : insight.severity === "critical" ? chalk10.red("\u25CF") : chalk10.dim("\u25CB");
|
|
7780
8736
|
if (insight.headline) continue;
|
|
7781
|
-
console.log(` ${dot} ${
|
|
8737
|
+
console.log(` ${dot} ${chalk10.dim(wrapInsight(insight.message))}`);
|
|
7782
8738
|
}
|
|
7783
8739
|
console.log();
|
|
7784
8740
|
}
|
|
@@ -7807,7 +8763,7 @@ function printDataQualityPanel(coverage, sourceType, tier) {
|
|
|
7807
8763
|
rows.push(["Ledger", "not loaded \u2014 retention inferred from CRM"]);
|
|
7808
8764
|
}
|
|
7809
8765
|
for (const [label, value] of rows) {
|
|
7810
|
-
console.log(` ${
|
|
8766
|
+
console.log(` ${chalk10.dim(String(label).padEnd(14))} ${value}`);
|
|
7811
8767
|
}
|
|
7812
8768
|
console.log();
|
|
7813
8769
|
}
|
|
@@ -7818,10 +8774,10 @@ function printCloseTrend(snapshot, cadence) {
|
|
|
7818
8774
|
console.log(" " + bold(`Close trend (${cadence})`));
|
|
7819
8775
|
console.log();
|
|
7820
8776
|
for (const b of recent) {
|
|
7821
|
-
const newStr = b.new_arr > 0 ?
|
|
7822
|
-
const expStr = b.expansion_arr > 0 ?
|
|
8777
|
+
const newStr = b.new_arr > 0 ? chalk10.dim(` new $${formatShort(b.new_arr)}`) : "";
|
|
8778
|
+
const expStr = b.expansion_arr > 0 ? chalk10.dim(` exp $${formatShort(b.expansion_arr)}`) : "";
|
|
7823
8779
|
console.log(
|
|
7824
|
-
` ${
|
|
8780
|
+
` ${chalk10.dim(b.period.padEnd(8))} ${chalk10.bold("$" + formatShort(b.closed_won_total))}${newStr}${expStr} ${chalk10.dim(`(${b.closed_won_count} deals)`)}`
|
|
7825
8781
|
);
|
|
7826
8782
|
}
|
|
7827
8783
|
console.log();
|
|
@@ -8083,7 +9039,7 @@ var diagnose_exports = {};
|
|
|
8083
9039
|
__export(diagnose_exports, {
|
|
8084
9040
|
handler: () => handler
|
|
8085
9041
|
});
|
|
8086
|
-
import
|
|
9042
|
+
import chalk11 from "chalk";
|
|
8087
9043
|
import ora2 from "ora";
|
|
8088
9044
|
async function handler(args, ctx) {
|
|
8089
9045
|
await hydrateAnalysisFromPersistedState(ctx);
|
|
@@ -8112,9 +9068,9 @@ async function handler(args, ctx) {
|
|
|
8112
9068
|
}
|
|
8113
9069
|
if (options.findings && !canUseReplAi(ctx)) {
|
|
8114
9070
|
console.log();
|
|
8115
|
-
console.log(" " +
|
|
8116
|
-
console.log(" " +
|
|
8117
|
-
console.log(" " +
|
|
9071
|
+
console.log(" " + chalk11.red("AI findings run only in the interactive REPL."));
|
|
9072
|
+
console.log(" " + chalk11.dim("Vital signs compute without a key \u2014 omit --findings for numbers only."));
|
|
9073
|
+
console.log(" " + chalk11.dim("Start with ") + paint("accent", "ntrp") + chalk11.dim(", set ") + paint("accent", "/config set api-key") + chalk11.dim(" or openai-api-key, then /diagnose --findings."));
|
|
8118
9074
|
console.log();
|
|
8119
9075
|
return;
|
|
8120
9076
|
}
|
|
@@ -8140,8 +9096,12 @@ async function handler(args, ctx) {
|
|
|
8140
9096
|
const companion = await resolveCompanionRecommendation(ctx);
|
|
8141
9097
|
printCompanionFooter(ctx, companion, { justCompleted: "gtm_health" });
|
|
8142
9098
|
}
|
|
9099
|
+
if (!ctx.skipTimeBankDiagnoseCredit) {
|
|
9100
|
+
creditDiagnoseComplete(ctx, options.findings);
|
|
9101
|
+
}
|
|
9102
|
+
ctx.skipTimeBankDiagnoseCredit = false;
|
|
8143
9103
|
if (ctx.oneShot && options.findings) {
|
|
8144
|
-
console.log(
|
|
9104
|
+
console.log(chalk11.dim(" For follow-up questions, run `ntrp` and ask in plain English."));
|
|
8145
9105
|
console.log();
|
|
8146
9106
|
}
|
|
8147
9107
|
return summary;
|
|
@@ -8198,7 +9158,7 @@ async function runDiagnose(options, ctx) {
|
|
|
8198
9158
|
});
|
|
8199
9159
|
return buildDiagnoseSummary(fullResult.aggregate, findings);
|
|
8200
9160
|
} catch (err) {
|
|
8201
|
-
console.error(
|
|
9161
|
+
console.error(chalk11.red(String(err)));
|
|
8202
9162
|
process.exit(1);
|
|
8203
9163
|
}
|
|
8204
9164
|
}
|
|
@@ -8210,7 +9170,7 @@ async function runSegmentDiagnose(options) {
|
|
|
8210
9170
|
spinner.succeed("Diagnosis complete");
|
|
8211
9171
|
} catch (err) {
|
|
8212
9172
|
spinner.fail("Diagnosis failed");
|
|
8213
|
-
console.error(
|
|
9173
|
+
console.error(chalk11.red(String(err)));
|
|
8214
9174
|
process.exit(1);
|
|
8215
9175
|
}
|
|
8216
9176
|
const needle = options.segment.toLowerCase();
|
|
@@ -8219,19 +9179,19 @@ async function runSegmentDiagnose(options) {
|
|
|
8219
9179
|
const subs = result.segments.filter((s) => s.segment.name.toLowerCase().includes(needle));
|
|
8220
9180
|
if (subs.length === 1) match = subs[0];
|
|
8221
9181
|
else if (subs.length > 1) {
|
|
8222
|
-
console.error(
|
|
9182
|
+
console.error(chalk11.yellow(`
|
|
8223
9183
|
"${options.segment}" matches multiple segments:`));
|
|
8224
|
-
for (const s of subs) console.log(
|
|
9184
|
+
for (const s of subs) console.log(chalk11.dim(` - ${s.segment.name}`));
|
|
8225
9185
|
console.log();
|
|
8226
9186
|
return;
|
|
8227
9187
|
}
|
|
8228
9188
|
}
|
|
8229
9189
|
if (!match) {
|
|
8230
|
-
console.error(
|
|
9190
|
+
console.error(chalk11.red(`
|
|
8231
9191
|
No segment matching "${options.segment}".`));
|
|
8232
9192
|
if (result.segments.length > 0) {
|
|
8233
|
-
console.log(
|
|
8234
|
-
for (const s of result.segments) console.log(
|
|
9193
|
+
console.log(chalk11.dim(" Available segments:"));
|
|
9194
|
+
for (const s of result.segments) console.log(chalk11.dim(` - ${s.segment.name}`));
|
|
8235
9195
|
}
|
|
8236
9196
|
console.log();
|
|
8237
9197
|
return;
|
|
@@ -8280,6 +9240,7 @@ var init_diagnose = __esm({
|
|
|
8280
9240
|
init_serialize();
|
|
8281
9241
|
init_theme();
|
|
8282
9242
|
init_companion();
|
|
9243
|
+
init_time_bank();
|
|
8283
9244
|
}
|
|
8284
9245
|
});
|
|
8285
9246
|
|
|
@@ -8290,7 +9251,7 @@ __export(compute_exports2, {
|
|
|
8290
9251
|
runConversationCompute: () => runConversationCompute
|
|
8291
9252
|
});
|
|
8292
9253
|
import ora3 from "ora";
|
|
8293
|
-
import
|
|
9254
|
+
import chalk12 from "chalk";
|
|
8294
9255
|
async function runConversationCompute(ctx) {
|
|
8295
9256
|
const lens = ctx.scope?.primary_lens ?? ctx.analysis.primary;
|
|
8296
9257
|
ctx.computeInProgress = true;
|
|
@@ -8313,6 +9274,8 @@ async function runConversationCompute(ctx) {
|
|
|
8313
9274
|
ctx.snapshot.computeResult = null;
|
|
8314
9275
|
invalidateGapAudit(ctx);
|
|
8315
9276
|
saveSessionState(ctx);
|
|
9277
|
+
creditGapCompute(ctx);
|
|
9278
|
+
creditMetricsComplete(ctx, false);
|
|
8316
9279
|
renderMetricsReport2(ctx, result.metrics.aggregate.metrics, result.coverage, result.data_source_type, {
|
|
8317
9280
|
snapshot: result.snapshot,
|
|
8318
9281
|
findings: result.findings,
|
|
@@ -8326,6 +9289,8 @@ async function runConversationCompute(ctx) {
|
|
|
8326
9289
|
}
|
|
8327
9290
|
}
|
|
8328
9291
|
const { handler: diagnose } = await Promise.resolve().then(() => (init_diagnose(), diagnose_exports));
|
|
9292
|
+
ctx.skipTimeBankDiagnoseCredit = true;
|
|
9293
|
+
creditGapCompute(ctx);
|
|
8329
9294
|
const summary = await diagnose([], ctx);
|
|
8330
9295
|
markLensCompleted(ctx, "gtm_health");
|
|
8331
9296
|
ctx.stage = "analyzed";
|
|
@@ -8336,7 +9301,7 @@ async function runConversationCompute(ctx) {
|
|
|
8336
9301
|
printCompanionFooter(ctx, companion, { justCompleted: "gtm_health" });
|
|
8337
9302
|
return typeof summary === "string" ? summary : "Health analysis ready";
|
|
8338
9303
|
} catch (err) {
|
|
8339
|
-
console.error(" " +
|
|
9304
|
+
console.error(" " + chalk12.red(String(err.message ?? err)));
|
|
8340
9305
|
return;
|
|
8341
9306
|
} finally {
|
|
8342
9307
|
ctx.computeInProgress = false;
|
|
@@ -8352,16 +9317,17 @@ var init_compute2 = __esm({
|
|
|
8352
9317
|
init_context2();
|
|
8353
9318
|
init_gap_audit();
|
|
8354
9319
|
init_companion();
|
|
9320
|
+
init_time_bank();
|
|
8355
9321
|
}
|
|
8356
9322
|
});
|
|
8357
9323
|
|
|
8358
9324
|
// src/config/demo.ts
|
|
8359
|
-
import
|
|
9325
|
+
import chalk13 from "chalk";
|
|
8360
9326
|
function printDemoDisabled() {
|
|
8361
9327
|
console.log();
|
|
8362
|
-
console.log(" " +
|
|
9328
|
+
console.log(" " + chalk13.red(DEMO_DISABLED_MESSAGE));
|
|
8363
9329
|
console.log(
|
|
8364
|
-
" " +
|
|
9330
|
+
" " + chalk13.dim("Re-enable with ") + paint("accent", "/config set demo-enabled true") + chalk13.dim(".")
|
|
8365
9331
|
);
|
|
8366
9332
|
console.log();
|
|
8367
9333
|
}
|
|
@@ -11109,18 +12075,18 @@ var init_generator = __esm({
|
|
|
11109
12075
|
});
|
|
11110
12076
|
|
|
11111
12077
|
// src/demo/taxonomy-cache.ts
|
|
11112
|
-
import { readFileSync as
|
|
12078
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync3 } from "fs";
|
|
11113
12079
|
import { homedir as homedir4 } from "os";
|
|
11114
|
-
import { join as
|
|
11115
|
-
function
|
|
11116
|
-
if (!
|
|
11117
|
-
|
|
12080
|
+
import { join as join10 } from "path";
|
|
12081
|
+
function ensureDir5() {
|
|
12082
|
+
if (!existsSync10(NTRP_DIR4)) {
|
|
12083
|
+
mkdirSync8(NTRP_DIR4, { recursive: true });
|
|
11118
12084
|
}
|
|
11119
12085
|
}
|
|
11120
12086
|
function loadCachedTaxonomy(profile) {
|
|
11121
|
-
if (!
|
|
12087
|
+
if (!existsSync10(TAXONOMY_PATH)) return null;
|
|
11122
12088
|
try {
|
|
11123
|
-
const parsed = JSON.parse(
|
|
12089
|
+
const parsed = JSON.parse(readFileSync8(TAXONOMY_PATH, "utf-8"));
|
|
11124
12090
|
if (!parsed || typeof parsed !== "object") return null;
|
|
11125
12091
|
if (parsed.profile_updated_at !== profile.updated_at) return null;
|
|
11126
12092
|
return parsed;
|
|
@@ -11129,15 +12095,15 @@ function loadCachedTaxonomy(profile) {
|
|
|
11129
12095
|
}
|
|
11130
12096
|
}
|
|
11131
12097
|
function saveCachedTaxonomy(taxonomy) {
|
|
11132
|
-
|
|
11133
|
-
|
|
12098
|
+
ensureDir5();
|
|
12099
|
+
writeFileSync7(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
11134
12100
|
}
|
|
11135
12101
|
var NTRP_DIR4, TAXONOMY_PATH;
|
|
11136
12102
|
var init_taxonomy_cache = __esm({
|
|
11137
12103
|
"src/demo/taxonomy-cache.ts"() {
|
|
11138
12104
|
"use strict";
|
|
11139
|
-
NTRP_DIR4 =
|
|
11140
|
-
TAXONOMY_PATH =
|
|
12105
|
+
NTRP_DIR4 = join10(homedir4(), ".ntrp");
|
|
12106
|
+
TAXONOMY_PATH = join10(NTRP_DIR4, "demo-taxonomy.json");
|
|
11141
12107
|
}
|
|
11142
12108
|
});
|
|
11143
12109
|
|
|
@@ -11373,16 +12339,16 @@ var generate_exports = {};
|
|
|
11373
12339
|
__export(generate_exports, {
|
|
11374
12340
|
handler: () => handler2
|
|
11375
12341
|
});
|
|
11376
|
-
import
|
|
12342
|
+
import chalk14 from "chalk";
|
|
11377
12343
|
import ora4 from "ora";
|
|
11378
12344
|
async function handler2(args, ctx) {
|
|
11379
12345
|
const { flags } = parseArgs(args, ["list-scenarios", "regen-taxonomy"]);
|
|
11380
12346
|
const quiet = ctx.execution.quiet;
|
|
11381
12347
|
if (getBool(flags, "list-scenarios")) {
|
|
11382
|
-
console.log(
|
|
12348
|
+
console.log(chalk14.bold("\n Available Scenarios:\n"));
|
|
11383
12349
|
for (const s of SCENARIO_LIST) {
|
|
11384
|
-
console.log(` ${
|
|
11385
|
-
console.log(` ${
|
|
12350
|
+
console.log(` ${chalk14.cyan(s.key.padEnd(20))} ${s.label}`);
|
|
12351
|
+
console.log(` ${chalk14.dim(" ".repeat(20))} ${s.description}
|
|
11386
12352
|
`);
|
|
11387
12353
|
}
|
|
11388
12354
|
return true;
|
|
@@ -11392,9 +12358,9 @@ async function handler2(args, ctx) {
|
|
|
11392
12358
|
const skipProfile = getFalse(flags, "profile");
|
|
11393
12359
|
if (!isProfileConfigured(profile) && !skipProfile) {
|
|
11394
12360
|
console.error();
|
|
11395
|
-
console.error(" " +
|
|
11396
|
-
console.error(" " +
|
|
11397
|
-
console.error(" " +
|
|
12361
|
+
console.error(" " + chalk14.red("No company profile found."));
|
|
12362
|
+
console.error(" " + chalk14.dim("Run ") + paint("accent", "/onboard") + chalk14.dim(" first for a richer demo,"));
|
|
12363
|
+
console.error(" " + chalk14.dim("or pass ") + paint("accent", "--no-profile") + chalk14.dim(" to skip."));
|
|
11398
12364
|
console.error();
|
|
11399
12365
|
markFailure(ctx);
|
|
11400
12366
|
return false;
|
|
@@ -11402,8 +12368,8 @@ async function handler2(args, ctx) {
|
|
|
11402
12368
|
const explicitScenario = getString(flags, "scenario", "s");
|
|
11403
12369
|
const resolvedScenario = resolveScenarioInput(explicitScenario);
|
|
11404
12370
|
if (resolvedScenario === null) {
|
|
11405
|
-
console.error(
|
|
11406
|
-
console.log(
|
|
12371
|
+
console.error(chalk14.red(` Unknown scenario: ${explicitScenario}`));
|
|
12372
|
+
console.log(chalk14.dim(` Valid: ${SCENARIO_LIST.map((s) => s.key).join(", ")}`));
|
|
11407
12373
|
markFailure(ctx);
|
|
11408
12374
|
return false;
|
|
11409
12375
|
}
|
|
@@ -11417,7 +12383,7 @@ async function handler2(args, ctx) {
|
|
|
11417
12383
|
const s = getScenario(scenario);
|
|
11418
12384
|
console.log();
|
|
11419
12385
|
console.log(" " + paint("accent", "\u2713 Scenario: ") + bold(s.label));
|
|
11420
|
-
console.log(" " +
|
|
12386
|
+
console.log(" " + chalk14.dim(s.story));
|
|
11421
12387
|
console.log();
|
|
11422
12388
|
}
|
|
11423
12389
|
const spinner = quiet ? null : ora4({ text: "Initializing database...", discardStdin: false }).start();
|
|
@@ -11443,17 +12409,17 @@ async function handler2(args, ctx) {
|
|
|
11443
12409
|
const result = await generateDemoData(config);
|
|
11444
12410
|
if (result.mode === "direct") {
|
|
11445
12411
|
if (spinner) {
|
|
11446
|
-
spinner.succeed(`Generated demo data for "${
|
|
12412
|
+
spinner.succeed(`Generated demo data for "${chalk14.cyan(scenario)}" scenario`);
|
|
11447
12413
|
console.log();
|
|
11448
12414
|
printEntityCounts(result.counts);
|
|
11449
12415
|
}
|
|
11450
12416
|
if (!quiet && ctx.analysis.primary !== "revenue_metrics") {
|
|
11451
|
-
console.log(
|
|
12417
|
+
console.log(chalk14.dim("\n Run /diagnose to compute vital signs.\n"));
|
|
11452
12418
|
}
|
|
11453
12419
|
}
|
|
11454
12420
|
} catch (err) {
|
|
11455
12421
|
if (spinner) spinner.fail("Generation failed");
|
|
11456
|
-
console.error(
|
|
12422
|
+
console.error(chalk14.red(String(err)));
|
|
11457
12423
|
markFailure(ctx);
|
|
11458
12424
|
return false;
|
|
11459
12425
|
}
|
|
@@ -11478,7 +12444,7 @@ async function loadOrBuildTaxonomy(profile, forceRegen, ctx) {
|
|
|
11478
12444
|
return taxonomy;
|
|
11479
12445
|
} catch (err) {
|
|
11480
12446
|
spinner.fail("Couldn't build market taxonomy \u2014 using generic data pools");
|
|
11481
|
-
console.log(" " +
|
|
12447
|
+
console.log(" " + chalk14.dim(String(err.message ?? err)));
|
|
11482
12448
|
return void 0;
|
|
11483
12449
|
}
|
|
11484
12450
|
}
|
|
@@ -11568,9 +12534,9 @@ var ingest_exports = {};
|
|
|
11568
12534
|
__export(ingest_exports, {
|
|
11569
12535
|
handler: () => handler3
|
|
11570
12536
|
});
|
|
11571
|
-
import
|
|
12537
|
+
import chalk15 from "chalk";
|
|
11572
12538
|
import ora5 from "ora";
|
|
11573
|
-
import { readFileSync as
|
|
12539
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
11574
12540
|
import { basename as basename2 } from "path";
|
|
11575
12541
|
async function handler3(args, ctx) {
|
|
11576
12542
|
const { positional, flags } = parseArgs(args, [
|
|
@@ -11590,21 +12556,21 @@ async function handler3(args, ctx) {
|
|
|
11590
12556
|
const source = getString(flags, "source", "s") ?? "salesforce";
|
|
11591
12557
|
const skipResolve = getBool(flags, "skip-resolve");
|
|
11592
12558
|
if (!file) {
|
|
11593
|
-
console.error(
|
|
11594
|
-
console.error(
|
|
12559
|
+
console.error(chalk15.red(" Usage: /ingest <file> [--source salesforce|hubspot|outreach]"));
|
|
12560
|
+
console.error(chalk15.dim(" /ingest --demo [--scenario <name>]"));
|
|
11595
12561
|
process.exit(1);
|
|
11596
12562
|
}
|
|
11597
|
-
if (!
|
|
11598
|
-
console.error(
|
|
12563
|
+
if (!existsSync11(file)) {
|
|
12564
|
+
console.error(chalk15.red(` File not found: ${file}`));
|
|
11599
12565
|
process.exit(1);
|
|
11600
12566
|
}
|
|
11601
12567
|
const profile = loadProfile();
|
|
11602
12568
|
const skipProfile = getFalse(flags, "profile");
|
|
11603
12569
|
if (!profile && !skipProfile) {
|
|
11604
12570
|
console.error();
|
|
11605
|
-
console.error(" " +
|
|
11606
|
-
console.error(" " +
|
|
11607
|
-
console.error(" " +
|
|
12571
|
+
console.error(" " + chalk15.red("No company profile found."));
|
|
12572
|
+
console.error(" " + chalk15.dim("Run ") + paint("accent", "/onboard") + chalk15.dim(" first for better column mapping,"));
|
|
12573
|
+
console.error(" " + chalk15.dim("or pass ") + paint("accent", "--no-profile") + chalk15.dim(" to skip."));
|
|
11608
12574
|
console.error();
|
|
11609
12575
|
process.exit(1);
|
|
11610
12576
|
}
|
|
@@ -11612,7 +12578,7 @@ async function handler3(args, ctx) {
|
|
|
11612
12578
|
try {
|
|
11613
12579
|
await initSchema();
|
|
11614
12580
|
spinner.text = "Parsing CSV...";
|
|
11615
|
-
const content =
|
|
12581
|
+
const content = readFileSync9(file, "utf-8");
|
|
11616
12582
|
const { rows, headers } = parseCSV(content);
|
|
11617
12583
|
if (rows.length === 0) {
|
|
11618
12584
|
spinner.fail("CSV is empty");
|
|
@@ -11636,22 +12602,22 @@ async function handler3(args, ctx) {
|
|
|
11636
12602
|
row_count: result2.imported
|
|
11637
12603
|
});
|
|
11638
12604
|
spinner.succeed(
|
|
11639
|
-
`Imported ${
|
|
12605
|
+
`Imported ${chalk15.bold(result2.imported.toString())} revenue events from ${chalk15.dim(basename2(file))}`
|
|
11640
12606
|
);
|
|
11641
12607
|
if (result2.errors.length > 0) {
|
|
11642
|
-
console.log(
|
|
12608
|
+
console.log(chalk15.yellow(` ${result2.errors.length} rows skipped`));
|
|
11643
12609
|
}
|
|
11644
12610
|
if (ctx.analysis) {
|
|
11645
12611
|
ctx.analysis.data_source_type = "revenue_ledger";
|
|
11646
12612
|
}
|
|
11647
|
-
console.log(
|
|
12613
|
+
console.log(chalk15.dim(" Run ") + chalk15.cyan("/metrics") + chalk15.dim(" for SaaS metrics with ledger-backed retention."));
|
|
11648
12614
|
return `${result2.imported} revenue events from ${basename2(file)}`;
|
|
11649
12615
|
}
|
|
11650
12616
|
spinner.text = "Detecting entity type...";
|
|
11651
12617
|
const detection = detectEntityType(headers, source);
|
|
11652
12618
|
if (!detection) {
|
|
11653
12619
|
spinner.fail(`Could not auto-detect entity type for source: ${source}`);
|
|
11654
|
-
console.log(
|
|
12620
|
+
console.log(chalk15.dim(" Headers found: " + headers.join(", ")));
|
|
11655
12621
|
process.exit(1);
|
|
11656
12622
|
}
|
|
11657
12623
|
spinner.text = `Importing ${rows.length} ${detection.entityType} rows...`;
|
|
@@ -11674,15 +12640,15 @@ async function handler3(args, ctx) {
|
|
|
11674
12640
|
row_count: result.imported
|
|
11675
12641
|
});
|
|
11676
12642
|
spinner.succeed(
|
|
11677
|
-
`Imported ${
|
|
12643
|
+
`Imported ${chalk15.bold(result.imported.toString())} ${detection.entityType} from ${chalk15.dim(basename2(file))} (${source})`
|
|
11678
12644
|
);
|
|
11679
12645
|
if (result.errors.length > 0) {
|
|
11680
|
-
console.log(
|
|
12646
|
+
console.log(chalk15.yellow(` ${result.errors.length} rows skipped`));
|
|
11681
12647
|
for (const err of result.errors.slice(0, 3)) {
|
|
11682
|
-
console.log(
|
|
12648
|
+
console.log(chalk15.dim(` - ${err}`));
|
|
11683
12649
|
}
|
|
11684
12650
|
if (result.errors.length > 3) {
|
|
11685
|
-
console.log(
|
|
12651
|
+
console.log(chalk15.dim(` ... and ${result.errors.length - 3} more`));
|
|
11686
12652
|
}
|
|
11687
12653
|
}
|
|
11688
12654
|
if (!skipResolve) {
|
|
@@ -11699,7 +12665,7 @@ async function handler3(args, ctx) {
|
|
|
11699
12665
|
return `${result.imported} ${detection.entityType} from ${basename2(file)}`;
|
|
11700
12666
|
} catch (err) {
|
|
11701
12667
|
spinner.fail("Import failed");
|
|
11702
|
-
console.error(
|
|
12668
|
+
console.error(chalk15.red(String(err)));
|
|
11703
12669
|
process.exit(1);
|
|
11704
12670
|
}
|
|
11705
12671
|
}
|
|
@@ -11728,10 +12694,10 @@ __export(ingest_chat_exports, {
|
|
|
11728
12694
|
loadDemoFromChat: () => loadDemoFromChat,
|
|
11729
12695
|
looksLikeFilePath: () => looksLikeFilePath
|
|
11730
12696
|
});
|
|
11731
|
-
import { existsSync as
|
|
12697
|
+
import { existsSync as existsSync12 } from "fs";
|
|
11732
12698
|
import { basename as basename3, resolve as resolve4 } from "path";
|
|
11733
12699
|
import { homedir as homedir5 } from "os";
|
|
11734
|
-
import
|
|
12700
|
+
import chalk16 from "chalk";
|
|
11735
12701
|
function extractFilePath(input) {
|
|
11736
12702
|
const trimmed = input.trim();
|
|
11737
12703
|
const patterns = [
|
|
@@ -11748,11 +12714,11 @@ function extractFilePath(input) {
|
|
|
11748
12714
|
const m = trimmed.match(re);
|
|
11749
12715
|
if (m?.[1]) {
|
|
11750
12716
|
const p = expandPath(m[1]);
|
|
11751
|
-
if (
|
|
12717
|
+
if (existsSync12(p)) return p;
|
|
11752
12718
|
}
|
|
11753
12719
|
if (!m?.[1] && re.test(trimmed) && trimmed.toLowerCase().endsWith(".csv")) {
|
|
11754
12720
|
const p = expandPath(trimmed.replace(/^["']|["']$/g, ""));
|
|
11755
|
-
if (
|
|
12721
|
+
if (existsSync12(p)) return p;
|
|
11756
12722
|
}
|
|
11757
12723
|
}
|
|
11758
12724
|
return null;
|
|
@@ -11766,7 +12732,7 @@ function looksLikeFilePath(input) {
|
|
|
11766
12732
|
}
|
|
11767
12733
|
async function ingestFromChat(ctx, filePath) {
|
|
11768
12734
|
if (!ctx.rl) {
|
|
11769
|
-
console.log(" " +
|
|
12735
|
+
console.log(" " + chalk16.red("Ingest confirm requires interactive mode."));
|
|
11770
12736
|
return false;
|
|
11771
12737
|
}
|
|
11772
12738
|
const name = basename3(filePath);
|
|
@@ -11774,7 +12740,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
11774
12740
|
try {
|
|
11775
12741
|
const ok = await prompts.confirm(`Ingest ${name} as CRM export?`, true);
|
|
11776
12742
|
if (!ok) {
|
|
11777
|
-
console.log(" " +
|
|
12743
|
+
console.log(" " + chalk16.dim("Ingest cancelled."));
|
|
11778
12744
|
return false;
|
|
11779
12745
|
}
|
|
11780
12746
|
} finally {
|
|
@@ -11782,12 +12748,12 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
11782
12748
|
}
|
|
11783
12749
|
const { handler: ingest } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
|
|
11784
12750
|
const { detectEntityType: detectEntityType2 } = await Promise.resolve().then(() => (init_csv_detect(), csv_detect_exports));
|
|
11785
|
-
const { readFileSync:
|
|
12751
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
11786
12752
|
const { parseCSV: parseCSV2 } = await Promise.resolve().then(() => (init_csv_parse(), csv_parse_exports));
|
|
11787
12753
|
const { getStoredApiKey } = await Promise.resolve().then(() => (init_repl_api(), repl_api_exports));
|
|
11788
12754
|
let headerCheckFailed = false;
|
|
11789
12755
|
try {
|
|
11790
|
-
const raw =
|
|
12756
|
+
const raw = readFileSync15(filePath, "utf-8");
|
|
11791
12757
|
const { headers } = parseCSV2(raw);
|
|
11792
12758
|
const detected = detectEntityType2(headers, "unknown");
|
|
11793
12759
|
if (!detected) headerCheckFailed = true;
|
|
@@ -11802,7 +12768,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
11802
12768
|
false
|
|
11803
12769
|
);
|
|
11804
12770
|
if (useAi) {
|
|
11805
|
-
console.log(" " +
|
|
12771
|
+
console.log(" " + chalk16.dim("AI column mapping is not wired to ingest yet \u2014 trying standard ingest."));
|
|
11806
12772
|
}
|
|
11807
12773
|
} finally {
|
|
11808
12774
|
prompts2.close();
|
|
@@ -11829,7 +12795,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
11829
12795
|
invalidateGapAudit(ctx);
|
|
11830
12796
|
saveSessionState(ctx);
|
|
11831
12797
|
console.log();
|
|
11832
|
-
console.log(" " + paint("accent", "\u2713 Data loaded") +
|
|
12798
|
+
console.log(" " + paint("accent", "\u2713 Data loaded") + chalk16.dim(` \u2014 ${name}`));
|
|
11833
12799
|
recordMessage(ctx, "user", `[ingested ${name}]`);
|
|
11834
12800
|
recordMessage(ctx, "agent", `Loaded ${name}. Checking what we can analyze\u2026`);
|
|
11835
12801
|
const audit = await refreshGapAudit(ctx);
|
|
@@ -13134,24 +14100,24 @@ JSON SHAPE:
|
|
|
13134
14100
|
|
|
13135
14101
|
// src/strategies/readers.ts
|
|
13136
14102
|
import { createHash } from "crypto";
|
|
13137
|
-
import { existsSync as
|
|
14103
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
13138
14104
|
import { extname, resolve as resolve5 } from "path";
|
|
13139
14105
|
import { parse as parseYaml } from "yaml";
|
|
13140
14106
|
import { PDFParse } from "pdf-parse";
|
|
13141
14107
|
async function readStrategyFile(pathOrDash) {
|
|
13142
14108
|
if (pathOrDash === "-") {
|
|
13143
|
-
const text2 =
|
|
14109
|
+
const text2 = readFileSync10(0, "utf-8");
|
|
13144
14110
|
return createDocument("stdin", null, text2, {});
|
|
13145
14111
|
}
|
|
13146
14112
|
const sourcePath = resolve5(pathOrDash);
|
|
13147
|
-
if (!
|
|
14113
|
+
if (!existsSync13(sourcePath)) {
|
|
13148
14114
|
throw new NtrpError("strategy_file_not_found", `Strategy file not found: ${pathOrDash}`, 2 /* Usage */);
|
|
13149
14115
|
}
|
|
13150
14116
|
const ext = extname(sourcePath).toLowerCase();
|
|
13151
14117
|
if (ext === ".pdf") {
|
|
13152
14118
|
return readPdf(sourcePath);
|
|
13153
14119
|
}
|
|
13154
|
-
const text =
|
|
14120
|
+
const text = readFileSync10(sourcePath, "utf-8");
|
|
13155
14121
|
if (ext === ".yaml" || ext === ".yml") {
|
|
13156
14122
|
const structured = parseStructuredYaml(text);
|
|
13157
14123
|
return createDocument("yaml", sourcePath, text, structured);
|
|
@@ -13166,7 +14132,7 @@ function readStrategyText(text) {
|
|
|
13166
14132
|
return createDocument("text", null, text, {});
|
|
13167
14133
|
}
|
|
13168
14134
|
async function readPdf(sourcePath) {
|
|
13169
|
-
const data =
|
|
14135
|
+
const data = readFileSync10(sourcePath);
|
|
13170
14136
|
const parser = new PDFParse({ data });
|
|
13171
14137
|
try {
|
|
13172
14138
|
const result = await parser.getText();
|
|
@@ -13213,15 +14179,15 @@ var init_readers = __esm({
|
|
|
13213
14179
|
});
|
|
13214
14180
|
|
|
13215
14181
|
// src/strategies/library.ts
|
|
13216
|
-
import { writeFileSync as
|
|
13217
|
-
import { join as
|
|
14182
|
+
import { writeFileSync as writeFileSync8 } from "fs";
|
|
14183
|
+
import { join as join11 } from "path";
|
|
13218
14184
|
import { stringify as stringifyYaml } from "yaml";
|
|
13219
14185
|
function strategyLibraryPath(slug) {
|
|
13220
|
-
return
|
|
14186
|
+
return join11(getStrategiesDir(), `${slug}.md`);
|
|
13221
14187
|
}
|
|
13222
14188
|
function writeStrategyMarkdown(strategy) {
|
|
13223
14189
|
const path = strategyLibraryPath(strategy.slug);
|
|
13224
|
-
|
|
14190
|
+
writeFileSync8(path, renderStrategyMarkdown(strategy), "utf-8");
|
|
13225
14191
|
return path;
|
|
13226
14192
|
}
|
|
13227
14193
|
function renderStrategyMarkdown(strategy) {
|
|
@@ -13295,7 +14261,7 @@ var init_library = __esm({
|
|
|
13295
14261
|
// src/strategies/connectors.ts
|
|
13296
14262
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
13297
14263
|
import { homedir as homedir6 } from "os";
|
|
13298
|
-
import { basename as basename4, extname as extname2, join as
|
|
14264
|
+
import { basename as basename4, extname as extname2, join as join12, relative, resolve as resolve6, sep as sep2 } from "path";
|
|
13299
14265
|
function createLocalFolderConnector(options) {
|
|
13300
14266
|
const rootPath = resolveUserPath(options.rootPath);
|
|
13301
14267
|
const name = options.name ?? (basename4(rootPath) || "local");
|
|
@@ -13338,7 +14304,7 @@ function createLocalFolderConnector(options) {
|
|
|
13338
14304
|
}
|
|
13339
14305
|
function walkLocalFolder(rootPath, currentPath, refs, opts) {
|
|
13340
14306
|
for (const entry of readdirSync2(currentPath, { withFileTypes: true })) {
|
|
13341
|
-
const absolutePath =
|
|
14307
|
+
const absolutePath = join12(currentPath, entry.name);
|
|
13342
14308
|
const relativePath = normalizePath(relative(rootPath, absolutePath));
|
|
13343
14309
|
if (entry.isDirectory()) {
|
|
13344
14310
|
if (shouldSkipDirectory(entry.name) || matchesAny(relativePath, opts.excludePatterns)) continue;
|
|
@@ -13402,7 +14368,7 @@ function normalizePath(path) {
|
|
|
13402
14368
|
}
|
|
13403
14369
|
function resolveUserPath(path) {
|
|
13404
14370
|
if (path === "~") return homedir6();
|
|
13405
|
-
if (path.startsWith("~/")) return
|
|
14371
|
+
if (path.startsWith("~/")) return join12(homedir6(), path.slice(2));
|
|
13406
14372
|
return resolve6(path);
|
|
13407
14373
|
}
|
|
13408
14374
|
var DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, SUPPORTED_EXTENSIONS, DEFAULT_EXCLUDED_DIRS;
|
|
@@ -13818,17 +14784,17 @@ var init_retrieval = __esm({
|
|
|
13818
14784
|
});
|
|
13819
14785
|
|
|
13820
14786
|
// src/memory/knowledge.ts
|
|
13821
|
-
import { existsSync as
|
|
13822
|
-
import { join as
|
|
13823
|
-
import { randomUUID as
|
|
14787
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, appendFileSync as appendFileSync3, readdirSync as readdirSync3 } from "fs";
|
|
14788
|
+
import { join as join15 } from "path";
|
|
14789
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
13824
14790
|
function knowledgePath() {
|
|
13825
|
-
return
|
|
14791
|
+
return join15(getMemoryDir(), KNOWLEDGE_FILE);
|
|
13826
14792
|
}
|
|
13827
14793
|
function loadKnowledgeChunks() {
|
|
13828
14794
|
const path = knowledgePath();
|
|
13829
|
-
if (!
|
|
14795
|
+
if (!existsSync14(path)) return [];
|
|
13830
14796
|
const out = [];
|
|
13831
|
-
for (const line of
|
|
14797
|
+
for (const line of readFileSync11(path, "utf-8").split("\n")) {
|
|
13832
14798
|
const trimmed = line.trim();
|
|
13833
14799
|
if (!trimmed) continue;
|
|
13834
14800
|
try {
|
|
@@ -13861,17 +14827,17 @@ __export(store_exports2, {
|
|
|
13861
14827
|
rewriteJsonl: () => rewriteJsonl,
|
|
13862
14828
|
scrubText: () => scrubText
|
|
13863
14829
|
});
|
|
13864
|
-
import { existsSync as
|
|
13865
|
-
import { join as
|
|
13866
|
-
import { randomUUID as
|
|
14830
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, appendFileSync as appendFileSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
14831
|
+
import { join as join16 } from "path";
|
|
14832
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
13867
14833
|
function memPath(file) {
|
|
13868
|
-
return
|
|
14834
|
+
return join16(getMemoryDir(), file);
|
|
13869
14835
|
}
|
|
13870
14836
|
function readJsonl(file) {
|
|
13871
14837
|
const path = memPath(file);
|
|
13872
|
-
if (!
|
|
14838
|
+
if (!existsSync15(path)) return [];
|
|
13873
14839
|
const out = [];
|
|
13874
|
-
for (const line of
|
|
14840
|
+
for (const line of readFileSync12(path, "utf-8").split("\n")) {
|
|
13875
14841
|
const trimmed = line.trim();
|
|
13876
14842
|
if (!trimmed) continue;
|
|
13877
14843
|
try {
|
|
@@ -13889,7 +14855,7 @@ function appendJsonl(file, obj) {
|
|
|
13889
14855
|
}
|
|
13890
14856
|
function rewriteJsonl(file, rows) {
|
|
13891
14857
|
try {
|
|
13892
|
-
|
|
14858
|
+
writeFileSync10(memPath(file), rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length ? "\n" : ""));
|
|
13893
14859
|
} catch {
|
|
13894
14860
|
}
|
|
13895
14861
|
}
|
|
@@ -13898,7 +14864,7 @@ function scrubText(text) {
|
|
|
13898
14864
|
}
|
|
13899
14865
|
function addFact(input) {
|
|
13900
14866
|
const fact = {
|
|
13901
|
-
id:
|
|
14867
|
+
id: randomUUID5(),
|
|
13902
14868
|
text: scrubText(input.text),
|
|
13903
14869
|
kind: input.kind ?? "fact",
|
|
13904
14870
|
source: input.source ?? "user",
|
|
@@ -13919,7 +14885,7 @@ function summarizeAnswer(answer) {
|
|
|
13919
14885
|
}
|
|
13920
14886
|
function recordAnalysis(input) {
|
|
13921
14887
|
const entry = {
|
|
13922
|
-
id:
|
|
14888
|
+
id: randomUUID5(),
|
|
13923
14889
|
question: scrubText(input.question).slice(0, 300),
|
|
13924
14890
|
summary: scrubText(summarizeAnswer(input.answer)),
|
|
13925
14891
|
tools: input.tools,
|
|
@@ -13951,7 +14917,7 @@ function loadWinSnippets() {
|
|
|
13951
14917
|
const out = [];
|
|
13952
14918
|
for (const name of readdirSync4(dir)) {
|
|
13953
14919
|
if (!name.endsWith(".md") || name.toLowerCase() === "readme.md") continue;
|
|
13954
|
-
const raw =
|
|
14920
|
+
const raw = readFileSync12(join16(dir, name), "utf-8");
|
|
13955
14921
|
const title = raw.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? name.replace(/\.md$/, "");
|
|
13956
14922
|
const body = raw.replace(/^#.*$/m, "").replace(/\s+/g, " ").trim().slice(0, 300);
|
|
13957
14923
|
out.push({ id: `win:${name}`, title, text: `${title}. ${body}` });
|
|
@@ -14125,7 +15091,7 @@ init_session_analysis();
|
|
|
14125
15091
|
init_queries();
|
|
14126
15092
|
init_diagnosis();
|
|
14127
15093
|
init_strategy();
|
|
14128
|
-
import { join as
|
|
15094
|
+
import { join as join14 } from "path";
|
|
14129
15095
|
|
|
14130
15096
|
// src/services/publish.ts
|
|
14131
15097
|
init_queries();
|
|
@@ -14290,8 +15256,8 @@ function buildSections(input) {
|
|
|
14290
15256
|
|
|
14291
15257
|
// src/repositories/markdown.ts
|
|
14292
15258
|
init_formatters();
|
|
14293
|
-
import { mkdirSync as
|
|
14294
|
-
import { basename as basename5, dirname as dirname2, join as
|
|
15259
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
15260
|
+
import { basename as basename5, dirname as dirname2, join as join13, resolve as resolve7 } from "path";
|
|
14295
15261
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
14296
15262
|
var markdownRepositoryAdapter = {
|
|
14297
15263
|
kind: "markdown",
|
|
@@ -14313,12 +15279,12 @@ var markdownRepositoryAdapter = {
|
|
|
14313
15279
|
write(pkg) {
|
|
14314
15280
|
const root = getRootPath(pkg.target);
|
|
14315
15281
|
const files = renderMarkdownFiles(pkg);
|
|
14316
|
-
|
|
15282
|
+
mkdirSync9(root, { recursive: true });
|
|
14317
15283
|
const written = [];
|
|
14318
15284
|
for (const file of files) {
|
|
14319
|
-
const absolutePath =
|
|
14320
|
-
|
|
14321
|
-
|
|
15285
|
+
const absolutePath = join13(root, file.relativePath);
|
|
15286
|
+
mkdirSync9(dirname2(absolutePath), { recursive: true });
|
|
15287
|
+
writeFileSync9(absolutePath, file.contents, "utf-8");
|
|
14322
15288
|
written.push(absolutePath);
|
|
14323
15289
|
}
|
|
14324
15290
|
return {
|
|
@@ -14634,7 +15600,7 @@ async function runSmokeProtocol(_input, ctx) {
|
|
|
14634
15600
|
});
|
|
14635
15601
|
const proposalResult = await proposeRepositoryExport({
|
|
14636
15602
|
target: "markdown",
|
|
14637
|
-
directory:
|
|
15603
|
+
directory: join14(getExportsDir(), "repository-smoke"),
|
|
14638
15604
|
source: "smoke_protocol",
|
|
14639
15605
|
modelOrFixture: "smoke-protocol-v1"
|
|
14640
15606
|
});
|
|
@@ -14801,8 +15767,8 @@ init_repl_api();
|
|
|
14801
15767
|
init_llm_config();
|
|
14802
15768
|
init_store();
|
|
14803
15769
|
init_profile();
|
|
14804
|
-
import { existsSync as
|
|
14805
|
-
import { join as
|
|
15770
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
15771
|
+
import { join as join17 } from "path";
|
|
14806
15772
|
|
|
14807
15773
|
// src/license/verify.ts
|
|
14808
15774
|
init_store();
|
|
@@ -14998,9 +15964,9 @@ function setupCheck() {
|
|
|
14998
15964
|
const home = ntrpHome();
|
|
14999
15965
|
let writable = false;
|
|
15000
15966
|
try {
|
|
15001
|
-
|
|
15002
|
-
const probe =
|
|
15003
|
-
|
|
15967
|
+
mkdirSync10(home, { recursive: true });
|
|
15968
|
+
const probe = join17(home, ".write-check");
|
|
15969
|
+
writeFileSync11(probe, "ok\n");
|
|
15004
15970
|
writable = true;
|
|
15005
15971
|
} catch {
|
|
15006
15972
|
writable = false;
|
|
@@ -15044,18 +16010,18 @@ init_serialize();
|
|
|
15044
16010
|
init_errors2();
|
|
15045
16011
|
|
|
15046
16012
|
// src/version.ts
|
|
15047
|
-
import { existsSync as
|
|
15048
|
-
import { dirname as dirname3, join as
|
|
16013
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
16014
|
+
import { dirname as dirname3, join as join18 } from "path";
|
|
15049
16015
|
import { fileURLToPath } from "url";
|
|
15050
16016
|
var cachedVersion;
|
|
15051
16017
|
function getInstalledVersion() {
|
|
15052
16018
|
if (cachedVersion) return cachedVersion;
|
|
15053
16019
|
const start = dirname3(fileURLToPath(import.meta.url));
|
|
15054
16020
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
15055
|
-
const path =
|
|
15056
|
-
if (!
|
|
16021
|
+
const path = join18(start, rel);
|
|
16022
|
+
if (!existsSync17(path)) continue;
|
|
15057
16023
|
try {
|
|
15058
|
-
const pkg = JSON.parse(
|
|
16024
|
+
const pkg = JSON.parse(readFileSync14(path, "utf-8"));
|
|
15059
16025
|
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
15060
16026
|
cachedVersion = pkg.version;
|
|
15061
16027
|
return cachedVersion;
|