@hasna/instructions 0.4.8 → 0.4.9

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
@@ -5359,8 +5359,12 @@ var init_redact = __esm(() => {
5359
5359
  });
5360
5360
 
5361
5361
  // src/lib/session-render-contract.ts
5362
- var SESSION_RENDER_SCHEMA = "hasna.configs.session-render/v1";
5363
- var init_session_render_contract = () => {};
5362
+ var SESSION_RENDER_SCHEMA = "hasna.configs.session-render/v1", SESSION_RENDER_MANAGED_NAMESPACE = ".hasna", SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR, SESSION_RENDER_MANIFEST_RELATIVE_PATH, SESSION_RENDER_SNAPSHOT_RELATIVE_DIR;
5363
+ var init_session_render_contract = __esm(() => {
5364
+ SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR = `${SESSION_RENDER_MANAGED_NAMESPACE}/instructions`;
5365
+ SESSION_RENDER_MANIFEST_RELATIVE_PATH = `${SESSION_RENDER_MANAGED_NAMESPACE}/session-render-manifest.json`;
5366
+ SESSION_RENDER_SNAPSHOT_RELATIVE_DIR = `${SESSION_RENDER_MANAGED_NAMESPACE}/session-render-snapshots`;
5367
+ });
5364
5368
 
5365
5369
  // src/lib/project-context.ts
5366
5370
  import { basename, dirname, isAbsolute, join as join3, parse, relative, resolve } from "path";
@@ -5645,7 +5649,7 @@ function applyTransform(source, output, context = {}) {
5645
5649
  var init_transforms = () => {};
5646
5650
 
5647
5651
  // src/lib/session-render.ts
5648
- var ANTIGRAVITY_RULE_FILE_CHAR_LIMIT = 12000, SESSION_RENDERER_OWNER_ID = "instructions-session-renderer", SESSION_RENDER_PROFILE_ENTRYPOINTS, SESSION_RENDER_OWNED_CONFIG_TARGETS;
5652
+ var ANTIGRAVITY_RULE_FILE_CHAR_LIMIT = 12000, SESSION_RENDERER_OWNER_ID = "instructions-session-renderer", SESSION_RENDER_PROFILE_ENTRYPOINTS, SESSION_RENDER_OWNED_CONFIG_TARGETS, CODEWITH_FLATTENED_ADAPTER, CODEWITH_NATIVE_ADAPTER, SESSION_TOOL_ADAPTERS, SESSION_RENDER_MANAGED_DIRS, SESSION_RENDER_SHARED_MANAGED_DIRS, SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS;
5649
5653
  var init_session_render = __esm(() => {
5650
5654
  init_global_agent_rules_standard();
5651
5655
  init_project_context();
@@ -5664,6 +5668,174 @@ var init_session_render = __esm(() => {
5664
5668
  ".gemini/GEMINI.md",
5665
5669
  ".gemini/ANTIGRAVITY.md"
5666
5670
  ];
5671
+ CODEWITH_FLATTENED_ADAPTER = {
5672
+ tool: "codewith",
5673
+ mode: "flattened-markdown",
5674
+ indexFile: "CODEWITH.md",
5675
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
5676
+ envVar: "CODEWITH_HOME",
5677
+ nativeImports: false,
5678
+ description: "Codewith CODEWITH.md flattened until native @ imports are implemented in Codewith."
5679
+ };
5680
+ CODEWITH_NATIVE_ADAPTER = {
5681
+ tool: "codewith",
5682
+ mode: "native-imports",
5683
+ indexFile: "CODEWITH.md",
5684
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
5685
+ envVar: "CODEWITH_HOME",
5686
+ nativeImports: true,
5687
+ description: "Codewith CODEWITH.md with gated @ imports into managed fragments."
5688
+ };
5689
+ SESSION_TOOL_ADAPTERS = {
5690
+ claude: {
5691
+ tool: "claude",
5692
+ mode: "native-imports",
5693
+ indexFile: "CLAUDE.md",
5694
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
5695
+ envVar: "CLAUDE_CONFIG_DIR",
5696
+ nativeImports: true,
5697
+ description: "Claude Code CLAUDE.md with @ imports into managed fragments."
5698
+ },
5699
+ codex: {
5700
+ tool: "codex",
5701
+ mode: "flattened-markdown",
5702
+ indexFile: "AGENTS.md",
5703
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
5704
+ envVar: "CODEX_HOME",
5705
+ nativeImports: false,
5706
+ description: "Codex AGENTS.md flattened instruction file."
5707
+ },
5708
+ cursor: {
5709
+ tool: "cursor",
5710
+ mode: "cursor-mdc",
5711
+ managedDir: ".cursor/rules",
5712
+ nativeImports: false,
5713
+ description: "Cursor project rule files in .cursor/rules/*.mdc."
5714
+ },
5715
+ opencode: {
5716
+ tool: "opencode",
5717
+ mode: "opencode-instructions",
5718
+ indexFile: "AGENTS.md",
5719
+ configFile: "opencode.json",
5720
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
5721
+ envVar: "OPENCODE_CONFIG_DIR",
5722
+ nativeImports: false,
5723
+ description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments."
5724
+ },
5725
+ aicopilot: {
5726
+ tool: "aicopilot",
5727
+ mode: "flattened-markdown",
5728
+ indexFile: "AICOPILOT.md",
5729
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
5730
+ envVar: "AICOPILOT_CONFIG_DIR",
5731
+ nativeImports: false,
5732
+ description: "AI Copilot AICOPILOT.md flattened instruction file."
5733
+ },
5734
+ qwen: {
5735
+ tool: "qwen",
5736
+ mode: "flattened-markdown",
5737
+ indexFile: "QWEN.md",
5738
+ managedDir: ".qwen/instructions",
5739
+ envVar: "QWEN_HOME",
5740
+ nativeImports: false,
5741
+ description: "Qwen Code QWEN.md hierarchical instructional context file."
5742
+ },
5743
+ antigravity: {
5744
+ tool: "antigravity",
5745
+ mode: "antigravity-rules",
5746
+ managedDir: ".agents/rules",
5747
+ nativeImports: false,
5748
+ description: "Google Antigravity project rules in .agents/rules/*.md."
5749
+ },
5750
+ codewith: CODEWITH_FLATTENED_ADAPTER
5751
+ };
5752
+ SESSION_RENDER_MANAGED_DIRS = [
5753
+ ...new Set([
5754
+ CODEWITH_FLATTENED_ADAPTER,
5755
+ CODEWITH_NATIVE_ADAPTER,
5756
+ ...Object.values(SESSION_TOOL_ADAPTERS)
5757
+ ].map((adapter) => adapter.managedDir))
5758
+ ];
5759
+ SESSION_RENDER_SHARED_MANAGED_DIRS = [".cursor/rules"];
5760
+ SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS = [
5761
+ ...SESSION_RENDER_MANAGED_DIRS.filter((dir) => !SESSION_RENDER_SHARED_MANAGED_DIRS.includes(dir)),
5762
+ SESSION_RENDER_MANIFEST_RELATIVE_PATH,
5763
+ SESSION_RENDER_SNAPSHOT_RELATIVE_DIR
5764
+ ];
5765
+ });
5766
+
5767
+ // src/lib/session-render-ownership.ts
5768
+ import { existsSync as existsSync3, readFileSync, statSync } from "fs";
5769
+ import { dirname as dirname2, join as join4, parse as parse2, relative as relative2, sep } from "path";
5770
+ function toSegments(absolutePath2) {
5771
+ return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
5772
+ }
5773
+ function pathIsSessionRenderManagedDir(absolutePath2) {
5774
+ const segments = toSegments(absolutePath2);
5775
+ return MANAGED_PATH_SEGMENTS.some((managed) => {
5776
+ if (managed.length === 0 || managed.length > segments.length)
5777
+ return false;
5778
+ for (let start = 0;start + managed.length <= segments.length; start += 1) {
5779
+ if (managed.every((segment, offset) => segments[start + offset] === segment))
5780
+ return true;
5781
+ }
5782
+ return false;
5783
+ });
5784
+ }
5785
+ function readManifestRelativePaths(manifestPath) {
5786
+ let stats;
5787
+ try {
5788
+ if (!existsSync3(manifestPath))
5789
+ return null;
5790
+ stats = statSync(manifestPath);
5791
+ } catch {
5792
+ return null;
5793
+ }
5794
+ const cached = manifestCache.get(manifestPath);
5795
+ if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
5796
+ return cached.relativePaths;
5797
+ }
5798
+ let manifest;
5799
+ try {
5800
+ manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
5801
+ } catch {
5802
+ return null;
5803
+ }
5804
+ if (manifest?.schema !== SESSION_RENDER_SCHEMA || !Array.isArray(manifest.files))
5805
+ return null;
5806
+ const writerId = manifest.targetOwner?.writer?.id;
5807
+ if (writerId !== undefined && writerId !== SESSION_RENDERER_OWNER_ID)
5808
+ return null;
5809
+ const relativePaths = new Set(manifest.files.map((file) => file?.relativePath).filter((relativePath) => typeof relativePath === "string").map((relativePath) => relativePath.replaceAll("\\", "/")));
5810
+ manifestCache.set(manifestPath, { mtimeMs: stats.mtimeMs, size: stats.size, relativePaths });
5811
+ return relativePaths;
5812
+ }
5813
+ function sessionRenderManifestClaimsPath(absolutePath2) {
5814
+ const root = parse2(absolutePath2).root;
5815
+ let home = dirname2(absolutePath2);
5816
+ for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
5817
+ const manifestPath = join4(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
5818
+ const relativePaths = readManifestRelativePaths(manifestPath);
5819
+ if (relativePaths) {
5820
+ const claimed = relative2(home, absolutePath2).split(sep).join("/");
5821
+ if (relativePaths.has(claimed))
5822
+ return true;
5823
+ }
5824
+ const parent = dirname2(home);
5825
+ if (parent === home || home === root)
5826
+ break;
5827
+ home = parent;
5828
+ }
5829
+ return false;
5830
+ }
5831
+ function sessionRenderOwnsPath(absolutePath2) {
5832
+ return pathIsSessionRenderManagedDir(absolutePath2) || sessionRenderManifestClaimsPath(absolutePath2);
5833
+ }
5834
+ var MANIFEST_ANCESTOR_LIMIT = 24, MANAGED_PATH_SEGMENTS, manifestCache;
5835
+ var init_session_render_ownership = __esm(() => {
5836
+ init_session_render();
5837
+ MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
5838
+ manifestCache = new Map;
5667
5839
  });
5668
5840
 
5669
5841
  // src/lib/apply.ts
@@ -5677,8 +5849,8 @@ __export(exports_apply, {
5677
5849
  applyConfigs: () => applyConfigs,
5678
5850
  applyConfig: () => applyConfig
5679
5851
  });
5680
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, realpathSync, writeFileSync } from "fs";
5681
- import { basename as basename3, dirname as dirname2, join as join4, resolve as resolve2 } from "path";
5852
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, realpathSync, writeFileSync } from "fs";
5853
+ import { basename as basename3, dirname as dirname3, join as join5, resolve as resolve2 } from "path";
5682
5854
  import { homedir as homedir2 } from "os";
5683
5855
  function getConfigHome() {
5684
5856
  return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir2();
@@ -5697,14 +5869,14 @@ function normalizeTargetPath(p) {
5697
5869
  let current = expanded;
5698
5870
  const missingSegments = [];
5699
5871
  while (true) {
5700
- if (existsSync3(current)) {
5872
+ if (existsSync4(current)) {
5701
5873
  try {
5702
5874
  return resolve2(realpathSync(current), ...missingSegments);
5703
5875
  } catch {
5704
5876
  return expanded;
5705
5877
  }
5706
5878
  }
5707
- const parent = dirname2(current);
5879
+ const parent = dirname3(current);
5708
5880
  const name = basename3(current);
5709
5881
  if (parent === current)
5710
5882
  return expanded;
@@ -5726,11 +5898,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
5726
5898
  throw new ConfigApplyError(`Antigravity rule file ${renderedTargetPath} is ${renderedContent.length} characters; split it before applying because Antigravity limits rule files to ${ANTIGRAVITY_RULE_FILE_CHAR_LIMIT} characters.`);
5727
5899
  }
5728
5900
  const path = expandPath(renderedTargetPath);
5729
- const previousContent = existsSync3(path) ? readFileSync(path, "utf-8") : null;
5901
+ const previousContent = existsSync4(path) ? readFileSync2(path, "utf-8") : null;
5730
5902
  const changed = previousContent !== renderedContent;
5731
5903
  if (!opts.dryRun) {
5732
- const dir = dirname2(path);
5733
- if (!existsSync3(dir)) {
5904
+ const dir = dirname3(path);
5905
+ if (!existsSync4(dir)) {
5734
5906
  mkdirSync2(dir, { recursive: true });
5735
5907
  }
5736
5908
  if (previousContent !== null && changed) {
@@ -6033,13 +6205,15 @@ function sessionRendererOwnsTarget(targetPath, opts) {
6033
6205
  return sessionRendererOwnsCanonicalTarget(canonicalApplyTargetPath(targetPath, opts), opts);
6034
6206
  }
6035
6207
  function sessionRendererOwnsCanonicalTarget(normalized, opts) {
6208
+ if (opts.allowSessionRendererOwned)
6209
+ return false;
6036
6210
  const homes = new Set([
6037
6211
  getConfigHome(),
6038
6212
  opts.vars?.["HOME_DIR"]
6039
6213
  ].filter((home) => typeof home === "string" && home.length > 0));
6040
- if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join4(home, ...relativePath.split("/"))))))
6214
+ if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join5(home, ...relativePath.split("/"))))))
6041
6215
  return true;
6042
- return normalized.replaceAll("\\", "/").includes("/.agents/rules/");
6216
+ return sessionRenderOwnsPath(normalized);
6043
6217
  }
6044
6218
  var init_apply = __esm(() => {
6045
6219
  init_types();
@@ -6047,6 +6221,7 @@ var init_apply = __esm(() => {
6047
6221
  init_config_agents();
6048
6222
  init_machine();
6049
6223
  init_session_render();
6224
+ init_session_render_ownership();
6050
6225
  init_transforms();
6051
6226
  });
6052
6227
 
@@ -6066,8 +6241,8 @@ __export(exports_sync, {
6066
6241
  KNOWN_CONFIGS: () => KNOWN_CONFIGS,
6067
6242
  CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
6068
6243
  });
6069
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2 } from "fs";
6070
- import { basename as basename4, extname as extname2, join as join5 } from "path";
6244
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3 } from "fs";
6245
+ import { basename as basename4, extname as extname2, join as join6 } from "path";
6071
6246
  function claudeRuleOutputs(fileName) {
6072
6247
  const stem = basename4(fileName, extname2(fileName));
6073
6248
  return [
@@ -6099,7 +6274,7 @@ function isGeneratedOutputTarget2(config, owners) {
6099
6274
  return !!ownerIds && !ownerIds.has(config.id);
6100
6275
  }
6101
6276
  function hasClaudePromptSource() {
6102
- return existsSync4(expandPath("~/.claude/CLAUDE.md"));
6277
+ return existsSync5(expandPath("~/.claude/CLAUDE.md"));
6103
6278
  }
6104
6279
  function hasClaudeRuleSourceForCursorTarget(targetPath) {
6105
6280
  const absoluteTargetPath = expandPath(targetPath);
@@ -6107,7 +6282,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
6107
6282
  if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
6108
6283
  return false;
6109
6284
  const stem = basename4(absoluteTargetPath, ".mdc");
6110
- return existsSync4(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync4(expandPath(`~/.claude/rules/${stem}.mdc`));
6285
+ return existsSync5(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync5(expandPath(`~/.claude/rules/${stem}.mdc`));
6111
6286
  }
6112
6287
  function isKnownGeneratedTargetPath(targetPath) {
6113
6288
  const normalizedTargetPath = normalizeTargetPath(targetPath);
@@ -6124,11 +6299,11 @@ async function syncProject(opts) {
6124
6299
  const allConfigs = await store.listConfigs();
6125
6300
  const machine = detectMachineContext();
6126
6301
  for (const pf of PROJECT_CONFIG_FILES) {
6127
- const abs = join5(absDir, pf.file);
6128
- if (!existsSync4(abs))
6302
+ const abs = join6(absDir, pf.file);
6303
+ if (!existsSync5(abs))
6129
6304
  continue;
6130
6305
  try {
6131
- const rawContent = readFileSync2(abs, "utf-8");
6306
+ const rawContent = readFileSync3(abs, "utf-8");
6132
6307
  if (rawContent.length > 500000) {
6133
6308
  result.skipped.push(pf.file);
6134
6309
  continue;
@@ -6157,15 +6332,15 @@ async function syncProject(opts) {
6157
6332
  }
6158
6333
  }
6159
6334
  for (const ruleDir of [
6160
- { dir: join5(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
6161
- { dir: join5(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" }
6335
+ { dir: join6(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
6336
+ { dir: join6(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" }
6162
6337
  ]) {
6163
- if (!existsSync4(ruleDir.dir))
6338
+ if (!existsSync5(ruleDir.dir))
6164
6339
  continue;
6165
6340
  const mdFiles = readdirSync(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
6166
6341
  for (const f of mdFiles) {
6167
- const abs = join5(ruleDir.dir, f);
6168
- const raw = readFileSync2(abs, "utf-8");
6342
+ const abs = join6(ruleDir.dir, f);
6343
+ const raw = readFileSync3(abs, "utf-8");
6169
6344
  const redacted = redactContent(raw, "markdown");
6170
6345
  const machineAware = templateizeMachineContent(redacted.content, machine);
6171
6346
  const content = machineAware.content;
@@ -6204,20 +6379,20 @@ async function syncKnown(opts = {}) {
6204
6379
  for (const known of targets) {
6205
6380
  if (known.rulesDir) {
6206
6381
  const absDir = expandPath(known.rulesDir);
6207
- if (!existsSync4(absDir)) {
6382
+ if (!existsSync5(absDir)) {
6208
6383
  result.skipped.push(known.rulesDir);
6209
6384
  continue;
6210
6385
  }
6211
6386
  const extensions = known.rulesExtensions ?? [".md", ".mdc"];
6212
6387
  const ruleFiles = readdirSync(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
6213
6388
  for (const f of ruleFiles) {
6214
- const abs2 = join5(absDir, f);
6389
+ const abs2 = join6(absDir, f);
6215
6390
  const targetPath = abs2.replace(home, "~");
6216
6391
  if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
6217
6392
  result.skipped.push(`${targetPath} (generated output)`);
6218
6393
  continue;
6219
6394
  }
6220
- const raw = readFileSync2(abs2, "utf-8");
6395
+ const raw = readFileSync3(abs2, "utf-8");
6221
6396
  const redacted = redactContent(raw, "markdown");
6222
6397
  const machineAware = templateizeMachineContent(redacted.content, machine);
6223
6398
  const content = machineAware.content;
@@ -6245,12 +6420,12 @@ async function syncKnown(opts = {}) {
6245
6420
  continue;
6246
6421
  }
6247
6422
  const abs = expandPath(known.path);
6248
- if (!existsSync4(abs)) {
6423
+ if (!existsSync5(abs)) {
6249
6424
  result.skipped.push(known.path);
6250
6425
  continue;
6251
6426
  }
6252
6427
  try {
6253
- const rawContent = readFileSync2(abs, "utf-8");
6428
+ const rawContent = readFileSync3(abs, "utf-8");
6254
6429
  if (rawContent.length > 500000) {
6255
6430
  result.skipped.push(known.path + " (too large)");
6256
6431
  continue;
@@ -6338,9 +6513,9 @@ async function syncToDisk(opts = {}) {
6338
6513
  }
6339
6514
  function buildDiff(expectedContent, targetPath) {
6340
6515
  const path = expandPath(targetPath);
6341
- if (!existsSync4(path))
6516
+ if (!existsSync5(path))
6342
6517
  return `(file not found on disk: ${path})`;
6343
- const diskContent = readFileSync2(path, "utf-8");
6518
+ const diskContent = readFileSync3(path, "utf-8");
6344
6519
  if (diskContent === expectedContent)
6345
6520
  return "(no diff \u2014 identical)";
6346
6521
  const stored = expectedContent.split(`
@@ -6520,8 +6695,8 @@ var init_sync = __esm(() => {
6520
6695
  });
6521
6696
 
6522
6697
  // src/lib/sync-dir.ts
6523
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "fs";
6524
- import { join as join6, relative as relative2 } from "path";
6698
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
6699
+ import { join as join7, relative as relative3 } from "path";
6525
6700
  import { homedir as homedir3 } from "os";
6526
6701
  function shouldSkip(p) {
6527
6702
  return SKIP.some((s) => p.includes(s));
@@ -6529,9 +6704,9 @@ function shouldSkip(p) {
6529
6704
  async function syncFromDir(dir, opts = {}) {
6530
6705
  const store = opts.store ?? resolveConfigStore();
6531
6706
  const absDir = expandPath(dir);
6532
- if (!existsSync5(absDir))
6707
+ if (!existsSync6(absDir))
6533
6708
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
6534
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join6(absDir, f)).filter((f) => statSync(f).isFile());
6709
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join7(absDir, f)).filter((f) => statSync2(f).isFile());
6535
6710
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
6536
6711
  const home = homedir3();
6537
6712
  const allConfigs = await store.listConfigs();
@@ -6541,7 +6716,7 @@ async function syncFromDir(dir, opts = {}) {
6541
6716
  continue;
6542
6717
  }
6543
6718
  try {
6544
- const content = readFileSync3(file, "utf-8");
6719
+ const content = readFileSync4(file, "utf-8");
6545
6720
  if (content.length > 500000) {
6546
6721
  result.skipped.push(file + " (too large)");
6547
6722
  continue;
@@ -6550,7 +6725,7 @@ async function syncFromDir(dir, opts = {}) {
6550
6725
  const existing = allConfigs.find((c) => c.target_path === targetPath);
6551
6726
  if (!existing) {
6552
6727
  if (!opts.dryRun)
6553
- await store.createConfig({ name: relative2(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content });
6728
+ await store.createConfig({ name: relative3(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content });
6554
6729
  result.added++;
6555
6730
  } else if (existing.content !== content) {
6556
6731
  if (!opts.dryRun)
@@ -6591,7 +6766,7 @@ async function syncToDir(dir, opts = {}) {
6591
6766
  }
6592
6767
  function walkDir(dir, files = []) {
6593
6768
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
6594
- const full = join6(dir, entry.name);
6769
+ const full = join7(dir, entry.name);
6595
6770
  if (shouldSkip(full))
6596
6771
  continue;
6597
6772
  if (entry.isDirectory())
@@ -6613,7 +6788,7 @@ var init_sync_dir = __esm(() => {
6613
6788
  var require_package = __commonJS((exports, module) => {
6614
6789
  module.exports = {
6615
6790
  name: "@hasna/instructions",
6616
- version: "0.4.8",
6791
+ version: "0.4.9",
6617
6792
  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.",
6618
6793
  type: "module",
6619
6794
  main: "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/instructions",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "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.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",