@hasna/instructions 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -97,19 +97,122 @@ var init_retired_storage_mode = __esm(() => {
97
97
  ];
98
98
  });
99
99
 
100
- // src/lib/raw-store-root.ts
100
+ // ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
101
101
  import { homedir } from "os";
102
- import { join as join2, resolve } from "path";
103
- function getRawStoreRoot() {
104
- return resolve(process.env[RAW_STORE_ROOT_ENV] || join2(process.env["HOME"] || homedir(), ".hasna", "instructions"));
102
+ import { join as join2 } from "path";
103
+ function assertApp(app) {
104
+ if (typeof app !== "string" || app.length === 0) {
105
+ throw new TypeError("paths: app must be a non-empty string");
106
+ }
107
+ if (!APP_SLUG_RE.test(app)) {
108
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
109
+ }
110
+ }
111
+ function envOf(options) {
112
+ return options.env ?? process.env;
113
+ }
114
+ function envValue(options, kind) {
115
+ const value = envOf(options)[KIND_ENV[kind]];
116
+ return typeof value === "string" && value.length > 0 ? value : undefined;
117
+ }
118
+ function isMacOS(platform) {
119
+ return platform === "darwin";
120
+ }
121
+ function baseDir(kind, options) {
122
+ const override = envValue(options, kind);
123
+ if (override)
124
+ return override;
125
+ const home = options.home ?? homedir();
126
+ const platform = options.platform ?? process.platform;
127
+ if (isMacOS(platform)) {
128
+ switch (kind) {
129
+ case "config":
130
+ case "data":
131
+ return join2(home, "Library", "Application Support", "Hasna");
132
+ case "cache":
133
+ return join2(home, "Library", "Caches", "Hasna");
134
+ case "state":
135
+ return join2(home, "Library", "Logs", "Hasna");
136
+ }
137
+ }
138
+ switch (kind) {
139
+ case "config":
140
+ return join2(home, ".config", "hasna");
141
+ case "data":
142
+ return join2(home, ".local", "share", "hasna");
143
+ case "state":
144
+ return join2(home, ".local", "state", "hasna");
145
+ case "cache":
146
+ return join2(home, ".cache", "hasna");
147
+ }
148
+ }
149
+ function resolvePath(kind, options) {
150
+ assertApp(options.app);
151
+ const appSegment = options.internal === true ? join2("internal", options.app) : options.app;
152
+ return join2(baseDir(kind, options), appSegment);
153
+ }
154
+ function configDir(options) {
155
+ return resolvePath("config", options);
156
+ }
157
+ var KIND_ENV, APP_SLUG_RE;
158
+ var init_dist = __esm(() => {
159
+ KIND_ENV = {
160
+ config: "HASNA_CONFIG_HOME",
161
+ data: "HASNA_DATA_HOME",
162
+ state: "HASNA_STATE_HOME",
163
+ cache: "HASNA_CACHE_HOME"
164
+ };
165
+ APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
166
+ });
167
+
168
+ // src/lib/app-home.ts
169
+ import { existsSync as existsSync2 } from "fs";
170
+ import { homedir as homedir2 } from "os";
171
+ import { join as join3, resolve } from "path";
172
+ function homeDir(env = process.env) {
173
+ return env["HOME"] || env["USERPROFILE"] || homedir2();
174
+ }
175
+ function legacyStoreHome(env = process.env) {
176
+ return resolve(join3(homeDir(env), ".hasna", "instructions"));
177
+ }
178
+ function resolverStoreHome(env = process.env) {
179
+ return configDir({ app: "configs", env, home: env.HOME || env.USERPROFILE || homedir2() });
180
+ }
181
+ function adoptResolverStoreHome(resolved, env = process.env) {
182
+ const override = env.HASNA_CONFIG_HOME;
183
+ if (typeof override === "string" && override.trim().length > 0)
184
+ return true;
185
+ return existsSync2(join3(resolved, "instructions.db"));
186
+ }
187
+ function exactStoreHome(env = process.env) {
188
+ const v = env[HASNA_CONFIGS_HOME_ENV];
189
+ return v && v.trim() ? v.trim() : undefined;
190
+ }
191
+ function getConfigsStoreHome(env = process.env) {
192
+ const exact = exactStoreHome(env);
193
+ if (exact)
194
+ return resolve(exact);
195
+ const resolved = resolverStoreHome(env);
196
+ return adoptResolverStoreHome(resolved, env) ? resolve(resolved) : legacyStoreHome(env);
197
+ }
198
+ var HASNA_CONFIGS_HOME_ENV = "HASNA_CONFIGS_HOME";
199
+ var init_app_home = __esm(() => {
200
+ init_dist();
201
+ });
202
+
203
+ // src/lib/raw-store-root.ts
204
+ import { resolve as resolve2 } from "path";
205
+ function getRawStoreRoot(env = process.env) {
206
+ return resolve2(getConfigsStoreHome(env));
105
207
  }
106
- var RAW_STORE_ROOT_ENV = "HASNA_CONFIGS_HOME";
107
- var init_raw_store_root = () => {};
208
+ var init_raw_store_root = __esm(() => {
209
+ init_app_home();
210
+ });
108
211
 
109
212
  // src/db/database.ts
110
213
  import { Database } from "bun:sqlite";
111
- import { existsSync as existsSync2, mkdirSync, rmSync } from "fs";
112
- import { join as join3 } from "path";
214
+ import { existsSync as existsSync3, mkdirSync, rmSync } from "fs";
215
+ import { join as join4 } from "path";
113
216
  import { randomUUID } from "crypto";
114
217
  function getDbPath() {
115
218
  if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
@@ -117,7 +220,7 @@ function getDbPath() {
117
220
  }
118
221
  const dir = getRawStoreRoot();
119
222
  mkdirSync(dir, { recursive: true });
120
- return join3(dir, "instructions.db");
223
+ return join4(dir, "instructions.db");
121
224
  }
122
225
  function uuid() {
123
226
  return randomUUID();
@@ -158,7 +261,7 @@ function resetLocalDatabase() {
158
261
  if (dbPath === ":memory:")
159
262
  return;
160
263
  for (const p of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
161
- if (existsSync2(p))
264
+ if (existsSync3(p))
162
265
  rmSync(p);
163
266
  }
164
267
  }
@@ -595,9 +698,9 @@ var init_template = __esm(() => {
595
698
  });
596
699
 
597
700
  // src/lib/machine.ts
598
- import { arch as currentArch, homedir as homedir2, hostname as currentHostname, type as currentOsType } from "os";
599
- import { existsSync as existsSync3 } from "fs";
600
- import { join as join4 } from "path";
701
+ import { arch as currentArch, homedir as homedir3, hostname as currentHostname, type as currentOsType } from "os";
702
+ import { existsSync as existsSync4 } from "fs";
703
+ import { join as join5 } from "path";
601
704
  function normalizeOsFamily(os) {
602
705
  const value = (os ?? "").trim().toLowerCase();
603
706
  if (value === "darwin" || value === "macos" || value === "mac" || value === "osx")
@@ -609,11 +712,11 @@ function normalizeOsFamily(os) {
609
712
  return value || "unknown";
610
713
  }
611
714
  function detectMachineContext(overrides = {}) {
612
- const homeDir = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir2();
715
+ const homeDir2 = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir3();
613
716
  const os = overrides.os ?? currentOsType();
614
717
  const osFamily = normalizeOsFamily(os);
615
- const bunBinDir = overrides.bun_bin_dir ?? join4(homeDir, ".bun", "bin");
616
- const defaultBunPath = osFamily === "macos" && existsSync3(BREW_BUN_PATH) ? BREW_BUN_PATH : join4(bunBinDir, "bun");
718
+ const bunBinDir = overrides.bun_bin_dir ?? join5(homeDir2, ".bun", "bin");
719
+ const defaultBunPath = osFamily === "macos" && existsSync4(BREW_BUN_PATH) ? BREW_BUN_PATH : join5(bunBinDir, "bun");
617
720
  return {
618
721
  id: "current-machine",
619
722
  hostname: overrides.hostname ?? currentHostname(),
@@ -622,11 +725,11 @@ function detectMachineContext(overrides = {}) {
622
725
  last_applied_at: null,
623
726
  created_at: "",
624
727
  os_family: osFamily,
625
- home_dir: homeDir,
626
- workspace_root: overrides.workspace_root ?? join4(homeDir, osFamily === "macos" ? "Workspace" : "workspace"),
728
+ home_dir: homeDir2,
729
+ workspace_root: overrides.workspace_root ?? join5(homeDir2, osFamily === "macos" ? "Workspace" : "workspace"),
627
730
  bun_bin_dir: bunBinDir,
628
731
  bun_path: overrides.bun_path ?? defaultBunPath,
629
- path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join4("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
732
+ path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join5("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
630
733
  };
631
734
  }
632
735
  function machineContextToVariables(machine) {
@@ -5139,7 +5242,7 @@ var init_session_render_contract = __esm(() => {
5139
5242
  });
5140
5243
 
5141
5244
  // src/lib/project-context.ts
5142
- import { basename, dirname as dirname2, isAbsolute, join as join5, parse, relative, resolve as resolve2 } from "path";
5245
+ import { basename, dirname as dirname2, isAbsolute, join as join6, parse, relative, resolve as resolve3 } from "path";
5143
5246
  function revisionKey(value) {
5144
5247
  const sequence = value.match(/^(?:rev-)?([0-9]+)$/);
5145
5248
  if (sequence)
@@ -5701,20 +5804,36 @@ var init_asset_plan = __esm(() => {
5701
5804
 
5702
5805
  // src/lib/cursor-authority.ts
5703
5806
  import { createHash } from "crypto";
5704
- import { homedir as homedir3 } from "os";
5705
- import { join as join6, resolve as resolve3 } from "path";
5807
+ import { homedir as homedir4 } from "os";
5808
+ import { join as join7, resolve as resolve4 } from "path";
5706
5809
  function sha256(content) {
5707
5810
  return createHash("sha256").update(content).digest("hex");
5708
5811
  }
5709
- function homeDir() {
5710
- return process.env["HOME"] || homedir3();
5812
+ function homeDir2() {
5813
+ return process.env["HOME"] || homedir4();
5814
+ }
5815
+ function markerPayload(content, markerLine, markerIndex) {
5816
+ const index = markerIndex ?? content.indexOf(markerLine);
5817
+ if (index < 0)
5818
+ return content;
5819
+ return content.slice(0, index) + content.slice(index + markerLine.length).replace(/^\n/, "");
5711
5820
  }
5712
5821
  function isCursorGlobalAuthorityPath(path) {
5713
- return resolve3(path) === resolve3(join6(homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
5822
+ return resolve4(path) === resolve4(join7(homeDir2(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
5714
5823
  }
5715
5824
  function stampCursorGlobalAuthorityMarker(content) {
5716
- if (CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN.test(content))
5717
- return content;
5825
+ const existing = content.match(CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN);
5826
+ if (existing) {
5827
+ const markerLine2 = existing[0];
5828
+ const index = existing.index ?? content.indexOf(markerLine2);
5829
+ const payload = markerPayload(content, markerLine2, index);
5830
+ if (sha256(payload) === existing[1].slice("sha256:".length)) {
5831
+ return content;
5832
+ }
5833
+ const digest2 = sha256(payload);
5834
+ const freshMarkerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest2} -->`;
5835
+ return content.slice(0, index) + freshMarkerLine + content.slice(index + markerLine2.length);
5836
+ }
5718
5837
  const digest = sha256(content);
5719
5838
  const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
5720
5839
  const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
@@ -6908,8 +7027,8 @@ var init_config_store = __esm(() => {
6908
7027
  });
6909
7028
 
6910
7029
  // src/lib/session-render-ownership.ts
6911
- import { existsSync as existsSync4, readFileSync as readFileSync2, statSync } from "fs";
6912
- import { dirname as dirname3, join as join7, parse as parse2, relative as relative2, sep } from "path";
7030
+ import { existsSync as existsSync5, readFileSync as readFileSync2, statSync } from "fs";
7031
+ import { dirname as dirname3, join as join8, parse as parse2, relative as relative2, sep } from "path";
6913
7032
  function toSegments(absolutePath2) {
6914
7033
  return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
6915
7034
  }
@@ -6928,7 +7047,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
6928
7047
  function readManifestRelativePaths(manifestPath) {
6929
7048
  let stats;
6930
7049
  try {
6931
- if (!existsSync4(manifestPath))
7050
+ if (!existsSync5(manifestPath))
6932
7051
  return null;
6933
7052
  stats = statSync(manifestPath);
6934
7053
  } catch {
@@ -6957,7 +7076,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
6957
7076
  const root = parse2(absolutePath2).root;
6958
7077
  let home = dirname3(absolutePath2);
6959
7078
  for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
6960
- const manifestPath = join7(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
7079
+ const manifestPath = join8(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
6961
7080
  const relativePaths = readManifestRelativePaths(manifestPath);
6962
7081
  if (relativePaths) {
6963
7082
  const claimed = relative2(home, absolutePath2).split(sep).join("/");
@@ -6992,17 +7111,17 @@ __export(exports_apply, {
6992
7111
  applyConfigs: () => applyConfigs,
6993
7112
  applyConfig: () => applyConfig
6994
7113
  });
6995
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync, writeFileSync } from "fs";
6996
- import { basename as basename3, dirname as dirname4, join as join8, resolve as resolve4 } from "path";
6997
- import { homedir as homedir4 } from "os";
7114
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync, writeFileSync } from "fs";
7115
+ import { basename as basename3, dirname as dirname4, join as join9, resolve as resolve5 } from "path";
7116
+ import { homedir as homedir5 } from "os";
6998
7117
  function getConfigHome() {
6999
- return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir4();
7118
+ return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir5();
7000
7119
  }
7001
7120
  function expandPath(p) {
7002
7121
  if (p.startsWith("~/")) {
7003
- return resolve4(getConfigHome(), p.slice(2));
7122
+ return resolve5(getConfigHome(), p.slice(2));
7004
7123
  }
7005
- return resolve4(p);
7124
+ return resolve5(p);
7006
7125
  }
7007
7126
  function normalizeTargetPath(p) {
7008
7127
  const expanded = expandPath(p);
@@ -7012,9 +7131,9 @@ function normalizeTargetPath(p) {
7012
7131
  let current = expanded;
7013
7132
  const missingSegments = [];
7014
7133
  while (true) {
7015
- if (existsSync5(current)) {
7134
+ if (existsSync6(current)) {
7016
7135
  try {
7017
- return resolve4(realpathSync(current), ...missingSegments);
7136
+ return resolve5(realpathSync(current), ...missingSegments);
7018
7137
  } catch {
7019
7138
  return expanded;
7020
7139
  }
@@ -7043,11 +7162,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
7043
7162
  }
7044
7163
  const path = expandPath(renderedTargetPath);
7045
7164
  const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
7046
- const previousContent = existsSync5(path) ? readFileSync3(path, "utf-8") : null;
7165
+ const previousContent = existsSync6(path) ? readFileSync3(path, "utf-8") : null;
7047
7166
  const changed = previousContent !== renderedForTarget;
7048
7167
  if (!opts.dryRun) {
7049
7168
  const dir = dirname4(path);
7050
- if (!existsSync5(dir)) {
7169
+ if (!existsSync6(dir)) {
7051
7170
  mkdirSync2(dir, { recursive: true });
7052
7171
  }
7053
7172
  if (previousContent !== null && changed) {
@@ -7081,7 +7200,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
7081
7200
  let current;
7082
7201
  try {
7083
7202
  const path = expandPath(targetPath);
7084
- if (!existsSync5(path))
7203
+ if (!existsSync6(path))
7085
7204
  return [];
7086
7205
  current = readFileSync3(path, "utf-8");
7087
7206
  } catch {
@@ -7407,7 +7526,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
7407
7526
  getConfigHome(),
7408
7527
  opts.vars?.["HOME_DIR"]
7409
7528
  ].filter((home) => typeof home === "string" && home.length > 0));
7410
- if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join8(home, ...relativePath.split("/"))))))
7529
+ if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join9(home, ...relativePath.split("/"))))))
7411
7530
  return true;
7412
7531
  return sessionRenderOwnsPath(normalized);
7413
7532
  }
@@ -7440,8 +7559,8 @@ __export(exports_sync, {
7440
7559
  KNOWN_CONFIGS: () => KNOWN_CONFIGS,
7441
7560
  CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
7442
7561
  });
7443
- import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync4 } from "fs";
7444
- import { basename as basename4, extname as extname2, join as join9 } from "path";
7562
+ import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync4 } from "fs";
7563
+ import { basename as basename4, extname as extname2, join as join10 } from "path";
7445
7564
  function claudeRuleOutputs(fileName) {
7446
7565
  const stem = basename4(fileName, extname2(fileName));
7447
7566
  return [
@@ -7480,7 +7599,7 @@ function isGeneratedOutputTarget2(config, owners) {
7480
7599
  return !!ownerIds && !ownerIds.has(config.id);
7481
7600
  }
7482
7601
  function hasClaudePromptSource() {
7483
- return existsSync6(expandPath("~/.claude/CLAUDE.md"));
7602
+ return existsSync7(expandPath("~/.claude/CLAUDE.md"));
7484
7603
  }
7485
7604
  function hasClaudeRuleSourceForCursorTarget(targetPath) {
7486
7605
  const absoluteTargetPath = expandPath(targetPath);
@@ -7488,7 +7607,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
7488
7607
  if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
7489
7608
  return false;
7490
7609
  const stem = basename4(absoluteTargetPath, ".mdc");
7491
- return existsSync6(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync6(expandPath(`~/.claude/rules/${stem}.mdc`));
7610
+ return existsSync7(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync7(expandPath(`~/.claude/rules/${stem}.mdc`));
7492
7611
  }
7493
7612
  function isKnownGeneratedTargetPath(targetPath) {
7494
7613
  const normalizedTargetPath = normalizeTargetPath(targetPath);
@@ -7505,8 +7624,8 @@ async function syncProject(opts) {
7505
7624
  const allConfigs = await store.listConfigs();
7506
7625
  const machine = detectMachineContext();
7507
7626
  for (const pf of PROJECT_CONFIG_FILES) {
7508
- const abs = join9(absDir, pf.file);
7509
- if (!existsSync6(abs))
7627
+ const abs = join10(absDir, pf.file);
7628
+ if (!existsSync7(abs))
7510
7629
  continue;
7511
7630
  try {
7512
7631
  const rawContent = readFileSync4(abs, "utf-8");
@@ -7538,19 +7657,19 @@ async function syncProject(opts) {
7538
7657
  }
7539
7658
  }
7540
7659
  for (const ruleDir of [
7541
- { dir: join9(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
7542
- { dir: join9(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
7543
- { dir: join9(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
7544
- { dir: join9(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
7545
- { dir: join9(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
7546
- { dir: join9(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
7547
- { dir: join9(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
7660
+ { dir: join10(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
7661
+ { dir: join10(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
7662
+ { dir: join10(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
7663
+ { dir: join10(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
7664
+ { dir: join10(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
7665
+ { dir: join10(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
7666
+ { dir: join10(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
7548
7667
  ]) {
7549
- if (!existsSync6(ruleDir.dir))
7668
+ if (!existsSync7(ruleDir.dir))
7550
7669
  continue;
7551
7670
  const mdFiles = readdirSync(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
7552
7671
  for (const f of mdFiles) {
7553
- const abs = join9(ruleDir.dir, f);
7672
+ const abs = join10(ruleDir.dir, f);
7554
7673
  const raw = readFileSync4(abs, "utf-8");
7555
7674
  const redacted = redactContent(raw, "markdown");
7556
7675
  const machineAware = templateizeMachineContent(redacted.content, machine);
@@ -7590,14 +7709,14 @@ async function syncKnown(opts = {}) {
7590
7709
  for (const known of targets) {
7591
7710
  if (known.rulesDir) {
7592
7711
  const absDir = expandPath(known.rulesDir);
7593
- if (!existsSync6(absDir)) {
7712
+ if (!existsSync7(absDir)) {
7594
7713
  result.skipped.push(known.rulesDir);
7595
7714
  continue;
7596
7715
  }
7597
7716
  const extensions = known.rulesExtensions ?? [".md", ".mdc"];
7598
7717
  const ruleFiles = readdirSync(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
7599
7718
  for (const f of ruleFiles) {
7600
- const abs2 = join9(absDir, f);
7719
+ const abs2 = join10(absDir, f);
7601
7720
  const targetPath = abs2.replace(home, "~");
7602
7721
  if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
7603
7722
  result.skipped.push(`${targetPath} (generated output)`);
@@ -7631,7 +7750,7 @@ async function syncKnown(opts = {}) {
7631
7750
  continue;
7632
7751
  }
7633
7752
  const abs = expandPath(known.path);
7634
- if (!existsSync6(abs)) {
7753
+ if (!existsSync7(abs)) {
7635
7754
  result.skipped.push(known.path);
7636
7755
  continue;
7637
7756
  }
@@ -7737,7 +7856,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
7737
7856
  }
7738
7857
  function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
7739
7858
  const path = expandPath(targetPath);
7740
- if (!existsSync6(path))
7859
+ if (!existsSync7(path))
7741
7860
  return `(file not found on disk: ${path})`;
7742
7861
  const diskContent = readFileSync4(path, "utf-8");
7743
7862
  if (diskContent === expectedContent)
@@ -7967,20 +8086,20 @@ var init_sync = __esm(() => {
7967
8086
  });
7968
8087
 
7969
8088
  // src/lib/sync-dir.ts
7970
- import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
7971
- import { join as join10, relative as relative3 } from "path";
7972
- import { homedir as homedir5 } from "os";
8089
+ import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
8090
+ import { join as join11, relative as relative3 } from "path";
8091
+ import { homedir as homedir6 } from "os";
7973
8092
  function shouldSkip(p) {
7974
8093
  return SKIP.some((s) => p.includes(s));
7975
8094
  }
7976
8095
  async function syncFromDir(dir, opts = {}) {
7977
8096
  const store = opts.store ?? resolveConfigStore();
7978
8097
  const absDir = expandPath(dir);
7979
- if (!existsSync7(absDir))
8098
+ if (!existsSync8(absDir))
7980
8099
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
7981
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join10(absDir, f)).filter((f) => statSync2(f).isFile());
8100
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join11(absDir, f)).filter((f) => statSync2(f).isFile());
7982
8101
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
7983
- const home = homedir5();
8102
+ const home = homedir6();
7984
8103
  const allConfigs = await store.listConfigs();
7985
8104
  for (const file of files) {
7986
8105
  if (shouldSkip(file)) {
@@ -8015,7 +8134,7 @@ async function syncFromDir(dir, opts = {}) {
8015
8134
  }
8016
8135
  async function syncToDir(dir, opts = {}) {
8017
8136
  const store = opts.store ?? resolveConfigStore();
8018
- const home = homedir5();
8137
+ const home = homedir6();
8019
8138
  const absDir = expandPath(dir);
8020
8139
  const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
8021
8140
  const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
@@ -8039,7 +8158,7 @@ async function syncToDir(dir, opts = {}) {
8039
8158
  }
8040
8159
  function walkDir(dir, files = []) {
8041
8160
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
8042
- const full = join10(dir, entry.name);
8161
+ const full = join11(dir, entry.name);
8043
8162
  if (shouldSkip(full))
8044
8163
  continue;
8045
8164
  if (entry.isDirectory())
@@ -8062,7 +8181,7 @@ var init_sync_dir = __esm(() => {
8062
8181
  var require_package = __commonJS((exports, module) => {
8063
8182
  module.exports = {
8064
8183
  name: "@hasna/instructions",
8065
- version: "0.5.2",
8184
+ version: "0.5.4",
8066
8185
  description: "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
8067
8186
  type: "module",
8068
8187
  main: "dist/index.js",
@@ -8103,7 +8222,7 @@ var require_package = __commonJS((exports, module) => {
8103
8222
  "dev:serve": "bun run src/server/index.ts",
8104
8223
  seed: "bun run scripts/seed.ts",
8105
8224
  prepublishOnly: "bun run scripts/check-publish-hold.ts && bun run build",
8106
- postinstall: "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true",
8225
+ postinstall: `node -e "const fs=require('node:fs'),path=require('node:path'),os=require('node:os');const home=process.env.HOME||process.env.USERPROFILE||os.homedir();const exact=process.env.HASNA_CONFIGS_HOME;const configHome=process.env.HASNA_CONFIG_HOME;const root=exact&&exact.trim()?path.resolve(exact.trim()):(configHome&&configHome.trim()?path.join(configHome.trim(),'configs'):path.join(home,'.hasna','instructions'));try{fs.mkdirSync(root,{recursive:true,mode:0o700});fs.mkdirSync(path.join(root,'backups'),{recursive:true,mode:0o700});}catch{}"`,
8107
8226
  prepack: "bun run build"
8108
8227
  },
8109
8228
  keywords: [
@@ -8138,12 +8257,13 @@ var require_package = __commonJS((exports, module) => {
8138
8257
  author: "Andrei Hasna <andrei@hasna.com>",
8139
8258
  license: "Apache-2.0",
8140
8259
  dependencies: {
8141
- "@hasna/contracts": "0.14.0",
8260
+ "@hasna/contracts": "0.14.1",
8142
8261
  "@hasna/events": "^0.1.16",
8262
+ "@hasna/paths": "0.1.0",
8143
8263
  "@modelcontextprotocol/sdk": "^1.12.1",
8144
8264
  chalk: "^5.4.1",
8145
8265
  commander: "^13.1.0",
8146
- hono: "^4.7.4",
8266
+ hono: "4.13.3",
8147
8267
  ink: "^5.2.0",
8148
8268
  pg: "^8.13.3",
8149
8269
  react: "^18.3.1",
@@ -1715,7 +1715,7 @@ class ProfileNotFoundError extends Error {
1715
1715
  }
1716
1716
  }
1717
1717
 
1718
- // ../../node_modules/.bun/@hasna+contracts@0.14.0+e8014c875821e0be/node_modules/@hasna/contracts/dist/auth/index.js
1718
+ // ../contracts/dist/auth/index.js
1719
1719
  import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
1720
1720
  var MAX_TENANT_ID_LENGTH = 64;
1721
1721
  var TENANT_ID_PATTERN = new RegExp(`^[A-Za-z0-9][A-Za-z0-9._-]{0,${MAX_TENANT_ID_LENGTH - 1}}$`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/instructions",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "AI coding agent instruction & configuration manager — store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,7 +41,7 @@
41
41
  "dev:serve": "bun run src/server/index.ts",
42
42
  "seed": "bun run scripts/seed.ts",
43
43
  "prepublishOnly": "bun run scripts/check-publish-hold.ts && bun run build",
44
- "postinstall": "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true",
44
+ "postinstall": "node -e \"const fs=require('node:fs'),path=require('node:path'),os=require('node:os');const home=process.env.HOME||process.env.USERPROFILE||os.homedir();const exact=process.env.HASNA_CONFIGS_HOME;const configHome=process.env.HASNA_CONFIG_HOME;const root=exact&&exact.trim()?path.resolve(exact.trim()):(configHome&&configHome.trim()?path.join(configHome.trim(),'configs'):path.join(home,'.hasna','instructions'));try{fs.mkdirSync(root,{recursive:true,mode:0o700});fs.mkdirSync(path.join(root,'backups'),{recursive:true,mode:0o700});}catch{}\"",
45
45
  "prepack": "bun run build"
46
46
  },
47
47
  "keywords": [
@@ -76,12 +76,13 @@
76
76
  "author": "Andrei Hasna <andrei@hasna.com>",
77
77
  "license": "Apache-2.0",
78
78
  "dependencies": {
79
- "@hasna/contracts": "0.14.0",
79
+ "@hasna/contracts": "0.14.1",
80
80
  "@hasna/events": "^0.1.16",
81
+ "@hasna/paths": "0.1.0",
81
82
  "@modelcontextprotocol/sdk": "^1.12.1",
82
83
  "chalk": "^5.4.1",
83
84
  "commander": "^13.1.0",
84
- "hono": "^4.7.4",
85
+ "hono": "4.13.3",
85
86
  "ink": "^5.2.0",
86
87
  "pg": "^8.13.3",
87
88
  "react": "^18.3.1",