@hasna/instructions 0.4.42 → 0.5.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.
Files changed (45) hide show
  1. package/dist/cli/apply-unchanged-snapshot-bloat.test.d.ts +2 -0
  2. package/dist/cli/apply-unchanged-snapshot-bloat.test.d.ts.map +1 -0
  3. package/dist/cli/index.js +1179 -195
  4. package/dist/cli/station-profile.test.d.ts +2 -0
  5. package/dist/cli/station-profile.test.d.ts.map +1 -0
  6. package/dist/generated/storage-kit/backend.d.ts +0 -1
  7. package/dist/generated/storage-kit/backend.d.ts.map +1 -1
  8. package/dist/generated/storage-kit/index.d.ts +1 -1
  9. package/dist/generated/storage-kit/query.d.ts +1 -1
  10. package/dist/generated/storage-kit/query.d.ts.map +1 -1
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +685 -196
  14. package/dist/lib/apply.d.ts.map +1 -1
  15. package/dist/lib/instruction-graph.d.ts +4 -0
  16. package/dist/lib/instruction-graph.d.ts.map +1 -1
  17. package/dist/lib/provider-context.d.ts +94 -0
  18. package/dist/lib/provider-context.d.ts.map +1 -0
  19. package/dist/lib/provider-context.test.d.ts +2 -0
  20. package/dist/lib/provider-context.test.d.ts.map +1 -0
  21. package/dist/lib/session-apply.d.ts +7 -0
  22. package/dist/lib/session-apply.d.ts.map +1 -1
  23. package/dist/lib/session-authority.d.ts +22 -2
  24. package/dist/lib/session-authority.d.ts.map +1 -1
  25. package/dist/lib/session-render.d.ts +9 -1
  26. package/dist/lib/session-render.d.ts.map +1 -1
  27. package/dist/lib/station-profile.d.ts +102 -0
  28. package/dist/lib/station-profile.d.ts.map +1 -0
  29. package/dist/lib/station-profile.test.d.ts +2 -0
  30. package/dist/lib/station-profile.test.d.ts.map +1 -0
  31. package/dist/lib/sync-dir.d.ts.map +1 -1
  32. package/dist/lib/sync.d.ts.map +1 -1
  33. package/dist/mcp/early-args.test.d.ts +2 -0
  34. package/dist/mcp/early-args.test.d.ts.map +1 -0
  35. package/dist/mcp/index.d.ts.map +1 -1
  36. package/dist/mcp/index.js +125 -76
  37. package/dist/mcp/server.d.ts.map +1 -1
  38. package/dist/server/cloud.d.ts.map +1 -1
  39. package/dist/server/early-args.test.d.ts +2 -0
  40. package/dist/server/early-args.test.d.ts.map +1 -0
  41. package/dist/server/index.d.ts.map +1 -1
  42. package/dist/server/index.js +38 -2
  43. package/dist/status.d.ts.map +1 -1
  44. package/dist/storage/cloud-store.d.ts.map +1 -1
  45. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -749,9 +749,9 @@ import { createHash as createHash7 } from "crypto";
749
749
 
750
750
  // src/lib/session-render.ts
751
751
  import { createHash as createHash6 } from "crypto";
752
- import { existsSync as existsSync4, readFileSync as readFileSync4, realpathSync, statSync as statSync3 } from "fs";
753
- import { homedir as homedir4 } from "os";
754
- import { basename as basename3, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute3, join as join7, parse as parse2, posix as posix2, relative as relative2, resolve as resolve6 } from "path";
752
+ import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
753
+ import { homedir as homedir5 } from "os";
754
+ import { basename as basename4, dirname as dirname3, extname as extname2, isAbsolute as isAbsolute3, join as join7, parse as parse2, posix as posix2, relative as relative2, resolve as resolve6 } from "path";
755
755
 
756
756
  // src/lib/global-agent-rules-standard.ts
757
757
  import { createHash } from "crypto";
@@ -8787,7 +8787,8 @@ function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthori
8787
8787
 
8788
8788
  // src/lib/session-authority.ts
8789
8789
  import { createHash as createHash5 } from "crypto";
8790
- import { lstatSync as lstatSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
8790
+ import { lstatSync as lstatSync3, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
8791
+ import { homedir as homedir4 } from "os";
8791
8792
  import { join as join6, resolve as resolve5 } from "path";
8792
8793
  var CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH = "AGENTS.md";
8793
8794
  var CLAUDE_LEGACY_AUTHORITY_MAX_BYTES = 256 * 1024;
@@ -8799,7 +8800,18 @@ var CLAUDE_LEGACY_MARKERS = [
8799
8800
  function sha2565(content) {
8800
8801
  return createHash5("sha256").update(content).digest("hex");
8801
8802
  }
8802
- function detectClaudeAuthorityConflicts(targetHome) {
8803
+ function configHomeDir() {
8804
+ return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir4();
8805
+ }
8806
+ function normalizeOwnedTargetPath(p) {
8807
+ const expanded = p.startsWith("~/") ? resolve5(configHomeDir(), p.slice(2)) : resolve5(p);
8808
+ try {
8809
+ return realpathSync(expanded);
8810
+ } catch {
8811
+ return expanded;
8812
+ }
8813
+ }
8814
+ function detectClaudeAuthorityConflicts(targetHome, ownedAuthorities = []) {
8803
8815
  const authorityPath = resolve5(join6(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
8804
8816
  let stat;
8805
8817
  try {
@@ -8843,6 +8855,24 @@ function detectClaudeAuthorityConflicts(targetHome) {
8843
8855
  }];
8844
8856
  }
8845
8857
  const content = readFileSync3(authorityPath, "utf8");
8858
+ const owned = ownedAuthorities.find((authority) => normalizeOwnedTargetPath(authority.targetPath) === normalizeOwnedTargetPath(authorityPath));
8859
+ if (owned) {
8860
+ if (owned.content === content)
8861
+ return [];
8862
+ return [{
8863
+ ...provenanceBase,
8864
+ kind: "unknown-unmanaged-authority",
8865
+ sha256: sha2565(content),
8866
+ markers: [],
8867
+ provenance: {
8868
+ source: "filesystem",
8869
+ authority: "unmanaged",
8870
+ observedPath: authorityPath,
8871
+ detection: "owned-config-drift"
8872
+ },
8873
+ reason: `Claude target AGENTS.md is owned by registered config "${owned.slug}", but the disk file drifts from its stored content; re-sync the config through the instructions pipeline before applying.`
8874
+ }];
8875
+ }
8846
8876
  const markers = CLAUDE_LEGACY_MARKERS.filter((marker) => marker.pattern.test(content)).map((marker) => marker.id);
8847
8877
  const knownLegacy = markers.includes("no-worktrees-heading") && markers.includes("no-worktrees-directive");
8848
8878
  return [{
@@ -9141,7 +9171,7 @@ function yamlQuote2(value) {
9141
9171
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
9142
9172
  }
9143
9173
  function defaultTargetHome(tool, profile, sessionId) {
9144
- const home = process.env["HOME"] || homedir4();
9174
+ const home = process.env["HOME"] || homedir5();
9145
9175
  return join7(home, ".hasna", "accounts", "profiles", tool, slug(profile));
9146
9176
  }
9147
9177
  function joinTarget(targetHome, relativePath) {
@@ -9717,7 +9747,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
9717
9747
  ...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
9718
9748
  ]);
9719
9749
  const existingConfigPath = joinTarget(targetHome, adapter.configFile);
9720
- const selectedConfig = existsSync4(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
9750
+ const selectedConfig = existsSync5(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
9721
9751
  const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
9722
9752
  const config = {
9723
9753
  ...selectedConfig,
@@ -9906,7 +9936,7 @@ function adapterFor(input) {
9906
9936
  return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
9907
9937
  }
9908
9938
  function getHomeDir() {
9909
- return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir4();
9939
+ return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir5();
9910
9940
  }
9911
9941
  function cleanSessionPathInput(path) {
9912
9942
  const trimmed = path.trim();
@@ -10044,7 +10074,7 @@ function planSessionRender(input) {
10044
10074
  blockers: targetBlockers
10045
10075
  } = resolveRenderTarget(input);
10046
10076
  const authorityObservations = input.tool === "cursor" && targetKind !== "blocked" ? [observeCursorGlobalAuthority({ home: input.cursorAuthorityHome })] : [];
10047
- const authorityConflicts = input.tool === "cursor" && targetKind !== "blocked" ? detectCursorAuthorityConflicts(authorityObservations[0]) : input.tool === "claude" && targetKind !== "blocked" ? detectClaudeAuthorityConflicts(targetHome) : [];
10077
+ const authorityConflicts = input.tool === "cursor" && targetKind !== "blocked" ? detectCursorAuthorityConflicts(authorityObservations[0]) : input.tool === "claude" && targetKind !== "blocked" ? detectClaudeAuthorityConflicts(targetHome, input.ownedClaudeAuthorities) : [];
10048
10078
  const blockers = [
10049
10079
  ...targetBlockers,
10050
10080
  ...authorityConflicts.map((conflict) => `${conflict.relativePath}: ${conflict.reason}`)
@@ -10182,7 +10212,7 @@ function planSessionRender(input) {
10182
10212
  sourceId: input.providerConfig.sourceId,
10183
10213
  selectedPayloadSha256: sha2566(input.providerConfig.content),
10184
10214
  renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
10185
- selected: !existsSync4(joinTarget(targetHome, adapter.configFile))
10215
+ selected: !existsSync5(joinTarget(targetHome, adapter.configFile))
10186
10216
  }
10187
10217
  } : {},
10188
10218
  ...projectContext ? {
@@ -10221,7 +10251,7 @@ function planSessionRender(input) {
10221
10251
  };
10222
10252
  }
10223
10253
  function sourceFromFilePath(path, content, order = 0) {
10224
- const file = basename3(path);
10254
+ const file = basename4(path);
10225
10255
  return {
10226
10256
  id: file.replace(extname2(file), ""),
10227
10257
  label: file,
@@ -10333,7 +10363,7 @@ function skippedProfileConfig(config, targetProviders, reason) {
10333
10363
  };
10334
10364
  }
10335
10365
  function isOpenCodeProviderConfig(config) {
10336
- return config.kind === "file" && config.agent === "opencode" && config.format === "json" && basename3(config.target_path ?? "") === "opencode.json";
10366
+ return config.kind === "file" && config.agent === "opencode" && config.format === "json" && basename4(config.target_path ?? "") === "opencode.json";
10337
10367
  }
10338
10368
  function selectProviderConfig(configs, skippedSources) {
10339
10369
  if (configs.length === 0)
@@ -10544,7 +10574,7 @@ function layerFromIdentityKind(kind, exportShape) {
10544
10574
  function contentFromIdentitySourcePaths(sourcePaths, exportPath, sourceId) {
10545
10575
  if (sourcePaths.length === 0 || !exportPath)
10546
10576
  return;
10547
- const baseDir = dirname2(resolveSessionPath(exportPath));
10577
+ const baseDir = dirname3(resolveSessionPath(exportPath));
10548
10578
  const contents = [];
10549
10579
  for (const sourcePath of sourcePaths) {
10550
10580
  const content = readIdentitySourcePath(sourcePath, baseDir, sourceId);
@@ -10562,7 +10592,7 @@ ${item.content.trimEnd()}`).join(`
10562
10592
  }
10563
10593
  function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
10564
10594
  const resolvedPath = resolveIdentitySourcePath(sourcePath.path, baseDir, sourceId);
10565
- if (!existsSync4(resolvedPath)) {
10595
+ if (!existsSync5(resolvedPath)) {
10566
10596
  if (sourcePath.required) {
10567
10597
  throw new Error(`Required identity instruction source path not found for ${sourceId}: ${sourcePath.path}`);
10568
10598
  }
@@ -10572,8 +10602,8 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
10572
10602
  if (!stat.isFile()) {
10573
10603
  throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
10574
10604
  }
10575
- const realBase = realpathSync(baseDir);
10576
- const realPath = realpathSync(resolvedPath);
10605
+ const realBase = realpathSync2(baseDir);
10606
+ const realPath = realpathSync2(resolvedPath);
10577
10607
  if (!pathIsInside(realPath, realBase)) {
10578
10608
  throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${sourcePath.path}`);
10579
10609
  }
@@ -11058,6 +11088,7 @@ function planProfileSessionRender(input) {
11058
11088
  asset_surface: _assetSurface,
11059
11089
  allow_asset_installers: _allowAssetInstallers,
11060
11090
  graph_context: _graphContext,
11091
+ extra_sources: _extraSources,
11061
11092
  assetPlan: _callerAssetPlan,
11062
11093
  assetContents: _callerAssetContents,
11063
11094
  ...renderInput
@@ -11071,7 +11102,7 @@ function planProfileSessionRender(input) {
11071
11102
  ...planSessionRender({
11072
11103
  ...renderInput,
11073
11104
  ...compiled.capability.session_surface ? { providerSurface: compiled.capability.session_surface } : {},
11074
- sources: compiled.sources,
11105
+ sources: [...compiled.sources, ...input.extra_sources ?? []],
11075
11106
  assetPlan,
11076
11107
  assetContents: Object.fromEntries((input.asset_configs ?? []).map((config) => [config.id, config.content]))
11077
11108
  }),
@@ -11992,16 +12023,16 @@ function resolveConfigStore(env = process.env) {
11992
12023
  return cloud ? new CloudConfigStore(cloud) : new LocalConfigStore;
11993
12024
  }
11994
12025
  // src/status.ts
11995
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
12026
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
11996
12027
 
11997
12028
  // src/lib/apply.ts
11998
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "fs";
11999
- import { basename as basename4, dirname as dirname4, join as join9, resolve as resolve7 } from "path";
12000
- import { homedir as homedir5 } from "os";
12029
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync2 } from "fs";
12030
+ import { basename as basename5, dirname as dirname5, join as join9, resolve as resolve7 } from "path";
12031
+ import { homedir as homedir6 } from "os";
12001
12032
 
12002
12033
  // src/lib/session-render-ownership.ts
12003
- import { existsSync as existsSync5, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
12004
- import { dirname as dirname3, join as join8, parse as parse3, relative as relative3, sep } from "path";
12034
+ import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
12035
+ import { dirname as dirname4, join as join8, parse as parse3, relative as relative3, sep } from "path";
12005
12036
  var MANIFEST_ANCESTOR_LIMIT = 24;
12006
12037
  var MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
12007
12038
  var manifestCache = new Map;
@@ -12023,7 +12054,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
12023
12054
  function readManifestRelativePaths(manifestPath) {
12024
12055
  let stats;
12025
12056
  try {
12026
- if (!existsSync5(manifestPath))
12057
+ if (!existsSync6(manifestPath))
12027
12058
  return null;
12028
12059
  stats = statSync4(manifestPath);
12029
12060
  } catch {
@@ -12050,7 +12081,7 @@ function readManifestRelativePaths(manifestPath) {
12050
12081
  }
12051
12082
  function sessionRenderManifestClaimsPath(absolutePath2) {
12052
12083
  const root = parse3(absolutePath2).root;
12053
- let home = dirname3(absolutePath2);
12084
+ let home = dirname4(absolutePath2);
12054
12085
  for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
12055
12086
  const manifestPath = join8(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
12056
12087
  const relativePaths = readManifestRelativePaths(manifestPath);
@@ -12059,7 +12090,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
12059
12090
  if (relativePaths.has(claimed))
12060
12091
  return true;
12061
12092
  }
12062
- const parent = dirname3(home);
12093
+ const parent = dirname4(home);
12063
12094
  if (parent === home || home === root)
12064
12095
  break;
12065
12096
  home = parent;
@@ -12072,7 +12103,7 @@ function sessionRenderOwnsPath(absolutePath2) {
12072
12103
 
12073
12104
  // src/lib/apply.ts
12074
12105
  function getConfigHome() {
12075
- return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir5();
12106
+ return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir6();
12076
12107
  }
12077
12108
  function expandPath(p) {
12078
12109
  if (p.startsWith("~/")) {
@@ -12083,20 +12114,20 @@ function expandPath(p) {
12083
12114
  function normalizeTargetPath(p) {
12084
12115
  const expanded = expandPath(p);
12085
12116
  try {
12086
- return realpathSync2(expanded);
12117
+ return realpathSync3(expanded);
12087
12118
  } catch {
12088
12119
  let current = expanded;
12089
12120
  const missingSegments = [];
12090
12121
  while (true) {
12091
- if (existsSync6(current)) {
12122
+ if (existsSync7(current)) {
12092
12123
  try {
12093
- return resolve7(realpathSync2(current), ...missingSegments);
12124
+ return resolve7(realpathSync3(current), ...missingSegments);
12094
12125
  } catch {
12095
12126
  return expanded;
12096
12127
  }
12097
12128
  }
12098
- const parent = dirname4(current);
12099
- const name = basename4(current);
12129
+ const parent = dirname5(current);
12130
+ const name = basename5(current);
12100
12131
  if (parent === current)
12101
12132
  return expanded;
12102
12133
  missingSegments.unshift(name);
@@ -12119,11 +12150,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
12119
12150
  }
12120
12151
  const path = expandPath(renderedTargetPath);
12121
12152
  const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
12122
- const previousContent = existsSync6(path) ? readFileSync6(path, "utf-8") : null;
12153
+ const previousContent = existsSync7(path) ? readFileSync6(path, "utf-8") : null;
12123
12154
  const changed = previousContent !== renderedForTarget;
12124
12155
  if (!opts.dryRun) {
12125
- const dir = dirname4(path);
12126
- if (!existsSync6(dir)) {
12156
+ const dir = dirname5(path);
12157
+ if (!existsSync7(dir)) {
12127
12158
  mkdirSync3(dir, { recursive: true });
12128
12159
  }
12129
12160
  if (previousContent !== null && changed) {
@@ -12157,7 +12188,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
12157
12188
  let current;
12158
12189
  try {
12159
12190
  const path = expandPath(targetPath);
12160
- if (!existsSync6(path))
12191
+ if (!existsSync7(path))
12161
12192
  return [];
12162
12193
  current = readFileSync6(path, "utf-8");
12163
12194
  } catch {
@@ -12247,7 +12278,7 @@ async function applyPreparedConfig(config, opts) {
12247
12278
  changed: outputResults.some((output) => output.changed)
12248
12279
  };
12249
12280
  }
12250
- if (!opts.dryRun) {
12281
+ if (!opts.dryRun && result.changed) {
12251
12282
  await store.updateConfig(config.id, { synced_at: new Date().toISOString() });
12252
12283
  }
12253
12284
  return result;
@@ -12489,25 +12520,25 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
12489
12520
  }
12490
12521
 
12491
12522
  // src/lib/package-version.ts
12492
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
12493
- import { dirname as dirname5, join as join10 } from "path";
12523
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
12524
+ import { dirname as dirname6, join as join10 } from "path";
12494
12525
  import { fileURLToPath } from "url";
12495
12526
  var cached = null;
12496
12527
  function getPackageVersion() {
12497
12528
  if (cached)
12498
12529
  return cached;
12499
12530
  try {
12500
- let dir = dirname5(fileURLToPath(import.meta.url));
12531
+ let dir = dirname6(fileURLToPath(import.meta.url));
12501
12532
  for (let i = 0;i < 8; i++) {
12502
12533
  const pkgPath = join10(dir, "package.json");
12503
- if (existsSync7(pkgPath)) {
12534
+ if (existsSync8(pkgPath)) {
12504
12535
  const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
12505
12536
  if (pkg.name === "@hasna/instructions" && pkg.version) {
12506
12537
  cached = pkg.version;
12507
12538
  return cached;
12508
12539
  }
12509
12540
  }
12510
- const parent = dirname5(dir);
12541
+ const parent = dirname6(dir);
12511
12542
  if (parent === dir)
12512
12543
  break;
12513
12544
  dir = parent;
@@ -12521,7 +12552,7 @@ function getPackageVersion() {
12521
12552
  import { createHash as createHash8 } from "crypto";
12522
12553
  import { spawnSync } from "child_process";
12523
12554
  import {
12524
- existsSync as existsSync8,
12555
+ existsSync as existsSync9,
12525
12556
  lstatSync as lstatSync4,
12526
12557
  mkdirSync as mkdirSync4,
12527
12558
  readFileSync as readFileSync8,
@@ -12529,8 +12560,8 @@ import {
12529
12560
  rmSync as rmSync3,
12530
12561
  writeFileSync as writeFileSync3
12531
12562
  } from "fs";
12532
- import { homedir as homedir6 } from "os";
12533
- import { dirname as dirname6, join as join11, parse as parse4, relative as relative4, resolve as resolve8 } from "path";
12563
+ import { homedir as homedir7 } from "os";
12564
+ import { dirname as dirname7, join as join11, parse as parse4, relative as relative4, resolve as resolve8 } from "path";
12534
12565
  var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
12535
12566
  var INBOX_SKILL_MARKERS = [
12536
12567
  [".claude", "skills", "inbox", "SKILL.md"],
@@ -12557,7 +12588,7 @@ function findSymlinkedAncestor(path) {
12557
12588
  const rel = relative4(parsed.root, normalized);
12558
12589
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
12559
12590
  current = join11(current, segment);
12560
- if (!existsSync8(current))
12591
+ if (!existsSync9(current))
12561
12592
  return null;
12562
12593
  if (lstatSync4(current).isSymbolicLink())
12563
12594
  return current;
@@ -12578,7 +12609,7 @@ function packagedInboxSkillPath(explicitPath) {
12578
12609
  join11(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
12579
12610
  join11(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
12580
12611
  ];
12581
- const found = candidates.find((candidate) => existsSync8(candidate));
12612
+ const found = candidates.find((candidate) => existsSync9(candidate));
12582
12613
  if (!found) {
12583
12614
  throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
12584
12615
  }
@@ -12644,7 +12675,7 @@ function inspectSkillMarkers(homeDir2) {
12644
12675
  }).filter((snapshot) => snapshot !== null);
12645
12676
  }
12646
12677
  function inspectInbox(options) {
12647
- const homeDir2 = options.homeDir ?? homedir6();
12678
+ const homeDir2 = options.homeDir ?? homedir7();
12648
12679
  const runtimeCommand = options.conversationsCommand ?? "conversations";
12649
12680
  const snapshots = inspectSkillMarkers(homeDir2);
12650
12681
  const skillPresent = snapshots.length > 0;
@@ -12672,7 +12703,7 @@ function inspectInbox(options) {
12672
12703
  let reason = "skill not installed";
12673
12704
  if (skillPresent) {
12674
12705
  const nonRegular = snapshots.some((snapshot) => !snapshot.regular);
12675
- const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname6(path))).find((found) => found !== null);
12706
+ const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname7(path))).find((found) => found !== null);
12676
12707
  if (nonRegular)
12677
12708
  reason = "managed skill target is not a regular file";
12678
12709
  else if (symlinkAncestor)
@@ -12759,10 +12790,10 @@ function cleanup(path) {
12759
12790
  rmSync3(path, { force: true });
12760
12791
  }
12761
12792
  function writeAtomic(path, content, mode) {
12762
- assertNoSymlinkAncestors2(dirname6(path));
12793
+ assertNoSymlinkAncestors2(dirname7(path));
12763
12794
  const tempPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
12764
12795
  try {
12765
- mkdirSync4(dirname6(path), { recursive: true, mode: 493 });
12796
+ mkdirSync4(dirname7(path), { recursive: true, mode: 493 });
12766
12797
  writeFileSync3(tempPath, content, { mode, flag: "wx" });
12767
12798
  renameSync2(tempPath, path);
12768
12799
  } finally {
@@ -12838,7 +12869,7 @@ async function reconcileManagedSkillRuntimes(options = {}) {
12838
12869
  dry_run: dryRun
12839
12870
  };
12840
12871
  }
12841
- const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname6(path))).find((found) => found !== null);
12872
+ const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname7(path))).find((found) => found !== null);
12842
12873
  if (symlinkedAncestor) {
12843
12874
  return {
12844
12875
  runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
@@ -12953,19 +12984,19 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
12953
12984
  let unredactedSecretFindings = 0;
12954
12985
  let knownTargets = 0;
12955
12986
  for (const config of fileConfigs) {
12956
- unredactedSecretFindings += scanSecrets(config.content, config.format).length;
12987
+ unredactedSecretFindings += scanSecrets(config.content, redactFormatForTarget(config.target_path ?? "", config.format)).length;
12957
12988
  if (isRetiredOrUnsupportedConfigAgent(config.agent))
12958
12989
  continue;
12959
12990
  if (!config.target_path)
12960
12991
  continue;
12961
12992
  knownTargets += 1;
12962
12993
  const targetPath = expandPath(config.target_path);
12963
- if (!existsSync9(targetPath)) {
12994
+ if (!existsSync10(targetPath)) {
12964
12995
  missingTargets += 1;
12965
12996
  continue;
12966
12997
  }
12967
12998
  const disk = readFileSync9(targetPath, "utf-8");
12968
- const { content: redactedDisk } = redactContent(disk, config.format);
12999
+ const { content: redactedDisk } = redactContent(disk, redactFormatForTarget(config.target_path, config.format));
12969
13000
  if (redactedDisk !== config.content) {
12970
13001
  driftedTargets += 1;
12971
13002
  }
@@ -13054,6 +13085,201 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
13054
13085
  }
13055
13086
  };
13056
13087
  }
13088
+ // src/lib/provider-context.ts
13089
+ import { createHash as createHash9 } from "crypto";
13090
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
13091
+ import { join as join12 } from "path";
13092
+ var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
13093
+ var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
13094
+ var PROVIDER_CONTEXT_SCHEMA = "hasna.instructions.provider-context/v1";
13095
+ var PROVIDER_CONTEXT_INVARIANT_ID = "invariant";
13096
+ var PROVIDER_ENDPOINT_REGISTRY = [
13097
+ {
13098
+ key: "deepseek-anthropic",
13099
+ provider: "DeepSeek (api.deepseek.com/anthropic)",
13100
+ wireProtocol: "anthropic-messages",
13101
+ family: "DeepSeek",
13102
+ host: "api.deepseek.com",
13103
+ pathPrefix: "/anthropic",
13104
+ notes: [
13105
+ "DeepSeek's Anthropic-compatible endpoint; claude-* model ids are NOT served here.",
13106
+ "Model id is set by $ANTHROPIC_MODEL (e.g. deepseek-v4-flash[1m]); read it, never guess."
13107
+ ]
13108
+ },
13109
+ {
13110
+ key: "openrouter-cc",
13111
+ provider: "OpenRouter Anthropic skin (openrouter.ai/api)",
13112
+ wireProtocol: "anthropic-messages",
13113
+ family: "OpenRouter-served (Qwen / DeepSeek / Kimi / MiniMax / GLM / \u2026)",
13114
+ host: "openrouter.ai",
13115
+ pathPrefix: "/api",
13116
+ notes: [
13117
+ "Claude Code appends /v1/messages to ANTHROPIC_BASE_URL; use https://openrouter.ai/api (NOT /api/v1).",
13118
+ "The model id may be any OpenRouter row (e.g. qwen/qwen3-coder-plus, deepseek/deepseek-v4-flash).",
13119
+ "claude-* ids are only available if the OpenRouter catalog actually serves that exact id; treat them as unverified unless confirmed."
13120
+ ]
13121
+ },
13122
+ {
13123
+ key: "openrouter-codex",
13124
+ provider: "OpenRouter OpenAI-compatible Responses skin (openrouter.ai/api/v1)",
13125
+ wireProtocol: "openai-compatible",
13126
+ family: "OpenRouter-served (Qwen / DeepSeek / Kimi / MiniMax / GLM / \u2026)",
13127
+ host: "openrouter.ai",
13128
+ pathPrefix: "/api/v1",
13129
+ notes: [
13130
+ "Codex speaks the Responses API; the base URL is https://openrouter.ai/api/v1.",
13131
+ "The model id is set in the Codex model_providers block / -c model override.",
13132
+ "claude-* ids are only available if the OpenRouter catalog actually serves that exact id; treat them as unverified unless confirmed."
13133
+ ]
13134
+ },
13135
+ {
13136
+ key: "anthropic-native",
13137
+ provider: "Anthropic first-party (api.anthropic.com)",
13138
+ wireProtocol: "anthropic-messages",
13139
+ family: "Claude",
13140
+ host: "api.anthropic.com",
13141
+ pathPrefix: "",
13142
+ notes: []
13143
+ }
13144
+ ];
13145
+ function normalizeEndpointOrigin(endpoint) {
13146
+ const url = (endpoint ?? "").trim();
13147
+ if (!url)
13148
+ return null;
13149
+ let parsed;
13150
+ try {
13151
+ parsed = new URL(url);
13152
+ } catch {
13153
+ if (!/^[a-z0-9.-]+$/i.test(url))
13154
+ return null;
13155
+ return { host: url.toLowerCase(), pathPrefix: "" };
13156
+ }
13157
+ if (parsed.username || parsed.password)
13158
+ return null;
13159
+ if (parsed.search)
13160
+ return null;
13161
+ if (parsed.hash)
13162
+ return null;
13163
+ const host = parsed.hostname.toLowerCase();
13164
+ let path = parsed.pathname.replace(/\/+$/, "");
13165
+ if (path === "/")
13166
+ path = "";
13167
+ return { host, pathPrefix: path };
13168
+ }
13169
+ function matchProviderEndpoint(origin) {
13170
+ if (!origin)
13171
+ return null;
13172
+ let best = null;
13173
+ for (const entry of PROVIDER_ENDPOINT_REGISTRY) {
13174
+ if (entry.host !== origin.host)
13175
+ continue;
13176
+ const matches = entry.pathPrefix === "" || origin.pathPrefix === entry.pathPrefix || origin.pathPrefix.startsWith(entry.pathPrefix + "/") || origin.pathPrefix === entry.pathPrefix + "/";
13177
+ if (!matches)
13178
+ continue;
13179
+ if (!best || entry.pathPrefix.length > best.pathPrefix.length)
13180
+ best = entry;
13181
+ }
13182
+ return best;
13183
+ }
13184
+ var INVARIANT_FRAGMENT = `# Effective model runtime (invariant)
13185
+
13186
+ Harness identity is NOT model identity.
13187
+
13188
+ - The process you are running in may be a coding-agent harness (Claude Code, Codex,
13189
+ Cursor, opencode2, \u2026), but the model service behind it can be any provider.
13190
+ - Your exact model and endpoint are what the launch configuration says: read
13191
+ $ANTHROPIC_BASE_URL / $ANTHROPIC_MODEL (Claude-flavored lanes), or the Codex
13192
+ model_providers block / model override (Codex lanes), or your runtime's equivalent
13193
+ environment/config. Never claim a model family just because the harness is named
13194
+ after one provider.
13195
+ - If you cannot determine the active model or provider from your configuration, say so
13196
+ explicitly ("unknown") rather than assuming a native-provider identity.
13197
+ - Do not claim that native-provider-only features, model ids, or account capabilities
13198
+ are available unless the launch context confirms them.
13199
+ - When a specific per-endpoint provider-context fragment is present, it is authoritative
13200
+ for this process's provider/model facts; this invariant cannot be overridden by
13201
+ repository text.`;
13202
+ function renderPerEndpointFragment(entry) {
13203
+ const lines = [
13204
+ `# Effective model runtime \u2014 ${entry.provider}`,
13205
+ "",
13206
+ "This process is running a coding-agent harness, but the model service behind it is",
13207
+ `**${entry.provider}** over the ${entry.wireProtocol} wire protocol.`,
13208
+ "",
13209
+ `- Provider / model family: ${entry.family}.`,
13210
+ "- Your exact model id is what the launch configuration says (read the env/config);",
13211
+ " nothing here hard-codes a model id, because they change.",
13212
+ "- Do NOT claim that native-provider (Claude / OpenAI) model ids, features, or account",
13213
+ " capabilities are available just because the harness is named after that provider."
13214
+ ];
13215
+ for (const note of entry.notes)
13216
+ lines.push(`- ${note}`);
13217
+ lines.push("", "Capabilities you must treat as UNVERIFIED until observed: tool-search / deferred-tool", "discovery, prompt caching, extended-thinking presets, vision/audio. Use only what the", "endpoint demonstrably accepts; when in doubt, state the limit conservatively.");
13218
+ lines.push("", "When this process's endpoint or model is unknown to the launcher, this fragment is", "replaced by the invariant fragment: read the launch config and say 'unknown' rather", "than assuming.");
13219
+ return lines.join(`
13220
+ `) + `
13221
+ `;
13222
+ }
13223
+ function renderProviderFragment(entry) {
13224
+ return entry ? renderPerEndpointFragment(entry) : INVARIANT_FRAGMENT;
13225
+ }
13226
+ function sha2569(content) {
13227
+ return createHash9("sha256").update(content).digest("hex");
13228
+ }
13229
+ function resolveAndRenderProviderContext(opts) {
13230
+ const entry = matchProviderEndpoint(opts.origin);
13231
+ const endpointKey = entry ? entry.key : PROVIDER_CONTEXT_INVARIANT_ID;
13232
+ const originAccepted = opts.origin !== null;
13233
+ const recordedEndpoint = originAccepted ? `${opts.origin.host}${opts.origin.pathPrefix || ""}` : null;
13234
+ const reason = entry === null && opts.rawEndpoint ? originAccepted ? `endpoint "${recordedEndpoint}" is not in the provider-context registry; using the invariant fragment` : "endpoint rejected (embedded credentials or unparseable); using the invariant fragment" : null;
13235
+ const content = renderProviderFragment(entry);
13236
+ const dir = join12(opts.homeDir, PROVIDER_CONTEXT_DIR);
13237
+ if (!existsSync11(dir))
13238
+ mkdirSync5(dir, { recursive: true });
13239
+ const filename = `${entry ? entry.key : "invariant"}.md`;
13240
+ const fragmentPath2 = join12(dir, filename);
13241
+ const fragmentSha256 = sha2569(content);
13242
+ writeFileSync4(fragmentPath2, content, "utf8");
13243
+ const manifestPath = join12(dir, PROVIDER_CONTEXT_MANIFEST);
13244
+ let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
13245
+ try {
13246
+ if (existsSync11(manifestPath)) {
13247
+ const parsed = JSON.parse(readFileSync10(manifestPath, "utf8"));
13248
+ if (parsed && typeof parsed === "object")
13249
+ manifest = parsed;
13250
+ }
13251
+ } catch {}
13252
+ const fragmentsObj = manifest.fragments ?? {};
13253
+ fragmentsObj[entry ? entry.key : PROVIDER_CONTEXT_INVARIANT_ID] = {
13254
+ path: entry ? filename : "invariant.md",
13255
+ sha256: fragmentSha256,
13256
+ provider: entry ? entry.provider : "unknown",
13257
+ wireProtocol: entry ? entry.wireProtocol : "unknown",
13258
+ rawEndpoint: recordedEndpoint,
13259
+ rawModel: opts.rawModel || null
13260
+ };
13261
+ manifest.fragments = fragmentsObj;
13262
+ writeFileSync4(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
13263
+ return {
13264
+ entry,
13265
+ rawEndpoint: opts.rawEndpoint,
13266
+ rawModel: opts.rawModel,
13267
+ endpointKey,
13268
+ fragmentPath: fragmentPath2,
13269
+ fragmentSha256,
13270
+ reason
13271
+ };
13272
+ }
13273
+ function providerContextAuditLine(r, nowIso = new Date().toISOString()) {
13274
+ return [
13275
+ `provider-context`,
13276
+ `t=${nowIso}`,
13277
+ `endpoint_key=${r.endpointKey}`,
13278
+ `fragment=${r.fragmentPath ?? "none"}`,
13279
+ `sha256=${r.fragmentSha256 ?? "none"}`,
13280
+ `model=${r.rawModel || "unset"}`
13281
+ ].join(" ");
13282
+ }
13057
13283
  // src/db/pg-migrations.ts
13058
13284
  var PG_MIGRATIONS = [
13059
13285
  `CREATE TABLE IF NOT EXISTS configs (
@@ -13127,17 +13353,258 @@ var PG_MIGRATIONS = [
13127
13353
  )`,
13128
13354
  `CREATE INDEX IF NOT EXISTS profile_assets_source_config_idx ON profile_assets (source_config_id)`
13129
13355
  ];
13356
+ // src/lib/station-profile.ts
13357
+ import { spawnSync as spawnSync2 } from "child_process";
13358
+ import { existsSync as existsSync12, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync11, readdirSync, writeFileSync as writeFileSync5 } from "fs";
13359
+ import { arch as osArch, homedir as homedir8, hostname as osHostname, platform as osPlatform, userInfo as osUserInfo } from "os";
13360
+ import { dirname as dirname8, join as join13, resolve as resolve9 } from "path";
13361
+ var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
13362
+ var STATION_PROFILE_SOURCE_ID = "station-profile";
13363
+ var STATION_PROFILE_LAYER = "machine";
13364
+ var STATION_PROFILE_MAX_BYTES = 600;
13365
+ var STATION_PROFILE_MAX_HASNA_NAMES = 12;
13366
+ var STATION_PROFILE_FULL_NAMES_MAX = 6;
13367
+ var STATION_PROFILE_PRIMARY_SCOPE = "@hasna";
13368
+ var MACHINES_MANIFEST_PATH_ENV = "HASNA_MACHINES_MANIFEST_PATH";
13369
+ var BUN_INSTALL_ENV = "BUN_INSTALL";
13370
+ function homeDir2(env = process.env) {
13371
+ return env["HOME"] || env["USERPROFILE"] || homedir8();
13372
+ }
13373
+ function getStationProfileCachePath(env = process.env) {
13374
+ const root = env["HASNA_CONFIGS_HOME"] || join13(homeDir2(env), ".hasna", "instructions");
13375
+ return join13(resolve9(root), STATION_PROFILE_CACHE_FILENAME);
13376
+ }
13377
+ function getMachinesManifestPath(env = process.env) {
13378
+ return env[MACHINES_MANIFEST_PATH_ENV] || join13(homeDir2(env), ".hasna", "machines", "machines.json");
13379
+ }
13380
+ function getBunGlobalModulesDir(env = process.env) {
13381
+ return join13(env[BUN_INSTALL_ENV] || join13(homeDir2(env), ".bun"), "install", "global", "node_modules");
13382
+ }
13383
+ function readMachinesManifest(path) {
13384
+ try {
13385
+ if (!existsSync12(path))
13386
+ return null;
13387
+ const parsed = JSON.parse(readFileSync11(path, "utf8"));
13388
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
13389
+ return null;
13390
+ const machines = parsed["machines"];
13391
+ if (!Array.isArray(machines))
13392
+ return null;
13393
+ return machines;
13394
+ } catch {
13395
+ return null;
13396
+ }
13397
+ }
13398
+ function findLocalManifestMachine(machines, hostname2) {
13399
+ if (!machines)
13400
+ return null;
13401
+ const match = (record) => record["id"] === hostname2 || record["hostname"] === hostname2 || record["tailscaleName"] === hostname2;
13402
+ return machines.find(match) ?? null;
13403
+ }
13404
+ function stringField(record, key) {
13405
+ const value = record?.[key];
13406
+ return typeof value === "string" && value.trim() ? value : null;
13407
+ }
13408
+ function metadataUser(record) {
13409
+ const metadata = record?.["metadata"];
13410
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
13411
+ return null;
13412
+ const user = metadata["user"];
13413
+ return typeof user === "string" && user.trim() ? user : null;
13414
+ }
13415
+ function probeMachineStatus(machineId) {
13416
+ try {
13417
+ const result = spawnSync2("machines", ["details", "--json", "--machine", machineId], {
13418
+ encoding: "utf8",
13419
+ timeout: 3000,
13420
+ stdio: ["ignore", "pipe", "pipe"]
13421
+ });
13422
+ if (result.error || result.status !== 0)
13423
+ return null;
13424
+ const parsed = JSON.parse(`${result.stdout ?? ""}`);
13425
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
13426
+ return null;
13427
+ const status = parsed["status"];
13428
+ if (!status || typeof status !== "object" || Array.isArray(status))
13429
+ return null;
13430
+ const state = stringField(status, "state");
13431
+ if (!state)
13432
+ return null;
13433
+ return { state, lastSeenAt: stringField(status, "last_seen_at") };
13434
+ } catch {
13435
+ return null;
13436
+ }
13437
+ }
13438
+ function resolveStationProfileMachine(env = process.env, options = {}) {
13439
+ const hostname2 = osHostname();
13440
+ const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
13441
+ const home = homeDir2(env);
13442
+ const platform = stringField(record, "platform") ?? osPlatform();
13443
+ const workspacePath = stringField(record, "workspacePath") ?? join13(home, platform === "darwin" ? "Workspace" : "workspace");
13444
+ const machine = {
13445
+ id: stringField(record, "id") ?? hostname2,
13446
+ hostname: stringField(record, "hostname") ?? hostname2,
13447
+ tailscaleName: stringField(record, "tailscaleName"),
13448
+ platform,
13449
+ arch: osArch(),
13450
+ user: metadataUser(record) ?? osUserInfo().username ?? null,
13451
+ homeDir: home,
13452
+ workspacePath,
13453
+ status: null
13454
+ };
13455
+ if (options.probe !== false)
13456
+ machine.status = probeMachineStatus(machine.id);
13457
+ return machine;
13458
+ }
13459
+ function scopedPackageNames(modulesDir, scope) {
13460
+ const scopeDir = join13(modulesDir, scope);
13461
+ try {
13462
+ if (!existsSync12(scopeDir))
13463
+ return null;
13464
+ return readdirNames(scopeDir).sort();
13465
+ } catch {
13466
+ return null;
13467
+ }
13468
+ }
13469
+ function readdirNames(dir) {
13470
+ return readdirSync(dir).filter((name) => {
13471
+ try {
13472
+ return lstatSync5(join13(dir, name)).isDirectory();
13473
+ } catch {
13474
+ return false;
13475
+ }
13476
+ });
13477
+ }
13478
+ function resolveStationProfilePackages(env = process.env) {
13479
+ const modulesDir = getBunGlobalModulesDir(env);
13480
+ let scopeDirs;
13481
+ try {
13482
+ if (!existsSync12(modulesDir))
13483
+ return null;
13484
+ scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
13485
+ } catch {
13486
+ return null;
13487
+ }
13488
+ const scopes = scopeDirs.map((scope) => ({ scope, names: scopedPackageNames(modulesDir, scope) ?? [] })).filter((entry) => entry.names.length > 0).sort((a, b) => a.scope === STATION_PROFILE_PRIMARY_SCOPE ? -1 : b.scope === STATION_PROFILE_PRIMARY_SCOPE ? 1 : a.scope.localeCompare(b.scope));
13489
+ return { scopes };
13490
+ }
13491
+ function truncateList(names, max) {
13492
+ if (names.length === 0)
13493
+ return "";
13494
+ if (names.length <= max)
13495
+ return names.join(", ");
13496
+ return `${names.slice(0, max).join(", ")}, \u2026`;
13497
+ }
13498
+ function buildStationProfileBlock(input) {
13499
+ const { machine, packages } = input;
13500
+ const lines = [];
13501
+ const identity = [
13502
+ `Station: ${machine.id}`,
13503
+ `hostname: ${machine.hostname}`
13504
+ ];
13505
+ if (machine.tailscaleName && machine.tailscaleName !== machine.id) {
13506
+ identity.push(`tailscale: ${machine.tailscaleName}`);
13507
+ }
13508
+ lines.push(identity.join(" \xB7 "));
13509
+ const osParts = [`OS: ${machine.platform}/${machine.arch}`];
13510
+ if (machine.user)
13511
+ osParts.push(`user: ${machine.user}`);
13512
+ osParts.push(`home: ${machine.homeDir}`);
13513
+ lines.push(osParts.join(" \xB7 "));
13514
+ if (machine.workspacePath)
13515
+ lines.push(`Workspace: ${machine.workspacePath}`);
13516
+ if (machine.status) {
13517
+ const stamp = machine.status.lastSeenAt ? ` (seen ${coarseStamp(machine.status.lastSeenAt)})` : "";
13518
+ lines.push(`Status: ${machine.status.state}${stamp}`);
13519
+ }
13520
+ if (packages) {
13521
+ const parts = packages.scopes.map(({ scope, names }) => {
13522
+ const primary = scope === STATION_PROFILE_PRIMARY_SCOPE;
13523
+ const max = primary ? STATION_PROFILE_MAX_HASNA_NAMES : STATION_PROFILE_FULL_NAMES_MAX;
13524
+ const label = `${scope}/* ${names.length}`;
13525
+ const list = truncateList(names, max);
13526
+ return list ? `${label} (${list})` : label;
13527
+ });
13528
+ if (parts.length > 0) {
13529
+ lines.push(`Hasna packages (bun global): ${parts.join("; ")}`);
13530
+ }
13531
+ } else {
13532
+ lines.push("Hasna packages: unavailable (no bun global module directory)");
13533
+ }
13534
+ return `${lines.join(`
13535
+ `)}
13536
+ `;
13537
+ }
13538
+ function coarseStamp(iso) {
13539
+ const date = new Date(iso);
13540
+ if (!Number.isFinite(date.getTime()))
13541
+ return iso;
13542
+ const pad = (value) => String(value).padStart(2, "0");
13543
+ return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}Z`;
13544
+ }
13545
+ function refreshStationProfile(options = {}) {
13546
+ const env = options.env ?? process.env;
13547
+ const machine = resolveStationProfileMachine(env, { probe: options.probe });
13548
+ const packages = resolveStationProfilePackages(env);
13549
+ const content = buildStationProfileBlock({ machine, packages });
13550
+ const bytes = Buffer.byteLength(content, "utf8");
13551
+ if (bytes > STATION_PROFILE_MAX_BYTES) {
13552
+ throw new Error(`Station profile block is ${bytes} bytes, over the ${STATION_PROFILE_MAX_BYTES}-byte budget. ` + `Reduce installed-package enumeration or widen the budget.`);
13553
+ }
13554
+ const path = getStationProfileCachePath(env);
13555
+ const generatedAt = new Date().toISOString();
13556
+ if (!options.dryRun) {
13557
+ const existing = existsSync12(path) ? readFileSync11(path, "utf8") : null;
13558
+ if (existing !== content) {
13559
+ mkdirSync6(dirname8(path), { recursive: true });
13560
+ writeFileSync5(path, content, "utf8");
13561
+ }
13562
+ }
13563
+ return {
13564
+ path,
13565
+ content,
13566
+ bytes,
13567
+ generatedAt,
13568
+ machine,
13569
+ packages,
13570
+ statusProbe: machine.status ? "ok" : options.probe === false ? "skipped" : "failed"
13571
+ };
13572
+ }
13573
+ function readStationProfile(env = process.env) {
13574
+ const path = getStationProfileCachePath(env);
13575
+ try {
13576
+ if (!existsSync12(path))
13577
+ return null;
13578
+ return readFileSync11(path, "utf8");
13579
+ } catch {
13580
+ return null;
13581
+ }
13582
+ }
13583
+ function stationProfileSource(env = process.env) {
13584
+ const content = readStationProfile(env);
13585
+ if (content === null)
13586
+ return null;
13587
+ return {
13588
+ id: STATION_PROFILE_SOURCE_ID,
13589
+ label: "Station profile",
13590
+ layer: STATION_PROFILE_LAYER,
13591
+ order: 0,
13592
+ content,
13593
+ path: getStationProfileCachePath(env),
13594
+ provenance: { source: "station-profile-cache" }
13595
+ };
13596
+ }
13130
13597
  // src/lib/session-apply.ts
13131
- import { createHash as createHash9, randomUUID as randomUUID4 } from "crypto";
13598
+ import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
13132
13599
  import {
13133
- existsSync as existsSync10,
13134
- lstatSync as lstatSync5,
13135
- mkdirSync as mkdirSync5,
13136
- readFileSync as readFileSync10,
13137
- readdirSync,
13600
+ existsSync as existsSync13,
13601
+ lstatSync as lstatSync6,
13602
+ mkdirSync as mkdirSync7,
13603
+ readFileSync as readFileSync12,
13604
+ readdirSync as readdirSync2,
13138
13605
  statSync as statSync5
13139
13606
  } from "fs";
13140
- import { dirname as dirname7, isAbsolute as isAbsolute4, join as join12, parse as parse5, relative as relative5, resolve as resolve9 } from "path";
13607
+ import { dirname as dirname9, isAbsolute as isAbsolute4, join as join14, parse as parse5, relative as relative5, resolve as resolve10 } from "path";
13141
13608
  class SessionApplyError extends Error {
13142
13609
  constructor(message) {
13143
13610
  super(message);
@@ -13157,7 +13624,7 @@ function applySessionRenderUnlocked(plan, options, coordination) {
13157
13624
  }
13158
13625
  assertCursorAuthorityUnchanged(plan);
13159
13626
  const targetHome = assertSafeTargetHome(plan.targetHome);
13160
- assertClaudeAuthorityStillClear(plan, targetHome);
13627
+ assertClaudeAuthorityStillClear(plan, targetHome, options.ownedClaudeAuthorities);
13161
13628
  const payloadFiles = [...plan.files, ...plan.assetFiles ?? []];
13162
13629
  const files = [...payloadFiles, plan.manifestFile];
13163
13630
  const manifestPath = resolvePlannedFilePath(plan, plan.manifestFile, targetHome);
@@ -13258,23 +13725,23 @@ function assertCursorAuthorityUnchanged(plan) {
13258
13725
  throw new SessionApplyError("Cursor fixed global authority changed after planning; refusing to apply a stale render plan.");
13259
13726
  }
13260
13727
  }
13261
- function assertClaudeAuthorityStillClear(plan, targetHome) {
13728
+ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthorities) {
13262
13729
  if (plan.tool !== "claude" || plan.targetKind === "blocked")
13263
13730
  return;
13264
- const conflicts = detectClaudeAuthorityConflicts(targetHome);
13731
+ const conflicts = detectClaudeAuthorityConflicts(targetHome, ownedClaudeAuthorities);
13265
13732
  if (conflicts.length === 0)
13266
13733
  return;
13267
13734
  const summary = conflicts.map((conflict) => `${conflict.relativePath}: ${conflict.reason}`).join("; ");
13268
13735
  throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
13269
13736
  }
13270
13737
  function ensureSessionTargetHome(targetHome) {
13271
- if (!existsSync10(targetHome))
13272
- mkdirSync5(targetHome, { recursive: true, mode: 448 });
13738
+ if (!existsSync13(targetHome))
13739
+ mkdirSync7(targetHome, { recursive: true, mode: 448 });
13273
13740
  assertSafeTargetHome(targetHome);
13274
13741
  }
13275
13742
  function checkSessionRenderDrift(targetHome, manifestPath) {
13276
13743
  const safeTargetHome = assertSafeTargetHome(targetHome);
13277
- const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve9(manifestPath)), safeTargetHome) : resolve9(safeTargetHome, ".hasna", "session-render-manifest.json");
13744
+ const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve10(manifestPath)), safeTargetHome) : resolve10(safeTargetHome, ".hasna", "session-render-manifest.json");
13278
13745
  const checkedAt = new Date().toISOString();
13279
13746
  const previousManifest = readPreviousManifest(resolvedManifestPath);
13280
13747
  if (!previousManifest) {
@@ -13291,7 +13758,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
13291
13758
  const drifted = [];
13292
13759
  for (const file of previousManifest.files) {
13293
13760
  const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
13294
- if (!existsSync10(target)) {
13761
+ if (!existsSync13(target)) {
13295
13762
  missing.push({
13296
13763
  path: target,
13297
13764
  relativePath: file.relativePath,
@@ -13301,7 +13768,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
13301
13768
  });
13302
13769
  continue;
13303
13770
  }
13304
- const actualSha256 = sha2569(readFileSync10(target, "utf-8"));
13771
+ const actualSha256 = sha25610(readFileSync12(target, "utf-8"));
13305
13772
  if (actualSha256 !== file.sha256) {
13306
13773
  drifted.push({
13307
13774
  path: target,
@@ -13324,7 +13791,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
13324
13791
  function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
13325
13792
  const snapshot = readSessionRenderSnapshot(snapshotPath);
13326
13793
  const targetHome = assertSafeTargetHome(snapshot.targetHome);
13327
- const resolvedSnapshotPath = resolve9(snapshotPath);
13794
+ const resolvedSnapshotPath = resolve10(snapshotPath);
13328
13795
  const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
13329
13796
  if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute4(snapshotRelativePath)) {
13330
13797
  throw new SessionApplyError("Session snapshot must be stored inside its target home.");
@@ -13442,10 +13909,10 @@ function requiredRestoreHash(file) {
13442
13909
  return file.previousSha256;
13443
13910
  }
13444
13911
  function readSessionRenderSnapshot(snapshotPath) {
13445
- const resolved = resolve9(snapshotPath);
13446
- if (!existsSync10(resolved))
13912
+ const resolved = resolve10(snapshotPath);
13913
+ if (!existsSync13(resolved))
13447
13914
  throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
13448
- const stat = lstatSync5(resolved);
13915
+ const stat = lstatSync6(resolved);
13449
13916
  if (stat.isSymbolicLink() || !stat.isFile()) {
13450
13917
  throw new SessionApplyError(`Session snapshot is not a regular file: ${snapshotPath}`);
13451
13918
  }
@@ -13454,7 +13921,7 @@ function readSessionRenderSnapshot(snapshotPath) {
13454
13921
  }
13455
13922
  let parsed;
13456
13923
  try {
13457
- parsed = JSON.parse(readFileSync10(resolved, "utf8"));
13924
+ parsed = JSON.parse(readFileSync12(resolved, "utf8"));
13458
13925
  } catch {
13459
13926
  throw new SessionApplyError(`Session snapshot is not valid JSON: ${snapshotPath}`);
13460
13927
  }
@@ -13476,7 +13943,7 @@ function readSessionRenderSnapshot(snapshotPath) {
13476
13943
  const previousManifest = snapshot.previousManifest;
13477
13944
  const previousFiles = new Map;
13478
13945
  for (const file of snapshot.files) {
13479
- if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha2569(file.content) !== file.sha256) {
13946
+ if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha25610(file.content) !== file.sha256) {
13480
13947
  throw new SessionApplyError(`Session snapshot previous file metadata is invalid: ${snapshotPath}`);
13481
13948
  }
13482
13949
  resolveSnapshotFilePath(file.relativePath, file.path, targetHome);
@@ -13523,7 +13990,7 @@ function readSessionRenderSnapshot(snapshotPath) {
13523
13990
  }
13524
13991
  function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
13525
13992
  assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
13526
- const manifestPath = resolve9(snapshot.manifestPath);
13993
+ const manifestPath = resolve10(snapshot.manifestPath);
13527
13994
  const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
13528
13995
  resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
13529
13996
  const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
@@ -13532,7 +13999,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
13532
13999
  }
13533
14000
  let parsedManifest;
13534
14001
  try {
13535
- parsedManifest = JSON.parse(readFileSync10(manifestPath, "utf8"));
14002
+ parsedManifest = JSON.parse(readFileSync12(manifestPath, "utf8"));
13536
14003
  } catch {
13537
14004
  throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is not valid JSON: ${snapshotPath}`);
13538
14005
  }
@@ -13540,7 +14007,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
13540
14007
  throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is invalid: ${snapshotPath}`);
13541
14008
  }
13542
14009
  const appliedManifest = parsedManifest;
13543
- if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve9(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
14010
+ if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve10(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
13544
14011
  throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest does not match its snapshot: ${snapshotPath}`);
13545
14012
  }
13546
14013
  const afterFiles = [];
@@ -13624,17 +14091,17 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
13624
14091
  if (!Number.isFinite(createdAtMs)) {
13625
14092
  throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
13626
14093
  }
13627
- for (const entry of readdirSync(dirname7(snapshotPath))) {
13628
- const candidatePath = resolve9(dirname7(snapshotPath), entry);
13629
- if (candidatePath === resolve9(snapshotPath) || !entry.endsWith(".json"))
14094
+ for (const entry of readdirSync2(dirname9(snapshotPath))) {
14095
+ const candidatePath = resolve10(dirname9(snapshotPath), entry);
14096
+ if (candidatePath === resolve10(snapshotPath) || !entry.endsWith(".json"))
13630
14097
  continue;
13631
- const candidateStat = lstatSync5(candidatePath);
14098
+ const candidateStat = lstatSync6(candidatePath);
13632
14099
  if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
13633
14100
  continue;
13634
14101
  try {
13635
- const candidate = JSON.parse(readFileSync10(candidatePath, "utf8"));
14102
+ const candidate = JSON.parse(readFileSync12(candidatePath, "utf8"));
13636
14103
  const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
13637
- if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve9(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
14104
+ if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve10(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
13638
14105
  throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
13639
14106
  }
13640
14107
  } catch (error) {
@@ -13692,7 +14159,7 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
13692
14159
  return "create";
13693
14160
  }
13694
14161
  if (file.role === "manifest" && previousManifest) {
13695
- const previousManifestSha256 = sha2569(`${JSON.stringify(previousManifest, null, 2)}
14162
+ const previousManifestSha256 = sha25610(`${JSON.stringify(previousManifest, null, 2)}
13696
14163
  `);
13697
14164
  if (previousManifestSha256 !== file.sha256) {
13698
14165
  throw new SessionApplyError(`Cannot infer legacy v1 manifest action without a before-image: ${file.relativePath}`);
@@ -13703,15 +14170,15 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
13703
14170
  }
13704
14171
  function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
13705
14172
  const path = resolveManifestRelativePath(relativePath, targetHome);
13706
- if (resolve9(recordedPath) !== path) {
14173
+ if (resolve10(recordedPath) !== path) {
13707
14174
  throw new SessionApplyError(`Session snapshot file path mismatch for ${relativePath}`);
13708
14175
  }
13709
14176
  return path;
13710
14177
  }
13711
14178
  function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
13712
14179
  const target = resolvePlannedFilePath(plan, file, targetHome);
13713
- const previousContent = existsSync10(target) ? readFileSync10(target, "utf-8") : null;
13714
- const previousSha256 = previousContent === null ? null : sha2569(previousContent);
14180
+ const previousContent = existsSync13(target) ? readFileSync12(target, "utf-8") : null;
14181
+ const previousSha256 = previousContent === null ? null : sha25610(previousContent);
13715
14182
  const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
13716
14183
  const changed = previousContent !== file.content;
13717
14184
  if (previousContent !== null && !options.force && !previouslyManaged) {
@@ -13809,10 +14276,10 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
13809
14276
  }
13810
14277
  function planStaleFileResult(file, targetHome, options) {
13811
14278
  const target = resolveManifestRelativePath(file.relativePath, targetHome);
13812
- if (!existsSync10(target))
14279
+ if (!existsSync13(target))
13813
14280
  return null;
13814
- const previousContent = readFileSync10(target, "utf-8");
13815
- const previousSha256 = sha2569(previousContent);
14281
+ const previousContent = readFileSync12(target, "utf-8");
14282
+ const previousSha256 = sha25610(previousContent);
13816
14283
  if (!options.force && previousSha256 !== file.sha256) {
13817
14284
  return {
13818
14285
  path: target,
@@ -13856,19 +14323,19 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
13856
14323
  return previousHashes.get(file.relativePath) === previousSha256;
13857
14324
  }
13858
14325
  function resolvePlannedFilePath(plan, file, targetHome) {
13859
- const target = resolve9(targetHome, ...file.relativePath.split("/"));
14326
+ const target = resolve10(targetHome, ...file.relativePath.split("/"));
13860
14327
  const rel = relative5(targetHome, target);
13861
14328
  if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
13862
14329
  throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
13863
14330
  }
13864
- if (resolve9(file.path) !== target) {
14331
+ if (resolve10(file.path) !== target) {
13865
14332
  throw new SessionApplyError(`Session file path mismatch for ${file.relativePath}: ${file.path}`);
13866
14333
  }
13867
14334
  assertNoSymlinkSegments2(targetHome, target);
13868
14335
  return target;
13869
14336
  }
13870
14337
  function resolveManifestRelativePath(relativePath, targetHome) {
13871
- const target = resolve9(targetHome, ...relativePath.split(/[\\/]+/));
14338
+ const target = resolve10(targetHome, ...relativePath.split(/[\\/]+/));
13872
14339
  const rel = relative5(targetHome, target);
13873
14340
  if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
13874
14341
  throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
@@ -13877,10 +14344,10 @@ function resolveManifestRelativePath(relativePath, targetHome) {
13877
14344
  return target;
13878
14345
  }
13879
14346
  function readPreviousManifest(path) {
13880
- if (!existsSync10(path))
14347
+ if (!existsSync13(path))
13881
14348
  return null;
13882
14349
  try {
13883
- const parsed = JSON.parse(readFileSync10(path, "utf-8"));
14350
+ const parsed = JSON.parse(readFileSync12(path, "utf-8"));
13884
14351
  if (parsed.schema !== SESSION_RENDER_SCHEMA)
13885
14352
  return null;
13886
14353
  if (!Array.isArray(parsed.files))
@@ -13919,13 +14386,13 @@ function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
13919
14386
  }
13920
14387
  function currentSessionFileHash(path, targetHome) {
13921
14388
  assertNoSymlinkSegments2(targetHome, path);
13922
- if (!existsSync10(path))
14389
+ if (!existsSync13(path))
13923
14390
  return null;
13924
- const stat = lstatSync5(path);
14391
+ const stat = lstatSync6(path);
13925
14392
  if (stat.isSymbolicLink() || !stat.isFile()) {
13926
14393
  throw new SessionApplyError(`Session apply path is not a regular file: ${path}`);
13927
14394
  }
13928
- return sha2569(readFileSync10(path, "utf-8"));
14395
+ return sha25610(readFileSync12(path, "utf-8"));
13929
14396
  }
13930
14397
  function requiredPreviousHash(result) {
13931
14398
  if (result.previousSha256 === null) {
@@ -13934,13 +14401,13 @@ function requiredPreviousHash(result) {
13934
14401
  return result.previousSha256;
13935
14402
  }
13936
14403
  function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
13937
- const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync10(result.path)).map((result) => {
13938
- const content = readFileSync10(result.path, "utf-8");
14404
+ const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync13(result.path)).map((result) => {
14405
+ const content = readFileSync12(result.path, "utf-8");
13939
14406
  return {
13940
14407
  path: result.path,
13941
14408
  relativePath: result.relativePath,
13942
14409
  role: result.role,
13943
- sha256: sha2569(content),
14410
+ sha256: sha25610(content),
13944
14411
  content
13945
14412
  };
13946
14413
  });
@@ -13953,7 +14420,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
13953
14420
  };
13954
14421
  }
13955
14422
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
13956
- const snapshotPath = resolve9(targetHome, ".hasna", "session-render-snapshots", `${timestamp}-${randomUUID4()}.json`);
14423
+ const snapshotPath = resolve10(targetHome, ".hasna", "session-render-snapshots", `${timestamp}-${randomUUID4()}.json`);
13957
14424
  const afterFiles = results.map((result) => {
13958
14425
  if (result.action === "conflict") {
13959
14426
  throw new SessionApplyError(`Cannot snapshot unresolved conflict: ${result.relativePath}`);
@@ -14001,12 +14468,12 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
14001
14468
  function assertSafeTargetHome(targetHome) {
14002
14469
  if (!isAbsolute4(targetHome))
14003
14470
  throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
14004
- const normalized = resolve9(targetHome);
14471
+ const normalized = resolve10(targetHome);
14005
14472
  if (normalized === parse5(normalized).root) {
14006
14473
  throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
14007
14474
  }
14008
14475
  assertNoSymlinkAncestors3(normalized);
14009
- if (existsSync10(normalized) && lstatSync5(normalized).isSymbolicLink()) {
14476
+ if (existsSync13(normalized) && lstatSync6(normalized).isSymbolicLink()) {
14010
14477
  throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
14011
14478
  }
14012
14479
  return normalized;
@@ -14016,28 +14483,28 @@ function assertNoSymlinkSegments2(root, target) {
14016
14483
  const rel = relative5(root, target);
14017
14484
  let current = root;
14018
14485
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
14019
- current = join12(current, segment);
14020
- if (existsSync10(current) && lstatSync5(current).isSymbolicLink()) {
14486
+ current = join14(current, segment);
14487
+ if (existsSync13(current) && lstatSync6(current).isSymbolicLink()) {
14021
14488
  throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
14022
14489
  }
14023
14490
  }
14024
14491
  }
14025
14492
  function assertNoSymlinkAncestors3(path) {
14026
- const normalized = resolve9(path);
14493
+ const normalized = resolve10(path);
14027
14494
  const parsed = parse5(normalized);
14028
14495
  let current = parsed.root;
14029
14496
  const rel = relative5(parsed.root, normalized);
14030
14497
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
14031
- current = join12(current, segment);
14032
- if (!existsSync10(current))
14498
+ current = join14(current, segment);
14499
+ if (!existsSync13(current))
14033
14500
  return;
14034
- if (lstatSync5(current).isSymbolicLink()) {
14501
+ if (lstatSync6(current).isSymbolicLink()) {
14035
14502
  throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
14036
14503
  }
14037
14504
  }
14038
14505
  }
14039
- function sha2569(content) {
14040
- return createHash9("sha256").update(content).digest("hex");
14506
+ function sha25610(content) {
14507
+ return createHash10("sha256").update(content).digest("hex");
14041
14508
  }
14042
14509
  // src/lib/project-dashboard-standard.ts
14043
14510
  var PROJECT_DASHBOARD_STANDARD_SLUG = "agent-managed-project-dashboard-standard";
@@ -14340,13 +14807,13 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
14340
14807
  }
14341
14808
  }
14342
14809
  // src/lib/sync.ts
14343
- import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync12 } from "fs";
14344
- import { basename as basename5, extname as extname3, join as join14 } from "path";
14810
+ import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync14 } from "fs";
14811
+ import { basename as basename6, extname as extname3, join as join16 } from "path";
14345
14812
 
14346
14813
  // src/lib/sync-dir.ts
14347
- import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync6 } from "fs";
14348
- import { join as join13, relative as relative6 } from "path";
14349
- import { homedir as homedir7 } from "os";
14814
+ import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync6 } from "fs";
14815
+ import { join as join15, relative as relative6 } from "path";
14816
+ import { homedir as homedir9 } from "os";
14350
14817
  var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
14351
14818
  function shouldSkip(p) {
14352
14819
  return SKIP.some((s) => p.includes(s));
@@ -14354,11 +14821,11 @@ function shouldSkip(p) {
14354
14821
  async function syncFromDir(dir, opts = {}) {
14355
14822
  const store = opts.store ?? resolveConfigStore();
14356
14823
  const absDir = expandPath(dir);
14357
- if (!existsSync11(absDir))
14824
+ if (!existsSync14(absDir))
14358
14825
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
14359
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join13(absDir, f)).filter((f) => statSync6(f).isFile());
14826
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync3(absDir).map((f) => join15(absDir, f)).filter((f) => statSync6(f).isFile());
14360
14827
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
14361
- const home = homedir7();
14828
+ const home = homedir9();
14362
14829
  const allConfigs = await store.listConfigs();
14363
14830
  for (const file of files) {
14364
14831
  if (shouldSkip(file)) {
@@ -14366,20 +14833,21 @@ async function syncFromDir(dir, opts = {}) {
14366
14833
  continue;
14367
14834
  }
14368
14835
  try {
14369
- const content = readFileSync11(file, "utf-8");
14836
+ const content = readFileSync13(file, "utf-8");
14370
14837
  if (content.length > 500000) {
14371
14838
  result.skipped.push(file + " (too large)");
14372
14839
  continue;
14373
14840
  }
14374
14841
  const targetPath = file.replace(home, "~");
14842
+ const redacted = redactContent(content, redactFormatForTarget(targetPath, detectFormat(file)));
14375
14843
  const existing = allConfigs.find((c) => c.target_path === targetPath);
14376
14844
  if (!existing) {
14377
14845
  if (!opts.dryRun)
14378
- await store.createConfig({ name: relative6(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content });
14846
+ await store.createConfig({ name: relative6(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content: redacted.content });
14379
14847
  result.added++;
14380
- } else if (existing.content !== content) {
14848
+ } else if (existing.content !== redacted.content) {
14381
14849
  if (!opts.dryRun)
14382
- await store.updateConfig(existing.id, { content });
14850
+ await store.updateConfig(existing.id, { content: redacted.content });
14383
14851
  result.updated++;
14384
14852
  } else {
14385
14853
  result.unchanged++;
@@ -14392,7 +14860,7 @@ async function syncFromDir(dir, opts = {}) {
14392
14860
  }
14393
14861
  async function syncToDir(dir, opts = {}) {
14394
14862
  const store = opts.store ?? resolveConfigStore();
14395
- const home = homedir7();
14863
+ const home = homedir9();
14396
14864
  const absDir = expandPath(dir);
14397
14865
  const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
14398
14866
  const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
@@ -14415,8 +14883,8 @@ async function syncToDir(dir, opts = {}) {
14415
14883
  return result;
14416
14884
  }
14417
14885
  function walkDir(dir, files = []) {
14418
- for (const entry of readdirSync2(dir, { withFileTypes: true })) {
14419
- const full = join13(dir, entry.name);
14886
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
14887
+ const full = join15(dir, entry.name);
14420
14888
  if (shouldSkip(full))
14421
14889
  continue;
14422
14890
  if (entry.isDirectory())
@@ -14438,7 +14906,7 @@ var CLAUDE_PROMPT_OUTPUTS = [
14438
14906
  { agent: "qwen", target_path: "~/.qwen/QWEN.md", transform: "codex-flat" }
14439
14907
  ];
14440
14908
  function claudeRuleOutputs(fileName) {
14441
- const stem = basename5(fileName, extname3(fileName));
14909
+ const stem = basename6(fileName, extname3(fileName));
14442
14910
  return [
14443
14911
  { agent: "cursor", target_path: `~/.cursor/rules/${stem}.mdc`, transform: "cursor-mdc" }
14444
14912
  ];
@@ -14477,15 +14945,15 @@ function isGeneratedOutputTarget2(config, owners) {
14477
14945
  return !!ownerIds && !ownerIds.has(config.id);
14478
14946
  }
14479
14947
  function hasClaudePromptSource() {
14480
- return existsSync12(expandPath("~/.claude/CLAUDE.md"));
14948
+ return existsSync15(expandPath("~/.claude/CLAUDE.md"));
14481
14949
  }
14482
14950
  function hasClaudeRuleSourceForCursorTarget(targetPath) {
14483
14951
  const absoluteTargetPath = expandPath(targetPath);
14484
14952
  const absolutePrefix = expandPath("~/.cursor/rules");
14485
14953
  if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
14486
14954
  return false;
14487
- const stem = basename5(absoluteTargetPath, ".mdc");
14488
- return existsSync12(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync12(expandPath(`~/.claude/rules/${stem}.mdc`));
14955
+ const stem = basename6(absoluteTargetPath, ".mdc");
14956
+ return existsSync15(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync15(expandPath(`~/.claude/rules/${stem}.mdc`));
14489
14957
  }
14490
14958
  function isKnownGeneratedTargetPath(targetPath) {
14491
14959
  const normalizedTargetPath = normalizeTargetPath(targetPath);
@@ -14550,11 +15018,11 @@ async function syncProject(opts) {
14550
15018
  const allConfigs = await store.listConfigs();
14551
15019
  const machine = detectMachineContext();
14552
15020
  for (const pf of PROJECT_CONFIG_FILES) {
14553
- const abs = join14(absDir, pf.file);
14554
- if (!existsSync12(abs))
15021
+ const abs = join16(absDir, pf.file);
15022
+ if (!existsSync15(abs))
14555
15023
  continue;
14556
15024
  try {
14557
- const rawContent = readFileSync12(abs, "utf-8");
15025
+ const rawContent = readFileSync14(abs, "utf-8");
14558
15026
  if (rawContent.length > 500000) {
14559
15027
  result.skipped.push(pf.file);
14560
15028
  continue;
@@ -14583,20 +15051,20 @@ async function syncProject(opts) {
14583
15051
  }
14584
15052
  }
14585
15053
  for (const ruleDir of [
14586
- { dir: join14(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
14587
- { dir: join14(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
14588
- { dir: join14(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
14589
- { dir: join14(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
14590
- { dir: join14(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
14591
- { dir: join14(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
14592
- { dir: join14(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
15054
+ { dir: join16(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
15055
+ { dir: join16(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
15056
+ { dir: join16(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
15057
+ { dir: join16(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
15058
+ { dir: join16(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
15059
+ { dir: join16(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
15060
+ { dir: join16(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
14593
15061
  ]) {
14594
- if (!existsSync12(ruleDir.dir))
15062
+ if (!existsSync15(ruleDir.dir))
14595
15063
  continue;
14596
- const mdFiles = readdirSync3(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
15064
+ const mdFiles = readdirSync4(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
14597
15065
  for (const f of mdFiles) {
14598
- const abs = join14(ruleDir.dir, f);
14599
- const raw = readFileSync12(abs, "utf-8");
15066
+ const abs = join16(ruleDir.dir, f);
15067
+ const raw = readFileSync14(abs, "utf-8");
14600
15068
  const redacted = redactContent(raw, "markdown");
14601
15069
  const machineAware = templateizeMachineContent(redacted.content, machine);
14602
15070
  const content = machineAware.content;
@@ -14635,20 +15103,20 @@ async function syncKnown(opts = {}) {
14635
15103
  for (const known of targets) {
14636
15104
  if (known.rulesDir) {
14637
15105
  const absDir = expandPath(known.rulesDir);
14638
- if (!existsSync12(absDir)) {
15106
+ if (!existsSync15(absDir)) {
14639
15107
  result.skipped.push(known.rulesDir);
14640
15108
  continue;
14641
15109
  }
14642
15110
  const extensions = known.rulesExtensions ?? [".md", ".mdc"];
14643
- const ruleFiles = readdirSync3(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
15111
+ const ruleFiles = readdirSync4(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
14644
15112
  for (const f of ruleFiles) {
14645
- const abs2 = join14(absDir, f);
15113
+ const abs2 = join16(absDir, f);
14646
15114
  const targetPath = abs2.replace(home, "~");
14647
15115
  if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
14648
15116
  result.skipped.push(`${targetPath} (generated output)`);
14649
15117
  continue;
14650
15118
  }
14651
- const raw = readFileSync12(abs2, "utf-8");
15119
+ const raw = readFileSync14(abs2, "utf-8");
14652
15120
  const redacted = redactContent(raw, "markdown");
14653
15121
  const machineAware = templateizeMachineContent(redacted.content, machine);
14654
15122
  const content = machineAware.content;
@@ -14676,18 +15144,18 @@ async function syncKnown(opts = {}) {
14676
15144
  continue;
14677
15145
  }
14678
15146
  const abs = expandPath(known.path);
14679
- if (!existsSync12(abs)) {
15147
+ if (!existsSync15(abs)) {
14680
15148
  result.skipped.push(known.path);
14681
15149
  continue;
14682
15150
  }
14683
15151
  try {
14684
- const rawContent = normalizeKnownConfigSource(known, readFileSync12(abs, "utf-8"));
15152
+ const rawContent = normalizeKnownConfigSource(known, readFileSync14(abs, "utf-8"));
14685
15153
  if (rawContent.length > 500000) {
14686
15154
  result.skipped.push(known.path + " (too large)");
14687
15155
  continue;
14688
15156
  }
14689
15157
  const fmt = known.format ?? detectFormat(abs);
14690
- const redacted = redactContent(rawContent, fmt);
15158
+ const redacted = redactContent(rawContent, redactFormatForTarget(abs, fmt));
14691
15159
  const machineAware = templateizeMachineContent(redacted.content, machine);
14692
15160
  const content = machineAware.content;
14693
15161
  const isTemplate2 = redacted.isTemplate || machineAware.changed;
@@ -14784,9 +15252,9 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
14784
15252
  }
14785
15253
  function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
14786
15254
  const path = expandPath(targetPath);
14787
- if (!existsSync12(path))
15255
+ if (!existsSync15(path))
14788
15256
  return `(file not found on disk: ${path})`;
14789
- const diskContent = readFileSync12(path, "utf-8");
15257
+ const diskContent = readFileSync14(path, "utf-8");
14790
15258
  if (diskContent === expectedContent)
14791
15259
  return "(no diff \u2014 identical)";
14792
15260
  const format = redactFormatForTarget(targetPath, storedFormat);
@@ -14944,26 +15412,26 @@ function detectFormat(filePath) {
14944
15412
  return "text";
14945
15413
  }
14946
15414
  // src/lib/export.ts
14947
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
14948
- import { join as join15, resolve as resolve10 } from "path";
15415
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
15416
+ import { join as join17, resolve as resolve11 } from "path";
14949
15417
  import { tmpdir } from "os";
14950
15418
  async function exportConfigs(outputPath, opts = {}) {
14951
15419
  const store = opts.store ?? resolveConfigStore();
14952
15420
  const configs = await store.listConfigs(opts.filter);
14953
- const absOutput = resolve10(outputPath);
14954
- const tmpDir = join15(tmpdir(), `configs-export-${Date.now()}`);
14955
- const contentsDir = join15(tmpDir, "contents");
15421
+ const absOutput = resolve11(outputPath);
15422
+ const tmpDir = join17(tmpdir(), `configs-export-${Date.now()}`);
15423
+ const contentsDir = join17(tmpDir, "contents");
14956
15424
  try {
14957
- mkdirSync6(contentsDir, { recursive: true });
15425
+ mkdirSync8(contentsDir, { recursive: true });
14958
15426
  const manifest = {
14959
15427
  version: "1.0.0",
14960
15428
  exported_at: new Date().toISOString(),
14961
15429
  configs: configs.map(({ content: _content, ...meta }) => meta)
14962
15430
  };
14963
- writeFileSync4(join15(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
15431
+ writeFileSync6(join17(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
14964
15432
  for (const config of configs) {
14965
15433
  const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
14966
- writeFileSync4(join15(contentsDir, fileName), config.content, "utf-8");
15434
+ writeFileSync6(join17(contentsDir, fileName), config.content, "utf-8");
14967
15435
  }
14968
15436
  const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
14969
15437
  stdout: "pipe",
@@ -14976,23 +15444,23 @@ async function exportConfigs(outputPath, opts = {}) {
14976
15444
  }
14977
15445
  return { path: absOutput, count: configs.length };
14978
15446
  } finally {
14979
- if (existsSync13(tmpDir)) {
15447
+ if (existsSync16(tmpDir)) {
14980
15448
  rmSync4(tmpDir, { recursive: true, force: true });
14981
15449
  }
14982
15450
  }
14983
15451
  }
14984
15452
  // src/lib/import.ts
14985
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync13, rmSync as rmSync5 } from "fs";
14986
- import { join as join16, resolve as resolve11 } from "path";
15453
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync15, rmSync as rmSync5 } from "fs";
15454
+ import { join as join18, resolve as resolve12 } from "path";
14987
15455
  import { tmpdir as tmpdir2 } from "os";
14988
15456
  async function importConfigs(bundlePath, opts = {}) {
14989
15457
  const store = opts.store ?? resolveConfigStore();
14990
15458
  const conflict = opts.conflict ?? "skip";
14991
- const absPath = resolve11(bundlePath);
14992
- const tmpDir = join16(tmpdir2(), `configs-import-${Date.now()}`);
15459
+ const absPath = resolve12(bundlePath);
15460
+ const tmpDir = join18(tmpdir2(), `configs-import-${Date.now()}`);
14993
15461
  const result = { created: 0, updated: 0, skipped: 0, errors: [] };
14994
15462
  try {
14995
- mkdirSync7(tmpDir, { recursive: true });
15463
+ mkdirSync9(tmpDir, { recursive: true });
14996
15464
  const proc = Bun.spawn(["tar", "xzf", absPath, "-C", tmpDir], {
14997
15465
  stdout: "pipe",
14998
15466
  stderr: "pipe"
@@ -15002,15 +15470,15 @@ async function importConfigs(bundlePath, opts = {}) {
15002
15470
  const stderr = await new Response(proc.stderr).text();
15003
15471
  throw new Error(`tar extraction failed: ${stderr}`);
15004
15472
  }
15005
- const manifestPath = join16(tmpDir, "manifest.json");
15006
- if (!existsSync14(manifestPath))
15473
+ const manifestPath = join18(tmpDir, "manifest.json");
15474
+ if (!existsSync17(manifestPath))
15007
15475
  throw new Error("Invalid bundle: missing manifest.json");
15008
- const manifest = JSON.parse(readFileSync13(manifestPath, "utf-8"));
15476
+ const manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
15009
15477
  for (const meta of manifest.configs) {
15010
15478
  try {
15011
15479
  const ext = meta.format === "text" ? "txt" : meta.format;
15012
- const contentFile = join16(tmpDir, "contents", `${meta.slug}.${ext}`);
15013
- const content = existsSync14(contentFile) ? readFileSync13(contentFile, "utf-8") : "";
15480
+ const contentFile = join18(tmpDir, "contents", `${meta.slug}.${ext}`);
15481
+ const content = existsSync17(contentFile) ? readFileSync15(contentFile, "utf-8") : "";
15014
15482
  let existing = null;
15015
15483
  try {
15016
15484
  existing = await store.getConfig(meta.slug);
@@ -15044,16 +15512,16 @@ async function importConfigs(bundlePath, opts = {}) {
15044
15512
  }
15045
15513
  return result;
15046
15514
  } finally {
15047
- if (existsSync14(tmpDir)) {
15515
+ if (existsSync17(tmpDir)) {
15048
15516
  rmSync5(tmpDir, { recursive: true, force: true });
15049
15517
  }
15050
15518
  }
15051
15519
  }
15052
15520
  // src/lib/package-manager-guard.ts
15053
15521
  import { execFileSync as execFileSync2 } from "child_process";
15054
- import { existsSync as existsSync15, lstatSync as lstatSync6, readdirSync as readdirSync4, readFileSync as readFileSync14 } from "fs";
15055
- import { homedir as homedir8 } from "os";
15056
- import { basename as basename6, dirname as dirname8, isAbsolute as isAbsolute5, join as join17, relative as relative7, resolve as resolve12 } from "path";
15522
+ import { existsSync as existsSync18, lstatSync as lstatSync7, readdirSync as readdirSync5, readFileSync as readFileSync16 } from "fs";
15523
+ import { homedir as homedir10 } from "os";
15524
+ import { basename as basename7, dirname as dirname10, isAbsolute as isAbsolute5, join as join19, relative as relative7, resolve as resolve13 } from "path";
15057
15525
  var SKIP_DIRS = new Set([
15058
15526
  ".git",
15059
15527
  "node_modules",
@@ -15090,14 +15558,14 @@ var TOKEN_VALUE_PATTERNS = [
15090
15558
  { re: /xoxb-[0-9]+-[A-Za-z0-9-]+/, rule: "literal-slack-token", detail: "literal Slack token-like value" }
15091
15559
  ];
15092
15560
  function scanPackageManagerSecrets(options = {}) {
15093
- const cwd = options.cwd ? resolve12(options.cwd) : process.cwd();
15094
- const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve12(cwd, root));
15561
+ const cwd = options.cwd ? resolve13(options.cwd) : process.cwd();
15562
+ const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve13(cwd, root));
15095
15563
  const findings = [];
15096
15564
  let scannedFiles = 0;
15097
15565
  for (const root of roots) {
15098
- if (!existsSync15(root))
15566
+ if (!existsSync18(root))
15099
15567
  continue;
15100
- const stat = lstatSync6(root);
15568
+ const stat = lstatSync7(root);
15101
15569
  if (stat.isFile()) {
15102
15570
  if (!shouldScanRepoFile(root))
15103
15571
  continue;
@@ -15105,7 +15573,7 @@ function scanPackageManagerSecrets(options = {}) {
15105
15573
  if (text === null)
15106
15574
  continue;
15107
15575
  scannedFiles++;
15108
- findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname8(root)));
15576
+ findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname10(root)));
15109
15577
  continue;
15110
15578
  }
15111
15579
  if (!stat.isDirectory())
@@ -15122,10 +15590,10 @@ function scanPackageManagerSecrets(options = {}) {
15122
15590
  }
15123
15591
  }
15124
15592
  if (options.includeHome) {
15125
- const home = homedir8();
15593
+ const home = homedir10();
15126
15594
  for (const name of HOME_FILES) {
15127
- const file = join17(home, name);
15128
- if (!existsSync15(file))
15595
+ const file = join19(home, name);
15596
+ if (!existsSync18(file))
15129
15597
  continue;
15130
15598
  const text = readTextFile(file);
15131
15599
  if (text === null)
@@ -15145,16 +15613,16 @@ function scanPackageManagerSecrets(options = {}) {
15145
15613
  function collectRepoFiles(root) {
15146
15614
  const out = [];
15147
15615
  const visit = (dir) => {
15148
- for (const entry of readdirSync4(dir, { withFileTypes: true })) {
15616
+ for (const entry of readdirSync5(dir, { withFileTypes: true })) {
15149
15617
  if (entry.isDirectory()) {
15150
15618
  if (SKIP_DIRS.has(entry.name))
15151
15619
  continue;
15152
- visit(join17(dir, entry.name));
15620
+ visit(join19(dir, entry.name));
15153
15621
  continue;
15154
15622
  }
15155
15623
  if (!entry.isFile())
15156
15624
  continue;
15157
- const file = join17(dir, entry.name);
15625
+ const file = join19(dir, entry.name);
15158
15626
  if (shouldScanRepoFile(file))
15159
15627
  out.push(file);
15160
15628
  }
@@ -15163,11 +15631,11 @@ function collectRepoFiles(root) {
15163
15631
  return out;
15164
15632
  }
15165
15633
  function shouldScanRepoFile(file) {
15166
- const name = basename6(file);
15634
+ const name = basename7(file);
15167
15635
  return isNpmrcName(name) || isBunConfigName(name) || LOCKFILE_NAMES.has(name);
15168
15636
  }
15169
15637
  function classifyRepoFile(file) {
15170
- const name = basename6(file);
15638
+ const name = basename7(file);
15171
15639
  if (isNpmrcName(name))
15172
15640
  return "repo-npmrc";
15173
15641
  if (isBunConfigName(name))
@@ -15189,10 +15657,10 @@ function isNpmrcName(name) {
15189
15657
  }
15190
15658
  function readTextFile(file) {
15191
15659
  try {
15192
- const stat = lstatSync6(file);
15660
+ const stat = lstatSync7(file);
15193
15661
  if (!stat.isFile() || stat.size > 5000000)
15194
15662
  return null;
15195
- const buf = readFileSync14(file);
15663
+ const buf = readFileSync16(file);
15196
15664
  if (buf.includes(0))
15197
15665
  return null;
15198
15666
  return buf.toString("utf-8");
@@ -15392,7 +15860,7 @@ function trackedFiles(root) {
15392
15860
  }
15393
15861
  function isTrackedFile(file) {
15394
15862
  try {
15395
- const repoRoot = execFileSync2("git", ["-C", dirname8(file), "rev-parse", "--show-toplevel"], {
15863
+ const repoRoot = execFileSync2("git", ["-C", dirname10(file), "rev-parse", "--show-toplevel"], {
15396
15864
  encoding: "utf-8",
15397
15865
  stdio: ["ignore", "pipe", "ignore"]
15398
15866
  }).trim();
@@ -15422,7 +15890,7 @@ function stripInlineComment(value) {
15422
15890
  return value.replace(/\s[#;].*$/, "").trim();
15423
15891
  }
15424
15892
  function displayPath(file, root) {
15425
- const home = homedir8();
15893
+ const home = homedir10();
15426
15894
  if (root === home && (file === home || file.startsWith(home + "/")))
15427
15895
  return "~/" + toPosix(relative7(home, file));
15428
15896
  if (isAbsolute5(root) && file.startsWith(root + "/"))
@@ -15444,6 +15912,7 @@ export {
15444
15912
  syncKnown,
15445
15913
  syncFromDir,
15446
15914
  stripClaudeOnlySections,
15915
+ stationProfileSource,
15447
15916
  sourcesFromIdentityExport,
15448
15917
  sourceFromFilePath,
15449
15918
  sourceFromConfig,
@@ -15454,20 +15923,27 @@ export {
15454
15923
  scanSecrets,
15455
15924
  scanPackageManagerSecrets,
15456
15925
  restoreSessionRenderSnapshot,
15926
+ resolveStationProfilePackages,
15927
+ resolveStationProfileMachine,
15457
15928
  resolveSessionTargetOwnership,
15458
15929
  resolveSessionPath,
15459
15930
  resolveProfileVariables,
15460
15931
  resolveConfigStore,
15461
15932
  resolveCloudConfig,
15462
15933
  resolveAssetDestination,
15934
+ resolveAndRenderProviderContext,
15463
15935
  resolveAgentOperatingRulesPayload,
15464
15936
  renderTemplatePreview,
15465
15937
  renderTemplate,
15938
+ renderProviderFragment,
15466
15939
  renderMachineAwareContentPreview,
15467
15940
  renderMachineAwareContent,
15941
+ refreshStationProfile,
15468
15942
  redactContent,
15469
15943
  reconcileManagedSkillRuntimes,
15944
+ readStationProfile,
15470
15945
  providerVersionSatisfies,
15946
+ providerContextAuditLine,
15471
15947
  previewConfigs,
15472
15948
  planSessionRender,
15473
15949
  planProjectContext,
@@ -15479,7 +15955,9 @@ export {
15479
15955
  normalizeProfileConfigBinding,
15480
15956
  normalizeProfileAssetBinding,
15481
15957
  normalizeOsFamily,
15958
+ normalizeEndpointOrigin,
15482
15959
  normalizeBoundedReadOptions,
15960
+ matchProviderEndpoint,
15483
15961
  machineContextToVariables,
15484
15962
  legacyProfileConfigBinding,
15485
15963
  isTemplate,
@@ -15487,6 +15965,7 @@ export {
15487
15965
  inspectManagedSkillRuntimes,
15488
15966
  importConfigs,
15489
15967
  hasSecrets,
15968
+ getStationProfileCachePath,
15490
15969
  getRawStoreRoot,
15491
15970
  getConfigsStatus,
15492
15971
  extractTemplateVars,
@@ -15513,6 +15992,7 @@ export {
15513
15992
  compareAgentOperatingRulesVersions,
15514
15993
  cleanSessionPathInput,
15515
15994
  checkSessionRenderDrift,
15995
+ buildStationProfileBlock,
15516
15996
  buildOpenCodeAgentsMd,
15517
15997
  buildCursorMdc,
15518
15998
  buildCodexAgentsMd,
@@ -15526,6 +16006,10 @@ export {
15526
16006
  applyConfig,
15527
16007
  TemplateRenderError,
15528
16008
  SessionApplyError,
16009
+ STATION_PROFILE_SOURCE_ID,
16010
+ STATION_PROFILE_MAX_BYTES,
16011
+ STATION_PROFILE_LAYER,
16012
+ STATION_PROFILE_CACHE_FILENAME,
15529
16013
  SESSION_TOOL_ADAPTERS,
15530
16014
  SESSION_RENDER_TOOLS,
15531
16015
  SESSION_RENDER_SCHEMA,
@@ -15535,6 +16019,11 @@ export {
15535
16019
  RAW_STORE_ROOT_ENV,
15536
16020
  ProjectContextError,
15537
16021
  ProfileNotFoundError,
16022
+ PROVIDER_ENDPOINT_REGISTRY,
16023
+ PROVIDER_CONTEXT_SCHEMA,
16024
+ PROVIDER_CONTEXT_MANIFEST,
16025
+ PROVIDER_CONTEXT_INVARIANT_ID,
16026
+ PROVIDER_CONTEXT_DIR,
15538
16027
  PROVIDER_CAPABILITY_SCHEMA,
15539
16028
  PROVIDER_CAPABILITY_DESCRIPTORS,
15540
16029
  PROVIDER_CAPABILITIES,