@hasna/instructions 0.4.35 → 0.4.39

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 (35) hide show
  1. package/README.md +4 -2
  2. package/assets/skills/inbox/SKILL.md +86 -0
  3. package/dashboard/README.md +34 -70
  4. package/dist/cli/index.js +1136 -526
  5. package/dist/cli/raw-store-root.test.d.ts +2 -0
  6. package/dist/cli/raw-store-root.test.d.ts.map +1 -0
  7. package/dist/db/configs.d.ts.map +1 -1
  8. package/dist/generated/storage-kit/index.d.ts +1 -1
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1276 -754
  12. package/dist/lib/apply.d.ts.map +1 -1
  13. package/dist/lib/cursor-authority.d.ts +36 -3
  14. package/dist/lib/cursor-authority.d.ts.map +1 -1
  15. package/dist/lib/managed-skill-runtimes.d.ts +80 -0
  16. package/dist/lib/managed-skill-runtimes.d.ts.map +1 -0
  17. package/dist/lib/managed-skill-runtimes.test.d.ts +2 -0
  18. package/dist/lib/managed-skill-runtimes.test.d.ts.map +1 -0
  19. package/dist/lib/raw-store-root.d.ts +17 -0
  20. package/dist/lib/raw-store-root.d.ts.map +1 -0
  21. package/dist/lib/session-apply.d.ts.map +1 -1
  22. package/dist/lib/session-authority.d.ts +27 -0
  23. package/dist/lib/session-authority.d.ts.map +1 -0
  24. package/dist/lib/session-authority.test.d.ts +2 -0
  25. package/dist/lib/session-authority.test.d.ts.map +1 -0
  26. package/dist/lib/session-render.d.ts +5 -3
  27. package/dist/lib/session-render.d.ts.map +1 -1
  28. package/dist/mcp/index.js +290 -233
  29. package/dist/server/index.js +425 -231
  30. package/dist/status.d.ts +11 -1
  31. package/dist/status.d.ts.map +1 -1
  32. package/dist/storage/cloud-store.d.ts.map +1 -1
  33. package/dist/storage/cloud-store.test.d.ts +2 -0
  34. package/dist/storage/cloud-store.test.d.ts.map +1 -0
  35. package/package.json +9 -7
package/dist/mcp/index.js CHANGED
@@ -97,19 +97,27 @@ var init_retired_storage_mode = __esm(() => {
97
97
  ];
98
98
  });
99
99
 
100
+ // src/lib/raw-store-root.ts
101
+ import { homedir } from "os";
102
+ import { join, resolve } from "path";
103
+ function getRawStoreRoot() {
104
+ return resolve(process.env[RAW_STORE_ROOT_ENV] || join(process.env["HOME"] || homedir(), ".hasna", "instructions"));
105
+ }
106
+ var RAW_STORE_ROOT_ENV = "HASNA_CONFIGS_HOME";
107
+ var init_raw_store_root = () => {};
108
+
100
109
  // src/db/database.ts
101
110
  import { Database } from "bun:sqlite";
102
111
  import { existsSync, mkdirSync, rmSync } from "fs";
103
- import { join } from "path";
112
+ import { join as join2 } from "path";
104
113
  import { randomUUID } from "crypto";
105
114
  function getDbPath() {
106
115
  if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
107
116
  return process.env["HASNA_INSTRUCTIONS_DB_PATH"];
108
117
  }
109
- const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
110
- const dir = join(home, ".hasna", "instructions");
118
+ const dir = getRawStoreRoot();
111
119
  mkdirSync(dir, { recursive: true });
112
- return join(dir, "instructions.db");
120
+ return join2(dir, "instructions.db");
113
121
  }
114
122
  function uuid() {
115
123
  return randomUUID();
@@ -207,6 +215,7 @@ function insertFeedback(input, db) {
207
215
  var MIGRATIONS, _db = null;
208
216
  var init_database = __esm(() => {
209
217
  init_retired_storage_mode();
218
+ init_raw_store_root();
210
219
  MIGRATIONS = [
211
220
  `
212
221
  CREATE TABLE IF NOT EXISTS configs (
@@ -291,6 +300,37 @@ var init_database = __esm(() => {
291
300
  ];
292
301
  });
293
302
 
303
+ // src/db/snapshots.ts
304
+ function createSnapshot(configId, content, version, db) {
305
+ const d = db || getDatabase();
306
+ const id = uuid();
307
+ const ts = now();
308
+ d.run("INSERT INTO config_snapshots (id, config_id, content, version, created_at) VALUES (?, ?, ?, ?, ?)", [id, configId, content, version, ts]);
309
+ return { id, config_id: configId, content, version, created_at: ts };
310
+ }
311
+ function listSnapshots(configId, db) {
312
+ const d = db || getDatabase();
313
+ return d.query("SELECT * FROM config_snapshots WHERE config_id = ? ORDER BY version DESC").all(configId);
314
+ }
315
+ function getSnapshot(id, db) {
316
+ const d = db || getDatabase();
317
+ return d.query("SELECT * FROM config_snapshots WHERE id = ?").get(id);
318
+ }
319
+ function getSnapshotByVersion(configId, version, db) {
320
+ const d = db || getDatabase();
321
+ return d.query("SELECT * FROM config_snapshots WHERE config_id = ? AND version = ?").get(configId, version);
322
+ }
323
+ function pruneSnapshots(configId, keep = 10, db) {
324
+ const d = db || getDatabase();
325
+ const result = d.run(`DELETE FROM config_snapshots WHERE config_id = ? AND id NOT IN (
326
+ SELECT id FROM config_snapshots WHERE config_id = ? ORDER BY version DESC LIMIT ?
327
+ )`, [configId, configId, keep]);
328
+ return result.changes;
329
+ }
330
+ var init_snapshots = __esm(() => {
331
+ init_database();
332
+ });
333
+
294
334
  // src/db/configs.ts
295
335
  function rowToConfig(row) {
296
336
  let outputs = [];
@@ -329,25 +369,28 @@ function createConfig(input, db) {
329
369
  const slug = uniqueSlug(input.name, d);
330
370
  const tags = JSON.stringify(input.tags || []);
331
371
  const outputs = JSON.stringify(input.outputs || []);
332
- d.run(`INSERT INTO configs (id, name, slug, kind, category, agent, target_path, outputs, format, content, description, tags, is_template, version, created_at, updated_at, synced_at)
333
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, NULL)`, [
334
- id,
335
- input.name,
336
- slug,
337
- input.kind ?? "file",
338
- input.category,
339
- input.agent ?? "global",
340
- input.target_path ?? null,
341
- outputs,
342
- input.format ?? "text",
343
- input.content,
344
- input.description ?? null,
345
- tags,
346
- input.is_template ? 1 : 0,
347
- ts,
348
- ts
349
- ]);
350
- return getConfig(id, d);
372
+ return d.transaction(() => {
373
+ d.run(`INSERT INTO configs (id, name, slug, kind, category, agent, target_path, outputs, format, content, description, tags, is_template, version, created_at, updated_at, synced_at)
374
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, NULL)`, [
375
+ id,
376
+ input.name,
377
+ slug,
378
+ input.kind ?? "file",
379
+ input.category,
380
+ input.agent ?? "global",
381
+ input.target_path ?? null,
382
+ outputs,
383
+ input.format ?? "text",
384
+ input.content,
385
+ input.description ?? null,
386
+ tags,
387
+ input.is_template ? 1 : 0,
388
+ ts,
389
+ ts
390
+ ]);
391
+ createSnapshot(id, input.content, 1, d);
392
+ return getConfig(id, d);
393
+ })();
351
394
  }
352
395
  function getConfig(idOrSlug, db) {
353
396
  const d = db || getDatabase();
@@ -452,9 +495,13 @@ function updateConfig(idOrSlug, input, db) {
452
495
  updates.push("synced_at = ?");
453
496
  params.push(input.synced_at);
454
497
  }
455
- params.push(existing.id);
456
- d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?`, params);
457
- return getConfigById(existing.id, d);
498
+ return d.transaction(() => {
499
+ params.push(existing.id);
500
+ d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?`, params);
501
+ const updated = getConfigById(existing.id, d);
502
+ createSnapshot(updated.id, updated.content, updated.version, d);
503
+ return updated;
504
+ })();
458
505
  }
459
506
  function deleteConfig(idOrSlug, db) {
460
507
  const d = db || getDatabase();
@@ -474,16 +521,17 @@ function getConfigStats(db) {
474
521
  var init_configs = __esm(() => {
475
522
  init_types();
476
523
  init_database();
524
+ init_snapshots();
477
525
  });
478
526
 
479
527
  // src/lib/template.ts
480
528
  var exports_template = {};
481
529
  __export(exports_template, {
482
- renderTemplatePreview: () => renderTemplatePreview,
483
- renderTemplate: () => renderTemplate,
484
- parseTemplateVars: () => parseTemplateVars,
530
+ extractTemplateVars: () => extractTemplateVars,
485
531
  isTemplate: () => isTemplate,
486
- extractTemplateVars: () => extractTemplateVars
532
+ parseTemplateVars: () => parseTemplateVars,
533
+ renderTemplate: () => renderTemplate,
534
+ renderTemplatePreview: () => renderTemplatePreview
487
535
  });
488
536
  function parseTemplateVars(content) {
489
537
  const names = new Set;
@@ -547,9 +595,9 @@ var init_template = __esm(() => {
547
595
  });
548
596
 
549
597
  // src/lib/machine.ts
550
- import { arch as currentArch, homedir, hostname as currentHostname, type as currentOsType } from "os";
598
+ import { arch as currentArch, homedir as homedir2, hostname as currentHostname, type as currentOsType } from "os";
551
599
  import { existsSync as existsSync2 } from "fs";
552
- import { join as join2 } from "path";
600
+ import { join as join3 } from "path";
553
601
  function normalizeOsFamily(os) {
554
602
  const value = (os ?? "").trim().toLowerCase();
555
603
  if (value === "darwin" || value === "macos" || value === "mac" || value === "osx")
@@ -561,11 +609,11 @@ function normalizeOsFamily(os) {
561
609
  return value || "unknown";
562
610
  }
563
611
  function detectMachineContext(overrides = {}) {
564
- const homeDir = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir();
612
+ const homeDir = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir2();
565
613
  const os = overrides.os ?? currentOsType();
566
614
  const osFamily = normalizeOsFamily(os);
567
- const bunBinDir = overrides.bun_bin_dir ?? join2(homeDir, ".bun", "bin");
568
- const defaultBunPath = osFamily === "macos" && existsSync2(BREW_BUN_PATH) ? BREW_BUN_PATH : join2(bunBinDir, "bun");
615
+ const bunBinDir = overrides.bun_bin_dir ?? join3(homeDir, ".bun", "bin");
616
+ const defaultBunPath = osFamily === "macos" && existsSync2(BREW_BUN_PATH) ? BREW_BUN_PATH : join3(bunBinDir, "bun");
569
617
  return {
570
618
  id: "current-machine",
571
619
  hostname: overrides.hostname ?? currentHostname(),
@@ -575,10 +623,10 @@ function detectMachineContext(overrides = {}) {
575
623
  created_at: "",
576
624
  os_family: osFamily,
577
625
  home_dir: homeDir,
578
- workspace_root: overrides.workspace_root ?? join2(homeDir, osFamily === "macos" ? "Workspace" : "workspace"),
626
+ workspace_root: overrides.workspace_root ?? join3(homeDir, osFamily === "macos" ? "Workspace" : "workspace"),
579
627
  bun_bin_dir: bunBinDir,
580
628
  bun_path: overrides.bun_path ?? defaultBunPath,
581
- path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join2("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
629
+ path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join3("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
582
630
  };
583
631
  }
584
632
  function machineContextToVariables(machine) {
@@ -4729,113 +4777,113 @@ var init_types2 = __esm(() => {
4729
4777
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
4730
4778
  var exports_external = {};
4731
4779
  __export(exports_external, {
4732
- void: () => voidType,
4733
- util: () => util,
4734
- unknown: () => unknownType,
4735
- union: () => unionType,
4736
- undefined: () => undefinedType,
4737
- tuple: () => tupleType,
4738
- transformer: () => effectsType,
4739
- symbol: () => symbolType,
4740
- string: () => stringType,
4741
- strictObject: () => strictObjectType,
4742
- setErrorMap: () => setErrorMap,
4743
- set: () => setType,
4744
- record: () => recordType,
4745
- quotelessJson: () => quotelessJson,
4746
- promise: () => promiseType,
4747
- preprocess: () => preprocessType,
4748
- pipeline: () => pipelineType,
4749
- ostring: () => ostring,
4750
- optional: () => optionalType,
4751
- onumber: () => onumber,
4752
- oboolean: () => oboolean,
4753
- objectUtil: () => objectUtil,
4754
- object: () => objectType,
4755
- number: () => numberType,
4756
- nullable: () => nullableType,
4757
- null: () => nullType,
4758
- never: () => neverType,
4759
- nativeEnum: () => nativeEnumType,
4760
- nan: () => nanType,
4761
- map: () => mapType,
4762
- makeIssue: () => makeIssue,
4763
- literal: () => literalType,
4764
- lazy: () => lazyType,
4765
- late: () => late,
4766
- isValid: () => isValid,
4767
- isDirty: () => isDirty,
4768
- isAsync: () => isAsync,
4769
- isAborted: () => isAborted,
4770
- intersection: () => intersectionType,
4771
- instanceof: () => instanceOfType,
4772
- getParsedType: () => getParsedType,
4773
- getErrorMap: () => getErrorMap,
4774
- function: () => functionType,
4775
- enum: () => enumType,
4776
- effect: () => effectsType,
4777
- discriminatedUnion: () => discriminatedUnionType,
4778
- defaultErrorMap: () => en_default,
4779
- datetimeRegex: () => datetimeRegex,
4780
- date: () => dateType,
4781
- custom: () => custom,
4782
- coerce: () => coerce,
4783
- boolean: () => booleanType,
4784
- bigint: () => bigIntType,
4785
- array: () => arrayType,
4786
- any: () => anyType,
4787
- addIssueToContext: () => addIssueToContext,
4788
- ZodVoid: () => ZodVoid,
4789
- ZodUnknown: () => ZodUnknown,
4790
- ZodUnion: () => ZodUnion,
4791
- ZodUndefined: () => ZodUndefined,
4792
- ZodType: () => ZodType,
4793
- ZodTuple: () => ZodTuple,
4794
- ZodTransformer: () => ZodEffects,
4795
- ZodSymbol: () => ZodSymbol,
4796
- ZodString: () => ZodString,
4797
- ZodSet: () => ZodSet,
4798
- ZodSchema: () => ZodType,
4799
- ZodRecord: () => ZodRecord,
4800
- ZodReadonly: () => ZodReadonly,
4801
- ZodPromise: () => ZodPromise,
4802
- ZodPipeline: () => ZodPipeline,
4803
- ZodParsedType: () => ZodParsedType,
4804
- ZodOptional: () => ZodOptional,
4805
- ZodObject: () => ZodObject,
4806
- ZodNumber: () => ZodNumber,
4807
- ZodNullable: () => ZodNullable,
4808
- ZodNull: () => ZodNull,
4809
- ZodNever: () => ZodNever,
4810
- ZodNativeEnum: () => ZodNativeEnum,
4811
- ZodNaN: () => ZodNaN,
4812
- ZodMap: () => ZodMap,
4813
- ZodLiteral: () => ZodLiteral,
4814
- ZodLazy: () => ZodLazy,
4815
- ZodIssueCode: () => ZodIssueCode,
4816
- ZodIntersection: () => ZodIntersection,
4817
- ZodFunction: () => ZodFunction,
4818
- ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
4819
- ZodError: () => ZodError,
4820
- ZodEnum: () => ZodEnum,
4821
- ZodEffects: () => ZodEffects,
4822
- ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
4823
- ZodDefault: () => ZodDefault,
4824
- ZodDate: () => ZodDate,
4825
- ZodCatch: () => ZodCatch,
4826
- ZodBranded: () => ZodBranded,
4827
- ZodBoolean: () => ZodBoolean,
4828
- ZodBigInt: () => ZodBigInt,
4829
- ZodArray: () => ZodArray,
4830
- ZodAny: () => ZodAny,
4831
- Schema: () => ZodType,
4832
- ParseStatus: () => ParseStatus,
4833
- OK: () => OK,
4834
- NEVER: () => NEVER,
4835
- INVALID: () => INVALID,
4836
- EMPTY_PATH: () => EMPTY_PATH,
4780
+ BRAND: () => BRAND,
4837
4781
  DIRTY: () => DIRTY,
4838
- BRAND: () => BRAND
4782
+ EMPTY_PATH: () => EMPTY_PATH,
4783
+ INVALID: () => INVALID,
4784
+ NEVER: () => NEVER,
4785
+ OK: () => OK,
4786
+ ParseStatus: () => ParseStatus,
4787
+ Schema: () => ZodType,
4788
+ ZodAny: () => ZodAny,
4789
+ ZodArray: () => ZodArray,
4790
+ ZodBigInt: () => ZodBigInt,
4791
+ ZodBoolean: () => ZodBoolean,
4792
+ ZodBranded: () => ZodBranded,
4793
+ ZodCatch: () => ZodCatch,
4794
+ ZodDate: () => ZodDate,
4795
+ ZodDefault: () => ZodDefault,
4796
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
4797
+ ZodEffects: () => ZodEffects,
4798
+ ZodEnum: () => ZodEnum,
4799
+ ZodError: () => ZodError,
4800
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
4801
+ ZodFunction: () => ZodFunction,
4802
+ ZodIntersection: () => ZodIntersection,
4803
+ ZodIssueCode: () => ZodIssueCode,
4804
+ ZodLazy: () => ZodLazy,
4805
+ ZodLiteral: () => ZodLiteral,
4806
+ ZodMap: () => ZodMap,
4807
+ ZodNaN: () => ZodNaN,
4808
+ ZodNativeEnum: () => ZodNativeEnum,
4809
+ ZodNever: () => ZodNever,
4810
+ ZodNull: () => ZodNull,
4811
+ ZodNullable: () => ZodNullable,
4812
+ ZodNumber: () => ZodNumber,
4813
+ ZodObject: () => ZodObject,
4814
+ ZodOptional: () => ZodOptional,
4815
+ ZodParsedType: () => ZodParsedType,
4816
+ ZodPipeline: () => ZodPipeline,
4817
+ ZodPromise: () => ZodPromise,
4818
+ ZodReadonly: () => ZodReadonly,
4819
+ ZodRecord: () => ZodRecord,
4820
+ ZodSchema: () => ZodType,
4821
+ ZodSet: () => ZodSet,
4822
+ ZodString: () => ZodString,
4823
+ ZodSymbol: () => ZodSymbol,
4824
+ ZodTransformer: () => ZodEffects,
4825
+ ZodTuple: () => ZodTuple,
4826
+ ZodType: () => ZodType,
4827
+ ZodUndefined: () => ZodUndefined,
4828
+ ZodUnion: () => ZodUnion,
4829
+ ZodUnknown: () => ZodUnknown,
4830
+ ZodVoid: () => ZodVoid,
4831
+ addIssueToContext: () => addIssueToContext,
4832
+ any: () => anyType,
4833
+ array: () => arrayType,
4834
+ bigint: () => bigIntType,
4835
+ boolean: () => booleanType,
4836
+ coerce: () => coerce,
4837
+ custom: () => custom,
4838
+ date: () => dateType,
4839
+ datetimeRegex: () => datetimeRegex,
4840
+ defaultErrorMap: () => en_default,
4841
+ discriminatedUnion: () => discriminatedUnionType,
4842
+ effect: () => effectsType,
4843
+ enum: () => enumType,
4844
+ function: () => functionType,
4845
+ getErrorMap: () => getErrorMap,
4846
+ getParsedType: () => getParsedType,
4847
+ instanceof: () => instanceOfType,
4848
+ intersection: () => intersectionType,
4849
+ isAborted: () => isAborted,
4850
+ isAsync: () => isAsync,
4851
+ isDirty: () => isDirty,
4852
+ isValid: () => isValid,
4853
+ late: () => late,
4854
+ lazy: () => lazyType,
4855
+ literal: () => literalType,
4856
+ makeIssue: () => makeIssue,
4857
+ map: () => mapType,
4858
+ nan: () => nanType,
4859
+ nativeEnum: () => nativeEnumType,
4860
+ never: () => neverType,
4861
+ null: () => nullType,
4862
+ nullable: () => nullableType,
4863
+ number: () => numberType,
4864
+ object: () => objectType,
4865
+ objectUtil: () => objectUtil,
4866
+ oboolean: () => oboolean,
4867
+ onumber: () => onumber,
4868
+ optional: () => optionalType,
4869
+ ostring: () => ostring,
4870
+ pipeline: () => pipelineType,
4871
+ preprocess: () => preprocessType,
4872
+ promise: () => promiseType,
4873
+ quotelessJson: () => quotelessJson,
4874
+ record: () => recordType,
4875
+ set: () => setType,
4876
+ setErrorMap: () => setErrorMap,
4877
+ strictObject: () => strictObjectType,
4878
+ string: () => stringType,
4879
+ symbol: () => symbolType,
4880
+ transformer: () => effectsType,
4881
+ tuple: () => tupleType,
4882
+ undefined: () => undefinedType,
4883
+ union: () => unionType,
4884
+ unknown: () => unknownType,
4885
+ util: () => util,
4886
+ void: () => voidType
4839
4887
  });
4840
4888
  var init_external = __esm(() => {
4841
4889
  init_errors();
@@ -4855,11 +4903,11 @@ var init_zod = __esm(() => {
4855
4903
  // src/lib/redact.ts
4856
4904
  var exports_redact = {};
4857
4905
  __export(exports_redact, {
4858
- scanSecrets: () => scanSecrets,
4859
- redactFormatForTarget: () => redactFormatForTarget,
4860
- redactContent: () => redactContent,
4906
+ hasSecrets: () => hasSecrets,
4861
4907
  isSecretVarName: () => isSecretVarName,
4862
- hasSecrets: () => hasSecrets
4908
+ redactContent: () => redactContent,
4909
+ redactFormatForTarget: () => redactFormatForTarget,
4910
+ scanSecrets: () => scanSecrets
4863
4911
  });
4864
4912
  function redactShell(content) {
4865
4913
  const redacted = [];
@@ -5067,7 +5115,7 @@ var init_session_render_contract = __esm(() => {
5067
5115
  });
5068
5116
 
5069
5117
  // src/lib/project-context.ts
5070
- import { basename, dirname, isAbsolute, join as join3, parse, relative, resolve } from "path";
5118
+ import { basename, dirname, isAbsolute, join as join4, parse, relative, resolve as resolve2 } from "path";
5071
5119
  function revisionKey(value) {
5072
5120
  const sequence = value.match(/^(?:rev-)?([0-9]+)$/);
5073
5121
  if (sequence)
@@ -5628,9 +5676,42 @@ var init_asset_plan = __esm(() => {
5628
5676
  });
5629
5677
 
5630
5678
  // src/lib/cursor-authority.ts
5631
- var CURSOR_GLOBAL_AUTHORITY_MAX_BYTES;
5679
+ import { createHash } from "crypto";
5680
+ import { homedir as homedir3 } from "os";
5681
+ import { join as join5, resolve as resolve3 } from "path";
5682
+ function sha256(content) {
5683
+ return createHash("sha256").update(content).digest("hex");
5684
+ }
5685
+ function homeDir() {
5686
+ return process.env["HOME"] || homedir3();
5687
+ }
5688
+ function isCursorGlobalAuthorityPath(path) {
5689
+ return resolve3(path) === resolve3(join5(homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
5690
+ }
5691
+ function stampCursorGlobalAuthorityMarker(content) {
5692
+ if (CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN.test(content))
5693
+ return content;
5694
+ const digest = sha256(content);
5695
+ const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
5696
+ const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
5697
+ if (frontmatter) {
5698
+ return `${frontmatter}${markerLine}
5699
+ ${content.slice(frontmatter.length)}`;
5700
+ }
5701
+ return `${markerLine}
5702
+ ${content}`;
5703
+ }
5704
+ var CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH = ".cursor/rules/hasna-global.mdc", CURSOR_GLOBAL_AUTHORITY_MAX_BYTES, CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER = "Managed by @hasna/configs cursor global authority", CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN, CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN;
5632
5705
  var init_cursor_authority = __esm(() => {
5633
5706
  CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
5707
+ CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN = /^<!-- Managed by @hasna\/configs cursor global authority hash=(sha256:[a-f0-9]{64}) -->$/m;
5708
+ CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN = /^---\n[\s\S]*?\n---(?:\n|$)/;
5709
+ });
5710
+
5711
+ // src/lib/session-authority.ts
5712
+ var CLAUDE_LEGACY_AUTHORITY_MAX_BYTES;
5713
+ var init_session_authority = __esm(() => {
5714
+ CLAUDE_LEGACY_AUTHORITY_MAX_BYTES = 256 * 1024;
5634
5715
  });
5635
5716
 
5636
5717
  // src/lib/session-render.ts
@@ -5643,8 +5724,11 @@ var init_session_render = __esm(() => {
5643
5724
  init_transforms();
5644
5725
  init_asset_plan();
5645
5726
  init_cursor_authority();
5727
+ init_session_authority();
5646
5728
  init_session_render_contract();
5647
5729
  init_session_render_contract();
5730
+ init_raw_store_root();
5731
+ init_raw_store_root();
5648
5732
  SESSION_RENDER_PROFILE_ENTRYPOINTS = [
5649
5733
  ".claude/CLAUDE.md",
5650
5734
  ".codex/AGENTS.md",
@@ -6217,37 +6301,6 @@ var init_profiles = __esm(() => {
6217
6301
  init_asset_plan();
6218
6302
  });
6219
6303
 
6220
- // src/db/snapshots.ts
6221
- function createSnapshot(configId, content, version, db) {
6222
- const d = db || getDatabase();
6223
- const id = uuid();
6224
- const ts = now();
6225
- d.run("INSERT INTO config_snapshots (id, config_id, content, version, created_at) VALUES (?, ?, ?, ?, ?)", [id, configId, content, version, ts]);
6226
- return { id, config_id: configId, content, version, created_at: ts };
6227
- }
6228
- function listSnapshots(configId, db) {
6229
- const d = db || getDatabase();
6230
- return d.query("SELECT * FROM config_snapshots WHERE config_id = ? ORDER BY version DESC").all(configId);
6231
- }
6232
- function getSnapshot(id, db) {
6233
- const d = db || getDatabase();
6234
- return d.query("SELECT * FROM config_snapshots WHERE id = ?").get(id);
6235
- }
6236
- function getSnapshotByVersion(configId, version, db) {
6237
- const d = db || getDatabase();
6238
- return d.query("SELECT * FROM config_snapshots WHERE config_id = ? AND version = ?").get(configId, version);
6239
- }
6240
- function pruneSnapshots(configId, keep = 10, db) {
6241
- const d = db || getDatabase();
6242
- const result = d.run(`DELETE FROM config_snapshots WHERE config_id = ? AND id NOT IN (
6243
- SELECT id FROM config_snapshots WHERE config_id = ? ORDER BY version DESC LIMIT ?
6244
- )`, [configId, configId, keep]);
6245
- return result.changes;
6246
- }
6247
- var init_snapshots = __esm(() => {
6248
- init_database();
6249
- });
6250
-
6251
6304
  // src/db/machines.ts
6252
6305
  import { arch, hostname, type } from "os";
6253
6306
  function currentHostname2() {
@@ -6827,7 +6880,7 @@ var init_config_store = __esm(() => {
6827
6880
 
6828
6881
  // src/lib/session-render-ownership.ts
6829
6882
  import { existsSync as existsSync3, readFileSync, statSync } from "fs";
6830
- import { dirname as dirname2, join as join4, parse as parse2, relative as relative2, sep } from "path";
6883
+ import { dirname as dirname2, join as join6, parse as parse2, relative as relative2, sep } from "path";
6831
6884
  function toSegments(absolutePath2) {
6832
6885
  return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
6833
6886
  }
@@ -6875,7 +6928,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
6875
6928
  const root = parse2(absolutePath2).root;
6876
6929
  let home = dirname2(absolutePath2);
6877
6930
  for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
6878
- const manifestPath = join4(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
6931
+ const manifestPath = join6(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
6879
6932
  const relativePaths = readManifestRelativePaths(manifestPath);
6880
6933
  if (relativePaths) {
6881
6934
  const claimed = relative2(home, absolutePath2).split(sep).join("/");
@@ -6902,25 +6955,25 @@ var init_session_render_ownership = __esm(() => {
6902
6955
  // src/lib/apply.ts
6903
6956
  var exports_apply = {};
6904
6957
  __export(exports_apply, {
6905
- previewConfigs: () => previewConfigs,
6906
- normalizeTargetPath: () => normalizeTargetPath,
6907
- getConfigHome: () => getConfigHome,
6908
- expandPath: () => expandPath,
6909
- applyConfigsWithReport: () => applyConfigsWithReport,
6958
+ applyConfig: () => applyConfig,
6910
6959
  applyConfigs: () => applyConfigs,
6911
- applyConfig: () => applyConfig
6960
+ applyConfigsWithReport: () => applyConfigsWithReport,
6961
+ expandPath: () => expandPath,
6962
+ getConfigHome: () => getConfigHome,
6963
+ normalizeTargetPath: () => normalizeTargetPath,
6964
+ previewConfigs: () => previewConfigs
6912
6965
  });
6913
6966
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, realpathSync, writeFileSync } from "fs";
6914
- import { basename as basename3, dirname as dirname3, join as join5, resolve as resolve2 } from "path";
6915
- import { homedir as homedir2 } from "os";
6967
+ import { basename as basename3, dirname as dirname3, join as join7, resolve as resolve4 } from "path";
6968
+ import { homedir as homedir4 } from "os";
6916
6969
  function getConfigHome() {
6917
- return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir2();
6970
+ return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir4();
6918
6971
  }
6919
6972
  function expandPath(p) {
6920
6973
  if (p.startsWith("~/")) {
6921
- return resolve2(getConfigHome(), p.slice(2));
6974
+ return resolve4(getConfigHome(), p.slice(2));
6922
6975
  }
6923
- return resolve2(p);
6976
+ return resolve4(p);
6924
6977
  }
6925
6978
  function normalizeTargetPath(p) {
6926
6979
  const expanded = expandPath(p);
@@ -6932,7 +6985,7 @@ function normalizeTargetPath(p) {
6932
6985
  while (true) {
6933
6986
  if (existsSync4(current)) {
6934
6987
  try {
6935
- return resolve2(realpathSync(current), ...missingSegments);
6988
+ return resolve4(realpathSync(current), ...missingSegments);
6936
6989
  } catch {
6937
6990
  return expanded;
6938
6991
  }
@@ -6960,8 +7013,9 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
6960
7013
  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.`);
6961
7014
  }
6962
7015
  const path = expandPath(renderedTargetPath);
7016
+ const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
6963
7017
  const previousContent = existsSync4(path) ? readFileSync2(path, "utf-8") : null;
6964
- const changed = previousContent !== renderedContent;
7018
+ const changed = previousContent !== renderedForTarget;
6965
7019
  if (!opts.dryRun) {
6966
7020
  const dir = dirname3(path);
6967
7021
  if (!existsSync4(dir)) {
@@ -6971,13 +7025,13 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
6971
7025
  const store = opts.store ?? resolveConfigStore();
6972
7026
  await store.createSnapshot(config.id, previousContent, config.version);
6973
7027
  }
6974
- writeFileSync(path, renderedContent, "utf-8");
7028
+ writeFileSync(path, renderedForTarget, "utf-8");
6975
7029
  }
6976
7030
  return {
6977
7031
  config_id: config.id,
6978
7032
  path,
6979
7033
  previous_content: previousContent,
6980
- new_content: renderedContent,
7034
+ new_content: renderedForTarget,
6981
7035
  dry_run: opts.dryRun ?? false,
6982
7036
  changed,
6983
7037
  primary_changed: changed,
@@ -7324,7 +7378,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
7324
7378
  getConfigHome(),
7325
7379
  opts.vars?.["HOME_DIR"]
7326
7380
  ].filter((home) => typeof home === "string" && home.length > 0));
7327
- if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join5(home, ...relativePath.split("/"))))))
7381
+ if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join7(home, ...relativePath.split("/"))))))
7328
7382
  return true;
7329
7383
  return sessionRenderOwnsPath(normalized);
7330
7384
  }
@@ -7338,26 +7392,27 @@ var init_apply = __esm(() => {
7338
7392
  init_redact();
7339
7393
  init_template();
7340
7394
  init_transforms();
7395
+ init_cursor_authority();
7341
7396
  });
7342
7397
 
7343
7398
  // src/lib/sync.ts
7344
7399
  var exports_sync = {};
7345
7400
  __export(exports_sync, {
7346
- syncToDisk: () => syncToDisk,
7347
- syncToDir: () => syncToDir,
7348
- syncProject: () => syncProject,
7349
- syncKnown: () => syncKnown,
7350
- syncFromDir: () => syncFromDir,
7351
- diffConfig: () => diffConfig,
7352
- detectFormat: () => detectFormat,
7353
- detectCategory: () => detectCategory,
7354
- detectAgent: () => detectAgent,
7355
- PROJECT_CONFIG_FILES: () => PROJECT_CONFIG_FILES,
7401
+ CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS,
7356
7402
  KNOWN_CONFIGS: () => KNOWN_CONFIGS,
7357
- CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
7403
+ PROJECT_CONFIG_FILES: () => PROJECT_CONFIG_FILES,
7404
+ detectAgent: () => detectAgent,
7405
+ detectCategory: () => detectCategory,
7406
+ detectFormat: () => detectFormat,
7407
+ diffConfig: () => diffConfig,
7408
+ syncFromDir: () => syncFromDir,
7409
+ syncKnown: () => syncKnown,
7410
+ syncProject: () => syncProject,
7411
+ syncToDir: () => syncToDir,
7412
+ syncToDisk: () => syncToDisk
7358
7413
  });
7359
7414
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3 } from "fs";
7360
- import { basename as basename4, extname as extname2, join as join6 } from "path";
7415
+ import { basename as basename4, extname as extname2, join as join8 } from "path";
7361
7416
  function claudeRuleOutputs(fileName) {
7362
7417
  const stem = basename4(fileName, extname2(fileName));
7363
7418
  return [
@@ -7421,7 +7476,7 @@ async function syncProject(opts) {
7421
7476
  const allConfigs = await store.listConfigs();
7422
7477
  const machine = detectMachineContext();
7423
7478
  for (const pf of PROJECT_CONFIG_FILES) {
7424
- const abs = join6(absDir, pf.file);
7479
+ const abs = join8(absDir, pf.file);
7425
7480
  if (!existsSync5(abs))
7426
7481
  continue;
7427
7482
  try {
@@ -7454,19 +7509,19 @@ async function syncProject(opts) {
7454
7509
  }
7455
7510
  }
7456
7511
  for (const ruleDir of [
7457
- { dir: join6(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
7458
- { dir: join6(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
7459
- { dir: join6(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
7460
- { dir: join6(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
7461
- { dir: join6(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
7462
- { dir: join6(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
7463
- { dir: join6(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
7512
+ { dir: join8(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
7513
+ { dir: join8(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
7514
+ { dir: join8(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
7515
+ { dir: join8(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
7516
+ { dir: join8(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
7517
+ { dir: join8(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
7518
+ { dir: join8(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
7464
7519
  ]) {
7465
7520
  if (!existsSync5(ruleDir.dir))
7466
7521
  continue;
7467
7522
  const mdFiles = readdirSync(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
7468
7523
  for (const f of mdFiles) {
7469
- const abs = join6(ruleDir.dir, f);
7524
+ const abs = join8(ruleDir.dir, f);
7470
7525
  const raw = readFileSync3(abs, "utf-8");
7471
7526
  const redacted = redactContent(raw, "markdown");
7472
7527
  const machineAware = templateizeMachineContent(redacted.content, machine);
@@ -7513,7 +7568,7 @@ async function syncKnown(opts = {}) {
7513
7568
  const extensions = known.rulesExtensions ?? [".md", ".mdc"];
7514
7569
  const ruleFiles = readdirSync(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
7515
7570
  for (const f of ruleFiles) {
7516
- const abs2 = join6(absDir, f);
7571
+ const abs2 = join8(absDir, f);
7517
7572
  const targetPath = abs2.replace(home, "~");
7518
7573
  if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
7519
7574
  result.skipped.push(`${targetPath} (generated output)`);
@@ -7884,8 +7939,8 @@ var init_sync = __esm(() => {
7884
7939
 
7885
7940
  // src/lib/sync-dir.ts
7886
7941
  import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
7887
- import { join as join7, relative as relative3 } from "path";
7888
- import { homedir as homedir3 } from "os";
7942
+ import { join as join9, relative as relative3 } from "path";
7943
+ import { homedir as homedir5 } from "os";
7889
7944
  function shouldSkip(p) {
7890
7945
  return SKIP.some((s) => p.includes(s));
7891
7946
  }
@@ -7894,9 +7949,9 @@ async function syncFromDir(dir, opts = {}) {
7894
7949
  const absDir = expandPath(dir);
7895
7950
  if (!existsSync6(absDir))
7896
7951
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
7897
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join7(absDir, f)).filter((f) => statSync2(f).isFile());
7952
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join9(absDir, f)).filter((f) => statSync2(f).isFile());
7898
7953
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
7899
- const home = homedir3();
7954
+ const home = homedir5();
7900
7955
  const allConfigs = await store.listConfigs();
7901
7956
  for (const file of files) {
7902
7957
  if (shouldSkip(file)) {
@@ -7930,7 +7985,7 @@ async function syncFromDir(dir, opts = {}) {
7930
7985
  }
7931
7986
  async function syncToDir(dir, opts = {}) {
7932
7987
  const store = opts.store ?? resolveConfigStore();
7933
- const home = homedir3();
7988
+ const home = homedir5();
7934
7989
  const absDir = expandPath(dir);
7935
7990
  const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
7936
7991
  const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
@@ -7954,7 +8009,7 @@ async function syncToDir(dir, opts = {}) {
7954
8009
  }
7955
8010
  function walkDir(dir, files = []) {
7956
8011
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
7957
- const full = join7(dir, entry.name);
8012
+ const full = join9(dir, entry.name);
7958
8013
  if (shouldSkip(full))
7959
8014
  continue;
7960
8015
  if (entry.isDirectory())
@@ -7973,10 +8028,10 @@ var init_sync_dir = __esm(() => {
7973
8028
  });
7974
8029
 
7975
8030
  // package.json
7976
- var require_package = __commonJS((exports, module) => {
8031
+ var require_package = __commonJS(function(exports, module) {
7977
8032
  module.exports = {
7978
8033
  name: "@hasna/instructions",
7979
- version: "0.4.35",
8034
+ version: "0.4.39",
7980
8035
  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.",
7981
8036
  type: "module",
7982
8037
  main: "dist/index.js",
@@ -7996,6 +8051,7 @@ var require_package = __commonJS((exports, module) => {
7996
8051
  },
7997
8052
  files: [
7998
8053
  "dist",
8054
+ "assets/skills/inbox/SKILL.md",
7999
8055
  "dashboard/dist",
8000
8056
  "LICENSE",
8001
8057
  "README.md"
@@ -8009,14 +8065,15 @@ var require_package = __commonJS((exports, module) => {
8009
8065
  "generate:sdk": "bun run scripts/generate-sdk.ts",
8010
8066
  "kit:check": "bunx @hasna/contracts vendor-kit --check",
8011
8067
  typecheck: "tsc --noEmit",
8012
- test: "bun test",
8013
- "check:package-secrets": "bun run src/cli/index.tsx package-manager-scan --fail-on-findings --home .",
8068
+ test: "env -u HASNA_INSTRUCTIONS_API_URL -u HASNA_INSTRUCTIONS_API_KEY bun test",
8069
+ "check:package-secrets": "bun run src/cli/index.tsx package-manager-scan --fail-on-findings .",
8014
8070
  "dev:cli": "bun run src/cli/index.tsx",
8015
8071
  "dev:mcp": "bun run src/mcp/index.ts",
8016
8072
  "dev:serve": "bun run src/server/index.ts",
8017
8073
  seed: "bun run scripts/seed.ts",
8018
8074
  prepublishOnly: "bun run scripts/check-publish-hold.ts && bun run build",
8019
- postinstall: "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true"
8075
+ postinstall: "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true",
8076
+ prepack: "bun run build"
8020
8077
  },
8021
8078
  keywords: [
8022
8079
  "instructions",
@@ -8050,8 +8107,8 @@ var require_package = __commonJS((exports, module) => {
8050
8107
  author: "Andrei Hasna <andrei@hasna.com>",
8051
8108
  license: "Apache-2.0",
8052
8109
  dependencies: {
8053
- "@hasna/contracts": "0.4.2",
8054
- "@hasna/events": "^0.1.6",
8110
+ "@hasna/contracts": "0.13.1",
8111
+ "@hasna/events": "^0.1.16",
8055
8112
  "@modelcontextprotocol/sdk": "^1.12.1",
8056
8113
  chalk: "^5.4.1",
8057
8114
  commander: "^13.1.0",