@cortexkit/aft-opencode 0.27.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7803,7 +7803,7 @@ var require_src2 = __commonJS((exports, module) => {
7803
7803
  });
7804
7804
 
7805
7805
  // src/index.ts
7806
- import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
7806
+ import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
7807
7807
  import { createRequire as createRequire2 } from "module";
7808
7808
  import { join as join22 } from "path";
7809
7809
 
@@ -8597,13 +8597,14 @@ import { pipeline } from "stream/promises";
8597
8597
  var PLATFORM_ARCH_MAP = {
8598
8598
  darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
8599
8599
  linux: { arm64: "linux-arm64", x64: "linux-x64" },
8600
- win32: { arm64: "win32-x64", x64: "win32-x64" }
8600
+ win32: { arm64: "win32-arm64", x64: "win32-x64" }
8601
8601
  };
8602
8602
  var PLATFORM_ASSET_MAP = {
8603
8603
  "darwin-arm64": "aft-darwin-arm64",
8604
8604
  "darwin-x64": "aft-darwin-x64",
8605
8605
  "linux-arm64": "aft-linux-arm64",
8606
8606
  "linux-x64": "aft-linux-x64",
8607
+ "win32-arm64": "aft-win32-arm64.exe",
8607
8608
  "win32-x64": "aft-win32-x64.exe"
8608
8609
  };
8609
8610
 
@@ -10184,6 +10185,9 @@ function formatZoomText(targetLabel, response) {
10184
10185
  return out.join(`
10185
10186
  `);
10186
10187
  }
10188
+ // src/bg-notifications.ts
10189
+ import { createHash as createHash4, randomUUID } from "crypto";
10190
+
10187
10191
  // src/logger.ts
10188
10192
  import * as fs from "fs";
10189
10193
  import * as os from "os";
@@ -10360,6 +10364,9 @@ async function resolvePromptContext(client, sessionId) {
10360
10364
  }
10361
10365
 
10362
10366
  // src/bg-notifications.ts
10367
+ function hashReminder(text) {
10368
+ return createHash4("sha256").update(text).digest("hex").slice(0, 16);
10369
+ }
10363
10370
  var sessionBgStates = new Map;
10364
10371
  var SESSION_BG_STATE_IDLE_TTL_MS = 60 * 60 * 1000;
10365
10372
  var DEBOUNCE_STEP_MS = 200;
@@ -10430,6 +10437,13 @@ async function appendInTurnBgCompletions(drainContext, output) {
10430
10437
  const deliveredCompletions = [...state.pendingCompletions];
10431
10438
  const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning);
10432
10439
  output.output = appendReminder(output.output ?? "", reminder);
10440
+ sessionLog(drainContext.sessionID, `${LOG_PREFIX} in-turn append`, {
10441
+ event: "bash_completion_in_turn_append",
10442
+ task_ids: deliveredCompletions.map((c) => c.task_id),
10443
+ long_running_task_ids: state.pendingLongRunning.map((r) => r.task_id),
10444
+ reminder_sha256: hashReminder(reminder),
10445
+ reminder_chars: reminder.length
10446
+ });
10433
10447
  state.pendingCompletions = [];
10434
10448
  state.pendingLongRunning = [];
10435
10449
  state.wakeRetryAttempts = 0;
@@ -10473,14 +10487,53 @@ async function triggerWakeIfPending(drainContext, skipDrain) {
10473
10487
  }
10474
10488
  if (promptContext?.variant)
10475
10489
  body.variant = promptContext.variant;
10476
- await client.session.promptAsync({
10477
- path: { id: drainContext.sessionID },
10478
- body
10490
+ const deliveryID = `aftdel_${randomUUID()}`;
10491
+ const taskIDs = deliveredCompletions.map((c) => c.task_id);
10492
+ const wakeMeta = {
10493
+ delivery_id: deliveryID,
10494
+ attempt: state.wakeRetryAttempts + 1,
10495
+ task_ids: taskIDs,
10496
+ directory: drainContext.directory,
10497
+ reminder_sha256: hashReminder(reminder),
10498
+ reminder_chars: reminder.length,
10499
+ prompt_context: promptContext ? {
10500
+ agent: promptContext.agent,
10501
+ model: promptContext.model ? {
10502
+ providerID: promptContext.model.providerID,
10503
+ modelID: promptContext.model.modelID
10504
+ } : null,
10505
+ variant: promptContext.variant ?? null
10506
+ } : null
10507
+ };
10508
+ sessionLog(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync start`, {
10509
+ event: "bash_completion_wake_prompt_async_start",
10510
+ ...wakeMeta
10479
10511
  });
10480
- await ackCompletions(drainContext, deliveredCompletions);
10512
+ try {
10513
+ await client.session.promptAsync({
10514
+ path: { id: drainContext.sessionID },
10515
+ body
10516
+ });
10517
+ } catch (err) {
10518
+ sessionWarn(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync error`, {
10519
+ event: "bash_completion_wake_prompt_async_error",
10520
+ delivery_id: deliveryID,
10521
+ attempt: state.wakeRetryAttempts + 1,
10522
+ task_ids: taskIDs,
10523
+ error: err instanceof Error ? err.message : String(err)
10524
+ });
10525
+ throw err;
10526
+ }
10527
+ sessionLog(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync ok`, {
10528
+ event: "bash_completion_wake_prompt_async_ok",
10529
+ delivery_id: deliveryID,
10530
+ attempt: state.wakeRetryAttempts + 1,
10531
+ task_ids: taskIDs
10532
+ });
10533
+ await ackCompletions(drainContext, deliveredCompletions, deliveryID);
10481
10534
  }, (err, hardStopped) => {
10482
10535
  sessionWarn(drainContext.sessionID, hardStopped ? `${LOG_PREFIX} wake send failed ${MAX_WAKE_SEND_ATTEMPTS} times; stopping retries: ${err instanceof Error ? err.message : String(err)}` : `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
10483
- });
10536
+ }, drainContext.sessionID);
10484
10537
  }
10485
10538
  function formatSystemReminder(completions) {
10486
10539
  const bullets = completions.map((completion) => formatCompletion(completion)).join(`
@@ -10544,7 +10597,7 @@ async function drainCompletions({ ctx, directory, sessionID }) {
10544
10597
  sessionWarn(sessionID, `${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
10545
10598
  }
10546
10599
  }
10547
- async function ackCompletions({ ctx, directory, sessionID }, completions) {
10600
+ async function ackCompletions({ ctx, directory, sessionID }, completions, deliveryID) {
10548
10601
  const taskIds = [...new Set(completions.map((completion) => completion.task_id))];
10549
10602
  if (taskIds.length === 0)
10550
10603
  return;
@@ -10556,12 +10609,18 @@ async function ackCompletions({ ctx, directory, sessionID }, completions) {
10556
10609
  });
10557
10610
  if (response.success === false) {
10558
10611
  sessionWarn(sessionID, `${LOG_PREFIX} ack failed: ${String(response.message ?? "unknown error")}`);
10612
+ return;
10559
10613
  }
10614
+ sessionLog(sessionID, `${LOG_PREFIX} ack ok`, {
10615
+ event: "bash_completion_ack_ok",
10616
+ delivery_id: deliveryID ?? null,
10617
+ task_ids: taskIds
10618
+ });
10560
10619
  } catch (err) {
10561
10620
  sessionWarn(sessionID, `${LOG_PREFIX} ack failed: ${err instanceof Error ? err.message : String(err)}`);
10562
10621
  }
10563
10622
  }
10564
- function scheduleWake(state, sendWake, onSendFailure) {
10623
+ function scheduleWake(state, sendWake, onSendFailure, sessionID) {
10565
10624
  if (state.wakeHardStopped)
10566
10625
  return;
10567
10626
  const now = Date.now();
@@ -10580,6 +10639,13 @@ function scheduleWake(state, sendWake, onSendFailure) {
10580
10639
  if (state.debounceTimer)
10581
10640
  clearTimeout(state.debounceTimer);
10582
10641
  const delay = state.retryDelayMs ?? Math.max(0, (state.scheduledFireAt ?? now) - now);
10642
+ sessionLog(sessionID, `${LOG_PREFIX} wake scheduled`, {
10643
+ event: "bash_completion_wake_scheduled",
10644
+ delay_ms: delay,
10645
+ pending_completions: state.pendingCompletions.length,
10646
+ pending_long_running: state.pendingLongRunning.length,
10647
+ retry_attempt: state.wakeRetryAttempts
10648
+ });
10583
10649
  state.debounceTimer = setTimeout(() => {
10584
10650
  const pending = state.pendingCompletions;
10585
10651
  const pendingLongRunning = state.pendingLongRunning;
@@ -10590,6 +10656,14 @@ function scheduleWake(state, sendWake, onSendFailure) {
10590
10656
  if (pending.length === 0 && pendingLongRunning.length === 0)
10591
10657
  return;
10592
10658
  const reminder = formatCombinedSystemReminder(pending, pendingLongRunning);
10659
+ sessionLog(sessionID, `${LOG_PREFIX} wake fire`, {
10660
+ event: "bash_completion_wake_fire",
10661
+ task_ids: pending.map((c) => c.task_id),
10662
+ long_running_task_ids: pendingLongRunning.map((r) => r.task_id),
10663
+ reminder_sha256: hashReminder(reminder),
10664
+ reminder_chars: reminder.length,
10665
+ retry_attempt: state.wakeRetryAttempts
10666
+ });
10593
10667
  state.pendingCompletions = [];
10594
10668
  state.pendingLongRunning = [];
10595
10669
  sendWake(reminder, pending).then(() => {
@@ -10608,7 +10682,7 @@ function scheduleWake(state, sendWake, onSendFailure) {
10608
10682
  }
10609
10683
  state.retryDelayMs = Math.min((delay || DEBOUNCE_STEP_MS) * 2, DEBOUNCE_CAP_MS);
10610
10684
  onSendFailure(err, false);
10611
- scheduleWake(state, sendWake, onSendFailure);
10685
+ scheduleWake(state, sendWake, onSendFailure, sessionID);
10612
10686
  });
10613
10687
  }, delay);
10614
10688
  state.debounceTimer.unref?.();
@@ -24346,6 +24420,14 @@ var ExperimentalConfigSchema = exports_external.object({
24346
24420
  }).optional(),
24347
24421
  lsp_ty: exports_external.boolean().optional()
24348
24422
  });
24423
+ var BashFeaturesSchema = exports_external.object({
24424
+ rewrite: exports_external.boolean().optional(),
24425
+ compress: exports_external.boolean().optional(),
24426
+ background: exports_external.boolean().optional(),
24427
+ long_running_reminder_enabled: exports_external.boolean().optional(),
24428
+ long_running_reminder_interval_ms: exports_external.number().int().positive().optional()
24429
+ });
24430
+ var BashConfigSchema = exports_external.union([exports_external.boolean(), BashFeaturesSchema]);
24349
24431
  var AftConfigSchema = exports_external.object({
24350
24432
  $schema: exports_external.string().optional(),
24351
24433
  format_on_edit: exports_external.boolean().optional(),
@@ -24359,6 +24441,7 @@ var AftConfigSchema = exports_external.object({
24359
24441
  restrict_to_project_root: exports_external.boolean().optional(),
24360
24442
  search_index: exports_external.boolean().optional(),
24361
24443
  semantic_search: exports_external.boolean().optional(),
24444
+ bash: BashConfigSchema.optional(),
24362
24445
  experimental: ExperimentalConfigSchema.optional(),
24363
24446
  lsp: LspConfigSchema.optional(),
24364
24447
  url_fetch_allow_private: exports_external.boolean().optional(),
@@ -24438,22 +24521,68 @@ function resolveProjectOverridesForConfigure(config2) {
24438
24521
  overrides.max_callgraph_files = config2.max_callgraph_files;
24439
24522
  return overrides;
24440
24523
  }
24441
- function resolveExperimentalConfigForConfigure(config2) {
24442
- const overrides = {};
24443
- if (config2.experimental?.bash?.rewrite !== undefined) {
24444
- overrides.experimental_bash_rewrite = config2.experimental.bash.rewrite;
24445
- }
24446
- if (config2.experimental?.bash?.compress !== undefined) {
24447
- overrides.experimental_bash_compress = config2.experimental.bash.compress;
24524
+ function resolveBashConfig(config2) {
24525
+ const top = config2.bash;
24526
+ const legacy = config2.experimental?.bash;
24527
+ const surface = config2.tool_surface ?? "recommended";
24528
+ const surfaceDefaultEnabled = surface !== "minimal";
24529
+ const reminderEnabled = (typeof top === "object" && top !== null ? top.long_running_reminder_enabled : undefined) ?? legacy?.long_running_reminder_enabled;
24530
+ const reminderInterval = (typeof top === "object" && top !== null ? top.long_running_reminder_interval_ms : undefined) ?? legacy?.long_running_reminder_interval_ms;
24531
+ const base = {
24532
+ enabled: false,
24533
+ rewrite: false,
24534
+ compress: false,
24535
+ background: false,
24536
+ long_running_reminder_enabled: reminderEnabled,
24537
+ long_running_reminder_interval_ms: reminderInterval
24538
+ };
24539
+ if (top === false) {
24540
+ return base;
24541
+ }
24542
+ if (top === true) {
24543
+ return { ...base, enabled: true, rewrite: true, compress: true, background: true };
24544
+ }
24545
+ if (typeof top === "object" && top !== null) {
24546
+ return {
24547
+ ...base,
24548
+ enabled: true,
24549
+ rewrite: top.rewrite ?? true,
24550
+ compress: top.compress ?? true,
24551
+ background: top.background ?? true
24552
+ };
24448
24553
  }
24449
- if (config2.experimental?.bash?.background !== undefined) {
24450
- overrides.experimental_bash_background = config2.experimental.bash.background;
24554
+ const hasLegacyFeatureFlag = legacy && (legacy.rewrite !== undefined || legacy.compress !== undefined || legacy.background !== undefined);
24555
+ if (hasLegacyFeatureFlag) {
24556
+ const rewrite = legacy.rewrite === true;
24557
+ const compress = legacy.compress === true;
24558
+ const background = legacy.background === true;
24559
+ return {
24560
+ ...base,
24561
+ enabled: rewrite || compress || background,
24562
+ rewrite,
24563
+ compress,
24564
+ background
24565
+ };
24451
24566
  }
24452
- if (config2.experimental?.bash?.long_running_reminder_enabled !== undefined) {
24453
- overrides.bash_long_running_reminder_enabled = config2.experimental.bash.long_running_reminder_enabled;
24567
+ return {
24568
+ ...base,
24569
+ enabled: surfaceDefaultEnabled,
24570
+ rewrite: surfaceDefaultEnabled,
24571
+ compress: surfaceDefaultEnabled,
24572
+ background: surfaceDefaultEnabled
24573
+ };
24574
+ }
24575
+ function resolveExperimentalConfigForConfigure(config2) {
24576
+ const overrides = {};
24577
+ const bash = resolveBashConfig(config2);
24578
+ overrides.experimental_bash_rewrite = bash.rewrite;
24579
+ overrides.experimental_bash_compress = bash.compress;
24580
+ overrides.experimental_bash_background = bash.background;
24581
+ if (bash.long_running_reminder_enabled !== undefined) {
24582
+ overrides.bash_long_running_reminder_enabled = bash.long_running_reminder_enabled;
24454
24583
  }
24455
- if (config2.experimental?.bash?.long_running_reminder_interval_ms !== undefined) {
24456
- overrides.bash_long_running_reminder_interval_ms = config2.experimental.bash.long_running_reminder_interval_ms;
24584
+ if (bash.long_running_reminder_interval_ms !== undefined) {
24585
+ overrides.bash_long_running_reminder_interval_ms = bash.long_running_reminder_interval_ms;
24457
24586
  }
24458
24587
  if (config2.experimental?.lsp_ty !== undefined) {
24459
24588
  overrides.experimental_lsp_ty = config2.experimental.lsp_ty;
@@ -24524,8 +24653,51 @@ function migrateRawConfig(rawConfig, configPath, logger) {
24524
24653
  delete rawConfig[migration.oldKey];
24525
24654
  oldKeys.push(migration.oldKey);
24526
24655
  }
24656
+ oldKeys.push(...migrateExperimentalBashBlock(rawConfig, configPath, logger));
24527
24657
  return oldKeys;
24528
24658
  }
24659
+ function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
24660
+ const experimental = rawConfig.experimental;
24661
+ if (typeof experimental !== "object" || experimental === null || Array.isArray(experimental)) {
24662
+ return [];
24663
+ }
24664
+ const expRecord = experimental;
24665
+ if (!Object.hasOwn(expRecord, "bash"))
24666
+ return [];
24667
+ const legacyBash = expRecord.bash;
24668
+ if (typeof legacyBash !== "object" || legacyBash === null || Array.isArray(legacyBash)) {
24669
+ delete expRecord.bash;
24670
+ if (Object.keys(expRecord).length === 0)
24671
+ delete rawConfig.experimental;
24672
+ return ["experimental.bash"];
24673
+ }
24674
+ const bashRecord = legacyBash;
24675
+ const hasFeatureFlag = "rewrite" in bashRecord || "compress" in bashRecord || "background" in bashRecord;
24676
+ if (!hasFeatureFlag)
24677
+ return [];
24678
+ const movedKeys = Object.keys(bashRecord).map((k) => `experimental.bash.${k}`);
24679
+ if (Object.hasOwn(rawConfig, "bash")) {
24680
+ logger?.warn(`Config migration conflict at ${configPath}: experimental.bash dropped because top-level "bash" is already set`);
24681
+ } else {
24682
+ const migrated = {
24683
+ rewrite: bashRecord.rewrite === true,
24684
+ compress: bashRecord.compress === true,
24685
+ background: bashRecord.background === true
24686
+ };
24687
+ if (bashRecord.long_running_reminder_enabled !== undefined) {
24688
+ migrated.long_running_reminder_enabled = bashRecord.long_running_reminder_enabled;
24689
+ }
24690
+ if (bashRecord.long_running_reminder_interval_ms !== undefined) {
24691
+ migrated.long_running_reminder_interval_ms = bashRecord.long_running_reminder_interval_ms;
24692
+ }
24693
+ rawConfig.bash = migrated;
24694
+ }
24695
+ delete expRecord.bash;
24696
+ if (Object.keys(expRecord).length === 0) {
24697
+ delete rawConfig.experimental;
24698
+ }
24699
+ return movedKeys;
24700
+ }
24529
24701
  function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
24530
24702
  if (!existsSync6(configPath)) {
24531
24703
  return { migrated: false, oldKeys: [] };
@@ -24661,6 +24833,22 @@ function mergeLspConfig(baseLsp, overrideLsp) {
24661
24833
  }
24662
24834
  return Object.fromEntries(Object.entries(lsp).filter(([, value]) => value !== undefined));
24663
24835
  }
24836
+ function mergeBashConfig(baseBash, overrideBash) {
24837
+ if (baseBash === undefined && overrideBash === undefined)
24838
+ return;
24839
+ if (baseBash === undefined)
24840
+ return overrideBash;
24841
+ if (overrideBash === undefined)
24842
+ return baseBash;
24843
+ const expand = (value) => {
24844
+ if (value === true)
24845
+ return { rewrite: true, compress: true, background: true };
24846
+ if (value === false)
24847
+ return { rewrite: false, compress: false, background: false };
24848
+ return { ...value ?? {} };
24849
+ };
24850
+ return { ...expand(baseBash), ...expand(overrideBash) };
24851
+ }
24664
24852
  function mergeExperimentalConfig(baseExperimental, overrideExperimental) {
24665
24853
  const bash = {
24666
24854
  ...baseExperimental?.bash,
@@ -24704,7 +24892,8 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
24704
24892
  "validate_on_edit",
24705
24893
  "search_index",
24706
24894
  "semantic_search",
24707
- "experimental"
24895
+ "experimental",
24896
+ "bash"
24708
24897
  ]);
24709
24898
  function pickProjectSafeFields(override) {
24710
24899
  const safe = {};
@@ -24734,13 +24923,16 @@ function mergeConfigs(base, override) {
24734
24923
  const semantic = mergeSemanticConfig(base.semantic, override.semantic);
24735
24924
  const lsp = mergeLspConfig(base.lsp, override.lsp);
24736
24925
  const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
24926
+ const bash = mergeBashConfig(base.bash, override.bash);
24737
24927
  const safeOverride = pickProjectSafeFields(override);
24928
+ delete safeOverride.bash;
24738
24929
  return {
24739
24930
  ...base,
24740
24931
  ...safeOverride,
24741
24932
  ...Object.keys(formatter).length > 0 ? { formatter } : {},
24742
24933
  ...Object.keys(checker).length > 0 ? { checker } : {},
24743
24934
  ...lsp ? { lsp } : {},
24935
+ ...bash !== undefined ? { bash } : {},
24744
24936
  experimental,
24745
24937
  semantic,
24746
24938
  ...disabledTools.length > 0 ? { disabled_tools: [...new Set(disabledTools)] } : {}
@@ -25398,8 +25590,8 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
25398
25590
 
25399
25591
  // src/lsp-auto-install.ts
25400
25592
  import { spawn as spawn3 } from "child_process";
25401
- import { createHash as createHash4 } from "crypto";
25402
- import { createReadStream, mkdirSync as mkdirSync8, readFileSync as readFileSync8, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync5 } from "fs";
25593
+ import { createHash as createHash5 } from "crypto";
25594
+ import { createReadStream, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync5 } from "fs";
25403
25595
  import { join as join15 } from "path";
25404
25596
 
25405
25597
  // src/lsp-cache.ts
@@ -25733,19 +25925,33 @@ var NPM_LSP_TABLE = [
25733
25925
  id: "vue",
25734
25926
  npm: "@vue/language-server",
25735
25927
  binary: "vue-language-server",
25736
- extensions: ["vue"]
25928
+ extensions: ["vue"],
25929
+ rootMarkers: [
25930
+ "vue.config.js",
25931
+ "vue.config.mjs",
25932
+ "vue.config.ts",
25933
+ "nuxt.config.js",
25934
+ "nuxt.config.mjs",
25935
+ "nuxt.config.ts",
25936
+ "nuxt.config.cjs"
25937
+ ],
25938
+ packageJsonDeps: ["vue", "@vue/runtime-core", "nuxt"]
25737
25939
  },
25738
25940
  {
25739
25941
  id: "astro",
25740
25942
  npm: "@astrojs/language-server",
25741
25943
  binary: "astro-ls",
25742
- extensions: ["astro"]
25944
+ extensions: ["astro"],
25945
+ rootMarkers: ["astro.config.js", "astro.config.mjs", "astro.config.ts", "astro.config.cjs"],
25946
+ packageJsonDeps: ["astro"]
25743
25947
  },
25744
25948
  {
25745
25949
  id: "svelte",
25746
25950
  npm: "svelte-language-server",
25747
25951
  binary: "svelteserver",
25748
- extensions: ["svelte"]
25952
+ extensions: ["svelte"],
25953
+ rootMarkers: ["svelte.config.js", "svelte.config.mjs", "svelte.config.ts", "svelte.config.cjs"],
25954
+ packageJsonDeps: ["svelte", "@sveltejs/kit"]
25749
25955
  },
25750
25956
  {
25751
25957
  id: "php-intelephense",
@@ -25763,7 +25969,7 @@ var NPM_LSP_TABLE = [
25763
25969
  ];
25764
25970
 
25765
25971
  // src/lsp-project-relevance.ts
25766
- import { existsSync as existsSync10, readdirSync as readdirSync3 } from "fs";
25972
+ import { existsSync as existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
25767
25973
  import { join as join14 } from "path";
25768
25974
  var MAX_WALK_DIRS = 200;
25769
25975
  var MAX_WALK_DEPTH = 4;
@@ -25786,6 +25992,34 @@ function hasRootMarker(projectRoot, rootMarkers) {
25786
25992
  }
25787
25993
  return false;
25788
25994
  }
25995
+ function hasPackageJsonDep(projectRoot, depNames) {
25996
+ if (!depNames || depNames.length === 0)
25997
+ return false;
25998
+ const pkg = readPackageJson(projectRoot);
25999
+ if (!pkg)
26000
+ return false;
26001
+ const merged = {
26002
+ ...typeof pkg.dependencies === "object" && pkg.dependencies ? pkg.dependencies : {},
26003
+ ...typeof pkg.devDependencies === "object" && pkg.devDependencies ? pkg.devDependencies : {},
26004
+ ...typeof pkg.peerDependencies === "object" && pkg.peerDependencies ? pkg.peerDependencies : {}
26005
+ };
26006
+ for (const name of depNames) {
26007
+ if (Object.hasOwn(merged, name))
26008
+ return true;
26009
+ }
26010
+ return false;
26011
+ }
26012
+ function readPackageJson(projectRoot) {
26013
+ try {
26014
+ const raw = readFileSync8(join14(projectRoot, "package.json"), "utf8");
26015
+ const parsed = JSON.parse(raw);
26016
+ if (typeof parsed !== "object" || parsed === null)
26017
+ return null;
26018
+ return parsed;
26019
+ } catch {
26020
+ return null;
26021
+ }
26022
+ }
25789
26023
  function relevantExtensionsInProject(projectRoot, extToServer) {
25790
26024
  const wanted = new Set(Object.keys(extToServer).map((ext) => ext.toLowerCase()));
25791
26025
  const found = new Set;
@@ -25878,6 +26112,8 @@ async function probeRegistry(npmPackage, graceDays, fetchImpl = fetch) {
25878
26112
  function isProjectRelevant(spec, projectRoot, projectExtensions) {
25879
26113
  if (hasRootMarker(projectRoot, spec.rootMarkers))
25880
26114
  return true;
26115
+ if (hasPackageJsonDep(projectRoot, spec.packageJsonDeps))
26116
+ return true;
25881
26117
  const extensions = projectExtensions();
25882
26118
  return spec.extensions.some((ext) => extensions.has(ext.toLowerCase()));
25883
26119
  }
@@ -26078,7 +26314,7 @@ function hashInstalledBinary(spec) {
26078
26314
  reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
26079
26315
  return;
26080
26316
  }
26081
- const hash2 = createHash4("sha256");
26317
+ const hash2 = createHash5("sha256");
26082
26318
  const stream = createReadStream(pathToHash);
26083
26319
  stream.on("error", reject);
26084
26320
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -26101,7 +26337,7 @@ function installedBinaryPath(spec) {
26101
26337
  return null;
26102
26338
  }
26103
26339
  function sha256OfFileSync(path2) {
26104
- return createHash4("sha256").update(readFileSync8(path2)).digest("hex");
26340
+ return createHash5("sha256").update(readFileSync9(path2)).digest("hex");
26105
26341
  }
26106
26342
  function quarantineCachedNpmInstall(spec, reason) {
26107
26343
  const packageDir = lspPackageDir(spec.npm);
@@ -26186,7 +26422,7 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
26186
26422
 
26187
26423
  // src/lsp-github-install.ts
26188
26424
  import { execFileSync as execFileSync2 } from "child_process";
26189
- import { createHash as createHash5, randomBytes } from "crypto";
26425
+ import { createHash as createHash6, randomBytes } from "crypto";
26190
26426
  import {
26191
26427
  copyFileSync as copyFileSync3,
26192
26428
  createReadStream as createReadStream2,
@@ -26195,7 +26431,7 @@ import {
26195
26431
  lstatSync as lstatSync2,
26196
26432
  mkdirSync as mkdirSync9,
26197
26433
  readdirSync as readdirSync4,
26198
- readFileSync as readFileSync9,
26434
+ readFileSync as readFileSync10,
26199
26435
  readlinkSync as readlinkSync2,
26200
26436
  realpathSync as realpathSync3,
26201
26437
  renameSync as renameSync6,
@@ -26329,7 +26565,7 @@ function readGithubInstalledMetaIn(installDir) {
26329
26565
  const path2 = join16(installDir, INSTALLED_META_FILE2);
26330
26566
  if (!statSync6(path2).isFile())
26331
26567
  return null;
26332
- const parsed = JSON.parse(readFileSync9(path2, "utf8"));
26568
+ const parsed = JSON.parse(readFileSync10(path2, "utf8"));
26333
26569
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
26334
26570
  return null;
26335
26571
  return {
@@ -26362,7 +26598,7 @@ var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
26362
26598
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
26363
26599
  function sha256OfFile(path2) {
26364
26600
  return new Promise((resolve4, reject) => {
26365
- const hash2 = createHash5("sha256");
26601
+ const hash2 = createHash6("sha256");
26366
26602
  const stream = createReadStream2(path2);
26367
26603
  stream.on("error", reject);
26368
26604
  stream.on("data", (chunk) => hash2.update(chunk));
@@ -26370,7 +26606,7 @@ function sha256OfFile(path2) {
26370
26606
  });
26371
26607
  }
26372
26608
  function sha256OfFileSync2(path2) {
26373
- return createHash5("sha256").update(readFileSync9(path2)).digest("hex");
26609
+ return createHash6("sha256").update(readFileSync10(path2)).digest("hex");
26374
26610
  }
26375
26611
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
26376
26612
  const candidates = [];
@@ -27069,7 +27305,7 @@ function normalizeToolMap(tools) {
27069
27305
  }
27070
27306
 
27071
27307
  // src/notifications.ts
27072
- import { existsSync as existsSync12, mkdirSync as mkdirSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
27308
+ import { existsSync as existsSync12, mkdirSync as mkdirSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
27073
27309
  import { homedir as homedir9, platform as platform2 } from "os";
27074
27310
  import { join as join17 } from "path";
27075
27311
  function isTuiMode() {
@@ -27112,7 +27348,7 @@ function readDesktopState(directory) {
27112
27348
  return { sessionId: null, serverUrl: null };
27113
27349
  }
27114
27350
  try {
27115
- const raw = readFileSync10(statePath, "utf-8");
27351
+ const raw = readFileSync11(statePath, "utf-8");
27116
27352
  const state = JSON.parse(raw);
27117
27353
  let serverUrl = null;
27118
27354
  const serverStr = state.server;
@@ -27208,25 +27444,34 @@ async function sendWarning(opts, message) {
27208
27444
  sessionLog(sessionId, `[aft-plugin] sending warning to session ${sessionId}`);
27209
27445
  await sendIgnoredMessage(opts.client, sessionId, text);
27210
27446
  }
27211
- async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
27447
+ async function sendFeatureAnnouncement(opts, version2, features, footer, storageDir) {
27212
27448
  if (storageDir) {
27213
27449
  const versionFile = join17(storageDir, "last_announced_version");
27214
27450
  try {
27215
27451
  if (existsSync12(versionFile)) {
27216
- const lastVersion = readFileSync10(versionFile, "utf-8").trim();
27452
+ const lastVersion = readFileSync11(versionFile, "utf-8").trim();
27217
27453
  if (lastVersion === version2)
27218
27454
  return;
27219
27455
  }
27220
27456
  } catch {}
27221
27457
  }
27222
- const featureText = features.map((f) => `\u2022 ${f}`).join(`
27458
+ const hasFooter = typeof footer === "string" && footer.trim().length > 0;
27459
+ const featureText = hasFooter ? [features.map((f) => `\u2022 ${f}`).join(`
27460
+ `), "", footer].join(`
27461
+ `) : features.map((f) => `\u2022 ${f}`).join(`
27223
27462
  `);
27224
27463
  const toastSent = await showTuiToast(opts.client, `AFT v${version2}`, featureText, "info", 12000);
27225
27464
  if (!toastSent) {
27226
27465
  const { sessionId } = readDesktopState(opts.directory);
27227
27466
  if (!sessionId)
27228
27467
  return;
27229
- const text = [`${FEATURE_MARKER} v${version2}:`, ...features.map((f) => ` \u2022 ${f}`)].join(`
27468
+ const sections = [
27469
+ `${FEATURE_MARKER} v${version2}:`,
27470
+ ...features.map((f) => ` \u2022 ${f}`)
27471
+ ];
27472
+ if (hasFooter)
27473
+ sections.push("", footer);
27474
+ const text = sections.join(`
27230
27475
  `);
27231
27476
  sessionLog(sessionId, `[aft-plugin] sending feature announcement for v${version2}`);
27232
27477
  await sendIgnoredMessage(opts.client, sessionId, text);
@@ -27356,11 +27601,11 @@ import { createServer } from "http";
27356
27601
  import { dirname as dirname7 } from "path";
27357
27602
 
27358
27603
  // src/shared/rpc-utils.ts
27359
- import { createHash as createHash6 } from "crypto";
27604
+ import { createHash as createHash7 } from "crypto";
27360
27605
  import { join as join18 } from "path";
27361
27606
  function projectHash(directory) {
27362
27607
  const normalized = directory.replace(/\/+$/, "");
27363
- return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
27608
+ return createHash7("sha256").update(normalized).digest("hex").slice(0, 16);
27364
27609
  }
27365
27610
  function rpcPortFilePath(storageDir, directory) {
27366
27611
  const hash2 = projectHash(directory);
@@ -27713,7 +27958,7 @@ function formatStatusMarkdown(status) {
27713
27958
 
27714
27959
  // src/shared/tui-config.ts
27715
27960
  var import_comment_json4 = __toESM(require_src2(), 1);
27716
- import { existsSync as existsSync13, mkdirSync as mkdirSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync11 } from "fs";
27961
+ import { existsSync as existsSync13, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
27717
27962
  import { dirname as dirname8, join as join20 } from "path";
27718
27963
 
27719
27964
  // src/shared/opencode-config-dir.ts
@@ -27761,7 +28006,7 @@ function ensureTuiPluginEntry() {
27761
28006
  const configPath = resolveTuiConfigPath();
27762
28007
  let config2 = {};
27763
28008
  if (existsSync13(configPath)) {
27764
- config2 = import_comment_json4.parse(readFileSync11(configPath, "utf-8")) ?? {};
28009
+ config2 = import_comment_json4.parse(readFileSync12(configPath, "utf-8")) ?? {};
27765
28010
  }
27766
28011
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((value) => typeof value === "string") : [];
27767
28012
  if (plugins.some((plugin) => plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`) || plugin.includes("opencode-plugin") || plugin.includes("aft-opencode"))) {
@@ -30079,11 +30324,7 @@ function hoistedTools(ctx) {
30079
30324
  aft_delete: createDeleteTool(ctx),
30080
30325
  aft_move: createMoveTool(ctx)
30081
30326
  };
30082
- const bashRewrite = ctx.config.experimental?.bash?.rewrite === true;
30083
- const bashCompress = ctx.config.experimental?.bash?.compress === true;
30084
- const bashBackground = ctx.config.experimental?.bash?.background === true;
30085
- const anyBashExperimental = bashRewrite || bashCompress || bashBackground;
30086
- if (anyBashExperimental) {
30327
+ if (resolveBashConfig(ctx.config).enabled) {
30087
30328
  tools.bash = createBashTool(ctx);
30088
30329
  tools.bash_status = createBashStatusTool(ctx);
30089
30330
  tools.bash_kill = createBashKillTool(ctx);
@@ -30137,11 +30378,7 @@ function aftPrefixedTools(ctx) {
30137
30378
  aft_delete: createDeleteTool(ctx),
30138
30379
  aft_move: createMoveTool(ctx)
30139
30380
  };
30140
- const bashRewrite = ctx.config.experimental?.bash?.rewrite === true;
30141
- const bashCompress = ctx.config.experimental?.bash?.compress === true;
30142
- const bashBackground = ctx.config.experimental?.bash?.background === true;
30143
- const anyBashExperimental = bashRewrite || bashCompress || bashBackground;
30144
- if (anyBashExperimental) {
30381
+ if (resolveBashConfig(ctx.config).enabled) {
30145
30382
  tools.aft_bash = createBashTool(ctx);
30146
30383
  tools.bash_status = createBashStatusTool(ctx);
30147
30384
  tools.bash_kill = createBashKillTool(ctx);
@@ -31124,7 +31361,7 @@ function buildHintsFromConfig(config2, disabledTools) {
31124
31361
  toolSurface: config2.tool_surface ?? "recommended",
31125
31362
  hoistBuiltins: config2.hoist_builtin_tools !== false,
31126
31363
  semanticEnabled: config2.semantic_search === true,
31127
- bashBackgroundEnabled: config2.experimental?.bash?.background === true,
31364
+ bashBackgroundEnabled: resolveBashConfig(config2).background,
31128
31365
  disabledTools
31129
31366
  });
31130
31367
  }
@@ -31205,13 +31442,13 @@ var PLUGIN_VERSION = (() => {
31205
31442
  return "0.0.0";
31206
31443
  }
31207
31444
  })();
31208
- var ANNOUNCEMENT_VERSION = "0.27.0";
31445
+ var ANNOUNCEMENT_VERSION = "0.28.0";
31209
31446
  var ANNOUNCEMENT_FEATURES = [
31210
- "Storage moved to ~/.local/share/cortexkit/aft (~/Library/Application Support/cortexkit/aft on macOS, %APPDATA%/cortexkit/aft on Windows). Your existing data migrated automatically on first launch.",
31211
- "Bash output compression now reports token savings \u2014 visible in /aft-status and the TUI sidebar (Session + Project totals).",
31212
- "Seven new languages supported by aft_outline / aft_zoom / aft_search / ast_grep: Java, Ruby, Kotlin, Swift, PHP, Lua, Perl.",
31213
- "Join us on Discord: https://discord.gg/F2uWxjGnU"
31447
+ "Bash hoisting is now default-on. Configure with top-level `bash: { rewrite, compress, background }` instead of `experimental.bash.*` \u2014 old config migrates automatically on first launch.",
31448
+ "Vue, Astro, and Svelte language servers now auto-install when the framework appears in your package.json (fixes #48).",
31449
+ "Native Windows ARM64 binary \u2014 ARM64 hosts no longer fall back to x64 under emulation."
31214
31450
  ];
31451
+ var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
31215
31452
  var plugin = async (input) => initializePluginForDirectory(input);
31216
31453
  async function initializePluginForDirectory(input) {
31217
31454
  const binaryPath = await findBinary(PLUGIN_VERSION);
@@ -31423,7 +31660,7 @@ ${lines}
31423
31660
  return {
31424
31661
  success: true,
31425
31662
  status: "not_initialized",
31426
- message: "Waiting for first tool call to populate"
31663
+ message: "AFT bridge is now spawned lazily, information here will be populated after first tool call."
31427
31664
  };
31428
31665
  }
31429
31666
  const cached2 = bridge.getCachedStatus();
@@ -31447,13 +31684,18 @@ ${lines}
31447
31684
  const versionFile = join22(storageDir, "last_announced_version");
31448
31685
  try {
31449
31686
  if (existsSync15(versionFile)) {
31450
- const lastVersion = readFileSync12(versionFile, "utf-8").trim();
31687
+ const lastVersion = readFileSync13(versionFile, "utf-8").trim();
31451
31688
  if (lastVersion === ANNOUNCEMENT_VERSION)
31452
31689
  return { show: false };
31453
31690
  }
31454
31691
  } catch {}
31455
31692
  }
31456
- return { show: true, version: ANNOUNCEMENT_VERSION, features: ANNOUNCEMENT_FEATURES };
31693
+ return {
31694
+ show: true,
31695
+ version: ANNOUNCEMENT_VERSION,
31696
+ features: ANNOUNCEMENT_FEATURES,
31697
+ footer: ANNOUNCEMENT_FOOTER
31698
+ };
31457
31699
  });
31458
31700
  rpcServer.handle("mark-announced", async () => {
31459
31701
  if (storageDir && ANNOUNCEMENT_VERSION) {
@@ -31487,7 +31729,7 @@ Install: ${getManualInstallHint()}`);
31487
31729
  };
31488
31730
  if (ANNOUNCEMENT_VERSION && ANNOUNCEMENT_FEATURES.length > 0) {
31489
31731
  setTimeout(() => {
31490
- sendFeatureAnnouncement(notifyOpts, ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, storageDir).catch(() => {});
31732
+ sendFeatureAnnouncement(notifyOpts, ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER, storageDir).catch(() => {});
31491
31733
  }, 8000);
31492
31734
  }
31493
31735
  if (onnxRuntimePromise) {