@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/cli/index.js
CHANGED
|
@@ -2160,19 +2160,122 @@ var init_retired_storage_mode = __esm(() => {
|
|
|
2160
2160
|
];
|
|
2161
2161
|
});
|
|
2162
2162
|
|
|
2163
|
-
//
|
|
2163
|
+
// ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
|
|
2164
2164
|
import { homedir as homedir2 } from "os";
|
|
2165
|
-
import { join as join2
|
|
2166
|
-
function
|
|
2167
|
-
|
|
2165
|
+
import { join as join2 } from "path";
|
|
2166
|
+
function assertApp(app) {
|
|
2167
|
+
if (typeof app !== "string" || app.length === 0) {
|
|
2168
|
+
throw new TypeError("paths: app must be a non-empty string");
|
|
2169
|
+
}
|
|
2170
|
+
if (!APP_SLUG_RE.test(app)) {
|
|
2171
|
+
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
function envOf(options) {
|
|
2175
|
+
return options.env ?? process.env;
|
|
2176
|
+
}
|
|
2177
|
+
function envValue(options, kind) {
|
|
2178
|
+
const value = envOf(options)[KIND_ENV[kind]];
|
|
2179
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
2180
|
+
}
|
|
2181
|
+
function isMacOS(platform) {
|
|
2182
|
+
return platform === "darwin";
|
|
2183
|
+
}
|
|
2184
|
+
function baseDir(kind, options) {
|
|
2185
|
+
const override = envValue(options, kind);
|
|
2186
|
+
if (override)
|
|
2187
|
+
return override;
|
|
2188
|
+
const home = options.home ?? homedir2();
|
|
2189
|
+
const platform = options.platform ?? process.platform;
|
|
2190
|
+
if (isMacOS(platform)) {
|
|
2191
|
+
switch (kind) {
|
|
2192
|
+
case "config":
|
|
2193
|
+
case "data":
|
|
2194
|
+
return join2(home, "Library", "Application Support", "Hasna");
|
|
2195
|
+
case "cache":
|
|
2196
|
+
return join2(home, "Library", "Caches", "Hasna");
|
|
2197
|
+
case "state":
|
|
2198
|
+
return join2(home, "Library", "Logs", "Hasna");
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
switch (kind) {
|
|
2202
|
+
case "config":
|
|
2203
|
+
return join2(home, ".config", "hasna");
|
|
2204
|
+
case "data":
|
|
2205
|
+
return join2(home, ".local", "share", "hasna");
|
|
2206
|
+
case "state":
|
|
2207
|
+
return join2(home, ".local", "state", "hasna");
|
|
2208
|
+
case "cache":
|
|
2209
|
+
return join2(home, ".cache", "hasna");
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function resolvePath(kind, options) {
|
|
2213
|
+
assertApp(options.app);
|
|
2214
|
+
const appSegment = options.internal === true ? join2("internal", options.app) : options.app;
|
|
2215
|
+
return join2(baseDir(kind, options), appSegment);
|
|
2216
|
+
}
|
|
2217
|
+
function configDir(options) {
|
|
2218
|
+
return resolvePath("config", options);
|
|
2219
|
+
}
|
|
2220
|
+
var KIND_ENV, APP_SLUG_RE;
|
|
2221
|
+
var init_dist = __esm(() => {
|
|
2222
|
+
KIND_ENV = {
|
|
2223
|
+
config: "HASNA_CONFIG_HOME",
|
|
2224
|
+
data: "HASNA_DATA_HOME",
|
|
2225
|
+
state: "HASNA_STATE_HOME",
|
|
2226
|
+
cache: "HASNA_CACHE_HOME"
|
|
2227
|
+
};
|
|
2228
|
+
APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2229
|
+
});
|
|
2230
|
+
|
|
2231
|
+
// src/lib/app-home.ts
|
|
2232
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2233
|
+
import { homedir as homedir3 } from "os";
|
|
2234
|
+
import { join as join3, resolve } from "path";
|
|
2235
|
+
function homeDir(env = process.env) {
|
|
2236
|
+
return env["HOME"] || env["USERPROFILE"] || homedir3();
|
|
2237
|
+
}
|
|
2238
|
+
function legacyStoreHome(env = process.env) {
|
|
2239
|
+
return resolve(join3(homeDir(env), ".hasna", "instructions"));
|
|
2240
|
+
}
|
|
2241
|
+
function resolverStoreHome(env = process.env) {
|
|
2242
|
+
return configDir({ app: "configs", env, home: env.HOME || env.USERPROFILE || homedir3() });
|
|
2243
|
+
}
|
|
2244
|
+
function adoptResolverStoreHome(resolved, env = process.env) {
|
|
2245
|
+
const override = env.HASNA_CONFIG_HOME;
|
|
2246
|
+
if (typeof override === "string" && override.trim().length > 0)
|
|
2247
|
+
return true;
|
|
2248
|
+
return existsSync2(join3(resolved, "instructions.db"));
|
|
2249
|
+
}
|
|
2250
|
+
function exactStoreHome(env = process.env) {
|
|
2251
|
+
const v = env[HASNA_CONFIGS_HOME_ENV];
|
|
2252
|
+
return v && v.trim() ? v.trim() : undefined;
|
|
2253
|
+
}
|
|
2254
|
+
function getConfigsStoreHome(env = process.env) {
|
|
2255
|
+
const exact = exactStoreHome(env);
|
|
2256
|
+
if (exact)
|
|
2257
|
+
return resolve(exact);
|
|
2258
|
+
const resolved = resolverStoreHome(env);
|
|
2259
|
+
return adoptResolverStoreHome(resolved, env) ? resolve(resolved) : legacyStoreHome(env);
|
|
2260
|
+
}
|
|
2261
|
+
var HASNA_CONFIGS_HOME_ENV = "HASNA_CONFIGS_HOME";
|
|
2262
|
+
var init_app_home = __esm(() => {
|
|
2263
|
+
init_dist();
|
|
2264
|
+
});
|
|
2265
|
+
|
|
2266
|
+
// src/lib/raw-store-root.ts
|
|
2267
|
+
import { resolve as resolve2 } from "path";
|
|
2268
|
+
function getRawStoreRoot(env = process.env) {
|
|
2269
|
+
return resolve2(getConfigsStoreHome(env));
|
|
2168
2270
|
}
|
|
2169
|
-
var
|
|
2170
|
-
|
|
2271
|
+
var init_raw_store_root = __esm(() => {
|
|
2272
|
+
init_app_home();
|
|
2273
|
+
});
|
|
2171
2274
|
|
|
2172
2275
|
// src/db/database.ts
|
|
2173
2276
|
import { Database } from "bun:sqlite";
|
|
2174
|
-
import { existsSync as
|
|
2175
|
-
import { join as
|
|
2277
|
+
import { existsSync as existsSync3, mkdirSync, rmSync } from "fs";
|
|
2278
|
+
import { join as join4 } from "path";
|
|
2176
2279
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2177
2280
|
function getDbPath() {
|
|
2178
2281
|
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
|
|
@@ -2180,7 +2283,7 @@ function getDbPath() {
|
|
|
2180
2283
|
}
|
|
2181
2284
|
const dir = getRawStoreRoot();
|
|
2182
2285
|
mkdirSync(dir, { recursive: true });
|
|
2183
|
-
return
|
|
2286
|
+
return join4(dir, "instructions.db");
|
|
2184
2287
|
}
|
|
2185
2288
|
function uuid() {
|
|
2186
2289
|
return randomUUID3();
|
|
@@ -2221,7 +2324,7 @@ function resetLocalDatabase() {
|
|
|
2221
2324
|
if (dbPath === ":memory:")
|
|
2222
2325
|
return;
|
|
2223
2326
|
for (const p of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
2224
|
-
if (
|
|
2327
|
+
if (existsSync3(p))
|
|
2225
2328
|
rmSync(p);
|
|
2226
2329
|
}
|
|
2227
2330
|
}
|
|
@@ -2658,9 +2761,9 @@ var init_template = __esm(() => {
|
|
|
2658
2761
|
});
|
|
2659
2762
|
|
|
2660
2763
|
// src/lib/machine.ts
|
|
2661
|
-
import { arch as currentArch, homedir as
|
|
2662
|
-
import { existsSync as
|
|
2663
|
-
import { join as
|
|
2764
|
+
import { arch as currentArch, homedir as homedir4, hostname as currentHostname, type as currentOsType } from "os";
|
|
2765
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2766
|
+
import { join as join5 } from "path";
|
|
2664
2767
|
function normalizeOsFamily(os) {
|
|
2665
2768
|
const value = (os ?? "").trim().toLowerCase();
|
|
2666
2769
|
if (value === "darwin" || value === "macos" || value === "mac" || value === "osx")
|
|
@@ -2672,11 +2775,11 @@ function normalizeOsFamily(os) {
|
|
|
2672
2775
|
return value || "unknown";
|
|
2673
2776
|
}
|
|
2674
2777
|
function detectMachineContext(overrides = {}) {
|
|
2675
|
-
const
|
|
2778
|
+
const homeDir2 = overrides.home_dir ?? process.env["CONFIGS_HOME"] ?? process.env["HOME"] ?? homedir4();
|
|
2676
2779
|
const os = overrides.os ?? currentOsType();
|
|
2677
2780
|
const osFamily = normalizeOsFamily(os);
|
|
2678
|
-
const bunBinDir = overrides.bun_bin_dir ??
|
|
2679
|
-
const defaultBunPath = osFamily === "macos" &&
|
|
2781
|
+
const bunBinDir = overrides.bun_bin_dir ?? join5(homeDir2, ".bun", "bin");
|
|
2782
|
+
const defaultBunPath = osFamily === "macos" && existsSync4(BREW_BUN_PATH) ? BREW_BUN_PATH : join5(bunBinDir, "bun");
|
|
2680
2783
|
return {
|
|
2681
2784
|
id: "current-machine",
|
|
2682
2785
|
hostname: overrides.hostname ?? currentHostname(),
|
|
@@ -2685,11 +2788,11 @@ function detectMachineContext(overrides = {}) {
|
|
|
2685
2788
|
last_applied_at: null,
|
|
2686
2789
|
created_at: "",
|
|
2687
2790
|
os_family: osFamily,
|
|
2688
|
-
home_dir:
|
|
2689
|
-
workspace_root: overrides.workspace_root ??
|
|
2791
|
+
home_dir: homeDir2,
|
|
2792
|
+
workspace_root: overrides.workspace_root ?? join5(homeDir2, osFamily === "macos" ? "Workspace" : "workspace"),
|
|
2690
2793
|
bun_bin_dir: bunBinDir,
|
|
2691
2794
|
bun_path: overrides.bun_path ?? defaultBunPath,
|
|
2692
|
-
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${
|
|
2795
|
+
path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join5("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
|
|
2693
2796
|
};
|
|
2694
2797
|
}
|
|
2695
2798
|
function machineContextToVariables(machine) {
|
|
@@ -7324,7 +7427,7 @@ import { dlopen, FFIType } from "bun:ffi";
|
|
|
7324
7427
|
import {
|
|
7325
7428
|
closeSync,
|
|
7326
7429
|
constants,
|
|
7327
|
-
existsSync as
|
|
7430
|
+
existsSync as existsSync5,
|
|
7328
7431
|
fstatSync,
|
|
7329
7432
|
fsyncSync,
|
|
7330
7433
|
lstatSync,
|
|
@@ -7337,7 +7440,7 @@ import {
|
|
|
7337
7440
|
statSync,
|
|
7338
7441
|
writeFileSync
|
|
7339
7442
|
} from "fs";
|
|
7340
|
-
import { basename, dirname, isAbsolute, join as
|
|
7443
|
+
import { basename, dirname, isAbsolute, join as join6, parse, relative, resolve as resolve3 } from "path";
|
|
7341
7444
|
function managedObservationMaxBytes(relativePath) {
|
|
7342
7445
|
return SESSION_MANAGED_OUTPUT_PATHS.includes(relativePath) ? SESSION_MANAGED_OUTPUT_MAX_BYTES : FOREIGN_INPUT_MAX_BYTES;
|
|
7343
7446
|
}
|
|
@@ -7417,7 +7520,7 @@ function planProjectContext(input) {
|
|
|
7417
7520
|
const inlineMarkerOverhead = nativeImports ? 0 : Buffer.byteLength(buildManagedBlock(bundle, "", `
|
|
7418
7521
|
`), "utf8");
|
|
7419
7522
|
const generated = buildCanonicalFragment(bundle, status, ageSeconds, PROJECT_CONTEXT_MAX_RENDERED_BYTES - Math.max(320, inlineMarkerOverhead), PROJECT_CONTEXT_MAX_APPROX_TOKENS - Math.max(80, Math.ceil(inlineMarkerOverhead / 4)));
|
|
7420
|
-
const previousTargetContent =
|
|
7523
|
+
const previousTargetContent = existsSync5(paths.target) ? readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target))) : null;
|
|
7421
7524
|
const markerParse = parseManagedBlock(previousTargetContent ?? "", input.force === true);
|
|
7422
7525
|
if (markerParse.block && markerParse.block.id !== bundle.project.id) {
|
|
7423
7526
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "managed block belongs to a different project");
|
|
@@ -7468,7 +7571,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7468
7571
|
return null;
|
|
7469
7572
|
const { runtime, workspace_root: workspaceRoot, observed_hashes: observedHashes } = guard;
|
|
7470
7573
|
const paths = runtimePaths(workspaceRoot, runtime);
|
|
7471
|
-
if (!
|
|
7574
|
+
if (!existsSync5(paths.manifest))
|
|
7472
7575
|
return null;
|
|
7473
7576
|
assertCodewithTargetIsConsumed(workspaceRoot, runtime);
|
|
7474
7577
|
const manifest = readProjectContextManifest(paths.manifest, workspaceRoot);
|
|
@@ -7497,7 +7600,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7497
7600
|
}
|
|
7498
7601
|
const fragment = readUtf8RegularFile(paths.fragment, workspaceRoot, PROJECT_CONTEXT_MAX_RENDERED_BYTES);
|
|
7499
7602
|
scanGeneratedContent(fragment);
|
|
7500
|
-
if (!
|
|
7603
|
+
if (!existsSync5(paths.target)) {
|
|
7501
7604
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider target is missing while durable context is active");
|
|
7502
7605
|
}
|
|
7503
7606
|
const currentTarget = readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target)));
|
|
@@ -7508,7 +7611,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7508
7611
|
if (currentMarkers.block.id !== cache.project_id || currentMarkers.block.revision !== cache.revision || currentMarkers.block.hash !== cache.hash) {
|
|
7509
7612
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider markers differ from the durable cache");
|
|
7510
7613
|
}
|
|
7511
|
-
const plannedIndexes = input.files.filter((file) => file.role === "index" &&
|
|
7614
|
+
const plannedIndexes = input.files.filter((file) => file.role === "index" && resolve3(file.path) === paths.target);
|
|
7512
7615
|
if (plannedIndexes.length !== 1) {
|
|
7513
7616
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "session renderer does not own the selected project-context provider target");
|
|
7514
7617
|
}
|
|
@@ -7566,7 +7669,7 @@ function withProjectContextSessionGuard(guard, action, options = {}) {
|
|
|
7566
7669
|
verify();
|
|
7567
7670
|
return action(null);
|
|
7568
7671
|
}
|
|
7569
|
-
const lockPath =
|
|
7672
|
+
const lockPath = resolve3(validated.workspace_root, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7570
7673
|
const lock = acquireWorkspaceLock(validated.workspace_root, lockPath);
|
|
7571
7674
|
try {
|
|
7572
7675
|
verify();
|
|
@@ -7592,7 +7695,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7592
7695
|
if (!isRecord(observed) || typeof observed.path !== "string") {
|
|
7593
7696
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains malformed hash metadata");
|
|
7594
7697
|
}
|
|
7595
|
-
const path =
|
|
7698
|
+
const path = resolve3(observed.path);
|
|
7596
7699
|
if (!allowedPaths.has(path) || observedPaths.has(path)) {
|
|
7597
7700
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains an unexpected or duplicate path");
|
|
7598
7701
|
}
|
|
@@ -7614,7 +7717,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7614
7717
|
function applyProjectContext(options) {
|
|
7615
7718
|
const workspaceRoot = assertSafeWorkspaceRoot(options.workspace_root);
|
|
7616
7719
|
const now3 = options.now ?? new Date;
|
|
7617
|
-
const lockPath =
|
|
7720
|
+
const lockPath = resolve3(workspaceRoot, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7618
7721
|
const lock = options.dry_run ? null : acquireWorkspaceLock(workspaceRoot, lockPath, options.test_hooks?.after_lock_open, options.test_hooks?.before_stale_lock_remove, options.test_hooks?.process_start_identity);
|
|
7619
7722
|
try {
|
|
7620
7723
|
const resolved = resolveBundleForApply(options, workspaceRoot, now3);
|
|
@@ -7761,7 +7864,7 @@ function resolveBundleForApply(options, workspaceRoot, now3) {
|
|
|
7761
7864
|
if (!options.expected_project_id) {
|
|
7762
7865
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_REQUIRED", "expected_project_id is required for stale-cache fallback");
|
|
7763
7866
|
}
|
|
7764
|
-
const cachePath =
|
|
7867
|
+
const cachePath = resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
7765
7868
|
const cache = readProjectContextCache(cachePath, workspaceRoot);
|
|
7766
7869
|
if (!cache)
|
|
7767
7870
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_MISSING", "no last-known-good project context cache exists");
|
|
@@ -7963,7 +8066,7 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
7963
8066
|
if (runtime !== "codewith" || !content)
|
|
7964
8067
|
return null;
|
|
7965
8068
|
const sessionManifestPath = runtimePaths(workspaceRoot, runtime).sessionManifest;
|
|
7966
|
-
if (!
|
|
8069
|
+
if (!existsSync5(sessionManifestPath))
|
|
7967
8070
|
return null;
|
|
7968
8071
|
const manifest = readSessionManifestRecord(sessionManifestPath, workspaceRoot);
|
|
7969
8072
|
if (!manifest || manifest["schema"] !== SESSION_RENDER_SCHEMA) {
|
|
@@ -8014,7 +8117,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8014
8117
|
const manifest = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8015
8118
|
if (manifest) {
|
|
8016
8119
|
const manifestHashHasRecoveryProof = manifest.projectContext.hash === plan.bundle.hash || metadataSnapshotMatchesManifest(plan, manifest);
|
|
8017
|
-
const canonicalStateAlreadyInstalled = cache !== null && canonicalCacheHash === plan.bundle.hash && cache.project_id === plan.bundle.project.id && cache.revision === plan.bundle.revision && plan.marker !== null && plan.marker.id === plan.bundle.project.id && plan.marker.revision === plan.bundle.revision && plan.marker.hash === plan.bundle.hash &&
|
|
8120
|
+
const canonicalStateAlreadyInstalled = cache !== null && canonicalCacheHash === plan.bundle.hash && cache.project_id === plan.bundle.project.id && cache.revision === plan.bundle.revision && plan.marker !== null && plan.marker.id === plan.bundle.project.id && plan.marker.revision === plan.bundle.revision && plan.marker.hash === plan.bundle.hash && existsSync5(plan.fragment_path) && fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && manifestHashHasRecoveryProof;
|
|
8018
8121
|
observations.push({
|
|
8019
8122
|
source: "manifest",
|
|
8020
8123
|
id: manifest.projectContext.projectId,
|
|
@@ -8022,7 +8125,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8022
8125
|
hash: canonicalStateAlreadyInstalled && manifest.projectContext.projectId === plan.bundle.project.id && manifest.projectContext.revision === plan.bundle.revision ? plan.bundle.hash : normalizePersistedHash(manifest.projectContext.revision, manifest.projectContext.hash)
|
|
8023
8126
|
});
|
|
8024
8127
|
const fragmentEntry = manifest.files.find((file) => file.relativePath === PROJECT_CONTEXT_FRAGMENT_PATH);
|
|
8025
|
-
if (fragmentEntry &&
|
|
8128
|
+
if (fragmentEntry && existsSync5(plan.fragment_path)) {
|
|
8026
8129
|
const actual = currentFileHash(plan.fragment_path, plan.workspace_root);
|
|
8027
8130
|
if (actual !== fragmentEntry.sha256 && !fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && !force) {
|
|
8028
8131
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "canonical project-context fragment changed outside Instructions");
|
|
@@ -8117,9 +8220,9 @@ function buildManifest(plan, now3) {
|
|
|
8117
8220
|
function buildSessionCompatibilityManifest(plan, now3) {
|
|
8118
8221
|
const paths = runtimePaths(plan.workspace_root, plan.runtime);
|
|
8119
8222
|
const tool = manifestTool(plan.runtime);
|
|
8120
|
-
const targetHome = plan.runtime === "codewith" ?
|
|
8223
|
+
const targetHome = plan.runtime === "codewith" ? resolve3(plan.workspace_root, ".codewith") : plan.workspace_root;
|
|
8121
8224
|
const targetRelativePath = sessionTargetRelativePath(plan.runtime);
|
|
8122
|
-
const existing =
|
|
8225
|
+
const existing = existsSync5(paths.sessionManifest) ? readSessionManifestRecord(paths.sessionManifest, plan.workspace_root) : {
|
|
8123
8226
|
schema: SESSION_RENDER_SCHEMA,
|
|
8124
8227
|
tool,
|
|
8125
8228
|
adapterMode: plan.native_imports ? "native-imports" : "flattened-markdown",
|
|
@@ -8139,7 +8242,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8139
8242
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest is malformed or incompatible");
|
|
8140
8243
|
}
|
|
8141
8244
|
const existingTargetHome = safeLegacyMetadataString(existing["targetHome"], null);
|
|
8142
|
-
if (existingTargetHome !== null &&
|
|
8245
|
+
if (existingTargetHome !== null && resolve3(existingTargetHome) !== targetHome) {
|
|
8143
8246
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest targets a different workspace");
|
|
8144
8247
|
}
|
|
8145
8248
|
const sources = sanitizeLegacySources(existing["sources"]).filter((source) => source["id"] !== "project-context-bundle");
|
|
@@ -8425,9 +8528,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8425
8528
|
const previous = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8426
8529
|
if (!previous || previous.projectContext.revision === plan.bundle.revision && previous.projectContext.hash === plan.bundle.hash)
|
|
8427
8530
|
return null;
|
|
8428
|
-
const snapshotDir =
|
|
8531
|
+
const snapshotDir = resolve3(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8429
8532
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
8430
|
-
const snapshotPath =
|
|
8533
|
+
const snapshotPath = resolve3(snapshotDir, `${safeFilename(previous.projectContext.revision)}-${previous.projectContext.hash.slice(-12)}.json`);
|
|
8431
8534
|
const snapshot = {
|
|
8432
8535
|
schema: "hasna.configs.session-render-snapshot/v1",
|
|
8433
8536
|
kind: "project-context-metadata",
|
|
@@ -8443,9 +8546,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8443
8546
|
return snapshotPath;
|
|
8444
8547
|
}
|
|
8445
8548
|
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
8446
|
-
const snapshotDir =
|
|
8447
|
-
const snapshotPath =
|
|
8448
|
-
if (!
|
|
8549
|
+
const snapshotDir = resolve3(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8550
|
+
const snapshotPath = resolve3(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
8551
|
+
if (!existsSync5(snapshotPath))
|
|
8449
8552
|
return false;
|
|
8450
8553
|
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
8451
8554
|
const result = projectContextMetadataSnapshotSchema.safeParse(record);
|
|
@@ -8493,10 +8596,10 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8493
8596
|
sha256: nextHash
|
|
8494
8597
|
};
|
|
8495
8598
|
});
|
|
8496
|
-
const snapshotDir =
|
|
8599
|
+
const snapshotDir = resolve3(plan.workspace_root, ...SESSION_RENDER_SNAPSHOT_RELATIVE_DIR.split("/"));
|
|
8497
8600
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
8498
8601
|
const timestamp = now3.toISOString().replace(/[:.]/g, "-");
|
|
8499
|
-
const snapshotPath =
|
|
8602
|
+
const snapshotPath = resolve3(snapshotDir, `${timestamp}-${randomUUID5()}.json`);
|
|
8500
8603
|
const snapshot = {
|
|
8501
8604
|
schema: "hasna.configs.session-render-snapshot/v2",
|
|
8502
8605
|
createdAt: now3.toISOString(),
|
|
@@ -8514,7 +8617,7 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8514
8617
|
return snapshotPath;
|
|
8515
8618
|
}
|
|
8516
8619
|
function readProjectContextManifest(path, workspaceRoot) {
|
|
8517
|
-
if (!
|
|
8620
|
+
if (!existsSync5(path))
|
|
8518
8621
|
return null;
|
|
8519
8622
|
const record = readJsonRecord(path, workspaceRoot);
|
|
8520
8623
|
const result = storedManifestObservationSchema.safeParse(record);
|
|
@@ -8529,7 +8632,7 @@ function readProjectContextManifest(path, workspaceRoot) {
|
|
|
8529
8632
|
};
|
|
8530
8633
|
}
|
|
8531
8634
|
function readProjectContextCache(path, workspaceRoot) {
|
|
8532
|
-
if (!
|
|
8635
|
+
if (!existsSync5(path))
|
|
8533
8636
|
return null;
|
|
8534
8637
|
const record = readJsonRecord(path, workspaceRoot);
|
|
8535
8638
|
const result = projectContextCacheSchema.safeParse(record);
|
|
@@ -8561,7 +8664,7 @@ function readSessionManifestRecord(path, workspaceRoot) {
|
|
|
8561
8664
|
}
|
|
8562
8665
|
}
|
|
8563
8666
|
function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash, afterExchange, atomicExchangeUnavailable = false, beforeInstall, portableCreateOnly = false, maxObservedBytes, allowPortableReplacement = false) {
|
|
8564
|
-
const dir =
|
|
8667
|
+
const dir = resolve3(path, "..");
|
|
8565
8668
|
ensureSafeDirectory(dir, workspaceRoot, 448);
|
|
8566
8669
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8567
8670
|
const anchoredOps = portableCreateOnly ? null : resolveAnchoredFsOps();
|
|
@@ -8578,7 +8681,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8578
8681
|
const previous = anchoredFileObservation(directory, targetName);
|
|
8579
8682
|
const previousMode = previous?.mode ?? defaultMode;
|
|
8580
8683
|
const tempName = `.project-context-${randomUUID5()}.tmp`;
|
|
8581
|
-
const tempPath =
|
|
8684
|
+
const tempPath = join6(dir, tempName);
|
|
8582
8685
|
let fd = null;
|
|
8583
8686
|
let preserveTemp = false;
|
|
8584
8687
|
let directoryChanged = false;
|
|
@@ -8709,7 +8812,7 @@ function atomicWritePortable(path, content, workspaceRoot, defaultMode, expected
|
|
|
8709
8812
|
}
|
|
8710
8813
|
const dir = dirname(path);
|
|
8711
8814
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8712
|
-
const tempPath =
|
|
8815
|
+
const tempPath = join6(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8713
8816
|
let fd = null;
|
|
8714
8817
|
let tempIdentity = null;
|
|
8715
8818
|
try {
|
|
@@ -8766,7 +8869,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8766
8869
|
}
|
|
8767
8870
|
const dir = dirname(path);
|
|
8768
8871
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8769
|
-
const tempPath =
|
|
8872
|
+
const tempPath = join6(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8770
8873
|
const desiredHash = sha2562(content);
|
|
8771
8874
|
let fd = null;
|
|
8772
8875
|
let tempIdentity = null;
|
|
@@ -8823,7 +8926,7 @@ function portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, s
|
|
|
8823
8926
|
function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
8824
8927
|
if (maxObservedBytes === undefined)
|
|
8825
8928
|
return currentFileHash(path, workspaceRoot);
|
|
8826
|
-
if (!
|
|
8929
|
+
if (!existsSync5(path))
|
|
8827
8930
|
return null;
|
|
8828
8931
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8829
8932
|
const stat = lstatSync(path);
|
|
@@ -8835,11 +8938,11 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
8835
8938
|
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
8836
8939
|
}
|
|
8837
8940
|
function writeProjectContextCoordinatedFile(input) {
|
|
8838
|
-
atomicWriteFile(
|
|
8941
|
+
atomicWriteFile(resolve3(input.path), input.content, assertSafeWorkspaceRoot(input.workspace_root), input.default_mode ?? 420, input.expected_hash, undefined, false, input.test_hooks?.before_install, input.force_portable_file_ops ?? false, input.max_observed_bytes, input.allow_portable_replacement ?? false);
|
|
8839
8942
|
}
|
|
8840
8943
|
function removeProjectContextCoordinatedFile(input) {
|
|
8841
8944
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
8842
|
-
const path =
|
|
8945
|
+
const path = resolve3(input.path);
|
|
8843
8946
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8844
8947
|
const dir = dirname(path);
|
|
8845
8948
|
const anchoredOps = input.force_portable_file_ops ? null : resolveAnchoredFsOps();
|
|
@@ -8865,7 +8968,7 @@ function removeProjectContextCoordinatedFile(input) {
|
|
|
8865
8968
|
throw new ProjectContextHashRace(`managed path changed during deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
8866
8969
|
}
|
|
8867
8970
|
displaced = true;
|
|
8868
|
-
input.test_hooks?.after_displace?.(
|
|
8971
|
+
input.test_hooks?.after_displace?.(join6(dir, displacedName));
|
|
8869
8972
|
const moved = anchoredFileObservation(directory, displacedName);
|
|
8870
8973
|
if (!moved || moved.dev !== observed.dev || moved.ino !== observed.ino || moved.hash !== input.expected_hash || anchoredFileObservation(directory, targetName) !== null) {
|
|
8871
8974
|
throw new ProjectContextHashRace(`managed path changed during deletion validation: ${relativePosix(workspaceRoot, path)}`);
|
|
@@ -8911,7 +9014,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
8911
9014
|
}
|
|
8912
9015
|
const dir = dirname(path);
|
|
8913
9016
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8914
|
-
const displacedPath =
|
|
9017
|
+
const displacedPath = join6(dir, `.project-context-delete-${randomUUID5()}.tmp`);
|
|
8915
9018
|
let displaced = false;
|
|
8916
9019
|
try {
|
|
8917
9020
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
@@ -8922,7 +9025,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
8922
9025
|
displaced = true;
|
|
8923
9026
|
afterDisplace?.(displacedPath);
|
|
8924
9027
|
const moved = lstatSync(displacedPath);
|
|
8925
|
-
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash ||
|
|
9028
|
+
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash || existsSync5(path)) {
|
|
8926
9029
|
throw new ProjectContextHashRace(`managed path changed during portable deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
8927
9030
|
}
|
|
8928
9031
|
rmSync2(displacedPath);
|
|
@@ -8982,7 +9085,7 @@ function anchoredOpenExclusive(directory, name, mode) {
|
|
|
8982
9085
|
const requestedMode = mode & 4095;
|
|
8983
9086
|
let fd;
|
|
8984
9087
|
try {
|
|
8985
|
-
fd = openSync(
|
|
9088
|
+
fd = openSync(join6(directory.path, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, requestedMode);
|
|
8986
9089
|
} catch {
|
|
8987
9090
|
throw new ProjectContextHashRace(`could not create prepared managed file in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
8988
9091
|
}
|
|
@@ -9025,7 +9128,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
9025
9128
|
const stat = fstatSync(fd);
|
|
9026
9129
|
if (!stat.isFile())
|
|
9027
9130
|
throw new ProjectContextHashRace("managed output is not a regular file");
|
|
9028
|
-
const relativePath = relativePosix(directory.workspaceRoot,
|
|
9131
|
+
const relativePath = relativePosix(directory.workspaceRoot, join6(directory.path, name));
|
|
9029
9132
|
const maxBytes = directory.maxObservedBytes === undefined ? managedObservationMaxBytes(relativePath) : directory.maxObservedBytes;
|
|
9030
9133
|
if (maxBytes !== null && stat.size > maxBytes) {
|
|
9031
9134
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePath}`);
|
|
@@ -9051,7 +9154,7 @@ function anchoredPreparedObservation(directory, name, path, stage) {
|
|
|
9051
9154
|
return observed;
|
|
9052
9155
|
}
|
|
9053
9156
|
function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
9054
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9157
|
+
assertNoSymlinkSegments(workspaceRoot, join6(path, ".project-context-directory-guard"));
|
|
9055
9158
|
let stat;
|
|
9056
9159
|
try {
|
|
9057
9160
|
stat = lstatSync(path);
|
|
@@ -9064,7 +9167,7 @@ function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
|
9064
9167
|
return { dev: stat.dev, ino: stat.ino };
|
|
9065
9168
|
}
|
|
9066
9169
|
function assertManagedDirectoryStable(path, workspaceRoot, expected) {
|
|
9067
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9170
|
+
assertNoSymlinkSegments(workspaceRoot, join6(path, ".project-context-directory-guard"));
|
|
9068
9171
|
let current;
|
|
9069
9172
|
try {
|
|
9070
9173
|
current = lstatSync(path);
|
|
@@ -9197,10 +9300,10 @@ function resolveAnchoredFsOps() {
|
|
|
9197
9300
|
return null;
|
|
9198
9301
|
}
|
|
9199
9302
|
function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRemove, processStartIdentityLookup = processStartIdentity) {
|
|
9200
|
-
const lockDirectory =
|
|
9303
|
+
const lockDirectory = resolve3(lockPath, "..");
|
|
9201
9304
|
ensureSafeDirectory(lockDirectory, workspaceRoot, 448);
|
|
9202
9305
|
assertNoSymlinkSegments(workspaceRoot, lockPath);
|
|
9203
|
-
const tempPath =
|
|
9306
|
+
const tempPath = join6(lockDirectory, `.project-context-lock-${randomUUID5()}.tmp`);
|
|
9204
9307
|
let fd = null;
|
|
9205
9308
|
let openedIdentity = null;
|
|
9206
9309
|
let openedContentHash = null;
|
|
@@ -9234,7 +9337,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9234
9337
|
linked = true;
|
|
9235
9338
|
}
|
|
9236
9339
|
fsyncDirectory(lockDirectory);
|
|
9237
|
-
if (
|
|
9340
|
+
if (existsSync5(tempPath)) {
|
|
9238
9341
|
rmSync2(tempPath);
|
|
9239
9342
|
fsyncDirectory(lockDirectory);
|
|
9240
9343
|
}
|
|
@@ -9249,7 +9352,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9249
9352
|
if (linked && openedIdentity && openedContentHash) {
|
|
9250
9353
|
removeOwnedLockByInode(lockPath, openedIdentity, openedContentHash);
|
|
9251
9354
|
}
|
|
9252
|
-
if (!preserveTemp &&
|
|
9355
|
+
if (!preserveTemp && existsSync5(tempPath)) {
|
|
9253
9356
|
try {
|
|
9254
9357
|
rmSync2(tempPath);
|
|
9255
9358
|
} catch {}
|
|
@@ -9264,7 +9367,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9264
9367
|
}
|
|
9265
9368
|
function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
9266
9369
|
try {
|
|
9267
|
-
if (!
|
|
9370
|
+
if (!existsSync5(lockPath))
|
|
9268
9371
|
return;
|
|
9269
9372
|
const current = lstatSync(lockPath);
|
|
9270
9373
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
@@ -9272,7 +9375,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
9272
9375
|
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
9273
9376
|
return;
|
|
9274
9377
|
rmSync2(lockPath);
|
|
9275
|
-
fsyncDirectory(
|
|
9378
|
+
fsyncDirectory(resolve3(lockPath, ".."));
|
|
9276
9379
|
} catch {}
|
|
9277
9380
|
}
|
|
9278
9381
|
function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentityLookup = processStartIdentity) {
|
|
@@ -9343,7 +9446,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9343
9446
|
const candidateInstalled = !current.isSymbolicLink() && current.dev === candidateIdentity.dev && current.ino === candidateIdentity.ino && currentFileHash(lockPath, workspaceRoot) === candidateHash;
|
|
9344
9447
|
const staleDisplaced = !displaced.isSymbolicLink() && displaced.dev === stale.identity.dev && displaced.ino === stale.identity.ino && currentFileHash(candidatePath, workspaceRoot) === stale.contentHash;
|
|
9345
9448
|
if (!candidateInstalled || !staleDisplaced) {
|
|
9346
|
-
if (candidateInstalled &&
|
|
9449
|
+
if (candidateInstalled && existsSync5(candidatePath)) {
|
|
9347
9450
|
atomicExchangePaths(candidatePath, lockPath);
|
|
9348
9451
|
exchanged = false;
|
|
9349
9452
|
return false;
|
|
@@ -9351,13 +9454,13 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9351
9454
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace lock changed during stale-lock takeover and could not be restored safely");
|
|
9352
9455
|
}
|
|
9353
9456
|
rmSync2(candidatePath);
|
|
9354
|
-
fsyncDirectory(
|
|
9457
|
+
fsyncDirectory(resolve3(lockPath, ".."));
|
|
9355
9458
|
exchanged = false;
|
|
9356
9459
|
return true;
|
|
9357
9460
|
} catch (error) {
|
|
9358
9461
|
if (exchanged) {
|
|
9359
9462
|
try {
|
|
9360
|
-
if (currentFileHash(lockPath, workspaceRoot) === candidateHash &&
|
|
9463
|
+
if (currentFileHash(lockPath, workspaceRoot) === candidateHash && existsSync5(candidatePath)) {
|
|
9361
9464
|
atomicExchangePaths(candidatePath, lockPath);
|
|
9362
9465
|
exchanged = false;
|
|
9363
9466
|
}
|
|
@@ -9370,7 +9473,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9370
9473
|
}
|
|
9371
9474
|
}
|
|
9372
9475
|
function assertWorkspaceLockHeld(lockPath, lock, workspaceRoot) {
|
|
9373
|
-
if (!
|
|
9476
|
+
if (!existsSync5(lockPath)) {
|
|
9374
9477
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace project-context lock changed during render");
|
|
9375
9478
|
}
|
|
9376
9479
|
const current = lstatSync(lockPath);
|
|
@@ -9430,8 +9533,8 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9430
9533
|
}
|
|
9431
9534
|
return;
|
|
9432
9535
|
}
|
|
9433
|
-
const lockDirectory =
|
|
9434
|
-
const releasePath =
|
|
9536
|
+
const lockDirectory = resolve3(lockPath, "..");
|
|
9537
|
+
const releasePath = join6(lockDirectory, `.project-context-release-${randomUUID5()}.tmp`);
|
|
9435
9538
|
let releaseFd = null;
|
|
9436
9539
|
let releaseIdentity = null;
|
|
9437
9540
|
let releaseHash = null;
|
|
@@ -9460,7 +9563,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9460
9563
|
const releaseInstalled = !installed.isSymbolicLink() && installed.dev === releaseIdentity.dev && installed.ino === releaseIdentity.ino && currentFileHash(lockPath, workspaceRoot) === releaseHash;
|
|
9461
9564
|
const ownedDisplaced = !displaced.isSymbolicLink() && displaced.dev === lock.identity.dev && displaced.ino === lock.identity.ino && currentFileHash(releasePath, workspaceRoot) === lock.contentHash;
|
|
9462
9565
|
if (!releaseInstalled || !ownedDisplaced) {
|
|
9463
|
-
if (releaseInstalled &&
|
|
9566
|
+
if (releaseInstalled && existsSync5(releasePath)) {
|
|
9464
9567
|
atomicExchangePaths(releasePath, lockPath);
|
|
9465
9568
|
exchanged = false;
|
|
9466
9569
|
}
|
|
@@ -9473,7 +9576,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9473
9576
|
} catch {
|
|
9474
9577
|
if (exchanged) {
|
|
9475
9578
|
try {
|
|
9476
|
-
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash &&
|
|
9579
|
+
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash && existsSync5(releasePath)) {
|
|
9477
9580
|
atomicExchangePaths(releasePath, lockPath);
|
|
9478
9581
|
exchanged = false;
|
|
9479
9582
|
}
|
|
@@ -9485,7 +9588,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9485
9588
|
closeSync(releaseFd);
|
|
9486
9589
|
} catch {}
|
|
9487
9590
|
}
|
|
9488
|
-
if (!exchanged &&
|
|
9591
|
+
if (!exchanged && existsSync5(releasePath)) {
|
|
9489
9592
|
try {
|
|
9490
9593
|
rmSync2(releasePath);
|
|
9491
9594
|
} catch {}
|
|
@@ -9511,15 +9614,15 @@ function ensureSafeDirectory(path, workspaceRoot, mode) {
|
|
|
9511
9614
|
const segments = rel.split(/[\\/]+/).filter(Boolean);
|
|
9512
9615
|
let current = workspaceRoot;
|
|
9513
9616
|
for (const segment of segments) {
|
|
9514
|
-
current =
|
|
9515
|
-
if (
|
|
9617
|
+
current = join6(current, segment);
|
|
9618
|
+
if (existsSync5(current)) {
|
|
9516
9619
|
if (lstatSync(current).isSymbolicLink())
|
|
9517
9620
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9518
9621
|
if (!statSync(current).isDirectory())
|
|
9519
9622
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", `managed path is not a directory: ${current}`);
|
|
9520
9623
|
} else {
|
|
9521
9624
|
mkdirSync2(current, { mode });
|
|
9522
|
-
fsyncDirectory(
|
|
9625
|
+
fsyncDirectory(resolve3(current, ".."));
|
|
9523
9626
|
}
|
|
9524
9627
|
}
|
|
9525
9628
|
}
|
|
@@ -9575,11 +9678,11 @@ function scanGeneratedContent(content) {
|
|
|
9575
9678
|
function runtimePaths(workspaceRoot, runtime) {
|
|
9576
9679
|
const relativeTarget = runtime === "claude" ? "CLAUDE.md" : runtime === "codewith" ? ".codewith/CODEWITH.md" : "AGENTS.md";
|
|
9577
9680
|
return {
|
|
9578
|
-
target:
|
|
9579
|
-
fragment:
|
|
9580
|
-
manifest:
|
|
9581
|
-
cache:
|
|
9582
|
-
sessionManifest: runtime === "codewith" ?
|
|
9681
|
+
target: resolve3(workspaceRoot, ...relativeTarget.split("/")),
|
|
9682
|
+
fragment: resolve3(workspaceRoot, ...PROJECT_CONTEXT_FRAGMENT_PATH.split("/")),
|
|
9683
|
+
manifest: resolve3(workspaceRoot, ...PROJECT_CONTEXT_MANIFEST_PATH.split("/")),
|
|
9684
|
+
cache: resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/")),
|
|
9685
|
+
sessionManifest: runtime === "codewith" ? resolve3(workspaceRoot, ".codewith", ".hasna", "session-render-manifest.json") : resolve3(workspaceRoot, ".hasna", "session-render-manifest.json")
|
|
9583
9686
|
};
|
|
9584
9687
|
}
|
|
9585
9688
|
function projectContextSessionGuardPaths(paths, runtime) {
|
|
@@ -9589,7 +9692,7 @@ function projectContextSessionGuardPaths(paths, runtime) {
|
|
|
9589
9692
|
paths.fragment,
|
|
9590
9693
|
paths.target,
|
|
9591
9694
|
paths.sessionManifest,
|
|
9592
|
-
...runtime === "codewith" ? [
|
|
9695
|
+
...runtime === "codewith" ? [resolve3(paths.target, "..", "CODEWITH.override.md")] : []
|
|
9593
9696
|
];
|
|
9594
9697
|
}
|
|
9595
9698
|
function sessionTargetRelativePath(runtime) {
|
|
@@ -9609,27 +9712,27 @@ function projectContextRuntimeForSessionTool(tool) {
|
|
|
9609
9712
|
return null;
|
|
9610
9713
|
}
|
|
9611
9714
|
function projectContextWorkspaceForSession(input, runtime) {
|
|
9612
|
-
const targetHome =
|
|
9715
|
+
const targetHome = resolve3(input.target_home);
|
|
9613
9716
|
if (runtime === "codewith") {
|
|
9614
9717
|
const workspaceRoot = basename(targetHome) === ".codewith" ? dirname(targetHome) : null;
|
|
9615
9718
|
if (!workspaceRoot)
|
|
9616
9719
|
return null;
|
|
9617
|
-
if (input.project_root &&
|
|
9720
|
+
if (input.project_root && resolve3(input.project_root) !== workspaceRoot) {
|
|
9618
9721
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "Codewith project_root must be the parent workspace of target_home");
|
|
9619
9722
|
}
|
|
9620
|
-
if (!
|
|
9723
|
+
if (!existsSync5(workspaceRoot) || !lstatSync(workspaceRoot).isDirectory())
|
|
9621
9724
|
return null;
|
|
9622
9725
|
return assertSafeWorkspaceRoot(workspaceRoot);
|
|
9623
9726
|
}
|
|
9624
|
-
if (!
|
|
9727
|
+
if (!existsSync5(targetHome) || !lstatSync(targetHome).isDirectory())
|
|
9625
9728
|
return null;
|
|
9626
9729
|
return assertSafeWorkspaceRoot(targetHome);
|
|
9627
9730
|
}
|
|
9628
9731
|
function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
9629
9732
|
if (runtime !== "codewith")
|
|
9630
9733
|
return;
|
|
9631
|
-
const override =
|
|
9632
|
-
if (!
|
|
9734
|
+
const override = resolve3(workspaceRoot, ".codewith", "CODEWITH.override.md");
|
|
9735
|
+
if (!existsSync5(override))
|
|
9633
9736
|
return;
|
|
9634
9737
|
assertNoSymlinkSegments(workspaceRoot, override);
|
|
9635
9738
|
if (!lstatSync(override).isFile())
|
|
@@ -9639,10 +9742,10 @@ function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
|
9639
9742
|
function assertSafeWorkspaceRoot(path) {
|
|
9640
9743
|
if (!isAbsolute(path))
|
|
9641
9744
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be absolute");
|
|
9642
|
-
const normalized =
|
|
9745
|
+
const normalized = resolve3(path);
|
|
9643
9746
|
if (normalized === parse(normalized).root)
|
|
9644
9747
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root cannot be the filesystem root");
|
|
9645
|
-
if (!
|
|
9748
|
+
if (!existsSync5(normalized) || !lstatSync(normalized).isDirectory())
|
|
9646
9749
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be an existing directory");
|
|
9647
9750
|
assertNoSymlinkAncestors(normalized);
|
|
9648
9751
|
if (lstatSync(normalized).isSymbolicLink())
|
|
@@ -9656,18 +9759,18 @@ function assertNoSymlinkSegments(root, target) {
|
|
|
9656
9759
|
}
|
|
9657
9760
|
let current = root;
|
|
9658
9761
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
9659
|
-
current =
|
|
9660
|
-
if (
|
|
9762
|
+
current = join6(current, segment);
|
|
9763
|
+
if (existsSync5(current) && lstatSync(current).isSymbolicLink()) {
|
|
9661
9764
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9662
9765
|
}
|
|
9663
9766
|
}
|
|
9664
9767
|
}
|
|
9665
9768
|
function assertNoSymlinkAncestors(path) {
|
|
9666
|
-
const normalized =
|
|
9769
|
+
const normalized = resolve3(path);
|
|
9667
9770
|
let current = parse(normalized).root;
|
|
9668
9771
|
for (const segment of relative(current, normalized).split(/[\\/]+/).filter(Boolean)) {
|
|
9669
|
-
current =
|
|
9670
|
-
if (!
|
|
9772
|
+
current = join6(current, segment);
|
|
9773
|
+
if (!existsSync5(current))
|
|
9671
9774
|
return;
|
|
9672
9775
|
if (lstatSync(current).isSymbolicLink())
|
|
9673
9776
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `workspace ancestor is a symlink: ${current}`);
|
|
@@ -9683,7 +9786,7 @@ function readUtf8RegularFile(path, workspaceRoot, maxBytes = FOREIGN_INPUT_MAX_B
|
|
|
9683
9786
|
return readFileSync(path, "utf8");
|
|
9684
9787
|
}
|
|
9685
9788
|
function currentFileHash(path, workspaceRoot) {
|
|
9686
|
-
if (!
|
|
9789
|
+
if (!existsSync5(path))
|
|
9687
9790
|
return null;
|
|
9688
9791
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
9689
9792
|
return sha2562(readUtf8RegularFile(path, workspaceRoot, managedObservationMaxBytes(relativePath)));
|
|
@@ -9702,10 +9805,10 @@ function fragmentMatchesBundle(path, bundle, workspaceRoot) {
|
|
|
9702
9805
|
}
|
|
9703
9806
|
function durableSourcePath(path, workspaceRoot) {
|
|
9704
9807
|
if (!path || path.startsWith("/dev/fd/"))
|
|
9705
|
-
return
|
|
9706
|
-
const normalized = isAbsolute(path) ?
|
|
9808
|
+
return resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9809
|
+
const normalized = isAbsolute(path) ? resolve3(path) : resolve3(workspaceRoot, path);
|
|
9707
9810
|
if (normalized.startsWith("/dev/fd/"))
|
|
9708
|
-
return
|
|
9811
|
+
return resolve3(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9709
9812
|
return normalized;
|
|
9710
9813
|
}
|
|
9711
9814
|
function compareRevisions(incoming, previous) {
|
|
@@ -10265,7 +10368,7 @@ function compareProviderVersions(left, right) {
|
|
|
10265
10368
|
|
|
10266
10369
|
// src/lib/asset-plan.ts
|
|
10267
10370
|
import { createHash as createHash3 } from "crypto";
|
|
10268
|
-
import { isAbsolute as isAbsolute2, posix, resolve as
|
|
10371
|
+
import { isAbsolute as isAbsolute2, posix, resolve as resolve4 } from "path";
|
|
10269
10372
|
function assetCapability(provider, surface, kind, support, strategies, note, providerVersionRange = "*") {
|
|
10270
10373
|
return Object.freeze({
|
|
10271
10374
|
schema: ASSET_CAPABILITY_SCHEMA,
|
|
@@ -10472,8 +10575,8 @@ function resolveAssetDestination(item, roots) {
|
|
|
10472
10575
|
if (!isAbsolute2(root))
|
|
10473
10576
|
throw new Error(`Asset ${item.assetKey} destination root must be absolute.`);
|
|
10474
10577
|
const relativePath = safeRelativePath(item.destination.relativePath);
|
|
10475
|
-
const target =
|
|
10476
|
-
const normalizedRoot =
|
|
10578
|
+
const target = resolve4(root, ...relativePath.split("/"));
|
|
10579
|
+
const normalizedRoot = resolve4(root);
|
|
10477
10580
|
if (target === normalizedRoot)
|
|
10478
10581
|
throw new Error(`Asset ${item.assetKey} destination cannot replace its root.`);
|
|
10479
10582
|
if (!target.startsWith(`${normalizedRoot}/`))
|
|
@@ -10653,13 +10756,13 @@ var init_asset_plan = __esm(() => {
|
|
|
10653
10756
|
// src/lib/cursor-authority.ts
|
|
10654
10757
|
import { createHash as createHash4 } from "crypto";
|
|
10655
10758
|
import { lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
10656
|
-
import { homedir as
|
|
10657
|
-
import { join as
|
|
10759
|
+
import { homedir as homedir5 } from "os";
|
|
10760
|
+
import { join as join7, resolve as resolve5 } from "path";
|
|
10658
10761
|
function sha2564(content) {
|
|
10659
10762
|
return createHash4("sha256").update(content).digest("hex");
|
|
10660
10763
|
}
|
|
10661
|
-
function
|
|
10662
|
-
return process.env["HOME"] ||
|
|
10764
|
+
function homeDir2() {
|
|
10765
|
+
return process.env["HOME"] || homedir5();
|
|
10663
10766
|
}
|
|
10664
10767
|
function markerPayload(content, markerLine, markerIndex) {
|
|
10665
10768
|
const index = markerIndex ?? content.indexOf(markerLine);
|
|
@@ -10675,12 +10778,12 @@ function baseObservation(path) {
|
|
|
10675
10778
|
};
|
|
10676
10779
|
}
|
|
10677
10780
|
function observeCursorGlobalAuthority(options = {}) {
|
|
10678
|
-
const authorityPath =
|
|
10781
|
+
const authorityPath = resolve5(join7(options.home ?? homeDir2(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10679
10782
|
const readFile2 = options.readFile ?? ((path) => readFileSync2(path, "utf8"));
|
|
10680
10783
|
return observeCursorGlobalAuthorityPath(authorityPath, readFile2);
|
|
10681
10784
|
}
|
|
10682
10785
|
function observeCursorGlobalAuthorityAtPath(authorityPath) {
|
|
10683
|
-
return observeCursorGlobalAuthorityPath(
|
|
10786
|
+
return observeCursorGlobalAuthorityPath(resolve5(authorityPath), (path) => readFileSync2(path, "utf8"));
|
|
10684
10787
|
}
|
|
10685
10788
|
function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
10686
10789
|
const base = baseObservation(authorityPath);
|
|
@@ -10825,11 +10928,21 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
|
10825
10928
|
};
|
|
10826
10929
|
}
|
|
10827
10930
|
function isCursorGlobalAuthorityPath(path) {
|
|
10828
|
-
return
|
|
10931
|
+
return resolve5(path) === resolve5(join7(homeDir2(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10829
10932
|
}
|
|
10830
10933
|
function stampCursorGlobalAuthorityMarker(content) {
|
|
10831
|
-
|
|
10832
|
-
|
|
10934
|
+
const existing = content.match(CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN);
|
|
10935
|
+
if (existing) {
|
|
10936
|
+
const markerLine2 = existing[0];
|
|
10937
|
+
const index = existing.index ?? content.indexOf(markerLine2);
|
|
10938
|
+
const payload = markerPayload(content, markerLine2, index);
|
|
10939
|
+
if (sha2564(payload) === existing[1].slice("sha256:".length)) {
|
|
10940
|
+
return content;
|
|
10941
|
+
}
|
|
10942
|
+
const digest2 = sha2564(payload);
|
|
10943
|
+
const freshMarkerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest2} -->`;
|
|
10944
|
+
return content.slice(0, index) + freshMarkerLine + content.slice(index + markerLine2.length);
|
|
10945
|
+
}
|
|
10833
10946
|
const digest = sha2564(content);
|
|
10834
10947
|
const markerLine = `<!-- ${CURSOR_GLOBAL_AUTHORITY_MANAGED_MARKER} hash=sha256:${digest} -->`;
|
|
10835
10948
|
const frontmatter = content.match(CURSOR_GLOBAL_AUTHORITY_FRONTMATTER_PATTERN)?.[0];
|
|
@@ -10871,16 +10984,16 @@ var init_cursor_authority = __esm(() => {
|
|
|
10871
10984
|
// src/lib/session-authority.ts
|
|
10872
10985
|
import { createHash as createHash5 } from "crypto";
|
|
10873
10986
|
import { lstatSync as lstatSync3, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
|
|
10874
|
-
import { homedir as
|
|
10875
|
-
import { join as
|
|
10987
|
+
import { homedir as homedir6 } from "os";
|
|
10988
|
+
import { join as join8, resolve as resolve6 } from "path";
|
|
10876
10989
|
function sha2565(content) {
|
|
10877
10990
|
return createHash5("sha256").update(content).digest("hex");
|
|
10878
10991
|
}
|
|
10879
10992
|
function configHomeDir() {
|
|
10880
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
10993
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir6();
|
|
10881
10994
|
}
|
|
10882
10995
|
function normalizeOwnedTargetPath(p) {
|
|
10883
|
-
const expanded = p.startsWith("~/") ?
|
|
10996
|
+
const expanded = p.startsWith("~/") ? resolve6(configHomeDir(), p.slice(2)) : resolve6(p);
|
|
10884
10997
|
try {
|
|
10885
10998
|
return realpathSync(expanded);
|
|
10886
10999
|
} catch {
|
|
@@ -10888,7 +11001,7 @@ function normalizeOwnedTargetPath(p) {
|
|
|
10888
11001
|
}
|
|
10889
11002
|
}
|
|
10890
11003
|
function detectClaudeAuthorityConflicts(targetHome, ownedAuthorities = []) {
|
|
10891
|
-
const authorityPath =
|
|
11004
|
+
const authorityPath = resolve6(join8(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
|
|
10892
11005
|
let stat;
|
|
10893
11006
|
try {
|
|
10894
11007
|
stat = lstatSync3(authorityPath);
|
|
@@ -10977,9 +11090,9 @@ var init_session_authority = __esm(() => {
|
|
|
10977
11090
|
|
|
10978
11091
|
// src/lib/session-render.ts
|
|
10979
11092
|
import { createHash as createHash6 } from "crypto";
|
|
10980
|
-
import { existsSync as
|
|
10981
|
-
import { homedir as
|
|
10982
|
-
import { basename as basename4, dirname as dirname3, extname as extname2, isAbsolute as isAbsolute3, join as
|
|
11093
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
11094
|
+
import { homedir as homedir7 } from "os";
|
|
11095
|
+
import { basename as basename4, dirname as dirname3, extname as extname2, isAbsolute as isAbsolute3, join as join9, parse as parse2, posix as posix2, relative as relative2, resolve as resolve7 } from "path";
|
|
10983
11096
|
function normalizeSessionInstructionLayer(value) {
|
|
10984
11097
|
if (value === "provider")
|
|
10985
11098
|
return "tool";
|
|
@@ -11065,13 +11178,13 @@ function yamlQuote2(value) {
|
|
|
11065
11178
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
11066
11179
|
}
|
|
11067
11180
|
function defaultTargetHome(tool, profile, sessionId) {
|
|
11068
|
-
const home = process.env["HOME"] ||
|
|
11069
|
-
return
|
|
11181
|
+
const home = process.env["HOME"] || homedir7();
|
|
11182
|
+
return join9(home, ".hasna", "accounts", "profiles", tool, slug(profile));
|
|
11070
11183
|
}
|
|
11071
11184
|
function joinTarget(targetHome, relativePath) {
|
|
11072
11185
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
11073
11186
|
const safeRelativePath2 = assertSafeRelativePath(relativePath);
|
|
11074
|
-
return
|
|
11187
|
+
return join9(safeTargetHome, ...safeRelativePath2.split("/"));
|
|
11075
11188
|
}
|
|
11076
11189
|
function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
11077
11190
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
@@ -11640,7 +11753,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
|
|
|
11640
11753
|
...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
|
|
11641
11754
|
]);
|
|
11642
11755
|
const existingConfigPath = joinTarget(targetHome, adapter.configFile);
|
|
11643
|
-
const selectedConfig =
|
|
11756
|
+
const selectedConfig = existsSync7(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
|
|
11644
11757
|
const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
|
|
11645
11758
|
const config = {
|
|
11646
11759
|
...selectedConfig,
|
|
@@ -11829,7 +11942,7 @@ function adapterFor(input) {
|
|
|
11829
11942
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
11830
11943
|
}
|
|
11831
11944
|
function getHomeDir() {
|
|
11832
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
11945
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir7();
|
|
11833
11946
|
}
|
|
11834
11947
|
function cleanSessionPathInput(path) {
|
|
11835
11948
|
const trimmed = path.trim();
|
|
@@ -11844,16 +11957,16 @@ function resolveSessionPath(path) {
|
|
|
11844
11957
|
throw new Error("Session render path cannot be empty.");
|
|
11845
11958
|
const home = getHomeDir();
|
|
11846
11959
|
if (cleaned === "~")
|
|
11847
|
-
return
|
|
11960
|
+
return resolve7(home);
|
|
11848
11961
|
if (cleaned.startsWith("~/"))
|
|
11849
|
-
return
|
|
11962
|
+
return resolve7(home, cleaned.slice(2));
|
|
11850
11963
|
if (cleaned === "{{HOME}}" || cleaned === "${HOME}")
|
|
11851
|
-
return
|
|
11964
|
+
return resolve7(home);
|
|
11852
11965
|
if (cleaned.startsWith("{{HOME}}/"))
|
|
11853
|
-
return
|
|
11966
|
+
return resolve7(home, cleaned.slice("{{HOME}}/".length));
|
|
11854
11967
|
if (cleaned.startsWith("${HOME}/"))
|
|
11855
|
-
return
|
|
11856
|
-
return
|
|
11968
|
+
return resolve7(home, cleaned.slice("${HOME}/".length));
|
|
11969
|
+
return resolve7(cleaned);
|
|
11857
11970
|
}
|
|
11858
11971
|
function assertSafeRelativePath(relativePath) {
|
|
11859
11972
|
if (!relativePath.trim())
|
|
@@ -11869,7 +11982,7 @@ function assertSafeRelativePath(relativePath) {
|
|
|
11869
11982
|
function assertSafeTargetRoot(targetHome) {
|
|
11870
11983
|
if (!isAbsolute3(targetHome))
|
|
11871
11984
|
throw new Error(`Session render target must be an absolute path: ${targetHome}`);
|
|
11872
|
-
const normalized =
|
|
11985
|
+
const normalized = resolve7(targetHome);
|
|
11873
11986
|
if (normalized === parse2(normalized).root) {
|
|
11874
11987
|
throw new Error(`Session render target cannot be the filesystem root: ${targetHome}`);
|
|
11875
11988
|
}
|
|
@@ -12105,7 +12218,7 @@ function planSessionRender(input) {
|
|
|
12105
12218
|
sourceId: input.providerConfig.sourceId,
|
|
12106
12219
|
selectedPayloadSha256: sha2566(input.providerConfig.content),
|
|
12107
12220
|
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
|
|
12108
|
-
selected: !
|
|
12221
|
+
selected: !existsSync7(joinTarget(targetHome, adapter.configFile))
|
|
12109
12222
|
}
|
|
12110
12223
|
} : {},
|
|
12111
12224
|
...projectContext ? {
|
|
@@ -12366,10 +12479,10 @@ function layerFromIdentityKind(kind, exportShape) {
|
|
|
12366
12479
|
function contentFromIdentitySourcePaths(sourcePaths, exportPath, sourceId) {
|
|
12367
12480
|
if (sourcePaths.length === 0 || !exportPath)
|
|
12368
12481
|
return;
|
|
12369
|
-
const
|
|
12482
|
+
const baseDir2 = dirname3(resolveSessionPath(exportPath));
|
|
12370
12483
|
const contents = [];
|
|
12371
12484
|
for (const sourcePath of sourcePaths) {
|
|
12372
|
-
const content = readIdentitySourcePath(sourcePath,
|
|
12485
|
+
const content = readIdentitySourcePath(sourcePath, baseDir2, sourceId);
|
|
12373
12486
|
if (content !== undefined)
|
|
12374
12487
|
contents.push({ path: sourcePath.path, content });
|
|
12375
12488
|
}
|
|
@@ -12382,9 +12495,9 @@ ${item.content.trimEnd()}`).join(`
|
|
|
12382
12495
|
|
|
12383
12496
|
`));
|
|
12384
12497
|
}
|
|
12385
|
-
function readIdentitySourcePath(sourcePath,
|
|
12386
|
-
const resolvedPath = resolveIdentitySourcePath(sourcePath.path,
|
|
12387
|
-
if (!
|
|
12498
|
+
function readIdentitySourcePath(sourcePath, baseDir2, sourceId) {
|
|
12499
|
+
const resolvedPath = resolveIdentitySourcePath(sourcePath.path, baseDir2, sourceId);
|
|
12500
|
+
if (!existsSync7(resolvedPath)) {
|
|
12388
12501
|
if (sourcePath.required) {
|
|
12389
12502
|
throw new Error(`Required identity instruction source path not found for ${sourceId}: ${sourcePath.path}`);
|
|
12390
12503
|
}
|
|
@@ -12394,27 +12507,27 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
12394
12507
|
if (!stat.isFile()) {
|
|
12395
12508
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
12396
12509
|
}
|
|
12397
|
-
const realBase = realpathSync2(
|
|
12510
|
+
const realBase = realpathSync2(baseDir2);
|
|
12398
12511
|
const realPath = realpathSync2(resolvedPath);
|
|
12399
12512
|
if (!pathIsInside(realPath, realBase)) {
|
|
12400
12513
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${sourcePath.path}`);
|
|
12401
12514
|
}
|
|
12402
12515
|
return readFileSync4(realPath, "utf-8");
|
|
12403
12516
|
}
|
|
12404
|
-
function resolveIdentitySourcePath(path,
|
|
12517
|
+
function resolveIdentitySourcePath(path, baseDir2, sourceId) {
|
|
12405
12518
|
const cleaned = cleanSessionPathInput(path);
|
|
12406
12519
|
if (!cleaned)
|
|
12407
12520
|
throw new Error(`Identity instruction source path cannot be empty for ${sourceId}.`);
|
|
12408
12521
|
if (cleaned.includes("\\"))
|
|
12409
12522
|
throw new Error(`Identity instruction source path must use POSIX separators for ${sourceId}: ${path}`);
|
|
12410
|
-
const resolvedPath = isAbsolute3(cleaned) ?
|
|
12411
|
-
if (!pathIsInside(resolvedPath,
|
|
12523
|
+
const resolvedPath = isAbsolute3(cleaned) ? resolve7(cleaned) : resolve7(baseDir2, cleaned);
|
|
12524
|
+
if (!pathIsInside(resolvedPath, resolve7(baseDir2))) {
|
|
12412
12525
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${path}`);
|
|
12413
12526
|
}
|
|
12414
12527
|
return resolvedPath;
|
|
12415
12528
|
}
|
|
12416
|
-
function pathIsInside(path,
|
|
12417
|
-
const rel = relative2(
|
|
12529
|
+
function pathIsInside(path, baseDir2) {
|
|
12530
|
+
const rel = relative2(baseDir2, path);
|
|
12418
12531
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
12419
12532
|
}
|
|
12420
12533
|
function providerTargetsTool(targets, tool) {
|
|
@@ -14074,8 +14187,8 @@ var init_config_store = __esm(() => {
|
|
|
14074
14187
|
});
|
|
14075
14188
|
|
|
14076
14189
|
// src/lib/session-render-ownership.ts
|
|
14077
|
-
import { existsSync as
|
|
14078
|
-
import { dirname as dirname4, join as
|
|
14190
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
14191
|
+
import { dirname as dirname4, join as join10, parse as parse3, relative as relative3, sep } from "path";
|
|
14079
14192
|
function toSegments(absolutePath2) {
|
|
14080
14193
|
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
14081
14194
|
}
|
|
@@ -14094,7 +14207,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
|
14094
14207
|
function readManifestRelativePaths(manifestPath) {
|
|
14095
14208
|
let stats;
|
|
14096
14209
|
try {
|
|
14097
|
-
if (!
|
|
14210
|
+
if (!existsSync8(manifestPath))
|
|
14098
14211
|
return null;
|
|
14099
14212
|
stats = statSync4(manifestPath);
|
|
14100
14213
|
} catch {
|
|
@@ -14123,7 +14236,7 @@ function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
|
14123
14236
|
const root = parse3(absolutePath2).root;
|
|
14124
14237
|
let home = dirname4(absolutePath2);
|
|
14125
14238
|
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
14126
|
-
const manifestPath =
|
|
14239
|
+
const manifestPath = join10(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
14127
14240
|
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
14128
14241
|
if (relativePaths) {
|
|
14129
14242
|
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
@@ -14158,17 +14271,17 @@ __export(exports_apply, {
|
|
|
14158
14271
|
applyConfigs: () => applyConfigs,
|
|
14159
14272
|
applyConfig: () => applyConfig
|
|
14160
14273
|
});
|
|
14161
|
-
import { existsSync as
|
|
14162
|
-
import { basename as basename5, dirname as dirname5, join as
|
|
14163
|
-
import { homedir as
|
|
14274
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
14275
|
+
import { basename as basename5, dirname as dirname5, join as join11, resolve as resolve8 } from "path";
|
|
14276
|
+
import { homedir as homedir8 } from "os";
|
|
14164
14277
|
function getConfigHome() {
|
|
14165
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
14278
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir8();
|
|
14166
14279
|
}
|
|
14167
14280
|
function expandPath(p) {
|
|
14168
14281
|
if (p.startsWith("~/")) {
|
|
14169
|
-
return
|
|
14282
|
+
return resolve8(getConfigHome(), p.slice(2));
|
|
14170
14283
|
}
|
|
14171
|
-
return
|
|
14284
|
+
return resolve8(p);
|
|
14172
14285
|
}
|
|
14173
14286
|
function normalizeTargetPath(p) {
|
|
14174
14287
|
const expanded = expandPath(p);
|
|
@@ -14178,9 +14291,9 @@ function normalizeTargetPath(p) {
|
|
|
14178
14291
|
let current = expanded;
|
|
14179
14292
|
const missingSegments = [];
|
|
14180
14293
|
while (true) {
|
|
14181
|
-
if (
|
|
14294
|
+
if (existsSync9(current)) {
|
|
14182
14295
|
try {
|
|
14183
|
-
return
|
|
14296
|
+
return resolve8(realpathSync3(current), ...missingSegments);
|
|
14184
14297
|
} catch {
|
|
14185
14298
|
return expanded;
|
|
14186
14299
|
}
|
|
@@ -14209,11 +14322,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
14209
14322
|
}
|
|
14210
14323
|
const path = expandPath(renderedTargetPath);
|
|
14211
14324
|
const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
|
|
14212
|
-
const previousContent =
|
|
14325
|
+
const previousContent = existsSync9(path) ? readFileSync6(path, "utf-8") : null;
|
|
14213
14326
|
const changed = previousContent !== renderedForTarget;
|
|
14214
14327
|
if (!opts.dryRun) {
|
|
14215
14328
|
const dir = dirname5(path);
|
|
14216
|
-
if (!
|
|
14329
|
+
if (!existsSync9(dir)) {
|
|
14217
14330
|
mkdirSync3(dir, { recursive: true });
|
|
14218
14331
|
}
|
|
14219
14332
|
if (previousContent !== null && changed) {
|
|
@@ -14247,7 +14360,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
|
14247
14360
|
let current;
|
|
14248
14361
|
try {
|
|
14249
14362
|
const path = expandPath(targetPath);
|
|
14250
|
-
if (!
|
|
14363
|
+
if (!existsSync9(path))
|
|
14251
14364
|
return [];
|
|
14252
14365
|
current = readFileSync6(path, "utf-8");
|
|
14253
14366
|
} catch {
|
|
@@ -14573,7 +14686,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
|
14573
14686
|
getConfigHome(),
|
|
14574
14687
|
opts.vars?.["HOME_DIR"]
|
|
14575
14688
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
14576
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
14689
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join11(home, ...relativePath.split("/"))))))
|
|
14577
14690
|
return true;
|
|
14578
14691
|
return sessionRenderOwnsPath(normalized);
|
|
14579
14692
|
}
|
|
@@ -14591,20 +14704,20 @@ var init_apply = __esm(() => {
|
|
|
14591
14704
|
});
|
|
14592
14705
|
|
|
14593
14706
|
// src/lib/sync-dir.ts
|
|
14594
|
-
import { existsSync as
|
|
14595
|
-
import { join as
|
|
14596
|
-
import { homedir as
|
|
14707
|
+
import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
14708
|
+
import { join as join12, relative as relative4 } from "path";
|
|
14709
|
+
import { homedir as homedir9 } from "os";
|
|
14597
14710
|
function shouldSkip(p) {
|
|
14598
14711
|
return SKIP.some((s) => p.includes(s));
|
|
14599
14712
|
}
|
|
14600
14713
|
async function syncFromDir(dir, opts = {}) {
|
|
14601
14714
|
const store = opts.store ?? resolveConfigStore();
|
|
14602
14715
|
const absDir = expandPath(dir);
|
|
14603
|
-
if (!
|
|
14716
|
+
if (!existsSync10(absDir))
|
|
14604
14717
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
14605
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) =>
|
|
14718
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join12(absDir, f)).filter((f) => statSync5(f).isFile());
|
|
14606
14719
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
14607
|
-
const home =
|
|
14720
|
+
const home = homedir9();
|
|
14608
14721
|
const allConfigs = await store.listConfigs();
|
|
14609
14722
|
for (const file of files) {
|
|
14610
14723
|
if (shouldSkip(file)) {
|
|
@@ -14639,7 +14752,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14639
14752
|
}
|
|
14640
14753
|
async function syncToDir(dir, opts = {}) {
|
|
14641
14754
|
const store = opts.store ?? resolveConfigStore();
|
|
14642
|
-
const home =
|
|
14755
|
+
const home = homedir9();
|
|
14643
14756
|
const absDir = expandPath(dir);
|
|
14644
14757
|
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
14645
14758
|
const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
@@ -14663,7 +14776,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
14663
14776
|
}
|
|
14664
14777
|
function walkDir(dir, files = []) {
|
|
14665
14778
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
14666
|
-
const full =
|
|
14779
|
+
const full = join12(dir, entry.name);
|
|
14667
14780
|
if (shouldSkip(full))
|
|
14668
14781
|
continue;
|
|
14669
14782
|
if (entry.isDirectory())
|
|
@@ -14698,8 +14811,8 @@ __export(exports_sync, {
|
|
|
14698
14811
|
KNOWN_CONFIGS: () => KNOWN_CONFIGS,
|
|
14699
14812
|
CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
|
|
14700
14813
|
});
|
|
14701
|
-
import { existsSync as
|
|
14702
|
-
import { basename as basename6, extname as extname3, join as
|
|
14814
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
|
|
14815
|
+
import { basename as basename6, extname as extname3, join as join13 } from "path";
|
|
14703
14816
|
function claudeRuleOutputs(fileName) {
|
|
14704
14817
|
const stem = basename6(fileName, extname3(fileName));
|
|
14705
14818
|
return [
|
|
@@ -14738,7 +14851,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
14738
14851
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
14739
14852
|
}
|
|
14740
14853
|
function hasClaudePromptSource() {
|
|
14741
|
-
return
|
|
14854
|
+
return existsSync11(expandPath("~/.claude/CLAUDE.md"));
|
|
14742
14855
|
}
|
|
14743
14856
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
14744
14857
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -14746,7 +14859,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
14746
14859
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
14747
14860
|
return false;
|
|
14748
14861
|
const stem = basename6(absoluteTargetPath, ".mdc");
|
|
14749
|
-
return
|
|
14862
|
+
return existsSync11(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync11(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
14750
14863
|
}
|
|
14751
14864
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
14752
14865
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -14763,8 +14876,8 @@ async function syncProject(opts) {
|
|
|
14763
14876
|
const allConfigs = await store.listConfigs();
|
|
14764
14877
|
const machine = detectMachineContext();
|
|
14765
14878
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
14766
|
-
const abs =
|
|
14767
|
-
if (!
|
|
14879
|
+
const abs = join13(absDir, pf.file);
|
|
14880
|
+
if (!existsSync11(abs))
|
|
14768
14881
|
continue;
|
|
14769
14882
|
try {
|
|
14770
14883
|
const rawContent = readFileSync8(abs, "utf-8");
|
|
@@ -14796,19 +14909,19 @@ async function syncProject(opts) {
|
|
|
14796
14909
|
}
|
|
14797
14910
|
}
|
|
14798
14911
|
for (const ruleDir of [
|
|
14799
|
-
{ dir:
|
|
14800
|
-
{ dir:
|
|
14801
|
-
{ dir:
|
|
14802
|
-
{ dir:
|
|
14803
|
-
{ dir:
|
|
14804
|
-
{ dir:
|
|
14805
|
-
{ dir:
|
|
14912
|
+
{ dir: join13(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
14913
|
+
{ dir: join13(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
|
|
14914
|
+
{ dir: join13(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
|
|
14915
|
+
{ dir: join13(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
|
|
14916
|
+
{ dir: join13(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
|
|
14917
|
+
{ dir: join13(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
|
|
14918
|
+
{ dir: join13(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
|
|
14806
14919
|
]) {
|
|
14807
|
-
if (!
|
|
14920
|
+
if (!existsSync11(ruleDir.dir))
|
|
14808
14921
|
continue;
|
|
14809
14922
|
const mdFiles = readdirSync2(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
14810
14923
|
for (const f of mdFiles) {
|
|
14811
|
-
const abs =
|
|
14924
|
+
const abs = join13(ruleDir.dir, f);
|
|
14812
14925
|
const raw = readFileSync8(abs, "utf-8");
|
|
14813
14926
|
const redacted = redactContent(raw, "markdown");
|
|
14814
14927
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
@@ -14848,14 +14961,14 @@ async function syncKnown(opts = {}) {
|
|
|
14848
14961
|
for (const known of targets) {
|
|
14849
14962
|
if (known.rulesDir) {
|
|
14850
14963
|
const absDir = expandPath(known.rulesDir);
|
|
14851
|
-
if (!
|
|
14964
|
+
if (!existsSync11(absDir)) {
|
|
14852
14965
|
result.skipped.push(known.rulesDir);
|
|
14853
14966
|
continue;
|
|
14854
14967
|
}
|
|
14855
14968
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
14856
14969
|
const ruleFiles = readdirSync2(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
14857
14970
|
for (const f of ruleFiles) {
|
|
14858
|
-
const abs2 =
|
|
14971
|
+
const abs2 = join13(absDir, f);
|
|
14859
14972
|
const targetPath = abs2.replace(home, "~");
|
|
14860
14973
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
14861
14974
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
@@ -14889,7 +15002,7 @@ async function syncKnown(opts = {}) {
|
|
|
14889
15002
|
continue;
|
|
14890
15003
|
}
|
|
14891
15004
|
const abs = expandPath(known.path);
|
|
14892
|
-
if (!
|
|
15005
|
+
if (!existsSync11(abs)) {
|
|
14893
15006
|
result.skipped.push(known.path);
|
|
14894
15007
|
continue;
|
|
14895
15008
|
}
|
|
@@ -14995,7 +15108,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
|
14995
15108
|
}
|
|
14996
15109
|
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
14997
15110
|
const path = expandPath(targetPath);
|
|
14998
|
-
if (!
|
|
15111
|
+
if (!existsSync11(path))
|
|
14999
15112
|
return `(file not found on disk: ${path})`;
|
|
15000
15113
|
const diskContent = readFileSync8(path, "utf-8");
|
|
15001
15114
|
if (diskContent === expectedContent)
|
|
@@ -15230,16 +15343,16 @@ __export(exports_package_manager_guard, {
|
|
|
15230
15343
|
scanPackageManagerSecrets: () => scanPackageManagerSecrets
|
|
15231
15344
|
});
|
|
15232
15345
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15233
|
-
import { existsSync as
|
|
15234
|
-
import { homedir as
|
|
15235
|
-
import { basename as basename7, dirname as dirname10, isAbsolute as isAbsolute5, join as
|
|
15346
|
+
import { existsSync as existsSync20, lstatSync as lstatSync7, readdirSync as readdirSync5, readFileSync as readFileSync16 } from "fs";
|
|
15347
|
+
import { homedir as homedir12 } from "os";
|
|
15348
|
+
import { basename as basename7, dirname as dirname10, isAbsolute as isAbsolute5, join as join21, relative as relative7, resolve as resolve13 } from "path";
|
|
15236
15349
|
function scanPackageManagerSecrets(options = {}) {
|
|
15237
15350
|
const cwd = options.cwd ? resolve13(options.cwd) : process.cwd();
|
|
15238
15351
|
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve13(cwd, root));
|
|
15239
15352
|
const findings = [];
|
|
15240
15353
|
let scannedFiles = 0;
|
|
15241
15354
|
for (const root of roots) {
|
|
15242
|
-
if (!
|
|
15355
|
+
if (!existsSync20(root))
|
|
15243
15356
|
continue;
|
|
15244
15357
|
const stat = lstatSync7(root);
|
|
15245
15358
|
if (stat.isFile()) {
|
|
@@ -15266,10 +15379,10 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15266
15379
|
}
|
|
15267
15380
|
}
|
|
15268
15381
|
if (options.includeHome) {
|
|
15269
|
-
const home =
|
|
15382
|
+
const home = homedir12();
|
|
15270
15383
|
for (const name of HOME_FILES) {
|
|
15271
|
-
const file =
|
|
15272
|
-
if (!
|
|
15384
|
+
const file = join21(home, name);
|
|
15385
|
+
if (!existsSync20(file))
|
|
15273
15386
|
continue;
|
|
15274
15387
|
const text = readTextFile(file);
|
|
15275
15388
|
if (text === null)
|
|
@@ -15293,12 +15406,12 @@ function collectRepoFiles(root) {
|
|
|
15293
15406
|
if (entry.isDirectory()) {
|
|
15294
15407
|
if (SKIP_DIRS.has(entry.name))
|
|
15295
15408
|
continue;
|
|
15296
|
-
visit(
|
|
15409
|
+
visit(join21(dir, entry.name));
|
|
15297
15410
|
continue;
|
|
15298
15411
|
}
|
|
15299
15412
|
if (!entry.isFile())
|
|
15300
15413
|
continue;
|
|
15301
|
-
const file =
|
|
15414
|
+
const file = join21(dir, entry.name);
|
|
15302
15415
|
if (shouldScanRepoFile(file))
|
|
15303
15416
|
out.push(file);
|
|
15304
15417
|
}
|
|
@@ -15566,7 +15679,7 @@ function stripInlineComment(value) {
|
|
|
15566
15679
|
return value.replace(/\s[#;].*$/, "").trim();
|
|
15567
15680
|
}
|
|
15568
15681
|
function displayPath(file, root) {
|
|
15569
|
-
const home =
|
|
15682
|
+
const home = homedir12();
|
|
15570
15683
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
15571
15684
|
return "~/" + toPosix(relative7(home, file));
|
|
15572
15685
|
if (isAbsolute5(root) && file.startsWith(root + "/"))
|
|
@@ -17169,9 +17282,9 @@ var {
|
|
|
17169
17282
|
// src/cli/index.tsx
|
|
17170
17283
|
init_apply();
|
|
17171
17284
|
import chalk from "chalk";
|
|
17172
|
-
import { existsSync as
|
|
17173
|
-
import { homedir as
|
|
17174
|
-
import { basename as basename8, join as
|
|
17285
|
+
import { existsSync as existsSync21, lstatSync as lstatSync8, readFileSync as readFileSync17, readSync, writeSync } from "fs";
|
|
17286
|
+
import { homedir as homedir13 } from "os";
|
|
17287
|
+
import { basename as basename8, join as join22, resolve as resolve14 } from "path";
|
|
17175
17288
|
|
|
17176
17289
|
// src/lib/config-target-identity.ts
|
|
17177
17290
|
init_apply();
|
|
@@ -17217,15 +17330,15 @@ init_redact();
|
|
|
17217
17330
|
|
|
17218
17331
|
// src/lib/export.ts
|
|
17219
17332
|
init_config_store();
|
|
17220
|
-
import { existsSync as
|
|
17221
|
-
import { join as
|
|
17333
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
17334
|
+
import { join as join14, resolve as resolve9 } from "path";
|
|
17222
17335
|
import { tmpdir } from "os";
|
|
17223
17336
|
async function exportConfigs(outputPath, opts = {}) {
|
|
17224
17337
|
const store = opts.store ?? resolveConfigStore();
|
|
17225
17338
|
const configs = await store.listConfigs(opts.filter);
|
|
17226
|
-
const absOutput =
|
|
17227
|
-
const tmpDir =
|
|
17228
|
-
const contentsDir =
|
|
17339
|
+
const absOutput = resolve9(outputPath);
|
|
17340
|
+
const tmpDir = join14(tmpdir(), `configs-export-${Date.now()}`);
|
|
17341
|
+
const contentsDir = join14(tmpDir, "contents");
|
|
17229
17342
|
try {
|
|
17230
17343
|
mkdirSync4(contentsDir, { recursive: true });
|
|
17231
17344
|
const manifest = {
|
|
@@ -17233,10 +17346,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17233
17346
|
exported_at: new Date().toISOString(),
|
|
17234
17347
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
17235
17348
|
};
|
|
17236
|
-
writeFileSync3(
|
|
17349
|
+
writeFileSync3(join14(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
17237
17350
|
for (const config of configs) {
|
|
17238
17351
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
17239
|
-
writeFileSync3(
|
|
17352
|
+
writeFileSync3(join14(contentsDir, fileName), config.content, "utf-8");
|
|
17240
17353
|
}
|
|
17241
17354
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
17242
17355
|
stdout: "pipe",
|
|
@@ -17249,7 +17362,7 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17249
17362
|
}
|
|
17250
17363
|
return { path: absOutput, count: configs.length };
|
|
17251
17364
|
} finally {
|
|
17252
|
-
if (
|
|
17365
|
+
if (existsSync12(tmpDir)) {
|
|
17253
17366
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
17254
17367
|
}
|
|
17255
17368
|
}
|
|
@@ -17257,14 +17370,14 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17257
17370
|
|
|
17258
17371
|
// src/lib/import.ts
|
|
17259
17372
|
init_config_store();
|
|
17260
|
-
import { existsSync as
|
|
17261
|
-
import { join as
|
|
17373
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync4 } from "fs";
|
|
17374
|
+
import { join as join15, resolve as resolve10 } from "path";
|
|
17262
17375
|
import { tmpdir as tmpdir2 } from "os";
|
|
17263
17376
|
async function importConfigs(bundlePath, opts = {}) {
|
|
17264
17377
|
const store = opts.store ?? resolveConfigStore();
|
|
17265
17378
|
const conflict = opts.conflict ?? "skip";
|
|
17266
|
-
const absPath =
|
|
17267
|
-
const tmpDir =
|
|
17379
|
+
const absPath = resolve10(bundlePath);
|
|
17380
|
+
const tmpDir = join15(tmpdir2(), `configs-import-${Date.now()}`);
|
|
17268
17381
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
17269
17382
|
try {
|
|
17270
17383
|
mkdirSync5(tmpDir, { recursive: true });
|
|
@@ -17277,15 +17390,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
17277
17390
|
const stderr = await new Response(proc.stderr).text();
|
|
17278
17391
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
17279
17392
|
}
|
|
17280
|
-
const manifestPath =
|
|
17281
|
-
if (!
|
|
17393
|
+
const manifestPath = join15(tmpDir, "manifest.json");
|
|
17394
|
+
if (!existsSync13(manifestPath))
|
|
17282
17395
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
17283
17396
|
const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
|
|
17284
17397
|
for (const meta of manifest.configs) {
|
|
17285
17398
|
try {
|
|
17286
17399
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
17287
|
-
const contentFile =
|
|
17288
|
-
const content =
|
|
17400
|
+
const contentFile = join15(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
17401
|
+
const content = existsSync13(contentFile) ? readFileSync9(contentFile, "utf-8") : "";
|
|
17289
17402
|
let existing = null;
|
|
17290
17403
|
try {
|
|
17291
17404
|
existing = await store.getConfig(meta.slug);
|
|
@@ -17319,7 +17432,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
17319
17432
|
}
|
|
17320
17433
|
return result;
|
|
17321
17434
|
} finally {
|
|
17322
|
-
if (
|
|
17435
|
+
if (existsSync13(tmpDir)) {
|
|
17323
17436
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
17324
17437
|
}
|
|
17325
17438
|
}
|
|
@@ -17336,14 +17449,14 @@ init_cursor_authority();
|
|
|
17336
17449
|
init_session_authority();
|
|
17337
17450
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
|
|
17338
17451
|
import {
|
|
17339
|
-
existsSync as
|
|
17452
|
+
existsSync as existsSync14,
|
|
17340
17453
|
lstatSync as lstatSync4,
|
|
17341
17454
|
mkdirSync as mkdirSync6,
|
|
17342
17455
|
readFileSync as readFileSync10,
|
|
17343
17456
|
readdirSync as readdirSync3,
|
|
17344
17457
|
statSync as statSync6
|
|
17345
17458
|
} from "fs";
|
|
17346
|
-
import { dirname as dirname6, isAbsolute as isAbsolute4, join as
|
|
17459
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join16, parse as parse4, relative as relative5, resolve as resolve11 } from "path";
|
|
17347
17460
|
|
|
17348
17461
|
class SessionApplyError extends Error {
|
|
17349
17462
|
constructor(message) {
|
|
@@ -17475,13 +17588,13 @@ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthoritie
|
|
|
17475
17588
|
throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
|
|
17476
17589
|
}
|
|
17477
17590
|
function ensureSessionTargetHome(targetHome) {
|
|
17478
|
-
if (!
|
|
17591
|
+
if (!existsSync14(targetHome))
|
|
17479
17592
|
mkdirSync6(targetHome, { recursive: true, mode: 448 });
|
|
17480
17593
|
assertSafeTargetHome(targetHome);
|
|
17481
17594
|
}
|
|
17482
17595
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
17483
17596
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
17484
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome,
|
|
17597
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve11(manifestPath)), safeTargetHome) : resolve11(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
17485
17598
|
const checkedAt = new Date().toISOString();
|
|
17486
17599
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
17487
17600
|
if (!previousManifest) {
|
|
@@ -17498,7 +17611,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
17498
17611
|
const drifted = [];
|
|
17499
17612
|
for (const file of previousManifest.files) {
|
|
17500
17613
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
17501
|
-
if (!
|
|
17614
|
+
if (!existsSync14(target)) {
|
|
17502
17615
|
missing.push({
|
|
17503
17616
|
path: target,
|
|
17504
17617
|
relativePath: file.relativePath,
|
|
@@ -17531,7 +17644,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
17531
17644
|
function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
17532
17645
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
17533
17646
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
17534
|
-
const resolvedSnapshotPath =
|
|
17647
|
+
const resolvedSnapshotPath = resolve11(snapshotPath);
|
|
17535
17648
|
const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
|
|
17536
17649
|
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute4(snapshotRelativePath)) {
|
|
17537
17650
|
throw new SessionApplyError("Session snapshot must be stored inside its target home.");
|
|
@@ -17649,8 +17762,8 @@ function requiredRestoreHash(file) {
|
|
|
17649
17762
|
return file.previousSha256;
|
|
17650
17763
|
}
|
|
17651
17764
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
17652
|
-
const resolved =
|
|
17653
|
-
if (!
|
|
17765
|
+
const resolved = resolve11(snapshotPath);
|
|
17766
|
+
if (!existsSync14(resolved))
|
|
17654
17767
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
17655
17768
|
const stat = lstatSync4(resolved);
|
|
17656
17769
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -17730,7 +17843,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
17730
17843
|
}
|
|
17731
17844
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
17732
17845
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
17733
|
-
const manifestPath =
|
|
17846
|
+
const manifestPath = resolve11(snapshot.manifestPath);
|
|
17734
17847
|
const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
|
|
17735
17848
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
17736
17849
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
@@ -17747,7 +17860,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
17747
17860
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is invalid: ${snapshotPath}`);
|
|
17748
17861
|
}
|
|
17749
17862
|
const appliedManifest = parsedManifest;
|
|
17750
|
-
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" ||
|
|
17863
|
+
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve11(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
|
|
17751
17864
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest does not match its snapshot: ${snapshotPath}`);
|
|
17752
17865
|
}
|
|
17753
17866
|
const afterFiles = [];
|
|
@@ -17832,8 +17945,8 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
17832
17945
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
17833
17946
|
}
|
|
17834
17947
|
for (const entry of readdirSync3(dirname6(snapshotPath))) {
|
|
17835
|
-
const candidatePath =
|
|
17836
|
-
if (candidatePath ===
|
|
17948
|
+
const candidatePath = resolve11(dirname6(snapshotPath), entry);
|
|
17949
|
+
if (candidatePath === resolve11(snapshotPath) || !entry.endsWith(".json"))
|
|
17837
17950
|
continue;
|
|
17838
17951
|
const candidateStat = lstatSync4(candidatePath);
|
|
17839
17952
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
@@ -17841,7 +17954,7 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
17841
17954
|
try {
|
|
17842
17955
|
const candidate = JSON.parse(readFileSync10(candidatePath, "utf8"));
|
|
17843
17956
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
17844
|
-
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" &&
|
|
17957
|
+
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve11(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
|
|
17845
17958
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
17846
17959
|
}
|
|
17847
17960
|
} catch (error) {
|
|
@@ -17910,14 +18023,14 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
17910
18023
|
}
|
|
17911
18024
|
function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
17912
18025
|
const path = resolveManifestRelativePath(relativePath, targetHome);
|
|
17913
|
-
if (
|
|
18026
|
+
if (resolve11(recordedPath) !== path) {
|
|
17914
18027
|
throw new SessionApplyError(`Session snapshot file path mismatch for ${relativePath}`);
|
|
17915
18028
|
}
|
|
17916
18029
|
return path;
|
|
17917
18030
|
}
|
|
17918
18031
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
17919
18032
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
17920
|
-
const previousContent =
|
|
18033
|
+
const previousContent = existsSync14(target) ? readFileSync10(target, "utf-8") : null;
|
|
17921
18034
|
const previousSha256 = previousContent === null ? null : sha2568(previousContent);
|
|
17922
18035
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
17923
18036
|
const changed = previousContent !== file.content;
|
|
@@ -18016,7 +18129,7 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
18016
18129
|
}
|
|
18017
18130
|
function planStaleFileResult(file, targetHome, options) {
|
|
18018
18131
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
18019
|
-
if (!
|
|
18132
|
+
if (!existsSync14(target))
|
|
18020
18133
|
return null;
|
|
18021
18134
|
const previousContent = readFileSync10(target, "utf-8");
|
|
18022
18135
|
const previousSha256 = sha2568(previousContent);
|
|
@@ -18063,19 +18176,19 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
18063
18176
|
return previousHashes.get(file.relativePath) === previousSha256;
|
|
18064
18177
|
}
|
|
18065
18178
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
18066
|
-
const target =
|
|
18179
|
+
const target = resolve11(targetHome, ...file.relativePath.split("/"));
|
|
18067
18180
|
const rel = relative5(targetHome, target);
|
|
18068
18181
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
18069
18182
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
18070
18183
|
}
|
|
18071
|
-
if (
|
|
18184
|
+
if (resolve11(file.path) !== target) {
|
|
18072
18185
|
throw new SessionApplyError(`Session file path mismatch for ${file.relativePath}: ${file.path}`);
|
|
18073
18186
|
}
|
|
18074
18187
|
assertNoSymlinkSegments2(targetHome, target);
|
|
18075
18188
|
return target;
|
|
18076
18189
|
}
|
|
18077
18190
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
18078
|
-
const target =
|
|
18191
|
+
const target = resolve11(targetHome, ...relativePath.split(/[\\/]+/));
|
|
18079
18192
|
const rel = relative5(targetHome, target);
|
|
18080
18193
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
18081
18194
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
@@ -18084,7 +18197,7 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
18084
18197
|
return target;
|
|
18085
18198
|
}
|
|
18086
18199
|
function readPreviousManifest(path) {
|
|
18087
|
-
if (!
|
|
18200
|
+
if (!existsSync14(path))
|
|
18088
18201
|
return null;
|
|
18089
18202
|
try {
|
|
18090
18203
|
const parsed = JSON.parse(readFileSync10(path, "utf-8"));
|
|
@@ -18126,7 +18239,7 @@ function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
|
18126
18239
|
}
|
|
18127
18240
|
function currentSessionFileHash(path, targetHome) {
|
|
18128
18241
|
assertNoSymlinkSegments2(targetHome, path);
|
|
18129
|
-
if (!
|
|
18242
|
+
if (!existsSync14(path))
|
|
18130
18243
|
return null;
|
|
18131
18244
|
const stat = lstatSync4(path);
|
|
18132
18245
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -18141,7 +18254,7 @@ function requiredPreviousHash(result) {
|
|
|
18141
18254
|
return result.previousSha256;
|
|
18142
18255
|
}
|
|
18143
18256
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
18144
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) =>
|
|
18257
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync14(result.path)).map((result) => {
|
|
18145
18258
|
const content = readFileSync10(result.path, "utf-8");
|
|
18146
18259
|
return {
|
|
18147
18260
|
path: result.path,
|
|
@@ -18160,7 +18273,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18160
18273
|
};
|
|
18161
18274
|
}
|
|
18162
18275
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
18163
|
-
const snapshotPath =
|
|
18276
|
+
const snapshotPath = resolve11(targetHome, ".hasna", "session-render-snapshots", `${timestamp}-${randomUUID7()}.json`);
|
|
18164
18277
|
const afterFiles = results.map((result) => {
|
|
18165
18278
|
if (result.action === "conflict") {
|
|
18166
18279
|
throw new SessionApplyError(`Cannot snapshot unresolved conflict: ${result.relativePath}`);
|
|
@@ -18208,12 +18321,12 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18208
18321
|
function assertSafeTargetHome(targetHome) {
|
|
18209
18322
|
if (!isAbsolute4(targetHome))
|
|
18210
18323
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
18211
|
-
const normalized =
|
|
18324
|
+
const normalized = resolve11(targetHome);
|
|
18212
18325
|
if (normalized === parse4(normalized).root) {
|
|
18213
18326
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
18214
18327
|
}
|
|
18215
18328
|
assertNoSymlinkAncestors2(normalized);
|
|
18216
|
-
if (
|
|
18329
|
+
if (existsSync14(normalized) && lstatSync4(normalized).isSymbolicLink()) {
|
|
18217
18330
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
18218
18331
|
}
|
|
18219
18332
|
return normalized;
|
|
@@ -18223,20 +18336,20 @@ function assertNoSymlinkSegments2(root, target) {
|
|
|
18223
18336
|
const rel = relative5(root, target);
|
|
18224
18337
|
let current = root;
|
|
18225
18338
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18226
|
-
current =
|
|
18227
|
-
if (
|
|
18339
|
+
current = join16(current, segment);
|
|
18340
|
+
if (existsSync14(current) && lstatSync4(current).isSymbolicLink()) {
|
|
18228
18341
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
18229
18342
|
}
|
|
18230
18343
|
}
|
|
18231
18344
|
}
|
|
18232
18345
|
function assertNoSymlinkAncestors2(path) {
|
|
18233
|
-
const normalized =
|
|
18346
|
+
const normalized = resolve11(path);
|
|
18234
18347
|
const parsed = parse4(normalized);
|
|
18235
18348
|
let current = parsed.root;
|
|
18236
18349
|
const rel = relative5(parsed.root, normalized);
|
|
18237
18350
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18238
|
-
current =
|
|
18239
|
-
if (!
|
|
18351
|
+
current = join16(current, segment);
|
|
18352
|
+
if (!existsSync14(current))
|
|
18240
18353
|
return;
|
|
18241
18354
|
if (lstatSync4(current).isSymbolicLink()) {
|
|
18242
18355
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
@@ -18285,10 +18398,11 @@ function formatGlobalSourceCoverageWarnings(result) {
|
|
|
18285
18398
|
}
|
|
18286
18399
|
|
|
18287
18400
|
// src/lib/station-profile.ts
|
|
18401
|
+
init_raw_store_root();
|
|
18288
18402
|
import { spawnSync } from "child_process";
|
|
18289
|
-
import { existsSync as
|
|
18290
|
-
import { arch as osArch, homedir as
|
|
18291
|
-
import { dirname as dirname7, join as
|
|
18403
|
+
import { existsSync as existsSync15, lstatSync as lstatSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
18404
|
+
import { arch as osArch, homedir as homedir10, hostname as osHostname, platform as osPlatform, userInfo as osUserInfo } from "os";
|
|
18405
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
18292
18406
|
var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
|
|
18293
18407
|
var STATION_PROFILE_SOURCE_ID = "station-profile";
|
|
18294
18408
|
var STATION_PROFILE_LAYER = "machine";
|
|
@@ -18298,22 +18412,21 @@ var STATION_PROFILE_FULL_NAMES_MAX = 6;
|
|
|
18298
18412
|
var STATION_PROFILE_PRIMARY_SCOPE = "@hasna";
|
|
18299
18413
|
var MACHINES_MANIFEST_PATH_ENV = "HASNA_MACHINES_MANIFEST_PATH";
|
|
18300
18414
|
var BUN_INSTALL_ENV = "BUN_INSTALL";
|
|
18301
|
-
function
|
|
18302
|
-
return env["HOME"] || env["USERPROFILE"] ||
|
|
18415
|
+
function homeDir3(env = process.env) {
|
|
18416
|
+
return env["HOME"] || env["USERPROFILE"] || homedir10();
|
|
18303
18417
|
}
|
|
18304
18418
|
function getStationProfileCachePath(env = process.env) {
|
|
18305
|
-
|
|
18306
|
-
return join16(resolve11(root), STATION_PROFILE_CACHE_FILENAME);
|
|
18419
|
+
return join17(getRawStoreRoot(env), STATION_PROFILE_CACHE_FILENAME);
|
|
18307
18420
|
}
|
|
18308
18421
|
function getMachinesManifestPath(env = process.env) {
|
|
18309
|
-
return env[MACHINES_MANIFEST_PATH_ENV] ||
|
|
18422
|
+
return env[MACHINES_MANIFEST_PATH_ENV] || join17(homeDir3(env), ".hasna", "machines", "machines.json");
|
|
18310
18423
|
}
|
|
18311
18424
|
function getBunGlobalModulesDir(env = process.env) {
|
|
18312
|
-
return
|
|
18425
|
+
return join17(env[BUN_INSTALL_ENV] || join17(homeDir3(env), ".bun"), "install", "global", "node_modules");
|
|
18313
18426
|
}
|
|
18314
18427
|
function readMachinesManifest(path) {
|
|
18315
18428
|
try {
|
|
18316
|
-
if (!
|
|
18429
|
+
if (!existsSync15(path))
|
|
18317
18430
|
return null;
|
|
18318
18431
|
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
18319
18432
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -18369,9 +18482,9 @@ function probeMachineStatus(machineId) {
|
|
|
18369
18482
|
function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
18370
18483
|
const hostname2 = osHostname();
|
|
18371
18484
|
const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
|
|
18372
|
-
const home =
|
|
18485
|
+
const home = homeDir3(env);
|
|
18373
18486
|
const platform = stringField(record, "platform") ?? osPlatform();
|
|
18374
|
-
const workspacePath = stringField(record, "workspacePath") ??
|
|
18487
|
+
const workspacePath = stringField(record, "workspacePath") ?? join17(home, platform === "darwin" ? "Workspace" : "workspace");
|
|
18375
18488
|
const machine = {
|
|
18376
18489
|
id: stringField(record, "id") ?? hostname2,
|
|
18377
18490
|
hostname: stringField(record, "hostname") ?? hostname2,
|
|
@@ -18388,9 +18501,9 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
|
18388
18501
|
return machine;
|
|
18389
18502
|
}
|
|
18390
18503
|
function scopedPackageNames(modulesDir, scope) {
|
|
18391
|
-
const scopeDir =
|
|
18504
|
+
const scopeDir = join17(modulesDir, scope);
|
|
18392
18505
|
try {
|
|
18393
|
-
if (!
|
|
18506
|
+
if (!existsSync15(scopeDir))
|
|
18394
18507
|
return null;
|
|
18395
18508
|
return readdirNames(scopeDir).sort();
|
|
18396
18509
|
} catch {
|
|
@@ -18400,7 +18513,7 @@ function scopedPackageNames(modulesDir, scope) {
|
|
|
18400
18513
|
function readdirNames(dir) {
|
|
18401
18514
|
return readdirSync4(dir).filter((name) => {
|
|
18402
18515
|
try {
|
|
18403
|
-
return lstatSync5(
|
|
18516
|
+
return lstatSync5(join17(dir, name)).isDirectory();
|
|
18404
18517
|
} catch {
|
|
18405
18518
|
return false;
|
|
18406
18519
|
}
|
|
@@ -18410,7 +18523,7 @@ function resolveStationProfilePackages(env = process.env) {
|
|
|
18410
18523
|
const modulesDir = getBunGlobalModulesDir(env);
|
|
18411
18524
|
let scopeDirs;
|
|
18412
18525
|
try {
|
|
18413
|
-
if (!
|
|
18526
|
+
if (!existsSync15(modulesDir))
|
|
18414
18527
|
return null;
|
|
18415
18528
|
scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
|
|
18416
18529
|
} catch {
|
|
@@ -18485,7 +18598,7 @@ function refreshStationProfile(options = {}) {
|
|
|
18485
18598
|
const path = getStationProfileCachePath(env);
|
|
18486
18599
|
const generatedAt = new Date().toISOString();
|
|
18487
18600
|
if (!options.dryRun) {
|
|
18488
|
-
const existing =
|
|
18601
|
+
const existing = existsSync15(path) ? readFileSync11(path, "utf8") : null;
|
|
18489
18602
|
if (existing !== content) {
|
|
18490
18603
|
mkdirSync7(dirname7(path), { recursive: true });
|
|
18491
18604
|
writeFileSync4(path, content, "utf8");
|
|
@@ -18504,7 +18617,7 @@ function refreshStationProfile(options = {}) {
|
|
|
18504
18617
|
function readStationProfile(env = process.env) {
|
|
18505
18618
|
const path = getStationProfileCachePath(env);
|
|
18506
18619
|
try {
|
|
18507
|
-
if (!
|
|
18620
|
+
if (!existsSync15(path))
|
|
18508
18621
|
return null;
|
|
18509
18622
|
return readFileSync11(path, "utf8");
|
|
18510
18623
|
} catch {
|
|
@@ -18843,7 +18956,7 @@ init_codewith_shared_todos_storage_standard();
|
|
|
18843
18956
|
import { createHash as createHash9 } from "crypto";
|
|
18844
18957
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
18845
18958
|
import {
|
|
18846
|
-
existsSync as
|
|
18959
|
+
existsSync as existsSync16,
|
|
18847
18960
|
lstatSync as lstatSync6,
|
|
18848
18961
|
mkdirSync as mkdirSync8,
|
|
18849
18962
|
readFileSync as readFileSync12,
|
|
@@ -18851,8 +18964,8 @@ import {
|
|
|
18851
18964
|
rmSync as rmSync5,
|
|
18852
18965
|
writeFileSync as writeFileSync5
|
|
18853
18966
|
} from "fs";
|
|
18854
|
-
import { homedir as
|
|
18855
|
-
import { dirname as dirname8, join as
|
|
18967
|
+
import { homedir as homedir11 } from "os";
|
|
18968
|
+
import { dirname as dirname8, join as join18, parse as parse5, relative as relative6, resolve as resolve12 } from "path";
|
|
18856
18969
|
var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
|
|
18857
18970
|
var INBOX_SKILL_MARKERS = [
|
|
18858
18971
|
[".claude", "skills", "inbox", "SKILL.md"],
|
|
@@ -18878,8 +18991,8 @@ function findSymlinkedAncestor(path) {
|
|
|
18878
18991
|
let current = parsed.root;
|
|
18879
18992
|
const rel = relative6(parsed.root, normalized);
|
|
18880
18993
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18881
|
-
current =
|
|
18882
|
-
if (!
|
|
18994
|
+
current = join18(current, segment);
|
|
18995
|
+
if (!existsSync16(current))
|
|
18883
18996
|
return null;
|
|
18884
18997
|
if (lstatSync6(current).isSymbolicLink())
|
|
18885
18998
|
return current;
|
|
@@ -18896,11 +19009,11 @@ function packagedInboxSkillPath(explicitPath) {
|
|
|
18896
19009
|
if (explicitPath)
|
|
18897
19010
|
return explicitPath;
|
|
18898
19011
|
const candidates = [
|
|
18899
|
-
|
|
18900
|
-
|
|
18901
|
-
|
|
19012
|
+
join18(import.meta.dir, "..", "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
19013
|
+
join18(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
19014
|
+
join18(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
|
|
18902
19015
|
];
|
|
18903
|
-
const found = candidates.find((candidate) =>
|
|
19016
|
+
const found = candidates.find((candidate) => existsSync16(candidate));
|
|
18904
19017
|
if (!found) {
|
|
18905
19018
|
throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
|
|
18906
19019
|
}
|
|
@@ -18949,8 +19062,8 @@ function compareVersions(left, right) {
|
|
|
18949
19062
|
}
|
|
18950
19063
|
return 0;
|
|
18951
19064
|
}
|
|
18952
|
-
function inspectSkillMarkers(
|
|
18953
|
-
return INBOX_SKILL_MARKERS.map((parts) =>
|
|
19065
|
+
function inspectSkillMarkers(homeDir4) {
|
|
19066
|
+
return INBOX_SKILL_MARKERS.map((parts) => join18(homeDir4, ...parts)).map((path) => {
|
|
18954
19067
|
const stat = lstatOrNull(path);
|
|
18955
19068
|
if (!stat)
|
|
18956
19069
|
return null;
|
|
@@ -18966,9 +19079,9 @@ function inspectSkillMarkers(homeDir3) {
|
|
|
18966
19079
|
}).filter((snapshot) => snapshot !== null);
|
|
18967
19080
|
}
|
|
18968
19081
|
function inspectInbox(options) {
|
|
18969
|
-
const
|
|
19082
|
+
const homeDir4 = options.homeDir ?? homedir11();
|
|
18970
19083
|
const runtimeCommand = options.conversationsCommand ?? "conversations";
|
|
18971
|
-
const snapshots = inspectSkillMarkers(
|
|
19084
|
+
const snapshots = inspectSkillMarkers(homeDir4);
|
|
18972
19085
|
const skillPresent = snapshots.length > 0;
|
|
18973
19086
|
let canonicalContent = null;
|
|
18974
19087
|
let canonicalSha256 = null;
|
|
@@ -19243,11 +19356,11 @@ init_project_context();
|
|
|
19243
19356
|
init_config_store();
|
|
19244
19357
|
init_apply();
|
|
19245
19358
|
init_config_agents();
|
|
19246
|
-
import { existsSync as
|
|
19359
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
19247
19360
|
|
|
19248
19361
|
// src/lib/package-version.ts
|
|
19249
|
-
import { existsSync as
|
|
19250
|
-
import { dirname as dirname9, join as
|
|
19362
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
19363
|
+
import { dirname as dirname9, join as join19 } from "path";
|
|
19251
19364
|
import { fileURLToPath } from "url";
|
|
19252
19365
|
var cached = null;
|
|
19253
19366
|
function getPackageVersion() {
|
|
@@ -19256,8 +19369,8 @@ function getPackageVersion() {
|
|
|
19256
19369
|
try {
|
|
19257
19370
|
let dir = dirname9(fileURLToPath(import.meta.url));
|
|
19258
19371
|
for (let i = 0;i < 8; i++) {
|
|
19259
|
-
const pkgPath =
|
|
19260
|
-
if (
|
|
19372
|
+
const pkgPath = join19(dir, "package.json");
|
|
19373
|
+
if (existsSync17(pkgPath)) {
|
|
19261
19374
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
19262
19375
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
19263
19376
|
cached = pkg.version;
|
|
@@ -19321,7 +19434,7 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
19321
19434
|
continue;
|
|
19322
19435
|
knownTargets += 1;
|
|
19323
19436
|
const targetPath = expandPath(config.target_path);
|
|
19324
|
-
if (!
|
|
19437
|
+
if (!existsSync18(targetPath)) {
|
|
19325
19438
|
missingTargets += 1;
|
|
19326
19439
|
continue;
|
|
19327
19440
|
}
|
|
@@ -19421,8 +19534,8 @@ init_config_store();
|
|
|
19421
19534
|
|
|
19422
19535
|
// src/lib/provider-context.ts
|
|
19423
19536
|
import { createHash as createHash10 } from "crypto";
|
|
19424
|
-
import { existsSync as
|
|
19425
|
-
import { join as
|
|
19537
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
19538
|
+
import { join as join20 } from "path";
|
|
19426
19539
|
var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
|
|
19427
19540
|
var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
|
|
19428
19541
|
var PROVIDER_CONTEXT_SCHEMA = "hasna.instructions.provider-context/v1";
|
|
@@ -19567,17 +19680,17 @@ function resolveAndRenderProviderContext(opts) {
|
|
|
19567
19680
|
const recordedEndpoint = originAccepted ? `${opts.origin.host}${opts.origin.pathPrefix || ""}` : null;
|
|
19568
19681
|
const reason = entry === null && opts.rawEndpoint ? originAccepted ? `endpoint "${recordedEndpoint}" is not in the provider-context registry; using the invariant fragment` : "endpoint rejected (embedded credentials or unparseable); using the invariant fragment" : null;
|
|
19569
19682
|
const content = renderProviderFragment(entry);
|
|
19570
|
-
const dir =
|
|
19571
|
-
if (!
|
|
19683
|
+
const dir = join20(opts.homeDir, PROVIDER_CONTEXT_DIR);
|
|
19684
|
+
if (!existsSync19(dir))
|
|
19572
19685
|
mkdirSync9(dir, { recursive: true });
|
|
19573
19686
|
const filename = `${entry ? entry.key : "invariant"}.md`;
|
|
19574
|
-
const fragmentPath2 =
|
|
19687
|
+
const fragmentPath2 = join20(dir, filename);
|
|
19575
19688
|
const fragmentSha256 = sha25610(content);
|
|
19576
19689
|
writeFileSync6(fragmentPath2, content, "utf8");
|
|
19577
|
-
const manifestPath =
|
|
19690
|
+
const manifestPath = join20(dir, PROVIDER_CONTEXT_MANIFEST);
|
|
19578
19691
|
let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
|
|
19579
19692
|
try {
|
|
19580
|
-
if (
|
|
19693
|
+
if (existsSync19(manifestPath)) {
|
|
19581
19694
|
const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
19582
19695
|
if (parsed && typeof parsed === "object")
|
|
19583
19696
|
manifest = parsed;
|
|
@@ -19721,7 +19834,7 @@ function parseSessionSource(value, order) {
|
|
|
19721
19834
|
if (!path)
|
|
19722
19835
|
throw new Error(`Invalid --source "${value}" (expected path or id=path)`);
|
|
19723
19836
|
const absPath = resolveSessionPath(path);
|
|
19724
|
-
if (!
|
|
19837
|
+
if (!existsSync21(absPath))
|
|
19725
19838
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
19726
19839
|
const content = readSessionInstructionSourceFile(absPath);
|
|
19727
19840
|
const source = sourceFromFilePath(absPath, content, order);
|
|
@@ -19802,7 +19915,7 @@ async function collectSessionSources(opts, tool, store) {
|
|
|
19802
19915
|
}
|
|
19803
19916
|
for (const value of opts.identityExport ?? []) {
|
|
19804
19917
|
const path = resolveSessionPath(value);
|
|
19805
|
-
if (!
|
|
19918
|
+
if (!existsSync21(path))
|
|
19806
19919
|
throw new Error(`Identity instruction export not found: ${path}`);
|
|
19807
19920
|
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
19808
19921
|
sources.push(...sourcesFromIdentityExport(parsed, { path, tool, orderOffset: sources.length }));
|
|
@@ -19936,7 +20049,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
19936
20049
|
if (value === "-")
|
|
19937
20050
|
return { json: readBoundedProjectContextStdin() };
|
|
19938
20051
|
const path = resolveSessionPath(value);
|
|
19939
|
-
if (!
|
|
20052
|
+
if (!existsSync21(path)) {
|
|
19940
20053
|
if (allowMissing)
|
|
19941
20054
|
return {};
|
|
19942
20055
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_MISSING", `bundle file not found: ${path}`);
|
|
@@ -20108,7 +20221,7 @@ program.command("tag <id>").description("Add or remove tags on a stored config (
|
|
|
20108
20221
|
});
|
|
20109
20222
|
program.command("add <path>").description("Ingest a file into the config DB").option("-n, --name <name>", "config name (defaults to filename)").option("-c, --category <cat>", "category override").option("-a, --agent <agent>", "agent override").option("-k, --kind <kind>", "kind: file|reference", "file").option("--template", "mark as template (has {{VAR}} placeholders)").option("--update", "if a config already owns this path, update that row in place instead of refusing").action(async (filePath, opts) => {
|
|
20110
20223
|
const abs = resolve14(filePath);
|
|
20111
|
-
if (!
|
|
20224
|
+
if (!existsSync21(abs)) {
|
|
20112
20225
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
20113
20226
|
process.exit(1);
|
|
20114
20227
|
}
|
|
@@ -20116,7 +20229,7 @@ program.command("add <path>").description("Ingest a file into the config DB").op
|
|
|
20116
20229
|
const storedFmt = detectFormat(abs);
|
|
20117
20230
|
const fmt = redactFormatForTarget(abs, storedFmt);
|
|
20118
20231
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
20119
|
-
const targetPath = abs.startsWith(
|
|
20232
|
+
const targetPath = abs.startsWith(homedir13()) ? abs.replace(homedir13(), "~") : abs;
|
|
20120
20233
|
const name = opts.name || filePath.split("/").pop();
|
|
20121
20234
|
const store = resolveConfigStore();
|
|
20122
20235
|
const allConfigs = await store.listConfigs();
|
|
@@ -20291,7 +20404,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
20291
20404
|
for (const entry of entries) {
|
|
20292
20405
|
if (!entry.isDirectory())
|
|
20293
20406
|
continue;
|
|
20294
|
-
const projDir =
|
|
20407
|
+
const projDir = join22(absDir, entry.name);
|
|
20295
20408
|
const hasAgentConfig = [
|
|
20296
20409
|
"CLAUDE.md",
|
|
20297
20410
|
".mcp.json",
|
|
@@ -20304,7 +20417,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
20304
20417
|
".aicopilot",
|
|
20305
20418
|
".cursor",
|
|
20306
20419
|
".agents"
|
|
20307
|
-
].some((marker) =>
|
|
20420
|
+
].some((marker) => existsSync21(join22(projDir, marker)));
|
|
20308
20421
|
if (!hasAgentConfig)
|
|
20309
20422
|
continue;
|
|
20310
20423
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun, store });
|
|
@@ -20355,7 +20468,7 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
20355
20468
|
});
|
|
20356
20469
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
20357
20470
|
const store = resolveConfigStore();
|
|
20358
|
-
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
20471
|
+
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join22(getRawStoreRoot(), "instructions.db");
|
|
20359
20472
|
const stats = await store.getConfigStats();
|
|
20360
20473
|
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
20361
20474
|
console.log(chalk.cyan(isApiTransport() ? "API:" : "DB:") + " " + dbPath);
|
|
@@ -21234,7 +21347,7 @@ mcpCmd.command("install").alias("add").description("Install configs MCP server i
|
|
|
21234
21347
|
} else if (target === "codex") {
|
|
21235
21348
|
const { appendFileSync, existsSync: ex } = await import("fs");
|
|
21236
21349
|
const { join: j } = await import("path");
|
|
21237
|
-
const configPath = j(
|
|
21350
|
+
const configPath = j(homedir13(), ".codex", "config.toml");
|
|
21238
21351
|
const block = `
|
|
21239
21352
|
[mcp_servers.configs]
|
|
21240
21353
|
command = "${mcpBinary}"
|
|
@@ -21252,7 +21365,7 @@ args = []
|
|
|
21252
21365
|
} else if (target === "antigravity") {
|
|
21253
21366
|
const { mkdirSync: md, readFileSync: rf, writeFileSync: wf, existsSync: ex } = await import("fs");
|
|
21254
21367
|
const { dirname: dn, join: j } = await import("path");
|
|
21255
|
-
const configPath = j(
|
|
21368
|
+
const configPath = j(homedir13(), ".gemini", "config", "mcp_config.json");
|
|
21256
21369
|
let settings = {};
|
|
21257
21370
|
if (ex(configPath)) {
|
|
21258
21371
|
try {
|
|
@@ -21336,7 +21449,7 @@ DB stats:`));
|
|
|
21336
21449
|
if (count > 0)
|
|
21337
21450
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
21338
21451
|
}
|
|
21339
|
-
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
21452
|
+
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join22(getRawStoreRoot(), "instructions.db");
|
|
21340
21453
|
console.log(chalk.dim(`
|
|
21341
21454
|
${isApiTransport() ? "API" : "DB"}: ${location}`));
|
|
21342
21455
|
});
|
|
@@ -21397,10 +21510,10 @@ managedSkillsCmd.command("apply").option("--dry-run", "preview without writing")
|
|
|
21397
21510
|
});
|
|
21398
21511
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
21399
21512
|
const { mkdirSync: mk } = await import("fs");
|
|
21400
|
-
const backupDir =
|
|
21513
|
+
const backupDir = join22(getRawStoreRoot(), "backups");
|
|
21401
21514
|
mk(backupDir, { recursive: true });
|
|
21402
21515
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
21403
|
-
const outPath =
|
|
21516
|
+
const outPath = join22(backupDir, `configs-${ts}.tar.gz`);
|
|
21404
21517
|
const result = await exportConfigs(outPath, { store: resolveConfigStore() });
|
|
21405
21518
|
const { statSync: st } = await import("fs");
|
|
21406
21519
|
const size = st(outPath).size;
|
|
@@ -21428,9 +21541,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
21428
21541
|
console.log(chalk.cyan("Known files on disk:"));
|
|
21429
21542
|
for (const k of KNOWN_CONFIGS) {
|
|
21430
21543
|
if (k.rulesDir) {
|
|
21431
|
-
|
|
21544
|
+
existsSync21(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail2(`${k.rulesDir}/ not found`);
|
|
21432
21545
|
} else {
|
|
21433
|
-
|
|
21546
|
+
existsSync21(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail2(`${k.path} not found`);
|
|
21434
21547
|
}
|
|
21435
21548
|
}
|
|
21436
21549
|
const allConfigs = await store.listConfigs();
|
|
@@ -21584,16 +21697,16 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21584
21697
|
for (const k of KNOWN_CONFIGS) {
|
|
21585
21698
|
if (k.rulesDir) {
|
|
21586
21699
|
const absDir = expandPath2(k.rulesDir);
|
|
21587
|
-
if (!
|
|
21700
|
+
if (!existsSync21(absDir))
|
|
21588
21701
|
continue;
|
|
21589
21702
|
const { readdirSync: readdirSync6 } = await import("fs");
|
|
21590
21703
|
for (const f of readdirSync6(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
21591
|
-
const abs =
|
|
21704
|
+
const abs = join22(absDir, f);
|
|
21592
21705
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21593
21706
|
}
|
|
21594
21707
|
} else {
|
|
21595
21708
|
const abs = expandPath2(k.path);
|
|
21596
|
-
if (
|
|
21709
|
+
if (existsSync21(abs))
|
|
21597
21710
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21598
21711
|
}
|
|
21599
21712
|
}
|
|
@@ -21601,7 +21714,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21601
21714
|
const tick = async () => {
|
|
21602
21715
|
let changed = 0;
|
|
21603
21716
|
for (const [abs, oldMtime] of mtimes) {
|
|
21604
|
-
if (!
|
|
21717
|
+
if (!existsSync21(abs))
|
|
21605
21718
|
continue;
|
|
21606
21719
|
const newMtime = st(abs).mtimeMs;
|
|
21607
21720
|
if (newMtime !== oldMtime) {
|
|
@@ -21613,10 +21726,10 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21613
21726
|
for (const k of KNOWN_CONFIGS) {
|
|
21614
21727
|
if (k.rulesDir) {
|
|
21615
21728
|
const absDir = expandPath2(k.rulesDir);
|
|
21616
|
-
if (!
|
|
21729
|
+
if (!existsSync21(absDir))
|
|
21617
21730
|
continue;
|
|
21618
21731
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
21619
|
-
const abs =
|
|
21732
|
+
const abs = join22(absDir, f);
|
|
21620
21733
|
if (!mtimes.has(abs)) {
|
|
21621
21734
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21622
21735
|
changed++;
|
|
@@ -21624,7 +21737,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21624
21737
|
}
|
|
21625
21738
|
} else {
|
|
21626
21739
|
const abs = expandPath2(k.path);
|
|
21627
|
-
if (
|
|
21740
|
+
if (existsSync21(abs) && !mtimes.has(abs)) {
|
|
21628
21741
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21629
21742
|
changed++;
|
|
21630
21743
|
}
|
|
@@ -21652,7 +21765,7 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
21652
21765
|
if (!c.target_path)
|
|
21653
21766
|
continue;
|
|
21654
21767
|
const abs = expandPath(c.target_path);
|
|
21655
|
-
if (!
|
|
21768
|
+
if (!existsSync21(abs)) {
|
|
21656
21769
|
missing++;
|
|
21657
21770
|
continue;
|
|
21658
21771
|
}
|
|
@@ -21723,7 +21836,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
21723
21836
|
if (!c.target_path)
|
|
21724
21837
|
continue;
|
|
21725
21838
|
const abs = expandPath(c.target_path);
|
|
21726
|
-
if (!
|
|
21839
|
+
if (!existsSync21(abs)) {
|
|
21727
21840
|
if (printed < maxPrinted) {
|
|
21728
21841
|
if (opts.dryRun) {
|
|
21729
21842
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${truncateMiddle(c.target_path, 88)})`)}`);
|
|
@@ -21869,13 +21982,13 @@ providerContextCmd.command("resolve").description("Resolve the endpoint to a pro
|
|
|
21869
21982
|
try {
|
|
21870
21983
|
const rawEndpoint = opts.endpoint ?? process.env["ANTHROPIC_BASE_URL"] ?? "";
|
|
21871
21984
|
const rawModel = opts.model ?? process.env["ANTHROPIC_MODEL"] ?? "";
|
|
21872
|
-
const
|
|
21985
|
+
const homeDir4 = opts.home ?? homedir13();
|
|
21873
21986
|
const origin = normalizeEndpointOrigin(rawEndpoint);
|
|
21874
21987
|
const resolution = resolveAndRenderProviderContext({
|
|
21875
21988
|
origin,
|
|
21876
21989
|
rawEndpoint,
|
|
21877
21990
|
rawModel,
|
|
21878
|
-
homeDir:
|
|
21991
|
+
homeDir: homeDir4
|
|
21879
21992
|
});
|
|
21880
21993
|
if (opts.json) {
|
|
21881
21994
|
printJson({
|