@sonnechasser/ntrp 1.5.1 → 1.5.3

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 CHANGED
@@ -9438,6 +9438,8 @@ var init_repl_globals = __esm({
9438
9438
  var prompts_exports = {};
9439
9439
  __export(prompts_exports, {
9440
9440
  createPromptSession: () => createPromptSession,
9441
+ formatConfirmDefaultHint: () => formatConfirmDefaultHint,
9442
+ formatEnterDefaultHint: () => formatEnterDefaultHint,
9441
9443
  presentRecommendedFirst: () => presentRecommendedFirst,
9442
9444
  presentRecommendedMultiFirst: () => presentRecommendedMultiFirst,
9443
9445
  resolveAskMultiInput: () => resolveAskMultiInput,
@@ -9458,17 +9460,29 @@ function secretPromptLine(question) {
9458
9460
  function stripTerminalArtifacts(input) {
9459
9461
  return input.replace(/\x1b\[[0-9;]*[a-zA-Z~]/g, "").replace(/\x1b\][^\x07]*(\x07|\x1b\\)/g, "").replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
9460
9462
  }
9463
+ function formatEnterDefaultHint(defaultValue) {
9464
+ const lower = defaultValue.trim().toLowerCase();
9465
+ if (lower === "y" || lower === "yes") return "\u23CE yes";
9466
+ if (lower === "n" || lower === "no") return "\u23CE no";
9467
+ return defaultValue;
9468
+ }
9469
+ function formatConfirmDefaultHint(defaultYes = true) {
9470
+ return formatEnterDefaultHint(defaultYes ? "yes" : "no");
9471
+ }
9461
9472
  function renderQuestion(question, defaultValue) {
9462
9473
  const base = ` ${marker()}${bold(question)}`;
9463
9474
  if (defaultValue !== void 0 && defaultValue !== "") {
9464
- return `${base} ${chalk4.dim(`[${defaultValue}]`)} `;
9475
+ return `${base} ${chalk4.dim(`[${formatEnterDefaultHint(defaultValue)}]`)} `;
9465
9476
  }
9466
9477
  return `${base} `;
9467
9478
  }
9468
9479
  function resolveConfirmInput(raw, defaultYes = true) {
9469
9480
  const answer = raw.trim().toLowerCase();
9470
9481
  if (!answer) return defaultYes;
9471
- return answer === "y" || answer === "yes";
9482
+ if (answer === "y" || answer === "yes" || answer === "yeah" || answer === "yep") {
9483
+ return true;
9484
+ }
9485
+ return false;
9472
9486
  }
9473
9487
  function presentRecommendedFirst(choices, recommended) {
9474
9488
  if (choices.length === 0) return [];
@@ -9543,8 +9557,7 @@ function createPromptSession(existing, ctx) {
9543
9557
  }
9544
9558
  }
9545
9559
  async function confirm(question, defaultYes = true) {
9546
- const hint = defaultYes ? "Y/n" : "y/N";
9547
- const raw = (await rl.question(renderQuestion(question, hint))).trim();
9560
+ const raw = (await rl.question(renderQuestion(question, defaultYes ? "yes" : "no"))).trim();
9548
9561
  assertNotGlobalReplCommand(raw);
9549
9562
  return resolveConfirmInput(raw, defaultYes);
9550
9563
  }
@@ -12346,6 +12359,8 @@ var init_guide_slides = __esm({
12346
12359
  lines: [
12347
12360
  `We designed the conversation so you don't need a slash to start. Type the question you actually need \u2014 "is our retention real for the board?" or "pipeline health" \u2014 and NTRP restates it as a scope card so you can confirm we're answering the right thing.`,
12348
12361
  "",
12362
+ "Same navigational toolkit everywhere: when yes is the default, bare Enter accepts it (\u23CE yes on scope and strategy cards; \u23CE yes on wizard confirms). Type something else \u2014 b/back, adjust, n, or a restated focus \u2014 for another path.",
12363
+ "",
12349
12364
  "Hit Enter by accident on \u23CE yes? Type b or back \u2014 that rewinds to the focus card. The same word leaves a strategy or think overlay. It does not unload demo data or undo compute; /scratch or a new session is the blunt reset.",
12350
12365
  "",
12351
12366
  "Empty dataset? Load a sample with \u23CE use demo data, paste a CSV path, or /ingest. When the gap card says the formulas can compute, \u23CE go ahead runs the local math.",
@@ -15484,6 +15499,190 @@ var init_explore_mode = __esm({
15484
15499
  }
15485
15500
  });
15486
15501
 
15502
+ // src/config/update-check.ts
15503
+ import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync17, unlinkSync as unlinkSync4, writeFileSync as writeFileSync14 } from "fs";
15504
+ import { join as join21 } from "path";
15505
+ function cachePath2() {
15506
+ return join21(ntrpHome(), "update-check.json");
15507
+ }
15508
+ function ensureDir6() {
15509
+ const dir = ntrpHome();
15510
+ if (!existsSync21(dir)) {
15511
+ mkdirSync12(dir, { recursive: true });
15512
+ }
15513
+ }
15514
+ function loadUpdateCheckCache() {
15515
+ const path = cachePath2();
15516
+ if (!existsSync21(path)) return null;
15517
+ try {
15518
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
15519
+ if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
15520
+ return null;
15521
+ }
15522
+ return parsed;
15523
+ } catch {
15524
+ return null;
15525
+ }
15526
+ }
15527
+ function saveUpdateCheckCache(cache2) {
15528
+ ensureDir6();
15529
+ writeFileSync14(cachePath2(), JSON.stringify(cache2, null, 2) + "\n");
15530
+ }
15531
+ function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
15532
+ if (!cache2) return false;
15533
+ return Date.now() - cache2.lastCheck < ttlMs;
15534
+ }
15535
+ function invalidateUpdateCheckCache() {
15536
+ const path = cachePath2();
15537
+ if (existsSync21(path)) {
15538
+ unlinkSync4(path);
15539
+ }
15540
+ }
15541
+ var CACHE_TTL_MS2;
15542
+ var init_update_check = __esm({
15543
+ "src/config/update-check.ts"() {
15544
+ "use strict";
15545
+ init_store();
15546
+ CACHE_TTL_MS2 = 864e5;
15547
+ }
15548
+ });
15549
+
15550
+ // src/version.ts
15551
+ import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
15552
+ import { dirname as dirname4, join as join22 } from "path";
15553
+ import { fileURLToPath } from "url";
15554
+ function readVersionFromPackageJson(packageJsonPath) {
15555
+ if (!existsSync22(packageJsonPath)) return null;
15556
+ try {
15557
+ const pkg = JSON.parse(readFileSync18(packageJsonPath, "utf-8"));
15558
+ if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
15559
+ } catch {
15560
+ }
15561
+ return null;
15562
+ }
15563
+ function readVersionNearEntry(entryPath) {
15564
+ const start = dirname4(entryPath);
15565
+ for (const rel of [join22(start, "..", "package.json"), join22(start, "../..", "package.json")]) {
15566
+ const version = readVersionFromPackageJson(rel);
15567
+ if (version) return version;
15568
+ }
15569
+ return null;
15570
+ }
15571
+ function readInstalledVersionFromDisk() {
15572
+ return readVersionNearEntry(fileURLToPath(import.meta.url));
15573
+ }
15574
+ function getInstalledVersion() {
15575
+ if (cachedVersion) return cachedVersion;
15576
+ cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
15577
+ return cachedVersion;
15578
+ }
15579
+ var cachedVersion;
15580
+ var init_version = __esm({
15581
+ "src/version.ts"() {
15582
+ "use strict";
15583
+ }
15584
+ });
15585
+
15586
+ // src/update/registry.ts
15587
+ var registry_exports = {};
15588
+ __export(registry_exports, {
15589
+ NPM_PACKAGE: () => NPM_PACKAGE,
15590
+ applyUpdateCheckResult: () => applyUpdateCheckResult,
15591
+ checkForUpdate: () => checkForUpdate,
15592
+ fetchLatestVersion: () => fetchLatestVersion,
15593
+ formatUpdateNudge: () => formatUpdateNudge,
15594
+ hasAvailableUpdate: () => hasAvailableUpdate,
15595
+ hydrateUpdateAvailableFromCache: () => hydrateUpdateAvailableFromCache,
15596
+ isNewerVersion: () => isNewerVersion,
15597
+ startBackgroundUpdateCheck: () => startBackgroundUpdateCheck
15598
+ });
15599
+ function registryUrl() {
15600
+ return process.env.NTRP_REGISTRY_URL ?? "https://registry.npmjs.org/@sonnechasser/ntrp/latest";
15601
+ }
15602
+ function parseVersionParts(version) {
15603
+ const cleaned = version.trim().replace(/^v/i, "");
15604
+ const core = cleaned.split("-")[0] ?? cleaned;
15605
+ const parts = core.split(".").map((p) => parseInt(p, 10));
15606
+ return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
15607
+ }
15608
+ function isNewerVersion(latest, current) {
15609
+ const [lMaj, lMin, lPatch] = parseVersionParts(latest);
15610
+ const [cMaj, cMin, cPatch] = parseVersionParts(current);
15611
+ if (lMaj !== cMaj) return lMaj > cMaj;
15612
+ if (lMin !== cMin) return lMin > cMin;
15613
+ return lPatch > cPatch;
15614
+ }
15615
+ function formatUpdateNudge(current, latest) {
15616
+ return `\u26A1 NTRP v${latest} available (running v${current}) \u2014 \u23CE /update`;
15617
+ }
15618
+ function hasAvailableUpdate(update) {
15619
+ return Boolean(update && isNewerVersion(update.latest, update.current));
15620
+ }
15621
+ function hydrateUpdateAvailableFromCache(current) {
15622
+ const cached2 = loadUpdateCheckCache();
15623
+ if (!cached2?.latestVersion) return void 0;
15624
+ if (!isNewerVersion(cached2.latestVersion, current)) return void 0;
15625
+ return { current, latest: cached2.latestVersion };
15626
+ }
15627
+ function applyUpdateCheckResult(ctx, result) {
15628
+ if (result?.updateAvailable) {
15629
+ ctx.updateAvailable = { current: result.current, latest: result.latest };
15630
+ return;
15631
+ }
15632
+ if (result && !result.updateAvailable) {
15633
+ ctx.updateAvailable = void 0;
15634
+ }
15635
+ }
15636
+ function startBackgroundUpdateCheck(ctx) {
15637
+ const pending = checkForUpdate({ force: true, timeoutMs: 5e3 });
15638
+ ctx.pendingUpdateCheck = pending;
15639
+ void pending.then((result) => applyUpdateCheckResult(ctx, result)).catch(() => void 0);
15640
+ }
15641
+ async function fetchLatestVersion(timeoutMs = 5e3) {
15642
+ try {
15643
+ const res = await fetch(registryUrl(), { signal: AbortSignal.timeout(timeoutMs) });
15644
+ if (!res.ok) return null;
15645
+ const data = await res.json();
15646
+ return typeof data.version === "string" && data.version.length > 0 ? data.version : null;
15647
+ } catch {
15648
+ return null;
15649
+ }
15650
+ }
15651
+ function buildResult(current, latest) {
15652
+ return {
15653
+ current,
15654
+ latest,
15655
+ updateAvailable: isNewerVersion(latest, current)
15656
+ };
15657
+ }
15658
+ async function checkForUpdate(options) {
15659
+ const current = getInstalledVersion();
15660
+ const timeoutMs = options?.timeoutMs ?? 5e3;
15661
+ const cached2 = loadUpdateCheckCache();
15662
+ if (!options?.force && isCacheFresh(cached2)) {
15663
+ return buildResult(current, cached2.latestVersion);
15664
+ }
15665
+ const latest = await fetchLatestVersion(timeoutMs);
15666
+ if (!latest) {
15667
+ if (cached2?.latestVersion) {
15668
+ return buildResult(current, cached2.latestVersion);
15669
+ }
15670
+ return null;
15671
+ }
15672
+ const nextCache = { lastCheck: Date.now(), latestVersion: latest };
15673
+ saveUpdateCheckCache(nextCache);
15674
+ return buildResult(current, latest);
15675
+ }
15676
+ var NPM_PACKAGE;
15677
+ var init_registry = __esm({
15678
+ "src/update/registry.ts"() {
15679
+ "use strict";
15680
+ init_update_check();
15681
+ init_version();
15682
+ NPM_PACKAGE = "@sonnechasser/ntrp";
15683
+ }
15684
+ });
15685
+
15487
15686
  // src/conversation/recommended-action.ts
15488
15687
  function resolveRecommendedAction(ctx) {
15489
15688
  if (!hasValidLicense()) return { submit: "/activate", hint: "/activate" };
@@ -15502,6 +15701,8 @@ function resolveRecommendedAction(ctx) {
15502
15701
  return ctx.strategistState?.step === "objective_confirm" ? { submit: "yes", hint: "yes" } : null;
15503
15702
  case "think":
15504
15703
  return null;
15704
+ case "orient":
15705
+ return hasAvailableUpdate(ctx.updateAvailable) ? { submit: "/update", hint: "/update" } : null;
15505
15706
  default:
15506
15707
  return null;
15507
15708
  }
@@ -15511,6 +15712,7 @@ var init_recommended_action = __esm({
15511
15712
  "use strict";
15512
15713
  init_repl_api();
15513
15714
  init_activation();
15715
+ init_registry();
15514
15716
  init_phase();
15515
15717
  }
15516
15718
  });
@@ -16309,17 +16511,17 @@ __export(play_outcomes_exports, {
16309
16511
  listPlayOutcomes: () => listPlayOutcomes,
16310
16512
  recordPlayOutcomes: () => recordPlayOutcomes
16311
16513
  });
16312
- import { existsSync as existsSync21, readFileSync as readFileSync17, appendFileSync as appendFileSync6 } from "fs";
16313
- import { join as join21 } from "path";
16514
+ import { existsSync as existsSync23, readFileSync as readFileSync19, appendFileSync as appendFileSync6 } from "fs";
16515
+ import { join as join23 } from "path";
16314
16516
  import { randomUUID as randomUUID7 } from "crypto";
16315
16517
  function outcomesPath() {
16316
- return join21(getMemoryDir(), OUTCOMES_FILE);
16518
+ return join23(getMemoryDir(), OUTCOMES_FILE);
16317
16519
  }
16318
16520
  function listPlayOutcomes() {
16319
16521
  const path = outcomesPath();
16320
- if (!existsSync21(path)) return [];
16522
+ if (!existsSync23(path)) return [];
16321
16523
  const out = [];
16322
- for (const line of readFileSync17(path, "utf-8").split("\n")) {
16524
+ for (const line of readFileSync19(path, "utf-8").split("\n")) {
16323
16525
  const trimmed = line.trim();
16324
16526
  if (!trimmed) continue;
16325
16527
  try {
@@ -19786,18 +19988,18 @@ var init_terminal = __esm({
19786
19988
  });
19787
19989
 
19788
19990
  // src/demo/taxonomy-cache.ts
19789
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync22, mkdirSync as mkdirSync12, unlinkSync as unlinkSync4 } from "fs";
19991
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync15, existsSync as existsSync24, mkdirSync as mkdirSync13, unlinkSync as unlinkSync5 } from "fs";
19790
19992
  import { homedir as homedir7 } from "os";
19791
- import { join as join22 } from "path";
19792
- function ensureDir6() {
19793
- if (!existsSync22(NTRP_DIR4)) {
19794
- mkdirSync12(NTRP_DIR4, { recursive: true });
19993
+ import { join as join24 } from "path";
19994
+ function ensureDir7() {
19995
+ if (!existsSync24(NTRP_DIR4)) {
19996
+ mkdirSync13(NTRP_DIR4, { recursive: true });
19795
19997
  }
19796
19998
  }
19797
19999
  function loadCachedTaxonomy(profile) {
19798
- if (!existsSync22(TAXONOMY_PATH)) return null;
20000
+ if (!existsSync24(TAXONOMY_PATH)) return null;
19799
20001
  try {
19800
- const parsed = JSON.parse(readFileSync18(TAXONOMY_PATH, "utf-8"));
20002
+ const parsed = JSON.parse(readFileSync20(TAXONOMY_PATH, "utf-8"));
19801
20003
  if (!parsed || typeof parsed !== "object") return null;
19802
20004
  if (parsed.profile_updated_at !== profile.updated_at) return null;
19803
20005
  return parsed;
@@ -19806,13 +20008,13 @@ function loadCachedTaxonomy(profile) {
19806
20008
  }
19807
20009
  }
19808
20010
  function saveCachedTaxonomy(taxonomy) {
19809
- ensureDir6();
19810
- writeFileSync14(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
20011
+ ensureDir7();
20012
+ writeFileSync15(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
19811
20013
  }
19812
20014
  function invalidateTaxonomy() {
19813
- if (existsSync22(TAXONOMY_PATH)) {
20015
+ if (existsSync24(TAXONOMY_PATH)) {
19814
20016
  try {
19815
- unlinkSync4(TAXONOMY_PATH);
20017
+ unlinkSync5(TAXONOMY_PATH);
19816
20018
  } catch {
19817
20019
  }
19818
20020
  }
@@ -19821,8 +20023,8 @@ var NTRP_DIR4, TAXONOMY_PATH;
19821
20023
  var init_taxonomy_cache = __esm({
19822
20024
  "src/demo/taxonomy-cache.ts"() {
19823
20025
  "use strict";
19824
- NTRP_DIR4 = join22(homedir7(), ".ntrp");
19825
- TAXONOMY_PATH = join22(NTRP_DIR4, "demo-taxonomy.json");
20026
+ NTRP_DIR4 = join24(homedir7(), ".ntrp");
20027
+ TAXONOMY_PATH = join24(NTRP_DIR4, "demo-taxonomy.json");
19826
20028
  }
19827
20029
  });
19828
20030
 
@@ -20280,7 +20482,7 @@ __export(inbox_setup_exports, {
20280
20482
  shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
20281
20483
  });
20282
20484
  import chalk19 from "chalk";
20283
- import { existsSync as existsSync23 } from "fs";
20485
+ import { existsSync as existsSync25 } from "fs";
20284
20486
  function markDemoOffered() {
20285
20487
  setConfigValue("ai-inbox-nudge-seen", "true");
20286
20488
  }
@@ -20312,7 +20514,7 @@ function printSkipHint(beat) {
20312
20514
  async function reuseInboxFolderIfPresent(session, beat, folderPath) {
20313
20515
  if (getAiInboxDir()) return false;
20314
20516
  const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
20315
- const existing = candidates.find((p) => existsSync23(p));
20517
+ const existing = candidates.find((p) => existsSync25(p));
20316
20518
  if (!existing) return false;
20317
20519
  console.log(" " + chalk19.dim("Pickup folder still on disk: ") + existing);
20318
20520
  const reuse = await session.confirm("Reuse this pickup folder?", true);
@@ -20418,7 +20620,7 @@ __export(ingest_exports, {
20418
20620
  handler: () => handler2
20419
20621
  });
20420
20622
  import chalk20 from "chalk";
20421
- import { readFileSync as readFileSync19, existsSync as existsSync24 } from "fs";
20623
+ import { readFileSync as readFileSync21, existsSync as existsSync26 } from "fs";
20422
20624
  import { basename as basename6 } from "path";
20423
20625
  async function handler2(args, ctx) {
20424
20626
  const { positional, flags } = parseArgs2(args, [
@@ -20442,7 +20644,7 @@ async function handler2(args, ctx) {
20442
20644
  console.error(chalk20.dim(" /ingest --demo [--scenario <name>]"));
20443
20645
  process.exit(1);
20444
20646
  }
20445
- if (!existsSync24(file)) {
20647
+ if (!existsSync26(file)) {
20446
20648
  console.error(chalk20.red(` File not found: ${file}`));
20447
20649
  process.exit(1);
20448
20650
  }
@@ -20460,7 +20662,7 @@ async function handler2(args, ctx) {
20460
20662
  try {
20461
20663
  await initSchema();
20462
20664
  spinner.text = "Parsing CSV\u2026";
20463
- const content = readFileSync19(file, "utf-8");
20665
+ const content = readFileSync21(file, "utf-8");
20464
20666
  const { rows, headers } = parseCSV(content);
20465
20667
  if (rows.length === 0) {
20466
20668
  spinner.fail("CSV is empty");
@@ -23373,7 +23575,9 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
23373
23575
  );
23374
23576
  } else if (phase === "orient") {
23375
23577
  lines.push("Already done: nothing locked yet");
23376
- lines.push("Available now: help the user name a focus; CLI will propose scope from their words");
23578
+ lines.push(
23579
+ "Available now: help the user name a focus; CLI will propose scope from their words. If \u23CE /update is armed, the CLI owns the install confirm \u2014 do not re-ask about updating"
23580
+ );
23377
23581
  } else if (phase === "think") {
23378
23582
  lines.push("Already done: analysis complete; think channel open");
23379
23583
  lines.push("Available now: socratic exploration; draft_strategy / draft_handoff when ready to graduate");
@@ -25144,7 +25348,7 @@ __export(onboard_tiers_exports, {
25144
25348
  resetOnboardTierProgress: () => resetOnboardTierProgress,
25145
25349
  resolveNextOnboardTier: () => resolveNextOnboardTier
25146
25350
  });
25147
- import { existsSync as existsSync25, statSync as statSync4 } from "fs";
25351
+ import { existsSync as existsSync27, statSync as statSync4 } from "fs";
25148
25352
  function flagSet(tier) {
25149
25353
  return Boolean(getConfigValue(TIER_CONFIG_KEYS[tier]));
25150
25354
  }
@@ -25171,7 +25375,7 @@ function hasProductionDataset(ctx) {
25171
25375
  if (source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source) || source.startsWith("~")) {
25172
25376
  return true;
25173
25377
  }
25174
- if (!source.includes(":") && existsSync25(source)) return true;
25378
+ if (!source.includes(":") && existsSync27(source)) return true;
25175
25379
  if (source.startsWith("csv:") || source.startsWith("file:") || source.startsWith("folder:")) {
25176
25380
  return true;
25177
25381
  }
@@ -25230,7 +25434,7 @@ function markDemoDataSeen() {
25230
25434
  }
25231
25435
  function pathLooksPresent(raw) {
25232
25436
  try {
25233
- return existsSync25(raw) && (statSync4(raw).isFile() || statSync4(raw).isDirectory());
25437
+ return existsSync27(raw) && (statSync4(raw).isFile() || statSync4(raw).isDirectory());
25234
25438
  } catch {
25235
25439
  return false;
25236
25440
  }
@@ -25635,7 +25839,7 @@ __export(onboard_exports, {
25635
25839
  profileExists: () => profileExists
25636
25840
  });
25637
25841
  import chalk25 from "chalk";
25638
- import { existsSync as existsSync26 } from "fs";
25842
+ import { existsSync as existsSync28 } from "fs";
25639
25843
  import { basename as basename7 } from "path";
25640
25844
  async function handler5(args, ctx) {
25641
25845
  const { flags } = parseArgs2(args, ["force", "skip-brand"]);
@@ -25910,7 +26114,7 @@ async function runProductionTier(session, ctx) {
25910
26114
  return "Production data skipped";
25911
26115
  }
25912
26116
  const resolved = resolveUserPath(trimmed);
25913
- if (!existsSync26(resolved)) {
26117
+ if (!existsSync28(resolved)) {
25914
26118
  console.log(" " + chalk25.red(`Path not found: ${resolved}`));
25915
26119
  console.log(" " + chalk25.dim("Try again with /onboard, or drop the path into the REPL."));
25916
26120
  return "Production path not found";
@@ -25929,18 +26133,22 @@ async function reviewLoop(session, initial) {
25929
26133
  let candidate = initial;
25930
26134
  for (; ; ) {
25931
26135
  renderProfile(candidate);
25932
- const action = (await session.ask("Is this profile correct? Type Y, edit, or redraft", { default: "Y" })).toLowerCase();
25933
- if (action === "y" || action === "yes" || action === "") break;
26136
+ const action = await session.choose(
26137
+ "Is this profile correct?",
26138
+ [
26139
+ { value: "yes", label: "Yes \u2014 save this profile" },
26140
+ { value: "edit", label: "Edit fields" },
26141
+ { value: "redraft", label: "Redraft from scratch" }
26142
+ ],
26143
+ { default: "yes" }
26144
+ );
26145
+ if (action === "yes") break;
25934
26146
  if (action === "redraft") {
25935
26147
  const fresh = await fillMissingFields(session, { company_url: candidate.company_url }, null);
25936
26148
  candidate = applyDraft(candidate, fresh);
25937
26149
  continue;
25938
26150
  }
25939
- if (action === "edit" || action === "e") {
25940
- candidate = await editLoop(session, candidate);
25941
- continue;
25942
- }
25943
- console.log(" " + chalk25.red(`Unknown choice: ${action}. Type Y, edit, or redraft.`));
26151
+ candidate = await editLoop(session, candidate);
25944
26152
  }
25945
26153
  return candidate;
25946
26154
  }
@@ -26313,7 +26521,7 @@ __export(new_exports, {
26313
26521
  handler: () => handler6
26314
26522
  });
26315
26523
  import chalk26 from "chalk";
26316
- import { existsSync as existsSync27 } from "fs";
26524
+ import { existsSync as existsSync29 } from "fs";
26317
26525
  import { basename as basename8 } from "path";
26318
26526
  async function handler6(args, ctx) {
26319
26527
  const { positional, flags } = parseArgs2(args, ["demo", "empty", "list-scenarios", "regen-taxonomy"]);
@@ -26335,7 +26543,7 @@ async function handler6(args, ctx) {
26335
26543
  console.error(chalk26.red(" Usage: /new <file.csv> | --demo [--scenario <name>] | --empty [--lens health|metrics]"));
26336
26544
  return;
26337
26545
  }
26338
- if (source.kind === "file" && !existsSync27(source.path)) {
26546
+ if (source.kind === "file" && !existsSync29(source.path)) {
26339
26547
  console.error(chalk26.red(` File not found: ${source.path}`));
26340
26548
  return;
26341
26549
  }
@@ -26562,7 +26770,7 @@ __export(end_exports, {
26562
26770
  handler: () => handler7
26563
26771
  });
26564
26772
  import chalk27 from "chalk";
26565
- import { existsSync as existsSync28 } from "fs";
26773
+ import { existsSync as existsSync30 } from "fs";
26566
26774
  async function handler7(args, ctx) {
26567
26775
  if (args.length > 0) {
26568
26776
  console.error(chalk27.red(" Usage: /end"));
@@ -26599,10 +26807,10 @@ async function handler7(args, ctx) {
26599
26807
  if (summary) {
26600
26808
  console.log(" " + chalk27.dim(summary));
26601
26809
  }
26602
- if (existsSync28(transcriptPathForSession(endedId))) {
26810
+ if (existsSync30(transcriptPathForSession(endedId))) {
26603
26811
  console.log(" " + chalk27.dim("Transcript: ") + chalk27.dim(transcriptPathForSession(endedId)));
26604
26812
  }
26605
- if (existsSync28(contextDocPathForSession(endedId))) {
26813
+ if (existsSync30(contextDocPathForSession(endedId))) {
26606
26814
  console.log(" " + chalk27.dim("Context brief: ") + chalk27.dim(contextDocPathForSession(endedId)));
26607
26815
  }
26608
26816
  console.log();
@@ -26623,8 +26831,8 @@ __export(session_exports, {
26623
26831
  handler: () => handler8
26624
26832
  });
26625
26833
  import chalk28 from "chalk";
26626
- import { join as join23 } from "path";
26627
- import { existsSync as existsSync29 } from "fs";
26834
+ import { join as join25 } from "path";
26835
+ import { existsSync as existsSync31 } from "fs";
26628
26836
  async function handler8(args, ctx) {
26629
26837
  const sub = args[0];
26630
26838
  if (!sub) return listSessionsView(ctx);
@@ -26727,7 +26935,7 @@ async function pickUp(idArg, ctx) {
26727
26935
  }
26728
26936
  resetContextForSwitch(ctx, {
26729
26937
  sessionId: target.id,
26730
- sessionFile: join23(getSessionsDir(), `${target.id}.json`),
26938
+ sessionFile: join25(getSessionsDir(), `${target.id}.json`),
26731
26939
  sessionName: session.name,
26732
26940
  messages: [...session.messages],
26733
26941
  conversation: session.thread ? [...session.thread] : [],
@@ -26760,7 +26968,7 @@ async function pickUp(idArg, ctx) {
26760
26968
  if (session.strategist && session.strategist.step !== "awaiting_analysis") {
26761
26969
  const objective = session.strategist.objective;
26762
26970
  console.log(
26763
- " " + chalk28.yellow("Resuming mid-strategy") + (objective ? chalk28.dim(`: "${objective}"`) : "") + chalk28.dim(" \u2014 say ") + chalk28.cyan("yes") + chalk28.dim(" to continue or ") + chalk28.cyan("cancel") + chalk28.dim(" to drop it.")
26971
+ " " + chalk28.yellow("Resuming mid-strategy") + (objective ? chalk28.dim(`: "${objective}"`) : "") + chalk28.dim(" \u2014 Confirm? ") + chalk28.cyan("\u23CE yes") + chalk28.dim(" \xB7 ") + chalk28.cyan("cancel") + chalk28.dim(" to drop it.")
26764
26972
  );
26765
26973
  }
26766
26974
  if (session.think && session.think.step === "active") {
@@ -26770,7 +26978,7 @@ async function pickUp(idArg, ctx) {
26770
26978
  );
26771
26979
  }
26772
26980
  const contextPath = contextDocPathForSession(target.id);
26773
- if (existsSync29(contextPath)) {
26981
+ if (existsSync31(contextPath)) {
26774
26982
  console.log(" " + chalk28.dim("Context brief: ") + chalk28.dim(contextPath));
26775
26983
  }
26776
26984
  console.log();
@@ -27061,8 +27269,8 @@ __export(report_exports, {
27061
27269
  handler: () => handler9
27062
27270
  });
27063
27271
  import chalk29 from "chalk";
27064
- import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
27065
- import { dirname as dirname4 } from "path";
27272
+ import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync16 } from "fs";
27273
+ import { dirname as dirname5 } from "path";
27066
27274
  async function handler9(args, ctx) {
27067
27275
  const { flags } = parseArgs2(args);
27068
27276
  const format = getString(flags, "format", "f") ?? getConfigValue("default-format") ?? "terminal";
@@ -27155,10 +27363,10 @@ async function handler9(args, ctx) {
27155
27363
  if (output) {
27156
27364
  const resolvedOutput = resolveUserPath(output);
27157
27365
  if (!isInsideNtrp(resolvedOutput)) {
27158
- console.warn(chalk29.yellow(` Warning: writing report outside NTRP home (${dirname4(resolvedOutput)})`));
27366
+ console.warn(chalk29.yellow(` Warning: writing report outside NTRP home (${dirname5(resolvedOutput)})`));
27159
27367
  }
27160
- mkdirSync13(dirname4(resolvedOutput), { recursive: true });
27161
- writeFileSync15(resolvedOutput, rendered);
27368
+ mkdirSync14(dirname5(resolvedOutput), { recursive: true });
27369
+ writeFileSync16(resolvedOutput, rendered);
27162
27370
  console.log(chalk29.green(` Report written to ${resolvedOutput}`));
27163
27371
  } else if (rendered) {
27164
27372
  console.log(rendered);
@@ -27189,8 +27397,8 @@ var init_report2 = __esm({
27189
27397
  });
27190
27398
 
27191
27399
  // src/output/notes-export.ts
27192
- import { mkdirSync as mkdirSync14 } from "fs";
27193
- import { join as join24 } from "path";
27400
+ import { mkdirSync as mkdirSync15 } from "fs";
27401
+ import { join as join26 } from "path";
27194
27402
  function exportToNotes(data) {
27195
27403
  const { computeResult, divergences, findings, exchanges } = data;
27196
27404
  const { aggregate, segments } = computeResult;
@@ -27199,8 +27407,8 @@ function exportToNotes(data) {
27199
27407
  const timeStr = formatTime(now2);
27200
27408
  const filename = `${dateStr}-${timeStr}-gtm-health.md`;
27201
27409
  const dir = data.dir ?? getArchiveKindDir("notes");
27202
- mkdirSync14(dir, { recursive: true });
27203
- const filepath = join24(dir, filename);
27410
+ mkdirSync15(dir, { recursive: true });
27411
+ const filepath = join26(dir, filename);
27204
27412
  const severityTags = /* @__PURE__ */ new Set();
27205
27413
  for (const f of findings) severityTags.add(f.severity);
27206
27414
  const tags = ["ntrp", "gtm-health", ...severityTags];
@@ -27445,8 +27653,8 @@ __export(backmeup_exports, {
27445
27653
  });
27446
27654
  import chalk31 from "chalk";
27447
27655
  import Papa5 from "papaparse";
27448
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync16 } from "fs";
27449
- import { join as join25 } from "path";
27656
+ import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync17 } from "fs";
27657
+ import { join as join27 } from "path";
27450
27658
  function sanitizeCsvValue(value) {
27451
27659
  if (typeof value !== "string") return value;
27452
27660
  return CSV_FORMULA_RE.test(value) ? `'${value}` : value;
@@ -27482,8 +27690,8 @@ async function handler11(args, _ctx) {
27482
27690
  if (!isInsideNtrp(baseDir)) {
27483
27691
  console.warn(chalk31.yellow(` Warning: writing backup outside NTRP home (${baseDir})`));
27484
27692
  }
27485
- const folder = join25(baseDir, folderName);
27486
- mkdirSync15(folder, { recursive: true });
27693
+ const folder = join27(baseDir, folderName);
27694
+ mkdirSync16(folder, { recursive: true });
27487
27695
  const generatedAt = now2.toISOString();
27488
27696
  let fileCount = 0;
27489
27697
  const coverRows = health.vital_signs.map((vs) => ({
@@ -27497,7 +27705,7 @@ async function handler11(args, _ctx) {
27497
27705
  "Total At Risk": health.total_value_at_risk != null ? formatCurrency(health.total_value_at_risk) : "N/A",
27498
27706
  "Generated At": generatedAt
27499
27707
  }));
27500
- writeFileSync16(join25(folder, "cover-sheet.csv"), Papa5.unparse(sanitizeCsvRows(coverRows)), "utf-8");
27708
+ writeFileSync17(join27(folder, "cover-sheet.csv"), Papa5.unparse(sanitizeCsvRows(coverRows)), "utf-8");
27501
27709
  fileCount++;
27502
27710
  if (findings.length > 0) {
27503
27711
  const findingsRows = findings.map((f) => ({
@@ -27507,7 +27715,7 @@ async function handler11(args, _ctx) {
27507
27715
  Finding: f.finding,
27508
27716
  "Recommended Plays": f.recommended_plays ? f.recommended_plays.map((p) => p.play_name).join("; ") : ""
27509
27717
  }));
27510
- writeFileSync16(join25(folder, "findings.csv"), Papa5.unparse(sanitizeCsvRows(findingsRows)), "utf-8");
27718
+ writeFileSync17(join27(folder, "findings.csv"), Papa5.unparse(sanitizeCsvRows(findingsRows)), "utf-8");
27511
27719
  fileCount++;
27512
27720
  }
27513
27721
  for (const vs of health.vital_signs) {
@@ -27517,7 +27725,7 @@ async function handler11(args, _ctx) {
27517
27725
  ...detail
27518
27726
  }));
27519
27727
  const filename = EVIDENCE_FILENAMES[vs.vital_sign] ?? `${vs.vital_sign}.csv`;
27520
- writeFileSync16(join25(folder, filename), Papa5.unparse(sanitizeCsvRows(rows)), "utf-8");
27728
+ writeFileSync17(join27(folder, filename), Papa5.unparse(sanitizeCsvRows(rows)), "utf-8");
27521
27729
  fileCount++;
27522
27730
  }
27523
27731
  const event = recordExportWrite({
@@ -27721,8 +27929,8 @@ var init_bundle = __esm({
27721
27929
  });
27722
27930
 
27723
27931
  // src/repositories/markdown.ts
27724
- import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync17 } from "fs";
27725
- import { basename as basename9, dirname as dirname5, join as join26, resolve as resolve8 } from "path";
27932
+ import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync18 } from "fs";
27933
+ import { basename as basename9, dirname as dirname6, join as join28, resolve as resolve8 } from "path";
27726
27934
  import { stringify as stringifyYaml2 } from "yaml";
27727
27935
  function renderMarkdownFiles(pkg) {
27728
27936
  const bundleJson = JSON.stringify(pkg, null, 2) + "\n";
@@ -27942,12 +28150,12 @@ var init_markdown3 = __esm({
27942
28150
  write(pkg) {
27943
28151
  const root = getRootPath(pkg.target);
27944
28152
  const files = renderMarkdownFiles(pkg);
27945
- mkdirSync16(root, { recursive: true });
28153
+ mkdirSync17(root, { recursive: true });
27946
28154
  const written = [];
27947
28155
  for (const file of files) {
27948
- const absolutePath = join26(root, file.relativePath);
27949
- mkdirSync16(dirname5(absolutePath), { recursive: true });
27950
- writeFileSync17(absolutePath, file.contents, "utf-8");
28156
+ const absolutePath = join28(root, file.relativePath);
28157
+ mkdirSync17(dirname6(absolutePath), { recursive: true });
28158
+ writeFileSync18(absolutePath, file.contents, "utf-8");
27951
28159
  written.push(absolutePath);
27952
28160
  }
27953
28161
  return {
@@ -28251,7 +28459,7 @@ __export(handoff_exports, {
28251
28459
  handler: () => handler13
28252
28460
  });
28253
28461
  import chalk33 from "chalk";
28254
- import { join as join27 } from "path";
28462
+ import { join as join29 } from "path";
28255
28463
  async function handler13(args, ctx) {
28256
28464
  const sub = args[0];
28257
28465
  if (!sub) {
@@ -28351,7 +28559,7 @@ async function runPublish2(args, ctx) {
28351
28559
  );
28352
28560
  if (sub === "propose" && !hasDir) {
28353
28561
  const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
28354
- const dir = join27(getArchiveKindDir("publish"), `ntrp-repository-${stamp}`);
28562
+ const dir = join29(getArchiveKindDir("publish"), `ntrp-repository-${stamp}`);
28355
28563
  publishArgs.push("--dir", dir);
28356
28564
  }
28357
28565
  const result = await publish(publishArgs, ctx);
@@ -30449,7 +30657,7 @@ async function handleStrategizeFlow(input, ctx) {
30449
30657
  " " + chalk37.dim("That looks like a question. A strategy objective is waiting.")
30450
30658
  );
30451
30659
  console.log(
30452
- " " + chalk37.dim("Type ") + chalk37.cyan("yes") + chalk37.dim(" to make the plan. Type ") + chalk37.cyan("b") + chalk37.dim(" / ") + chalk37.cyan("adjust") + chalk37.dim(" to change the objective. Type ") + chalk37.cyan("cancel") + chalk37.dim(" to answer questions first.")
30660
+ " " + chalk37.dim("Confirm? ") + chalk37.cyan("\u23CE yes") + chalk37.dim(" \xB7 ") + chalk37.cyan("b back") + chalk37.dim(" / ") + chalk37.cyan("adjust") + chalk37.dim(" \xB7 ") + chalk37.cyan("cancel") + chalk37.dim(" to answer questions first.")
30453
30661
  );
30454
30662
  console.log();
30455
30663
  return "Awaiting confirm";
@@ -30462,7 +30670,7 @@ async function handleStrategizeFlow(input, ctx) {
30462
30670
  }
30463
30671
  console.log();
30464
30672
  console.log(
30465
- " " + chalk37.dim("Type ") + chalk37.cyan("yes") + chalk37.dim(" to make the plan. Type ") + chalk37.cyan("b") + chalk37.dim(" / ") + chalk37.cyan("adjust") + chalk37.dim(" to change the objective. Type ") + chalk37.cyan("cancel") + chalk37.dim(" to stop.")
30673
+ " " + chalk37.dim("Confirm? ") + chalk37.cyan("\u23CE yes") + chalk37.dim(" \xB7 ") + chalk37.cyan("b back") + chalk37.dim(" / ") + chalk37.cyan("adjust") + chalk37.dim(" \xB7 ") + chalk37.cyan("cancel")
30466
30674
  );
30467
30675
  console.log();
30468
30676
  return "Awaiting confirm";
@@ -30737,8 +30945,8 @@ __export(strategy_review_exports, {
30737
30945
  persistStrategyReview: () => persistStrategyReview,
30738
30946
  resolveReviewableStrategy: () => resolveReviewableStrategy
30739
30947
  });
30740
- import { writeFileSync as writeFileSync18 } from "fs";
30741
- import { join as join28 } from "path";
30948
+ import { writeFileSync as writeFileSync19 } from "fs";
30949
+ import { join as join30 } from "path";
30742
30950
  function groupByBatch(vitals, metrics) {
30743
30951
  const map = /* @__PURE__ */ new Map();
30744
30952
  const order = [];
@@ -31028,7 +31236,7 @@ async function persistStrategyReview(report, milestoneVerdicts, notes) {
31028
31236
  }
31029
31237
  function logWin(strategy, hits, milestoneWins) {
31030
31238
  const date = isoToday();
31031
- const path = join28(getWinsDir(), `${strategy.slug}-${date}.md`);
31239
+ const path = join30(getWinsDir(), `${strategy.slug}-${date}.md`);
31032
31240
  const lines = [
31033
31241
  `# Win \u2014 ${strategy.title}`,
31034
31242
  "",
@@ -31045,7 +31253,7 @@ function logWin(strategy, hits, milestoneWins) {
31045
31253
  for (const label of milestoneWins) lines.push(`- ${label}`);
31046
31254
  }
31047
31255
  lines.push("", "_Logged by /strategy review._", "");
31048
- writeFileSync18(path, lines.join("\n"));
31256
+ writeFileSync19(path, lines.join("\n"));
31049
31257
  return path;
31050
31258
  }
31051
31259
  var VITAL_TOKENS, COMPONENT_TOKENS;
@@ -32442,15 +32650,15 @@ var init_checkout = __esm({
32442
32650
  });
32443
32651
 
32444
32652
  // src/services/setup.ts
32445
- import { existsSync as existsSync30, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "fs";
32446
- import { join as join29 } from "path";
32653
+ import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "fs";
32654
+ import { join as join31 } from "path";
32447
32655
  function setupCheck() {
32448
32656
  const home = ntrpHome();
32449
32657
  let writable = false;
32450
32658
  try {
32451
- mkdirSync17(home, { recursive: true });
32452
- const probe = join29(home, ".write-check");
32453
- writeFileSync19(probe, "ok\n");
32659
+ mkdirSync18(home, { recursive: true });
32660
+ const probe = join31(home, ".write-check");
32661
+ writeFileSync20(probe, "ok\n");
32454
32662
  writable = true;
32455
32663
  } catch {
32456
32664
  writable = false;
@@ -32489,7 +32697,7 @@ function setupCheck() {
32489
32697
  };
32490
32698
  }
32491
32699
  function readProfileInput(pathOrDash) {
32492
- const raw = pathOrDash === "-" ? readFileSync20(0, "utf-8") : readFileSync20(pathOrDash, "utf-8");
32700
+ const raw = pathOrDash === "-" ? readFileSync22(0, "utf-8") : readFileSync22(pathOrDash, "utf-8");
32493
32701
  return JSON.parse(raw);
32494
32702
  }
32495
32703
  function writeAgentProfile(input) {
@@ -33279,7 +33487,7 @@ var init_orchestrator = __esm({
33279
33487
  });
33280
33488
 
33281
33489
  // src/services/smoke-protocol.ts
33282
- import { join as join30 } from "path";
33490
+ import { join as join32 } from "path";
33283
33491
  function isSmokeProtocolTrigger(input) {
33284
33492
  return normalize2(input).includes(SMOKE_TRIGGER_PHRASE);
33285
33493
  }
@@ -33315,7 +33523,7 @@ async function runSmokeProtocol(_input, ctx) {
33315
33523
  });
33316
33524
  const proposalResult = await proposeRepositoryExport({
33317
33525
  target: "markdown",
33318
- directory: join30(getExportsDir(), "repository-smoke"),
33526
+ directory: join32(getExportsDir(), "repository-smoke"),
33319
33527
  source: "smoke_protocol",
33320
33528
  modelOrFixture: "smoke-protocol-v1"
33321
33529
  });
@@ -34247,7 +34455,7 @@ var init_recall = __esm({
34247
34455
 
34248
34456
  // src/memory/feedback.ts
34249
34457
  import { appendFileSync as appendFileSync7 } from "fs";
34250
- import { join as join31 } from "path";
34458
+ import { join as join33 } from "path";
34251
34459
  import { randomUUID as randomUUID8 } from "crypto";
34252
34460
  function summarize(text) {
34253
34461
  return text.replace(/[#*`>_]/g, "").replace(/\s+/g, " ").trim().slice(0, 200);
@@ -34279,7 +34487,7 @@ function recordFeedback(input) {
34279
34487
  created_at: (/* @__PURE__ */ new Date()).toISOString()
34280
34488
  };
34281
34489
  try {
34282
- appendFileSync7(join31(getMemoryDir(), "feedback.jsonl"), JSON.stringify(entry) + "\n");
34490
+ appendFileSync7(join33(getMemoryDir(), "feedback.jsonl"), JSON.stringify(entry) + "\n");
34283
34491
  } catch {
34284
34492
  }
34285
34493
  if (input.rating === "positive") {
@@ -34449,7 +34657,7 @@ __export(sessions_exports, {
34449
34657
  handler: () => handler35
34450
34658
  });
34451
34659
  import chalk62 from "chalk";
34452
- import { existsSync as existsSync31 } from "fs";
34660
+ import { existsSync as existsSync33 } from "fs";
34453
34661
  async function handler35(args, _ctx) {
34454
34662
  const sub = args[0] ?? "list";
34455
34663
  if (sub === "list" || !args[0]) {
@@ -34556,12 +34764,12 @@ function showSession(idArg) {
34556
34764
  console.log();
34557
34765
  const transcriptPath = transcriptPathForSession(session.id);
34558
34766
  const contextPath = contextDocPathForSession(session.id);
34559
- if (existsSync31(transcriptPath) || existsSync31(contextPath)) {
34767
+ if (existsSync33(transcriptPath) || existsSync33(contextPath)) {
34560
34768
  console.log(" " + chalk62.dim("\u2500".repeat(40)));
34561
- if (existsSync31(contextPath)) {
34769
+ if (existsSync33(contextPath)) {
34562
34770
  console.log(" " + chalk62.dim("Context brief: ") + chalk62.dim(contextPath));
34563
34771
  }
34564
- if (existsSync31(transcriptPath)) {
34772
+ if (existsSync33(transcriptPath)) {
34565
34773
  console.log(" " + chalk62.dim("Full transcript: ") + chalk62.dim(transcriptPath));
34566
34774
  }
34567
34775
  console.log();
@@ -34714,7 +34922,7 @@ var switch_exports = {};
34714
34922
  __export(switch_exports, {
34715
34923
  handler: () => handler38
34716
34924
  });
34717
- import { join as join32 } from "path";
34925
+ import { join as join34 } from "path";
34718
34926
  import chalk65 from "chalk";
34719
34927
  async function handler38(args, ctx) {
34720
34928
  if (args.length === 0) {
@@ -34744,7 +34952,7 @@ async function handler38(args, ctx) {
34744
34952
  }
34745
34953
  const context = buildSwitchContext(session);
34746
34954
  const newId = makeSessionId();
34747
- const newFile = join32(getSessionsDir(), `${newId}.json`);
34955
+ const newFile = join34(getSessionsDir(), `${newId}.json`);
34748
34956
  resetContextForSwitch(ctx, {
34749
34957
  sessionId: newId,
34750
34958
  sessionFile: newFile,
@@ -34770,7 +34978,7 @@ async function handler38(args, ctx) {
34770
34978
  return `Switched to "${targetName}"`;
34771
34979
  } else {
34772
34980
  const newId = makeSessionId();
34773
- const newFile = join32(getSessionsDir(), `${newId}.json`);
34981
+ const newFile = join34(getSessionsDir(), `${newId}.json`);
34774
34982
  resetContextForSwitch(ctx, {
34775
34983
  sessionId: newId,
34776
34984
  sessionFile: newFile,
@@ -35897,186 +36105,6 @@ var init_model = __esm({
35897
36105
  }
35898
36106
  });
35899
36107
 
35900
- // src/config/update-check.ts
35901
- import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
35902
- import { join as join33 } from "path";
35903
- function cachePath2() {
35904
- return join33(ntrpHome(), "update-check.json");
35905
- }
35906
- function ensureDir7() {
35907
- const dir = ntrpHome();
35908
- if (!existsSync32(dir)) {
35909
- mkdirSync18(dir, { recursive: true });
35910
- }
35911
- }
35912
- function loadUpdateCheckCache() {
35913
- const path = cachePath2();
35914
- if (!existsSync32(path)) return null;
35915
- try {
35916
- const parsed = JSON.parse(readFileSync21(path, "utf-8"));
35917
- if (!parsed || typeof parsed !== "object" || typeof parsed.lastCheck !== "number" || typeof parsed.latestVersion !== "string") {
35918
- return null;
35919
- }
35920
- return parsed;
35921
- } catch {
35922
- return null;
35923
- }
35924
- }
35925
- function saveUpdateCheckCache(cache2) {
35926
- ensureDir7();
35927
- writeFileSync20(cachePath2(), JSON.stringify(cache2, null, 2) + "\n");
35928
- }
35929
- function isCacheFresh(cache2, ttlMs = CACHE_TTL_MS2) {
35930
- if (!cache2) return false;
35931
- return Date.now() - cache2.lastCheck < ttlMs;
35932
- }
35933
- function invalidateUpdateCheckCache() {
35934
- const path = cachePath2();
35935
- if (existsSync32(path)) {
35936
- unlinkSync5(path);
35937
- }
35938
- }
35939
- var CACHE_TTL_MS2;
35940
- var init_update_check = __esm({
35941
- "src/config/update-check.ts"() {
35942
- "use strict";
35943
- init_store();
35944
- CACHE_TTL_MS2 = 864e5;
35945
- }
35946
- });
35947
-
35948
- // src/version.ts
35949
- import { existsSync as existsSync33, readFileSync as readFileSync22 } from "fs";
35950
- import { dirname as dirname6, join as join34 } from "path";
35951
- import { fileURLToPath } from "url";
35952
- function readVersionFromPackageJson(packageJsonPath) {
35953
- if (!existsSync33(packageJsonPath)) return null;
35954
- try {
35955
- const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
35956
- if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
35957
- } catch {
35958
- }
35959
- return null;
35960
- }
35961
- function readVersionNearEntry(entryPath) {
35962
- const start = dirname6(entryPath);
35963
- for (const rel of [join34(start, "..", "package.json"), join34(start, "../..", "package.json")]) {
35964
- const version = readVersionFromPackageJson(rel);
35965
- if (version) return version;
35966
- }
35967
- return null;
35968
- }
35969
- function readInstalledVersionFromDisk() {
35970
- return readVersionNearEntry(fileURLToPath(import.meta.url));
35971
- }
35972
- function getInstalledVersion() {
35973
- if (cachedVersion) return cachedVersion;
35974
- cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
35975
- return cachedVersion;
35976
- }
35977
- var cachedVersion;
35978
- var init_version = __esm({
35979
- "src/version.ts"() {
35980
- "use strict";
35981
- }
35982
- });
35983
-
35984
- // src/update/registry.ts
35985
- var registry_exports = {};
35986
- __export(registry_exports, {
35987
- NPM_PACKAGE: () => NPM_PACKAGE,
35988
- applyUpdateCheckResult: () => applyUpdateCheckResult,
35989
- checkForUpdate: () => checkForUpdate,
35990
- fetchLatestVersion: () => fetchLatestVersion,
35991
- formatUpdateNudge: () => formatUpdateNudge,
35992
- hydrateUpdateAvailableFromCache: () => hydrateUpdateAvailableFromCache,
35993
- isNewerVersion: () => isNewerVersion,
35994
- startBackgroundUpdateCheck: () => startBackgroundUpdateCheck
35995
- });
35996
- function registryUrl() {
35997
- return process.env.NTRP_REGISTRY_URL ?? "https://registry.npmjs.org/@sonnechasser/ntrp/latest";
35998
- }
35999
- function parseVersionParts(version) {
36000
- const cleaned = version.trim().replace(/^v/i, "");
36001
- const core = cleaned.split("-")[0] ?? cleaned;
36002
- const parts = core.split(".").map((p) => parseInt(p, 10));
36003
- return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
36004
- }
36005
- function isNewerVersion(latest, current) {
36006
- const [lMaj, lMin, lPatch] = parseVersionParts(latest);
36007
- const [cMaj, cMin, cPatch] = parseVersionParts(current);
36008
- if (lMaj !== cMaj) return lMaj > cMaj;
36009
- if (lMin !== cMin) return lMin > cMin;
36010
- return lPatch > cPatch;
36011
- }
36012
- function formatUpdateNudge(current, latest) {
36013
- return `\u26A1 NTRP v${latest} available (running v${current}) \u2014 type /update`;
36014
- }
36015
- function hydrateUpdateAvailableFromCache(current) {
36016
- const cached2 = loadUpdateCheckCache();
36017
- if (!cached2?.latestVersion) return void 0;
36018
- if (!isNewerVersion(cached2.latestVersion, current)) return void 0;
36019
- return { current, latest: cached2.latestVersion };
36020
- }
36021
- function applyUpdateCheckResult(ctx, result) {
36022
- if (result?.updateAvailable) {
36023
- ctx.updateAvailable = { current: result.current, latest: result.latest };
36024
- return;
36025
- }
36026
- if (result && !result.updateAvailable) {
36027
- ctx.updateAvailable = void 0;
36028
- }
36029
- }
36030
- function startBackgroundUpdateCheck(ctx) {
36031
- const pending = checkForUpdate({ force: true, timeoutMs: 5e3 });
36032
- ctx.pendingUpdateCheck = pending;
36033
- void pending.then((result) => applyUpdateCheckResult(ctx, result)).catch(() => void 0);
36034
- }
36035
- async function fetchLatestVersion(timeoutMs = 5e3) {
36036
- try {
36037
- const res = await fetch(registryUrl(), { signal: AbortSignal.timeout(timeoutMs) });
36038
- if (!res.ok) return null;
36039
- const data = await res.json();
36040
- return typeof data.version === "string" && data.version.length > 0 ? data.version : null;
36041
- } catch {
36042
- return null;
36043
- }
36044
- }
36045
- function buildResult(current, latest) {
36046
- return {
36047
- current,
36048
- latest,
36049
- updateAvailable: isNewerVersion(latest, current)
36050
- };
36051
- }
36052
- async function checkForUpdate(options) {
36053
- const current = getInstalledVersion();
36054
- const timeoutMs = options?.timeoutMs ?? 5e3;
36055
- const cached2 = loadUpdateCheckCache();
36056
- if (!options?.force && isCacheFresh(cached2)) {
36057
- return buildResult(current, cached2.latestVersion);
36058
- }
36059
- const latest = await fetchLatestVersion(timeoutMs);
36060
- if (!latest) {
36061
- if (cached2?.latestVersion) {
36062
- return buildResult(current, cached2.latestVersion);
36063
- }
36064
- return null;
36065
- }
36066
- const nextCache = { lastCheck: Date.now(), latestVersion: latest };
36067
- saveUpdateCheckCache(nextCache);
36068
- return buildResult(current, latest);
36069
- }
36070
- var NPM_PACKAGE;
36071
- var init_registry = __esm({
36072
- "src/update/registry.ts"() {
36073
- "use strict";
36074
- init_update_check();
36075
- init_version();
36076
- NPM_PACKAGE = "@sonnechasser/ntrp";
36077
- }
36078
- });
36079
-
36080
36108
  // src/update/relaunch.ts
36081
36109
  var relaunch_exports = {};
36082
36110
  __export(relaunch_exports, {
@@ -36223,6 +36251,22 @@ async function handler44(_args, ctx) {
36223
36251
  console.log();
36224
36252
  return;
36225
36253
  }
36254
+ if (!ctx.oneShot) {
36255
+ const prompts = createPromptSession(ctx.rl, ctx);
36256
+ let ok2 = false;
36257
+ try {
36258
+ console.log();
36259
+ ok2 = await prompts.confirm(`Install NTRP v${current} \u2192 v${latest}?`, true);
36260
+ } finally {
36261
+ prompts.close();
36262
+ }
36263
+ if (!ok2) {
36264
+ console.log();
36265
+ console.log(chalk73.dim(" Update cancelled."));
36266
+ console.log();
36267
+ return "Update cancelled";
36268
+ }
36269
+ }
36226
36270
  console.log();
36227
36271
  console.log(` Updating NTRP v${current} \u2192 v${latest}...`);
36228
36272
  const { ok, output } = runGlobalInstall();
@@ -36269,6 +36313,7 @@ var PERMISSIONS_URL;
36269
36313
  var init_update = __esm({
36270
36314
  "src/commands/update.ts"() {
36271
36315
  "use strict";
36316
+ init_prompts();
36272
36317
  init_update_check();
36273
36318
  init_registry();
36274
36319
  init_relaunch();
@@ -37228,7 +37273,9 @@ section: Settings
37228
37273
  handler: ../commands/update.ts
37229
37274
  ---
37230
37275
 
37231
- Install the latest global NTRP package via npm.`
37276
+ Install the latest global NTRP package via npm.
37277
+ Interactive confirms with \u23CE yes, then re-execs onto home.
37278
+ One-shot installs immediately and prints a restart hint.`
37232
37279
  },
37233
37280
  {
37234
37281
  name: "resume",
@@ -37582,7 +37629,7 @@ hidden: true
37582
37629
  ---
37583
37630
 
37584
37631
  Mark every in-progress session as ended. Transcripts and dataset files stay.
37585
- Interactive ntrp only. Confirm with y/N, or pass \`--confirm\` in one-shot.`
37632
+ Interactive ntrp only. Confirm with \u23CE no (type yes to proceed), or pass \`--confirm\` in one-shot.`
37586
37633
  },
37587
37634
  {
37588
37635
  name: "deactivate-demo",
@@ -39456,9 +39503,37 @@ async function conversationRouter(input, ctx) {
39456
39503
  const line = input.trim();
39457
39504
  if (!line) return { handled: true };
39458
39505
  if (FRESH_START_RE.test(line)) {
39506
+ if (ctx.rl) {
39507
+ const { createPromptSession: createPromptSession2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
39508
+ const prompts = createPromptSession2(ctx.rl, ctx);
39509
+ let ok = false;
39510
+ try {
39511
+ ok = await prompts.confirm(
39512
+ "Start a fresh analysis? Prior sessions stay saved.",
39513
+ true
39514
+ );
39515
+ } finally {
39516
+ prompts.close();
39517
+ }
39518
+ if (!ok) {
39519
+ console.log();
39520
+ console.log(
39521
+ " " + chalk84.dim("Staying on this session. Type ") + chalk84.cyan("/home") + chalk84.dim(" for the dashboard.")
39522
+ );
39523
+ console.log();
39524
+ return { handled: true };
39525
+ }
39526
+ recordMessage(ctx, "user", line);
39527
+ const { handler: handler51 } = await Promise.resolve().then(() => (init_new(), new_exports));
39528
+ const summary = await handler51([], ctx);
39529
+ return {
39530
+ handled: true,
39531
+ summary: typeof summary === "string" ? summary : "New analysis"
39532
+ };
39533
+ }
39459
39534
  console.log();
39460
39535
  console.log(
39461
- " " + chalk84.dim("Start a fresh analysis? This keeps prior sessions \u2014 say ") + chalk84.cyan("yes") + chalk84.dim(" to confirm or ") + chalk84.cyan("/home") + chalk84.dim(" for the dashboard.")
39536
+ " " + chalk84.dim("Start a fresh analysis with ") + chalk84.cyan("/new") + chalk84.dim(" \u2014 or ") + chalk84.cyan("/home") + chalk84.dim(" for the dashboard.")
39462
39537
  );
39463
39538
  console.log();
39464
39539
  return { handled: true };
@@ -39870,7 +39945,7 @@ function ntrpStatusRow(version, update) {
39870
39945
  return {
39871
39946
  label: "ntrp",
39872
39947
  state: badge("UPDATE", "warning"),
39873
- detail: `v${update.latest} \xB7 type /update`
39948
+ detail: `v${update.latest} \xB7 \u23CE /update`
39874
39949
  };
39875
39950
  }
39876
39951
  if (disk && isNewerVersion(disk, getInstalledVersion())) {
@@ -40750,7 +40825,14 @@ function printHelp() {
40750
40825
  console.log();
40751
40826
  console.log(" " + sectionHeading("Keys"));
40752
40827
  console.log(
40753
- " " + paint("accent", "\u23CE") + chalk88.dim(" Runs the armed action shown at the prompt (yes, use demo data, go ahead, /connect).")
40828
+ " " + paint("accent", "\u23CE") + chalk88.dim(
40829
+ " Accepts the default. At the main prompt it runs the armed action (yes, use demo data, go ahead, /connect, /update)."
40830
+ )
40831
+ );
40832
+ console.log(
40833
+ " " + chalk88.dim(
40834
+ " On yes/no questions and menus it accepts yes (or the recommended option). Type something else to decline or pick another path."
40835
+ )
40754
40836
  );
40755
40837
  console.log(
40756
40838
  " " + paint("accent", "b") + chalk88.dim(" / ") + paint("accent", "back") + chalk88.dim(" Steps back one confirm gate, or leaves a modal. Does not unload data or undo compute.")