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