@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 +30 -7
- package/dist/index.js +24 -6
- package/dist/lib/apply.d.ts.map +1 -1
- package/dist/lib/cursor-authority.d.ts +26 -3
- package/dist/lib/cursor-authority.d.ts.map +1 -1
- package/dist/mcp/index.js +60 -31
- package/package.json +1 -1
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: "
|
|
10721
|
+
status: "managed",
|
|
10722
10722
|
sha256: contentSha256,
|
|
10723
10723
|
markers,
|
|
10724
10724
|
markerSha256,
|
|
10725
10725
|
provenance: {
|
|
10726
10726
|
source: "filesystem",
|
|
10727
|
-
authority: "
|
|
10727
|
+
authority: "managed",
|
|
10728
10728
|
observedPath: authorityPath,
|
|
10729
|
-
detection: "
|
|
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 !==
|
|
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,
|
|
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:
|
|
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: "
|
|
8678
|
+
status: "managed",
|
|
8678
8679
|
sha256: contentSha256,
|
|
8679
8680
|
markers,
|
|
8680
8681
|
markerSha256,
|
|
8681
8682
|
provenance: {
|
|
8682
8683
|
source: "filesystem",
|
|
8683
|
-
authority: "
|
|
8684
|
+
authority: "managed",
|
|
8684
8685
|
observedPath: authorityPath,
|
|
8685
|
-
detection: "
|
|
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 !==
|
|
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,
|
|
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:
|
|
11995
|
+
new_content: renderedForTarget,
|
|
11978
11996
|
dry_run: opts.dryRun ?? false,
|
|
11979
11997
|
changed,
|
|
11980
11998
|
primary_changed: changed,
|
package/dist/lib/apply.d.ts.map
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;
|
|
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.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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;
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
6887
|
-
import { homedir as
|
|
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"] ||
|
|
6916
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir3();
|
|
6890
6917
|
}
|
|
6891
6918
|
function expandPath(p) {
|
|
6892
6919
|
if (p.startsWith("~/")) {
|
|
6893
|
-
return
|
|
6920
|
+
return resolve3(getConfigHome(), p.slice(2));
|
|
6894
6921
|
}
|
|
6895
|
-
return
|
|
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
|
|
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 !==
|
|
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,
|
|
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:
|
|
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(
|
|
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
|
|
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 =
|
|
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:
|
|
7430
|
-
{ dir:
|
|
7431
|
-
{ dir:
|
|
7432
|
-
{ dir:
|
|
7433
|
-
{ dir:
|
|
7434
|
-
{ dir:
|
|
7435
|
-
{ dir:
|
|
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 =
|
|
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 =
|
|
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
|
|
7860
|
-
import { homedir as
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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.
|
|
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.
|
|
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",
|