@hasna/instructions 0.4.33 → 0.4.34

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/cli/index.js CHANGED
@@ -10718,18 +10718,34 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
10718
10718
  return {
10719
10719
  ...base,
10720
10720
  fileType,
10721
- status: "unmanaged",
10721
+ status: "managed",
10722
10722
  sha256: contentSha256,
10723
10723
  markers,
10724
10724
  markerSha256,
10725
10725
  provenance: {
10726
10726
  source: "filesystem",
10727
- authority: "unmanaged",
10727
+ authority: "managed",
10728
10728
  observedPath: authorityPath,
10729
- detection: "unknown-content"
10729
+ detection: "managed-marker"
10730
10730
  }
10731
10731
  };
10732
10732
  }
10733
+ function isCursorGlobalAuthorityPath(path) {
10734
+ return resolve3(path) === resolve3(join5(homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
10735
+ }
10736
+ function stampCursorGlobalAuthorityMarker(content) {
10737
+ if (CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN.test(content))
10738
+ return content;
10739
+ const digest = sha2564(content);
10740
+ const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
10741
+ const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
10742
+ if (frontmatter) {
10743
+ return `${frontmatter}${markerLine}
10744
+ ${content.slice(frontmatter.length)}`;
10745
+ }
10746
+ return `${markerLine}
10747
+ ${content}`;
10748
+ }
10733
10749
  function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthority()) {
10734
10750
  if (observation.status === "absent" || observation.status === "managed")
10735
10751
  return [];
@@ -10751,10 +10767,11 @@ function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthori
10751
10767
  reason: invalid ? `Cursor fixed global authority ${CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH} is not a verifiable regular managed file; refusing to render project rules.` : `Cursor fixed global authority ${CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH} is unmanaged; refusing to guess whether it conflicts with the managed project render.`
10752
10768
  }];
10753
10769
  }
10754
- 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;
10770
+ 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;
10755
10771
  var init_cursor_authority = __esm(() => {
10756
10772
  CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
10757
10773
  CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN = /^<!-- Managed by @hasna\/configs cursor global authority hash=(sha256:[a-f0-9]{64}) -->$/m;
10774
+ CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN = /^---\n[\s\S]*?\n---(?:\n|$)/;
10758
10775
  });
10759
10776
 
10760
10777
  // src/lib/session-render.ts
@@ -13979,8 +13996,9 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
13979
13996
  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.`);
13980
13997
  }
13981
13998
  const path = expandPath(renderedTargetPath);
13999
+ const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
13982
14000
  const previousContent = existsSync7(path) ? readFileSync5(path, "utf-8") : null;
13983
- const changed = previousContent !== renderedContent;
14001
+ const changed = previousContent !== renderedForTarget;
13984
14002
  if (!opts.dryRun) {
13985
14003
  const dir = dirname4(path);
13986
14004
  if (!existsSync7(dir)) {
@@ -13990,13 +14008,13 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
13990
14008
  const store = opts.store ?? resolveConfigStore();
13991
14009
  await store.createSnapshot(config.id, previousContent, config.version);
13992
14010
  }
13993
- writeFileSync2(path, renderedContent, "utf-8");
14011
+ writeFileSync2(path, renderedForTarget, "utf-8");
13994
14012
  }
13995
14013
  return {
13996
14014
  config_id: config.id,
13997
14015
  path,
13998
14016
  previous_content: previousContent,
13999
- new_content: renderedContent,
14017
+ new_content: renderedForTarget,
14000
14018
  dry_run: opts.dryRun ?? false,
14001
14019
  changed,
14002
14020
  primary_changed: changed,
@@ -14357,6 +14375,7 @@ var init_apply = __esm(() => {
14357
14375
  init_redact();
14358
14376
  init_template();
14359
14377
  init_transforms();
14378
+ init_cursor_authority();
14360
14379
  });
14361
14380
 
14362
14381
  // src/lib/sync-dir.ts
@@ -18775,6 +18794,8 @@ sessionCmd.command("plan").description("Produce a dry-run render plan for profil
18775
18794
  ...planJsonForOutput(plan),
18776
18795
  ...globalCoverage ? { globalSourceCoverage: globalCoverage } : {}
18777
18796
  });
18797
+ if (plan.blocked)
18798
+ process.exitCode = 1;
18778
18799
  return;
18779
18800
  }
18780
18801
  console.log(chalk.bold(`${plan.tool} session render plan`) + chalk.dim(` (${plan.adapter.mode})`));
@@ -18801,6 +18822,8 @@ sessionCmd.command("plan").description("Produce a dry-run render plan for profil
18801
18822
  if (globalCoverage.complete)
18802
18823
  console.log(chalk.dim(`global source coverage: ${globalCoverage.expectedSlugs.length}/${globalCoverage.expectedSlugs.length} complete`));
18803
18824
  }
18825
+ if (plan.blocked)
18826
+ process.exitCode = 1;
18804
18827
  console.log(chalk.dim("Dry run only. No files were written."));
18805
18828
  } catch (e) {
18806
18829
  console.error(chalk.red(formatCliError(e)));
package/dist/index.js CHANGED
@@ -8513,6 +8513,7 @@ var CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH = ".cursor/rules/hasna-global.mdc";
8513
8513
  var CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
8514
8514
  var CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER = "Managed by @hasna/configs cursor global authority";
8515
8515
  var CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN = /^<!-- Managed by @hasna\/configs cursor global authority hash=(sha256:[a-f0-9]{64}) -->$/m;
8516
+ var CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN = /^---\n[\s\S]*?\n---(?:\n|$)/;
8516
8517
  function sha2564(content) {
8517
8518
  return createHash4("sha256").update(content).digest("hex");
8518
8519
  }
@@ -8674,18 +8675,34 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile) {
8674
8675
  return {
8675
8676
  ...base,
8676
8677
  fileType,
8677
- status: "unmanaged",
8678
+ status: "managed",
8678
8679
  sha256: contentSha256,
8679
8680
  markers,
8680
8681
  markerSha256,
8681
8682
  provenance: {
8682
8683
  source: "filesystem",
8683
- authority: "unmanaged",
8684
+ authority: "managed",
8684
8685
  observedPath: authorityPath,
8685
- detection: "unknown-content"
8686
+ detection: "managed-marker"
8686
8687
  }
8687
8688
  };
8688
8689
  }
8690
+ function isCursorGlobalAuthorityPath(path) {
8691
+ return resolve3(path) === resolve3(join4(homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
8692
+ }
8693
+ function stampCursorGlobalAuthorityMarker(content) {
8694
+ if (CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN.test(content))
8695
+ return content;
8696
+ const digest = sha2564(content);
8697
+ const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
8698
+ const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
8699
+ if (frontmatter) {
8700
+ return `${frontmatter}${markerLine}
8701
+ ${content.slice(frontmatter.length)}`;
8702
+ }
8703
+ return `${markerLine}
8704
+ ${content}`;
8705
+ }
8689
8706
  function detectCursorAuthorityConflicts(observation = observeCursorGlobalAuthority()) {
8690
8707
  if (observation.status === "absent" || observation.status === "managed")
8691
8708
  return [];
@@ -11957,8 +11974,9 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
11957
11974
  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.`);
11958
11975
  }
11959
11976
  const path = expandPath(renderedTargetPath);
11977
+ const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
11960
11978
  const previousContent = existsSync6(path) ? readFileSync5(path, "utf-8") : null;
11961
- const changed = previousContent !== renderedContent;
11979
+ const changed = previousContent !== renderedForTarget;
11962
11980
  if (!opts.dryRun) {
11963
11981
  const dir = dirname4(path);
11964
11982
  if (!existsSync6(dir)) {
@@ -11968,13 +11986,13 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
11968
11986
  const store = opts.store ?? resolveConfigStore();
11969
11987
  await store.createSnapshot(config.id, previousContent, config.version);
11970
11988
  }
11971
- writeFileSync2(path, renderedContent, "utf-8");
11989
+ writeFileSync2(path, renderedForTarget, "utf-8");
11972
11990
  }
11973
11991
  return {
11974
11992
  config_id: config.id,
11975
11993
  path,
11976
11994
  previous_content: previousContent,
11977
- new_content: renderedContent,
11995
+ new_content: renderedForTarget,
11978
11996
  dry_run: opts.dryRun ?? false,
11979
11997
  changed,
11980
11998
  primary_changed: changed,
@@ -1 +1 @@
1
- {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAA8B,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQ1D,OAAO,EAGL,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,yBAAyB,GAAG,2BAA2B,GAAG,yBAAyB,GAAG,+BAA+B,CAAC;IACpI,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,QAAQ,EAAE,yBAAyB,EAAE,CAAC;CACvC;AA+QD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,CAAC,CA+BtB;AA2ED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CAMxB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAiC7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAM,GACtC,OAAO,CAAC,kBAAkB,CAAC,CAE7B"}
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAA8B,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQ1D,OAAO,EAGL,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAO7B,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,yBAAyB,GAAG,2BAA2B,GAAG,yBAAyB,GAAG,+BAA+B,CAAC;IACpI,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,QAAQ,EAAE,yBAAyB,EAAE,CAAC;CACvC;AAyRD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,CAAC,CA+BtB;AA2ED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CAMxB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAiC7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAM,GACtC,OAAO,CAAC,kBAAkB,CAAC,CAE7B"}
@@ -44,11 +44,34 @@ export interface CursorAuthorityObservationOptions {
44
44
  * Cursor reads this fixed global rule before project-scoped `.cursor/rules`
45
45
  * files. The session renderer owns only the project-scoped files, so an
46
46
  * unmanaged global rule is a second authority. A missing file is the clean
47
- * case. Existing content remains blocked unless a future registry-backed
48
- * migration contract proves package ownership; a self-declared marker is
49
- * intentionally insufficient.
47
+ * case. The package's own apply path stamps every write to this path with
48
+ * {@link stampCursorGlobalAuthorityMarker}; a regular file carrying that
49
+ * marker with a matching payload hash is the package's own output and is
50
+ * accepted as managed. Everything else — foreign content, a tampered marker,
51
+ * a non-regular file — remains blocked, because the renderer cannot guess
52
+ * whether it conflicts with the managed project render.
50
53
  */
51
54
  export declare function observeCursorGlobalAuthority(options?: CursorAuthorityObservationOptions): CursorAuthorityObservation;
52
55
  export declare function observeCursorGlobalAuthorityAtPath(authorityPath: string): CursorAuthorityObservation;
56
+ /**
57
+ * Whether an expanded target path is the Cursor fixed global authority path
58
+ * under the current home. Used by the apply path to decide when to stamp the
59
+ * managed marker; the comparison mirrors {@link observeCursorGlobalAuthority}'s
60
+ * resolution so the writer and the observer always agree.
61
+ */
62
+ export declare function isCursorGlobalAuthorityPath(path: string): boolean;
63
+ /**
64
+ * Stamp the managed marker onto content destined for the Cursor fixed global
65
+ * authority path. The marker carries the sha256 of the payload AFTER the
66
+ * marker line, which is exactly the payload the observer recomputes in
67
+ * {@link observeCursorGlobalAuthorityPath} — so a stamped file round-trips as
68
+ * managed. Cursor requires YAML frontmatter to start the file, so content that
69
+ * opens with a `---` frontmatter block is stamped AFTER the closing `---`,
70
+ * matching how the package's own session-render cursor files place their
71
+ * managed marker; plain content without frontmatter is stamped at the top.
72
+ * Content that already carries a valid marker anywhere is returned unchanged
73
+ * (idempotent re-apply).
74
+ */
75
+ export declare function stampCursorGlobalAuthorityMarker(content: string): string;
53
76
  export declare function detectCursorAuthorityConflicts(observation?: CursorAuthorityObservation): CursorAuthorityConflict[];
54
77
  //# sourceMappingURL=cursor-authority.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-authority.d.ts","sourceRoot":"","sources":["../../src/lib/cursor-authority.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,qCAAqC,mCAAmC,CAAC;AACtF,eAAO,MAAM,iCAAiC,QAAa,CAAC;AAC5D,eAAO,MAAM,sCAAsC,sDAAsD,CAAC;AAK1G,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAChG,MAAM,MAAM,gCAAgC,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAC9F,MAAM,MAAM,wBAAwB,GAChC,SAAS,GACT,gBAAgB,GAChB,iBAAiB,GACjB,2BAA2B,GAC3B,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,EAAE,OAAO,qCAAqC,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,uBAAuB,CAAC;IAClC,MAAM,EAAE,gCAAgC,CAAC;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE;QACV,MAAM,EAAE,YAAY,CAAC;QACrB,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;QAC9C,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,wBAAwB,CAAC;KACrC,CAAC;CACH;AAED,MAAM,MAAM,2BAA2B,GACnC,6BAA6B,GAC7B,6BAA6B,CAAC;AAElC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,EAAE,OAAO,qCAAqC,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,2BAA2B,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE;QACV,MAAM,EAAE,YAAY,CAAC;QACrB,SAAS,EAAE,WAAW,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,OAAO,CAAC,wBAAwB,EAAE,SAAS,GAAG,gBAAgB,CAAC,CAAC;KAC5E,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAyBD;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,GAAE,iCAAsC,GAC9C,0BAA0B,CAI5B;AAED,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,MAAM,GACpB,0BAA0B,CAK5B;AAoKD,wBAAgB,8BAA8B,CAC5C,WAAW,6BAAiC,GAC3C,uBAAuB,EAAE,CAsB3B"}
1
+ {"version":3,"file":"cursor-authority.d.ts","sourceRoot":"","sources":["../../src/lib/cursor-authority.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,qCAAqC,mCAAmC,CAAC;AACtF,eAAO,MAAM,iCAAiC,QAAa,CAAC;AAC5D,eAAO,MAAM,sCAAsC,sDAAsD,CAAC;AAQ1G,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAChG,MAAM,MAAM,gCAAgC,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAC9F,MAAM,MAAM,wBAAwB,GAChC,SAAS,GACT,gBAAgB,GAChB,iBAAiB,GACjB,2BAA2B,GAC3B,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,EAAE,OAAO,qCAAqC,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,uBAAuB,CAAC;IAClC,MAAM,EAAE,gCAAgC,CAAC;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE;QACV,MAAM,EAAE,YAAY,CAAC;QACrB,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;QAC9C,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,wBAAwB,CAAC;KACrC,CAAC;CACH;AAED,MAAM,MAAM,2BAA2B,GACnC,6BAA6B,GAC7B,6BAA6B,CAAC;AAElC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,EAAE,OAAO,qCAAqC,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,2BAA2B,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE;QACV,MAAM,EAAE,YAAY,CAAC;QACrB,SAAS,EAAE,WAAW,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,OAAO,CAAC,wBAAwB,EAAE,SAAS,GAAG,gBAAgB,CAAC,CAAC;KAC5E,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAyBD;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,GAAE,iCAAsC,GAC9C,0BAA0B,CAI5B;AAED,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,MAAM,GACpB,0BAA0B,CAK5B;AAoKD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASxE;AAED,wBAAgB,8BAA8B,CAC5C,WAAW,6BAAiC,GAC3C,uBAAuB,EAAE,CAsB3B"}
package/dist/mcp/index.js CHANGED
@@ -5602,9 +5602,36 @@ var init_asset_plan = __esm(() => {
5602
5602
  });
5603
5603
 
5604
5604
  // src/lib/cursor-authority.ts
5605
- var CURSOR_GLOBAL_AUTHORITY_MAX_BYTES;
5605
+ import { createHash } from "crypto";
5606
+ import { homedir as homedir2 } from "os";
5607
+ import { join as join4, resolve as resolve2 } from "path";
5608
+ function sha256(content) {
5609
+ return createHash("sha256").update(content).digest("hex");
5610
+ }
5611
+ function homeDir() {
5612
+ return process.env["HOME"] || homedir2();
5613
+ }
5614
+ function isCursorGlobalAuthorityPath(path) {
5615
+ return resolve2(path) === resolve2(join4(homeDir(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
5616
+ }
5617
+ function stampCursorGlobalAuthorityMarker(content) {
5618
+ if (CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN.test(content))
5619
+ return content;
5620
+ const digest = sha256(content);
5621
+ const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
5622
+ const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
5623
+ if (frontmatter) {
5624
+ return `${frontmatter}${markerLine}
5625
+ ${content.slice(frontmatter.length)}`;
5626
+ }
5627
+ return `${markerLine}
5628
+ ${content}`;
5629
+ }
5630
+ 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;
5606
5631
  var init_cursor_authority = __esm(() => {
5607
5632
  CURSOR_GLOBAL_AUTHORITY_MAX_BYTES = 256 * 1024;
5633
+ CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN = /^<!-- Managed by @hasna\/configs cursor global authority hash=(sha256:[a-f0-9]{64}) -->$/m;
5634
+ CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN = /^---\n[\s\S]*?\n---(?:\n|$)/;
5608
5635
  });
5609
5636
 
5610
5637
  // src/lib/session-render.ts
@@ -6799,7 +6826,7 @@ var init_config_store = __esm(() => {
6799
6826
 
6800
6827
  // src/lib/session-render-ownership.ts
6801
6828
  import { existsSync as existsSync3, readFileSync, statSync } from "fs";
6802
- import { dirname as dirname2, join as join4, parse as parse2, relative as relative2, sep } from "path";
6829
+ import { dirname as dirname2, join as join5, parse as parse2, relative as relative2, sep } from "path";
6803
6830
  function toSegments(absolutePath2) {
6804
6831
  return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
6805
6832
  }
@@ -6847,7 +6874,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
6847
6874
  const root = parse2(absolutePath2).root;
6848
6875
  let home = dirname2(absolutePath2);
6849
6876
  for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
6850
- const manifestPath = join4(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
6877
+ const manifestPath = join5(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
6851
6878
  const relativePaths = readManifestRelativePaths(manifestPath);
6852
6879
  if (relativePaths) {
6853
6880
  const claimed = relative2(home, absolutePath2).split(sep).join("/");
@@ -6883,16 +6910,16 @@ __export(exports_apply, {
6883
6910
  applyConfig: () => applyConfig
6884
6911
  });
6885
6912
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, realpathSync, writeFileSync } from "fs";
6886
- import { basename as basename3, dirname as dirname3, join as join5, resolve as resolve2 } from "path";
6887
- import { homedir as homedir2 } from "os";
6913
+ import { basename as basename3, dirname as dirname3, join as join6, resolve as resolve3 } from "path";
6914
+ import { homedir as homedir3 } from "os";
6888
6915
  function getConfigHome() {
6889
- return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir2();
6916
+ return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir3();
6890
6917
  }
6891
6918
  function expandPath(p) {
6892
6919
  if (p.startsWith("~/")) {
6893
- return resolve2(getConfigHome(), p.slice(2));
6920
+ return resolve3(getConfigHome(), p.slice(2));
6894
6921
  }
6895
- return resolve2(p);
6922
+ return resolve3(p);
6896
6923
  }
6897
6924
  function normalizeTargetPath(p) {
6898
6925
  const expanded = expandPath(p);
@@ -6904,7 +6931,7 @@ function normalizeTargetPath(p) {
6904
6931
  while (true) {
6905
6932
  if (existsSync4(current)) {
6906
6933
  try {
6907
- return resolve2(realpathSync(current), ...missingSegments);
6934
+ return resolve3(realpathSync(current), ...missingSegments);
6908
6935
  } catch {
6909
6936
  return expanded;
6910
6937
  }
@@ -6932,8 +6959,9 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
6932
6959
  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.`);
6933
6960
  }
6934
6961
  const path = expandPath(renderedTargetPath);
6962
+ const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
6935
6963
  const previousContent = existsSync4(path) ? readFileSync2(path, "utf-8") : null;
6936
- const changed = previousContent !== renderedContent;
6964
+ const changed = previousContent !== renderedForTarget;
6937
6965
  if (!opts.dryRun) {
6938
6966
  const dir = dirname3(path);
6939
6967
  if (!existsSync4(dir)) {
@@ -6943,13 +6971,13 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
6943
6971
  const store = opts.store ?? resolveConfigStore();
6944
6972
  await store.createSnapshot(config.id, previousContent, config.version);
6945
6973
  }
6946
- writeFileSync(path, renderedContent, "utf-8");
6974
+ writeFileSync(path, renderedForTarget, "utf-8");
6947
6975
  }
6948
6976
  return {
6949
6977
  config_id: config.id,
6950
6978
  path,
6951
6979
  previous_content: previousContent,
6952
- new_content: renderedContent,
6980
+ new_content: renderedForTarget,
6953
6981
  dry_run: opts.dryRun ?? false,
6954
6982
  changed,
6955
6983
  primary_changed: changed,
@@ -7296,7 +7324,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
7296
7324
  getConfigHome(),
7297
7325
  opts.vars?.["HOME_DIR"]
7298
7326
  ].filter((home) => typeof home === "string" && home.length > 0));
7299
- if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join5(home, ...relativePath.split("/"))))))
7327
+ if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join6(home, ...relativePath.split("/"))))))
7300
7328
  return true;
7301
7329
  return sessionRenderOwnsPath(normalized);
7302
7330
  }
@@ -7310,6 +7338,7 @@ var init_apply = __esm(() => {
7310
7338
  init_redact();
7311
7339
  init_template();
7312
7340
  init_transforms();
7341
+ init_cursor_authority();
7313
7342
  });
7314
7343
 
7315
7344
  // src/lib/sync.ts
@@ -7329,7 +7358,7 @@ __export(exports_sync, {
7329
7358
  CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
7330
7359
  });
7331
7360
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3 } from "fs";
7332
- import { basename as basename4, extname as extname2, join as join6 } from "path";
7361
+ import { basename as basename4, extname as extname2, join as join7 } from "path";
7333
7362
  function claudeRuleOutputs(fileName) {
7334
7363
  const stem = basename4(fileName, extname2(fileName));
7335
7364
  return [
@@ -7393,7 +7422,7 @@ async function syncProject(opts) {
7393
7422
  const allConfigs = await store.listConfigs();
7394
7423
  const machine = detectMachineContext();
7395
7424
  for (const pf of PROJECT_CONFIG_FILES) {
7396
- const abs = join6(absDir, pf.file);
7425
+ const abs = join7(absDir, pf.file);
7397
7426
  if (!existsSync5(abs))
7398
7427
  continue;
7399
7428
  try {
@@ -7426,19 +7455,19 @@ async function syncProject(opts) {
7426
7455
  }
7427
7456
  }
7428
7457
  for (const ruleDir of [
7429
- { dir: join6(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
7430
- { dir: join6(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
7431
- { dir: join6(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
7432
- { dir: join6(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
7433
- { dir: join6(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
7434
- { dir: join6(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
7435
- { dir: join6(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
7458
+ { dir: join7(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
7459
+ { dir: join7(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
7460
+ { dir: join7(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
7461
+ { dir: join7(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
7462
+ { dir: join7(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
7463
+ { dir: join7(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
7464
+ { dir: join7(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
7436
7465
  ]) {
7437
7466
  if (!existsSync5(ruleDir.dir))
7438
7467
  continue;
7439
7468
  const mdFiles = readdirSync(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
7440
7469
  for (const f of mdFiles) {
7441
- const abs = join6(ruleDir.dir, f);
7470
+ const abs = join7(ruleDir.dir, f);
7442
7471
  const raw = readFileSync3(abs, "utf-8");
7443
7472
  const redacted = redactContent(raw, "markdown");
7444
7473
  const machineAware = templateizeMachineContent(redacted.content, machine);
@@ -7485,7 +7514,7 @@ async function syncKnown(opts = {}) {
7485
7514
  const extensions = known.rulesExtensions ?? [".md", ".mdc"];
7486
7515
  const ruleFiles = readdirSync(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
7487
7516
  for (const f of ruleFiles) {
7488
- const abs2 = join6(absDir, f);
7517
+ const abs2 = join7(absDir, f);
7489
7518
  const targetPath = abs2.replace(home, "~");
7490
7519
  if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
7491
7520
  result.skipped.push(`${targetPath} (generated output)`);
@@ -7856,8 +7885,8 @@ var init_sync = __esm(() => {
7856
7885
 
7857
7886
  // src/lib/sync-dir.ts
7858
7887
  import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
7859
- import { join as join7, relative as relative3 } from "path";
7860
- import { homedir as homedir3 } from "os";
7888
+ import { join as join8, relative as relative3 } from "path";
7889
+ import { homedir as homedir4 } from "os";
7861
7890
  function shouldSkip(p) {
7862
7891
  return SKIP.some((s) => p.includes(s));
7863
7892
  }
@@ -7866,9 +7895,9 @@ async function syncFromDir(dir, opts = {}) {
7866
7895
  const absDir = expandPath(dir);
7867
7896
  if (!existsSync6(absDir))
7868
7897
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
7869
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join7(absDir, f)).filter((f) => statSync2(f).isFile());
7898
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join8(absDir, f)).filter((f) => statSync2(f).isFile());
7870
7899
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
7871
- const home = homedir3();
7900
+ const home = homedir4();
7872
7901
  const allConfigs = await store.listConfigs();
7873
7902
  for (const file of files) {
7874
7903
  if (shouldSkip(file)) {
@@ -7902,7 +7931,7 @@ async function syncFromDir(dir, opts = {}) {
7902
7931
  }
7903
7932
  async function syncToDir(dir, opts = {}) {
7904
7933
  const store = opts.store ?? resolveConfigStore();
7905
- const home = homedir3();
7934
+ const home = homedir4();
7906
7935
  const absDir = expandPath(dir);
7907
7936
  const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
7908
7937
  const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
@@ -7926,7 +7955,7 @@ async function syncToDir(dir, opts = {}) {
7926
7955
  }
7927
7956
  function walkDir(dir, files = []) {
7928
7957
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
7929
- const full = join7(dir, entry.name);
7958
+ const full = join8(dir, entry.name);
7930
7959
  if (shouldSkip(full))
7931
7960
  continue;
7932
7961
  if (entry.isDirectory())
@@ -7948,7 +7977,7 @@ var init_sync_dir = __esm(() => {
7948
7977
  var require_package = __commonJS((exports, module) => {
7949
7978
  module.exports = {
7950
7979
  name: "@hasna/instructions",
7951
- version: "0.4.33",
7980
+ version: "0.4.34",
7952
7981
  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.",
7953
7982
  type: "module",
7954
7983
  main: "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/instructions",
3
- "version": "0.4.33",
3
+ "version": "0.4.34",
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",