@hasna/instructions 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +388 -328
- package/dist/index.js +349 -294
- package/dist/lib/project-context.d.ts.map +1 -1
- package/dist/lib/session-apply.d.ts.map +1 -1
- package/dist/lib/session-render-state.d.ts +48 -0
- package/dist/lib/session-render-state.d.ts.map +1 -0
- package/dist/lib/session-render-state.test.d.ts +2 -0
- package/dist/lib/session-render-state.test.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -2
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -2217,6 +2217,9 @@ function resolvePath2(kind, options) {
|
|
|
2217
2217
|
function configDir(options) {
|
|
2218
2218
|
return resolvePath2("config", options);
|
|
2219
2219
|
}
|
|
2220
|
+
function stateDir(options) {
|
|
2221
|
+
return resolvePath2("state", options);
|
|
2222
|
+
}
|
|
2220
2223
|
var KIND_ENV2, APP_SLUG_RE2;
|
|
2221
2224
|
var init_dist = __esm(() => {
|
|
2222
2225
|
KIND_ENV2 = {
|
|
@@ -7420,6 +7423,55 @@ var init_session_render_contract = __esm(() => {
|
|
|
7420
7423
|
];
|
|
7421
7424
|
});
|
|
7422
7425
|
|
|
7426
|
+
// src/lib/session-render-state.ts
|
|
7427
|
+
import { existsSync as existsSync7, readdirSync } from "fs";
|
|
7428
|
+
import { homedir as homedir7 } from "os";
|
|
7429
|
+
import { dirname, join as join9, resolve as resolve4 } from "path";
|
|
7430
|
+
function homeDir2(env = process.env) {
|
|
7431
|
+
return env["HOME"] || env["USERPROFILE"] || homedir7();
|
|
7432
|
+
}
|
|
7433
|
+
function legacySnapshotDir(targetHome) {
|
|
7434
|
+
return resolve4(join9(targetHome, ".hasna", "session-render-snapshots"));
|
|
7435
|
+
}
|
|
7436
|
+
function resolverSnapshotDir(env = process.env) {
|
|
7437
|
+
const override = env.HASNA_STATE_HOME;
|
|
7438
|
+
if (typeof override === "string" && override.trim().length > 0 && !existsSync7(override)) {
|
|
7439
|
+
return stateDir({
|
|
7440
|
+
app: SESSION_RENDER_STATE_APP,
|
|
7441
|
+
env: { ...env, HASNA_STATE_HOME: undefined },
|
|
7442
|
+
home: homeDir2(env)
|
|
7443
|
+
});
|
|
7444
|
+
}
|
|
7445
|
+
return stateDir({ app: SESSION_RENDER_STATE_APP, env, home: homeDir2(env) });
|
|
7446
|
+
}
|
|
7447
|
+
function adoptResolverSnapshotDir(resolved, env = process.env) {
|
|
7448
|
+
const override = env.HASNA_STATE_HOME;
|
|
7449
|
+
if (typeof override === "string" && override.trim().length > 0 && existsSync7(override)) {
|
|
7450
|
+
return true;
|
|
7451
|
+
}
|
|
7452
|
+
if (!existsSync7(resolved))
|
|
7453
|
+
return false;
|
|
7454
|
+
return readdirSync(resolved).some((name) => name.endsWith(".json"));
|
|
7455
|
+
}
|
|
7456
|
+
function resolveSessionRenderSnapshotLocation(targetHome, env = process.env) {
|
|
7457
|
+
const resolved = resolverSnapshotDir(env);
|
|
7458
|
+
if (!adoptResolverSnapshotDir(resolved, env)) {
|
|
7459
|
+
return { dir: legacySnapshotDir(targetHome), workspaceRoot: targetHome, adopted: false };
|
|
7460
|
+
}
|
|
7461
|
+
const resolvedPath = resolve4(resolved);
|
|
7462
|
+
return { dir: resolvedPath, workspaceRoot: dirname(resolvedPath), adopted: true };
|
|
7463
|
+
}
|
|
7464
|
+
function getSessionRenderSnapshotDir(targetHome, env = process.env) {
|
|
7465
|
+
return resolveSessionRenderSnapshotLocation(targetHome, env).dir;
|
|
7466
|
+
}
|
|
7467
|
+
function sessionRenderSnapshotWorkspaceRoot(targetHome, env = process.env) {
|
|
7468
|
+
return resolveSessionRenderSnapshotLocation(targetHome, env).workspaceRoot;
|
|
7469
|
+
}
|
|
7470
|
+
var SESSION_RENDER_STATE_APP = "instructions";
|
|
7471
|
+
var init_session_render_state = __esm(() => {
|
|
7472
|
+
init_dist();
|
|
7473
|
+
});
|
|
7474
|
+
|
|
7423
7475
|
// src/lib/project-context.ts
|
|
7424
7476
|
import { createHash as createHash2, randomUUID as randomUUID5 } from "crypto";
|
|
7425
7477
|
import { execFileSync } from "child_process";
|
|
@@ -7427,7 +7479,7 @@ import { dlopen, FFIType } from "bun:ffi";
|
|
|
7427
7479
|
import {
|
|
7428
7480
|
closeSync,
|
|
7429
7481
|
constants,
|
|
7430
|
-
existsSync as
|
|
7482
|
+
existsSync as existsSync8,
|
|
7431
7483
|
fstatSync,
|
|
7432
7484
|
fsyncSync,
|
|
7433
7485
|
lstatSync,
|
|
@@ -7440,7 +7492,7 @@ import {
|
|
|
7440
7492
|
statSync,
|
|
7441
7493
|
writeFileSync
|
|
7442
7494
|
} from "fs";
|
|
7443
|
-
import { basename, dirname, isAbsolute, join as
|
|
7495
|
+
import { basename, dirname as dirname2, isAbsolute, join as join10, parse, relative, resolve as resolve5 } from "path";
|
|
7444
7496
|
function managedObservationMaxBytes(relativePath) {
|
|
7445
7497
|
return SESSION_MANAGED_OUTPUT_PATHS.includes(relativePath) ? SESSION_MANAGED_OUTPUT_MAX_BYTES : FOREIGN_INPUT_MAX_BYTES;
|
|
7446
7498
|
}
|
|
@@ -7520,7 +7572,7 @@ function planProjectContext(input) {
|
|
|
7520
7572
|
const inlineMarkerOverhead = nativeImports ? 0 : Buffer.byteLength(buildManagedBlock(bundle, "", `
|
|
7521
7573
|
`), "utf8");
|
|
7522
7574
|
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)));
|
|
7523
|
-
const previousTargetContent =
|
|
7575
|
+
const previousTargetContent = existsSync8(paths.target) ? readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target))) : null;
|
|
7524
7576
|
const markerParse = parseManagedBlock(previousTargetContent ?? "", input.force === true);
|
|
7525
7577
|
if (markerParse.block && markerParse.block.id !== bundle.project.id) {
|
|
7526
7578
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "managed block belongs to a different project");
|
|
@@ -7571,7 +7623,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7571
7623
|
return null;
|
|
7572
7624
|
const { runtime, workspace_root: workspaceRoot, observed_hashes: observedHashes } = guard;
|
|
7573
7625
|
const paths = runtimePaths(workspaceRoot, runtime);
|
|
7574
|
-
if (!
|
|
7626
|
+
if (!existsSync8(paths.manifest))
|
|
7575
7627
|
return null;
|
|
7576
7628
|
assertCodewithTargetIsConsumed(workspaceRoot, runtime);
|
|
7577
7629
|
const manifest = readProjectContextManifest(paths.manifest, workspaceRoot);
|
|
@@ -7600,7 +7652,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7600
7652
|
}
|
|
7601
7653
|
const fragment = readUtf8RegularFile(paths.fragment, workspaceRoot, PROJECT_CONTEXT_MAX_RENDERED_BYTES);
|
|
7602
7654
|
scanGeneratedContent(fragment);
|
|
7603
|
-
if (!
|
|
7655
|
+
if (!existsSync8(paths.target)) {
|
|
7604
7656
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider target is missing while durable context is active");
|
|
7605
7657
|
}
|
|
7606
7658
|
const currentTarget = readUtf8RegularFile(paths.target, workspaceRoot, managedObservationMaxBytes(relativePosix(workspaceRoot, paths.target)));
|
|
@@ -7611,7 +7663,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7611
7663
|
if (currentMarkers.block.id !== cache.project_id || currentMarkers.block.revision !== cache.revision || currentMarkers.block.hash !== cache.hash) {
|
|
7612
7664
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "project-context provider markers differ from the durable cache");
|
|
7613
7665
|
}
|
|
7614
|
-
const plannedIndexes = input.files.filter((file) => file.role === "index" &&
|
|
7666
|
+
const plannedIndexes = input.files.filter((file) => file.role === "index" && resolve5(file.path) === paths.target);
|
|
7615
7667
|
if (plannedIndexes.length !== 1) {
|
|
7616
7668
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "session renderer does not own the selected project-context provider target");
|
|
7617
7669
|
}
|
|
@@ -7669,7 +7721,7 @@ function withProjectContextSessionGuard(guard, action, options = {}) {
|
|
|
7669
7721
|
verify();
|
|
7670
7722
|
return action(null);
|
|
7671
7723
|
}
|
|
7672
|
-
const lockPath =
|
|
7724
|
+
const lockPath = resolve5(validated.workspace_root, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7673
7725
|
const lock = acquireWorkspaceLock(validated.workspace_root, lockPath);
|
|
7674
7726
|
try {
|
|
7675
7727
|
verify();
|
|
@@ -7695,7 +7747,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7695
7747
|
if (!isRecord(observed) || typeof observed.path !== "string") {
|
|
7696
7748
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains malformed hash metadata");
|
|
7697
7749
|
}
|
|
7698
|
-
const path =
|
|
7750
|
+
const path = resolve5(observed.path);
|
|
7699
7751
|
if (!allowedPaths.has(path) || observedPaths.has(path)) {
|
|
7700
7752
|
throw new ProjectContextError("PROJECT_CONTEXT_SESSION_STALE", "session project-context guard contains an unexpected or duplicate path");
|
|
7701
7753
|
}
|
|
@@ -7717,7 +7769,7 @@ function validateProjectContextSessionGuard(guard) {
|
|
|
7717
7769
|
function applyProjectContext(options) {
|
|
7718
7770
|
const workspaceRoot = assertSafeWorkspaceRoot(options.workspace_root);
|
|
7719
7771
|
const now3 = options.now ?? new Date;
|
|
7720
|
-
const lockPath =
|
|
7772
|
+
const lockPath = resolve5(workspaceRoot, ...PROJECT_CONTEXT_LOCK_PATH.split("/"));
|
|
7721
7773
|
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);
|
|
7722
7774
|
try {
|
|
7723
7775
|
const resolved = resolveBundleForApply(options, workspaceRoot, now3);
|
|
@@ -7864,7 +7916,7 @@ function resolveBundleForApply(options, workspaceRoot, now3) {
|
|
|
7864
7916
|
if (!options.expected_project_id) {
|
|
7865
7917
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_REQUIRED", "expected_project_id is required for stale-cache fallback");
|
|
7866
7918
|
}
|
|
7867
|
-
const cachePath =
|
|
7919
|
+
const cachePath = resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
7868
7920
|
const cache = readProjectContextCache(cachePath, workspaceRoot);
|
|
7869
7921
|
if (!cache)
|
|
7870
7922
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_MISSING", "no last-known-good project context cache exists");
|
|
@@ -8066,7 +8118,7 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
8066
8118
|
if (runtime !== "codewith" || !content)
|
|
8067
8119
|
return null;
|
|
8068
8120
|
const sessionManifestPath = runtimePaths(workspaceRoot, runtime).sessionManifest;
|
|
8069
|
-
if (!
|
|
8121
|
+
if (!existsSync8(sessionManifestPath))
|
|
8070
8122
|
return null;
|
|
8071
8123
|
const manifest = readSessionManifestRecord(sessionManifestPath, workspaceRoot);
|
|
8072
8124
|
if (!manifest || manifest["schema"] !== SESSION_RENDER_SCHEMA) {
|
|
@@ -8117,7 +8169,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8117
8169
|
const manifest = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8118
8170
|
if (manifest) {
|
|
8119
8171
|
const manifestHashHasRecoveryProof = manifest.projectContext.hash === plan.bundle.hash || metadataSnapshotMatchesManifest(plan, manifest);
|
|
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 &&
|
|
8172
|
+
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 && existsSync8(plan.fragment_path) && fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && manifestHashHasRecoveryProof;
|
|
8121
8173
|
observations.push({
|
|
8122
8174
|
source: "manifest",
|
|
8123
8175
|
id: manifest.projectContext.projectId,
|
|
@@ -8125,7 +8177,7 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8125
8177
|
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)
|
|
8126
8178
|
});
|
|
8127
8179
|
const fragmentEntry = manifest.files.find((file) => file.relativePath === PROJECT_CONTEXT_FRAGMENT_PATH);
|
|
8128
|
-
if (fragmentEntry &&
|
|
8180
|
+
if (fragmentEntry && existsSync8(plan.fragment_path)) {
|
|
8129
8181
|
const actual = currentFileHash(plan.fragment_path, plan.workspace_root);
|
|
8130
8182
|
if (actual !== fragmentEntry.sha256 && !fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && !force) {
|
|
8131
8183
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "canonical project-context fragment changed outside Instructions");
|
|
@@ -8220,9 +8272,9 @@ function buildManifest(plan, now3) {
|
|
|
8220
8272
|
function buildSessionCompatibilityManifest(plan, now3) {
|
|
8221
8273
|
const paths = runtimePaths(plan.workspace_root, plan.runtime);
|
|
8222
8274
|
const tool = manifestTool(plan.runtime);
|
|
8223
|
-
const targetHome = plan.runtime === "codewith" ?
|
|
8275
|
+
const targetHome = plan.runtime === "codewith" ? resolve5(plan.workspace_root, ".codewith") : plan.workspace_root;
|
|
8224
8276
|
const targetRelativePath = sessionTargetRelativePath(plan.runtime);
|
|
8225
|
-
const existing =
|
|
8277
|
+
const existing = existsSync8(paths.sessionManifest) ? readSessionManifestRecord(paths.sessionManifest, plan.workspace_root) : {
|
|
8226
8278
|
schema: SESSION_RENDER_SCHEMA,
|
|
8227
8279
|
tool,
|
|
8228
8280
|
adapterMode: plan.native_imports ? "native-imports" : "flattened-markdown",
|
|
@@ -8242,7 +8294,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8242
8294
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest is malformed or incompatible");
|
|
8243
8295
|
}
|
|
8244
8296
|
const existingTargetHome = safeLegacyMetadataString(existing["targetHome"], null);
|
|
8245
|
-
if (existingTargetHome !== null &&
|
|
8297
|
+
if (existingTargetHome !== null && resolve5(existingTargetHome) !== targetHome) {
|
|
8246
8298
|
throw new ProjectContextError("PROJECT_CONTEXT_MANIFEST_INVALID", "provider session manifest targets a different workspace");
|
|
8247
8299
|
}
|
|
8248
8300
|
const sources = sanitizeLegacySources(existing["sources"]).filter((source) => source["id"] !== "project-context-bundle");
|
|
@@ -8528,9 +8580,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8528
8580
|
const previous = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8529
8581
|
if (!previous || previous.projectContext.revision === plan.bundle.revision && previous.projectContext.hash === plan.bundle.hash)
|
|
8530
8582
|
return null;
|
|
8531
|
-
const snapshotDir =
|
|
8583
|
+
const snapshotDir = resolve5(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8532
8584
|
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
8533
|
-
const snapshotPath =
|
|
8585
|
+
const snapshotPath = resolve5(snapshotDir, `${safeFilename(previous.projectContext.revision)}-${previous.projectContext.hash.slice(-12)}.json`);
|
|
8534
8586
|
const snapshot = {
|
|
8535
8587
|
schema: "hasna.configs.session-render-snapshot/v1",
|
|
8536
8588
|
kind: "project-context-metadata",
|
|
@@ -8546,9 +8598,9 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8546
8598
|
return snapshotPath;
|
|
8547
8599
|
}
|
|
8548
8600
|
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
8549
|
-
const snapshotDir =
|
|
8550
|
-
const snapshotPath =
|
|
8551
|
-
if (!
|
|
8601
|
+
const snapshotDir = resolve5(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
8602
|
+
const snapshotPath = resolve5(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
8603
|
+
if (!existsSync8(snapshotPath))
|
|
8552
8604
|
return false;
|
|
8553
8605
|
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
8554
8606
|
const result = projectContextMetadataSnapshotSchema.safeParse(record);
|
|
@@ -8596,10 +8648,11 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8596
8648
|
sha256: nextHash
|
|
8597
8649
|
};
|
|
8598
8650
|
});
|
|
8599
|
-
const
|
|
8600
|
-
|
|
8651
|
+
const snapshotWorkspaceRoot = sessionRenderSnapshotWorkspaceRoot(plan.workspace_root);
|
|
8652
|
+
const snapshotDir = getSessionRenderSnapshotDir(plan.workspace_root);
|
|
8653
|
+
ensureSafeDirectory(snapshotDir, snapshotWorkspaceRoot, 448);
|
|
8601
8654
|
const timestamp = now3.toISOString().replace(/[:.]/g, "-");
|
|
8602
|
-
const snapshotPath =
|
|
8655
|
+
const snapshotPath = resolve5(snapshotDir, `${timestamp}-${randomUUID5()}.json`);
|
|
8603
8656
|
const snapshot = {
|
|
8604
8657
|
schema: "hasna.configs.session-render-snapshot/v2",
|
|
8605
8658
|
createdAt: now3.toISOString(),
|
|
@@ -8613,11 +8666,11 @@ function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
|
8613
8666
|
afterFiles
|
|
8614
8667
|
};
|
|
8615
8668
|
atomicWriteFile(snapshotPath, `${JSON.stringify(snapshot, null, 2)}
|
|
8616
|
-
`,
|
|
8669
|
+
`, snapshotWorkspaceRoot, 384, null);
|
|
8617
8670
|
return snapshotPath;
|
|
8618
8671
|
}
|
|
8619
8672
|
function readProjectContextManifest(path, workspaceRoot) {
|
|
8620
|
-
if (!
|
|
8673
|
+
if (!existsSync8(path))
|
|
8621
8674
|
return null;
|
|
8622
8675
|
const record = readJsonRecord(path, workspaceRoot);
|
|
8623
8676
|
const result = storedManifestObservationSchema.safeParse(record);
|
|
@@ -8632,7 +8685,7 @@ function readProjectContextManifest(path, workspaceRoot) {
|
|
|
8632
8685
|
};
|
|
8633
8686
|
}
|
|
8634
8687
|
function readProjectContextCache(path, workspaceRoot) {
|
|
8635
|
-
if (!
|
|
8688
|
+
if (!existsSync8(path))
|
|
8636
8689
|
return null;
|
|
8637
8690
|
const record = readJsonRecord(path, workspaceRoot);
|
|
8638
8691
|
const result = projectContextCacheSchema.safeParse(record);
|
|
@@ -8664,7 +8717,7 @@ function readSessionManifestRecord(path, workspaceRoot) {
|
|
|
8664
8717
|
}
|
|
8665
8718
|
}
|
|
8666
8719
|
function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash, afterExchange, atomicExchangeUnavailable = false, beforeInstall, portableCreateOnly = false, maxObservedBytes, allowPortableReplacement = false) {
|
|
8667
|
-
const dir =
|
|
8720
|
+
const dir = resolve5(path, "..");
|
|
8668
8721
|
ensureSafeDirectory(dir, workspaceRoot, 448);
|
|
8669
8722
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8670
8723
|
const anchoredOps = portableCreateOnly ? null : resolveAnchoredFsOps();
|
|
@@ -8681,7 +8734,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8681
8734
|
const previous = anchoredFileObservation(directory, targetName);
|
|
8682
8735
|
const previousMode = previous?.mode ?? defaultMode;
|
|
8683
8736
|
const tempName = `.project-context-${randomUUID5()}.tmp`;
|
|
8684
|
-
const tempPath =
|
|
8737
|
+
const tempPath = join10(dir, tempName);
|
|
8685
8738
|
let fd = null;
|
|
8686
8739
|
let preserveTemp = false;
|
|
8687
8740
|
let directoryChanged = false;
|
|
@@ -8810,9 +8863,9 @@ function atomicWritePortable(path, content, workspaceRoot, defaultMode, expected
|
|
|
8810
8863
|
if (currentHash !== null) {
|
|
8811
8864
|
throw new ProjectContextHashRace(`managed path appeared before portable creation: ${relativePosix(workspaceRoot, path)}`);
|
|
8812
8865
|
}
|
|
8813
|
-
const dir =
|
|
8866
|
+
const dir = dirname2(path);
|
|
8814
8867
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8815
|
-
const tempPath =
|
|
8868
|
+
const tempPath = join10(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8816
8869
|
let fd = null;
|
|
8817
8870
|
let tempIdentity = null;
|
|
8818
8871
|
try {
|
|
@@ -8867,9 +8920,9 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8867
8920
|
if (current.isSymbolicLink() || !current.isFile()) {
|
|
8868
8921
|
throw new ProjectContextHashRace(`managed path is not a regular file: ${relativePosix(workspaceRoot, path)}`);
|
|
8869
8922
|
}
|
|
8870
|
-
const dir =
|
|
8923
|
+
const dir = dirname2(path);
|
|
8871
8924
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8872
|
-
const tempPath =
|
|
8925
|
+
const tempPath = join10(dir, `.project-context-${randomUUID5()}.tmp`);
|
|
8873
8926
|
const desiredHash = sha2562(content);
|
|
8874
8927
|
let fd = null;
|
|
8875
8928
|
let tempIdentity = null;
|
|
@@ -8926,7 +8979,7 @@ function portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, s
|
|
|
8926
8979
|
function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
8927
8980
|
if (maxObservedBytes === undefined)
|
|
8928
8981
|
return currentFileHash(path, workspaceRoot);
|
|
8929
|
-
if (!
|
|
8982
|
+
if (!existsSync8(path))
|
|
8930
8983
|
return null;
|
|
8931
8984
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8932
8985
|
const stat = lstatSync(path);
|
|
@@ -8938,13 +8991,13 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
8938
8991
|
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
8939
8992
|
}
|
|
8940
8993
|
function writeProjectContextCoordinatedFile(input) {
|
|
8941
|
-
atomicWriteFile(
|
|
8994
|
+
atomicWriteFile(resolve5(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);
|
|
8942
8995
|
}
|
|
8943
8996
|
function removeProjectContextCoordinatedFile(input) {
|
|
8944
8997
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
8945
|
-
const path =
|
|
8998
|
+
const path = resolve5(input.path);
|
|
8946
8999
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8947
|
-
const dir =
|
|
9000
|
+
const dir = dirname2(path);
|
|
8948
9001
|
const anchoredOps = input.force_portable_file_ops ? null : resolveAnchoredFsOps();
|
|
8949
9002
|
if (!anchoredOps) {
|
|
8950
9003
|
if (!input.allow_portable_removal) {
|
|
@@ -8968,7 +9021,7 @@ function removeProjectContextCoordinatedFile(input) {
|
|
|
8968
9021
|
throw new ProjectContextHashRace(`managed path changed during deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
8969
9022
|
}
|
|
8970
9023
|
displaced = true;
|
|
8971
|
-
input.test_hooks?.after_displace?.(
|
|
9024
|
+
input.test_hooks?.after_displace?.(join10(dir, displacedName));
|
|
8972
9025
|
const moved = anchoredFileObservation(directory, displacedName);
|
|
8973
9026
|
if (!moved || moved.dev !== observed.dev || moved.ino !== observed.ino || moved.hash !== input.expected_hash || anchoredFileObservation(directory, targetName) !== null) {
|
|
8974
9027
|
throw new ProjectContextHashRace(`managed path changed during deletion validation: ${relativePosix(workspaceRoot, path)}`);
|
|
@@ -9012,9 +9065,9 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
9012
9065
|
if (observed.isSymbolicLink() || !observed.isFile()) {
|
|
9013
9066
|
throw new ProjectContextHashRace(`managed path is not a regular file: ${relativePosix(workspaceRoot, path)}`);
|
|
9014
9067
|
}
|
|
9015
|
-
const dir =
|
|
9068
|
+
const dir = dirname2(path);
|
|
9016
9069
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
9017
|
-
const displacedPath =
|
|
9070
|
+
const displacedPath = join10(dir, `.project-context-delete-${randomUUID5()}.tmp`);
|
|
9018
9071
|
let displaced = false;
|
|
9019
9072
|
try {
|
|
9020
9073
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
@@ -9025,7 +9078,7 @@ function removePortableCoordinatedFile(path, workspaceRoot, expectedHash, maxObs
|
|
|
9025
9078
|
displaced = true;
|
|
9026
9079
|
afterDisplace?.(displacedPath);
|
|
9027
9080
|
const moved = lstatSync(displacedPath);
|
|
9028
|
-
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash ||
|
|
9081
|
+
if (moved.isSymbolicLink() || !moved.isFile() || moved.dev !== observed.dev || moved.ino !== observed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash || existsSync8(path)) {
|
|
9029
9082
|
throw new ProjectContextHashRace(`managed path changed during portable deletion: ${relativePosix(workspaceRoot, path)}`);
|
|
9030
9083
|
}
|
|
9031
9084
|
rmSync2(displacedPath);
|
|
@@ -9051,7 +9104,7 @@ function restorePortableDisplacedFile(displacedPath, path, workspaceRoot, expect
|
|
|
9051
9104
|
if (displaced.isSymbolicLink() || installed.isSymbolicLink() || !displaced.isFile() || !installed.isFile() || displaced.dev !== expected.dev || displaced.ino !== expected.ino || displaced.dev !== installed.dev || displaced.ino !== installed.ino || portableFileHash(displacedPath, workspaceRoot, maxObservedBytes) !== expectedHash || portableFileHash(path, workspaceRoot, maxObservedBytes) !== expectedHash)
|
|
9052
9105
|
return false;
|
|
9053
9106
|
rmSync2(displacedPath);
|
|
9054
|
-
fsyncDirectory(
|
|
9107
|
+
fsyncDirectory(dirname2(path));
|
|
9055
9108
|
return true;
|
|
9056
9109
|
} catch {
|
|
9057
9110
|
return false;
|
|
@@ -9085,7 +9138,7 @@ function anchoredOpenExclusive(directory, name, mode) {
|
|
|
9085
9138
|
const requestedMode = mode & 4095;
|
|
9086
9139
|
let fd;
|
|
9087
9140
|
try {
|
|
9088
|
-
fd = openSync(
|
|
9141
|
+
fd = openSync(join10(directory.path, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, requestedMode);
|
|
9089
9142
|
} catch {
|
|
9090
9143
|
throw new ProjectContextHashRace(`could not create prepared managed file in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
9091
9144
|
}
|
|
@@ -9128,7 +9181,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
9128
9181
|
const stat = fstatSync(fd);
|
|
9129
9182
|
if (!stat.isFile())
|
|
9130
9183
|
throw new ProjectContextHashRace("managed output is not a regular file");
|
|
9131
|
-
const relativePath = relativePosix(directory.workspaceRoot,
|
|
9184
|
+
const relativePath = relativePosix(directory.workspaceRoot, join10(directory.path, name));
|
|
9132
9185
|
const maxBytes = directory.maxObservedBytes === undefined ? managedObservationMaxBytes(relativePath) : directory.maxObservedBytes;
|
|
9133
9186
|
if (maxBytes !== null && stat.size > maxBytes) {
|
|
9134
9187
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePath}`);
|
|
@@ -9154,7 +9207,7 @@ function anchoredPreparedObservation(directory, name, path, stage) {
|
|
|
9154
9207
|
return observed;
|
|
9155
9208
|
}
|
|
9156
9209
|
function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
9157
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9210
|
+
assertNoSymlinkSegments(workspaceRoot, join10(path, ".project-context-directory-guard"));
|
|
9158
9211
|
let stat;
|
|
9159
9212
|
try {
|
|
9160
9213
|
stat = lstatSync(path);
|
|
@@ -9167,7 +9220,7 @@ function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
|
9167
9220
|
return { dev: stat.dev, ino: stat.ino };
|
|
9168
9221
|
}
|
|
9169
9222
|
function assertManagedDirectoryStable(path, workspaceRoot, expected) {
|
|
9170
|
-
assertNoSymlinkSegments(workspaceRoot,
|
|
9223
|
+
assertNoSymlinkSegments(workspaceRoot, join10(path, ".project-context-directory-guard"));
|
|
9171
9224
|
let current;
|
|
9172
9225
|
try {
|
|
9173
9226
|
current = lstatSync(path);
|
|
@@ -9300,10 +9353,10 @@ function resolveAnchoredFsOps() {
|
|
|
9300
9353
|
return null;
|
|
9301
9354
|
}
|
|
9302
9355
|
function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRemove, processStartIdentityLookup = processStartIdentity) {
|
|
9303
|
-
const lockDirectory =
|
|
9356
|
+
const lockDirectory = resolve5(lockPath, "..");
|
|
9304
9357
|
ensureSafeDirectory(lockDirectory, workspaceRoot, 448);
|
|
9305
9358
|
assertNoSymlinkSegments(workspaceRoot, lockPath);
|
|
9306
|
-
const tempPath =
|
|
9359
|
+
const tempPath = join10(lockDirectory, `.project-context-lock-${randomUUID5()}.tmp`);
|
|
9307
9360
|
let fd = null;
|
|
9308
9361
|
let openedIdentity = null;
|
|
9309
9362
|
let openedContentHash = null;
|
|
@@ -9337,7 +9390,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9337
9390
|
linked = true;
|
|
9338
9391
|
}
|
|
9339
9392
|
fsyncDirectory(lockDirectory);
|
|
9340
|
-
if (
|
|
9393
|
+
if (existsSync8(tempPath)) {
|
|
9341
9394
|
rmSync2(tempPath);
|
|
9342
9395
|
fsyncDirectory(lockDirectory);
|
|
9343
9396
|
}
|
|
@@ -9352,7 +9405,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9352
9405
|
if (linked && openedIdentity && openedContentHash) {
|
|
9353
9406
|
removeOwnedLockByInode(lockPath, openedIdentity, openedContentHash);
|
|
9354
9407
|
}
|
|
9355
|
-
if (!preserveTemp &&
|
|
9408
|
+
if (!preserveTemp && existsSync8(tempPath)) {
|
|
9356
9409
|
try {
|
|
9357
9410
|
rmSync2(tempPath);
|
|
9358
9411
|
} catch {}
|
|
@@ -9367,7 +9420,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9367
9420
|
}
|
|
9368
9421
|
function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
9369
9422
|
try {
|
|
9370
|
-
if (!
|
|
9423
|
+
if (!existsSync8(lockPath))
|
|
9371
9424
|
return;
|
|
9372
9425
|
const current = lstatSync(lockPath);
|
|
9373
9426
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
@@ -9375,7 +9428,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
9375
9428
|
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
9376
9429
|
return;
|
|
9377
9430
|
rmSync2(lockPath);
|
|
9378
|
-
fsyncDirectory(
|
|
9431
|
+
fsyncDirectory(resolve5(lockPath, ".."));
|
|
9379
9432
|
} catch {}
|
|
9380
9433
|
}
|
|
9381
9434
|
function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentityLookup = processStartIdentity) {
|
|
@@ -9446,7 +9499,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9446
9499
|
const candidateInstalled = !current.isSymbolicLink() && current.dev === candidateIdentity.dev && current.ino === candidateIdentity.ino && currentFileHash(lockPath, workspaceRoot) === candidateHash;
|
|
9447
9500
|
const staleDisplaced = !displaced.isSymbolicLink() && displaced.dev === stale.identity.dev && displaced.ino === stale.identity.ino && currentFileHash(candidatePath, workspaceRoot) === stale.contentHash;
|
|
9448
9501
|
if (!candidateInstalled || !staleDisplaced) {
|
|
9449
|
-
if (candidateInstalled &&
|
|
9502
|
+
if (candidateInstalled && existsSync8(candidatePath)) {
|
|
9450
9503
|
atomicExchangePaths(candidatePath, lockPath);
|
|
9451
9504
|
exchanged = false;
|
|
9452
9505
|
return false;
|
|
@@ -9454,13 +9507,13 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9454
9507
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace lock changed during stale-lock takeover and could not be restored safely");
|
|
9455
9508
|
}
|
|
9456
9509
|
rmSync2(candidatePath);
|
|
9457
|
-
fsyncDirectory(
|
|
9510
|
+
fsyncDirectory(resolve5(lockPath, ".."));
|
|
9458
9511
|
exchanged = false;
|
|
9459
9512
|
return true;
|
|
9460
9513
|
} catch (error) {
|
|
9461
9514
|
if (exchanged) {
|
|
9462
9515
|
try {
|
|
9463
|
-
if (currentFileHash(lockPath, workspaceRoot) === candidateHash &&
|
|
9516
|
+
if (currentFileHash(lockPath, workspaceRoot) === candidateHash && existsSync8(candidatePath)) {
|
|
9464
9517
|
atomicExchangePaths(candidatePath, lockPath);
|
|
9465
9518
|
exchanged = false;
|
|
9466
9519
|
}
|
|
@@ -9473,7 +9526,7 @@ function tryTakeoverStaleWorkspaceLock(candidatePath, lockPath, workspaceRoot, c
|
|
|
9473
9526
|
}
|
|
9474
9527
|
}
|
|
9475
9528
|
function assertWorkspaceLockHeld(lockPath, lock, workspaceRoot) {
|
|
9476
|
-
if (!
|
|
9529
|
+
if (!existsSync8(lockPath)) {
|
|
9477
9530
|
throw new ProjectContextError("PROJECT_CONTEXT_LOCK_LOST", "workspace project-context lock changed during render");
|
|
9478
9531
|
}
|
|
9479
9532
|
const current = lstatSync(lockPath);
|
|
@@ -9533,8 +9586,8 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9533
9586
|
}
|
|
9534
9587
|
return;
|
|
9535
9588
|
}
|
|
9536
|
-
const lockDirectory =
|
|
9537
|
-
const releasePath =
|
|
9589
|
+
const lockDirectory = resolve5(lockPath, "..");
|
|
9590
|
+
const releasePath = join10(lockDirectory, `.project-context-release-${randomUUID5()}.tmp`);
|
|
9538
9591
|
let releaseFd = null;
|
|
9539
9592
|
let releaseIdentity = null;
|
|
9540
9593
|
let releaseHash = null;
|
|
@@ -9563,7 +9616,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9563
9616
|
const releaseInstalled = !installed.isSymbolicLink() && installed.dev === releaseIdentity.dev && installed.ino === releaseIdentity.ino && currentFileHash(lockPath, workspaceRoot) === releaseHash;
|
|
9564
9617
|
const ownedDisplaced = !displaced.isSymbolicLink() && displaced.dev === lock.identity.dev && displaced.ino === lock.identity.ino && currentFileHash(releasePath, workspaceRoot) === lock.contentHash;
|
|
9565
9618
|
if (!releaseInstalled || !ownedDisplaced) {
|
|
9566
|
-
if (releaseInstalled &&
|
|
9619
|
+
if (releaseInstalled && existsSync8(releasePath)) {
|
|
9567
9620
|
atomicExchangePaths(releasePath, lockPath);
|
|
9568
9621
|
exchanged = false;
|
|
9569
9622
|
}
|
|
@@ -9576,7 +9629,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9576
9629
|
} catch {
|
|
9577
9630
|
if (exchanged) {
|
|
9578
9631
|
try {
|
|
9579
|
-
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash &&
|
|
9632
|
+
if (releaseHash && currentFileHash(lockPath, workspaceRoot) === releaseHash && existsSync8(releasePath)) {
|
|
9580
9633
|
atomicExchangePaths(releasePath, lockPath);
|
|
9581
9634
|
exchanged = false;
|
|
9582
9635
|
}
|
|
@@ -9588,7 +9641,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9588
9641
|
closeSync(releaseFd);
|
|
9589
9642
|
} catch {}
|
|
9590
9643
|
}
|
|
9591
|
-
if (!exchanged &&
|
|
9644
|
+
if (!exchanged && existsSync8(releasePath)) {
|
|
9592
9645
|
try {
|
|
9593
9646
|
rmSync2(releasePath);
|
|
9594
9647
|
} catch {}
|
|
@@ -9614,15 +9667,15 @@ function ensureSafeDirectory(path, workspaceRoot, mode) {
|
|
|
9614
9667
|
const segments = rel.split(/[\\/]+/).filter(Boolean);
|
|
9615
9668
|
let current = workspaceRoot;
|
|
9616
9669
|
for (const segment of segments) {
|
|
9617
|
-
current =
|
|
9618
|
-
if (
|
|
9670
|
+
current = join10(current, segment);
|
|
9671
|
+
if (existsSync8(current)) {
|
|
9619
9672
|
if (lstatSync(current).isSymbolicLink())
|
|
9620
9673
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9621
9674
|
if (!statSync(current).isDirectory())
|
|
9622
9675
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", `managed path is not a directory: ${current}`);
|
|
9623
9676
|
} else {
|
|
9624
9677
|
mkdirSync2(current, { mode });
|
|
9625
|
-
fsyncDirectory(
|
|
9678
|
+
fsyncDirectory(resolve5(current, ".."));
|
|
9626
9679
|
}
|
|
9627
9680
|
}
|
|
9628
9681
|
}
|
|
@@ -9678,11 +9731,11 @@ function scanGeneratedContent(content) {
|
|
|
9678
9731
|
function runtimePaths(workspaceRoot, runtime) {
|
|
9679
9732
|
const relativeTarget = runtime === "claude" ? "CLAUDE.md" : runtime === "codewith" ? ".codewith/CODEWITH.md" : "AGENTS.md";
|
|
9680
9733
|
return {
|
|
9681
|
-
target:
|
|
9682
|
-
fragment:
|
|
9683
|
-
manifest:
|
|
9684
|
-
cache:
|
|
9685
|
-
sessionManifest: runtime === "codewith" ?
|
|
9734
|
+
target: resolve5(workspaceRoot, ...relativeTarget.split("/")),
|
|
9735
|
+
fragment: resolve5(workspaceRoot, ...PROJECT_CONTEXT_FRAGMENT_PATH.split("/")),
|
|
9736
|
+
manifest: resolve5(workspaceRoot, ...PROJECT_CONTEXT_MANIFEST_PATH.split("/")),
|
|
9737
|
+
cache: resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/")),
|
|
9738
|
+
sessionManifest: runtime === "codewith" ? resolve5(workspaceRoot, ".codewith", ".hasna", "session-render-manifest.json") : resolve5(workspaceRoot, ".hasna", "session-render-manifest.json")
|
|
9686
9739
|
};
|
|
9687
9740
|
}
|
|
9688
9741
|
function projectContextSessionGuardPaths(paths, runtime) {
|
|
@@ -9692,7 +9745,7 @@ function projectContextSessionGuardPaths(paths, runtime) {
|
|
|
9692
9745
|
paths.fragment,
|
|
9693
9746
|
paths.target,
|
|
9694
9747
|
paths.sessionManifest,
|
|
9695
|
-
...runtime === "codewith" ? [
|
|
9748
|
+
...runtime === "codewith" ? [resolve5(paths.target, "..", "CODEWITH.override.md")] : []
|
|
9696
9749
|
];
|
|
9697
9750
|
}
|
|
9698
9751
|
function sessionTargetRelativePath(runtime) {
|
|
@@ -9712,27 +9765,27 @@ function projectContextRuntimeForSessionTool(tool) {
|
|
|
9712
9765
|
return null;
|
|
9713
9766
|
}
|
|
9714
9767
|
function projectContextWorkspaceForSession(input, runtime) {
|
|
9715
|
-
const targetHome =
|
|
9768
|
+
const targetHome = resolve5(input.target_home);
|
|
9716
9769
|
if (runtime === "codewith") {
|
|
9717
|
-
const workspaceRoot = basename(targetHome) === ".codewith" ?
|
|
9770
|
+
const workspaceRoot = basename(targetHome) === ".codewith" ? dirname2(targetHome) : null;
|
|
9718
9771
|
if (!workspaceRoot)
|
|
9719
9772
|
return null;
|
|
9720
|
-
if (input.project_root &&
|
|
9773
|
+
if (input.project_root && resolve5(input.project_root) !== workspaceRoot) {
|
|
9721
9774
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "Codewith project_root must be the parent workspace of target_home");
|
|
9722
9775
|
}
|
|
9723
|
-
if (!
|
|
9776
|
+
if (!existsSync8(workspaceRoot) || !lstatSync(workspaceRoot).isDirectory())
|
|
9724
9777
|
return null;
|
|
9725
9778
|
return assertSafeWorkspaceRoot(workspaceRoot);
|
|
9726
9779
|
}
|
|
9727
|
-
if (!
|
|
9780
|
+
if (!existsSync8(targetHome) || !lstatSync(targetHome).isDirectory())
|
|
9728
9781
|
return null;
|
|
9729
9782
|
return assertSafeWorkspaceRoot(targetHome);
|
|
9730
9783
|
}
|
|
9731
9784
|
function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
9732
9785
|
if (runtime !== "codewith")
|
|
9733
9786
|
return;
|
|
9734
|
-
const override =
|
|
9735
|
-
if (!
|
|
9787
|
+
const override = resolve5(workspaceRoot, ".codewith", "CODEWITH.override.md");
|
|
9788
|
+
if (!existsSync8(override))
|
|
9736
9789
|
return;
|
|
9737
9790
|
assertNoSymlinkSegments(workspaceRoot, override);
|
|
9738
9791
|
if (!lstatSync(override).isFile())
|
|
@@ -9742,10 +9795,10 @@ function assertCodewithTargetIsConsumed(workspaceRoot, runtime) {
|
|
|
9742
9795
|
function assertSafeWorkspaceRoot(path) {
|
|
9743
9796
|
if (!isAbsolute(path))
|
|
9744
9797
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be absolute");
|
|
9745
|
-
const normalized =
|
|
9798
|
+
const normalized = resolve5(path);
|
|
9746
9799
|
if (normalized === parse(normalized).root)
|
|
9747
9800
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root cannot be the filesystem root");
|
|
9748
|
-
if (!
|
|
9801
|
+
if (!existsSync8(normalized) || !lstatSync(normalized).isDirectory())
|
|
9749
9802
|
throw new ProjectContextError("PROJECT_CONTEXT_PATH_INVALID", "workspace root must be an existing directory");
|
|
9750
9803
|
assertNoSymlinkAncestors(normalized);
|
|
9751
9804
|
if (lstatSync(normalized).isSymbolicLink())
|
|
@@ -9759,18 +9812,18 @@ function assertNoSymlinkSegments(root, target) {
|
|
|
9759
9812
|
}
|
|
9760
9813
|
let current = root;
|
|
9761
9814
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
9762
|
-
current =
|
|
9763
|
-
if (
|
|
9815
|
+
current = join10(current, segment);
|
|
9816
|
+
if (existsSync8(current) && lstatSync(current).isSymbolicLink()) {
|
|
9764
9817
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `managed path uses a symlink: ${current}`);
|
|
9765
9818
|
}
|
|
9766
9819
|
}
|
|
9767
9820
|
}
|
|
9768
9821
|
function assertNoSymlinkAncestors(path) {
|
|
9769
|
-
const normalized =
|
|
9822
|
+
const normalized = resolve5(path);
|
|
9770
9823
|
let current = parse(normalized).root;
|
|
9771
9824
|
for (const segment of relative(current, normalized).split(/[\\/]+/).filter(Boolean)) {
|
|
9772
|
-
current =
|
|
9773
|
-
if (!
|
|
9825
|
+
current = join10(current, segment);
|
|
9826
|
+
if (!existsSync8(current))
|
|
9774
9827
|
return;
|
|
9775
9828
|
if (lstatSync(current).isSymbolicLink())
|
|
9776
9829
|
throw new ProjectContextError("PROJECT_CONTEXT_SYMLINK_REJECTED", `workspace ancestor is a symlink: ${current}`);
|
|
@@ -9786,7 +9839,7 @@ function readUtf8RegularFile(path, workspaceRoot, maxBytes = FOREIGN_INPUT_MAX_B
|
|
|
9786
9839
|
return readFileSync(path, "utf8");
|
|
9787
9840
|
}
|
|
9788
9841
|
function currentFileHash(path, workspaceRoot) {
|
|
9789
|
-
if (!
|
|
9842
|
+
if (!existsSync8(path))
|
|
9790
9843
|
return null;
|
|
9791
9844
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
9792
9845
|
return sha2562(readUtf8RegularFile(path, workspaceRoot, managedObservationMaxBytes(relativePath)));
|
|
@@ -9805,10 +9858,10 @@ function fragmentMatchesBundle(path, bundle, workspaceRoot) {
|
|
|
9805
9858
|
}
|
|
9806
9859
|
function durableSourcePath(path, workspaceRoot) {
|
|
9807
9860
|
if (!path || path.startsWith("/dev/fd/"))
|
|
9808
|
-
return
|
|
9809
|
-
const normalized = isAbsolute(path) ?
|
|
9861
|
+
return resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9862
|
+
const normalized = isAbsolute(path) ? resolve5(path) : resolve5(workspaceRoot, path);
|
|
9810
9863
|
if (normalized.startsWith("/dev/fd/"))
|
|
9811
|
-
return
|
|
9864
|
+
return resolve5(workspaceRoot, ...PROJECT_CONTEXT_CACHE_PATH.split("/"));
|
|
9812
9865
|
return normalized;
|
|
9813
9866
|
}
|
|
9814
9867
|
function compareRevisions(incoming, previous) {
|
|
@@ -9980,6 +10033,7 @@ var init_project_context = __esm(() => {
|
|
|
9980
10033
|
init_zod();
|
|
9981
10034
|
init_redact();
|
|
9982
10035
|
init_session_render_contract();
|
|
10036
|
+
init_session_render_state();
|
|
9983
10037
|
PROJECT_CONTEXT_SUPPORTED_SCHEMAS = [PROJECT_CONTEXT_SCHEMA, PROJECT_CONTEXT_SCHEMA_V2];
|
|
9984
10038
|
projectContextSchema = exports_external.enum(PROJECT_CONTEXT_SUPPORTED_SCHEMAS);
|
|
9985
10039
|
PROJECT_CONTEXT_MAX_INPUT_BYTES = 8 * 1024;
|
|
@@ -10368,7 +10422,7 @@ function compareProviderVersions(left, right) {
|
|
|
10368
10422
|
|
|
10369
10423
|
// src/lib/asset-plan.ts
|
|
10370
10424
|
import { createHash as createHash3 } from "crypto";
|
|
10371
|
-
import { isAbsolute as isAbsolute2, posix, resolve as
|
|
10425
|
+
import { isAbsolute as isAbsolute2, posix, resolve as resolve6 } from "path";
|
|
10372
10426
|
function assetCapability(provider, surface, kind, support, strategies, note, providerVersionRange = "*") {
|
|
10373
10427
|
return Object.freeze({
|
|
10374
10428
|
schema: ASSET_CAPABILITY_SCHEMA,
|
|
@@ -10575,8 +10629,8 @@ function resolveAssetDestination(item, roots) {
|
|
|
10575
10629
|
if (!isAbsolute2(root))
|
|
10576
10630
|
throw new Error(`Asset ${item.assetKey} destination root must be absolute.`);
|
|
10577
10631
|
const relativePath = safeRelativePath(item.destination.relativePath);
|
|
10578
|
-
const target =
|
|
10579
|
-
const normalizedRoot =
|
|
10632
|
+
const target = resolve6(root, ...relativePath.split("/"));
|
|
10633
|
+
const normalizedRoot = resolve6(root);
|
|
10580
10634
|
if (target === normalizedRoot)
|
|
10581
10635
|
throw new Error(`Asset ${item.assetKey} destination cannot replace its root.`);
|
|
10582
10636
|
if (!target.startsWith(`${normalizedRoot}/`))
|
|
@@ -10756,13 +10810,13 @@ var init_asset_plan = __esm(() => {
|
|
|
10756
10810
|
// src/lib/cursor-authority.ts
|
|
10757
10811
|
import { createHash as createHash4 } from "crypto";
|
|
10758
10812
|
import { lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
10759
|
-
import { homedir as
|
|
10760
|
-
import { join as
|
|
10813
|
+
import { homedir as homedir8 } from "os";
|
|
10814
|
+
import { join as join11, resolve as resolve7 } from "path";
|
|
10761
10815
|
function sha2564(content) {
|
|
10762
10816
|
return createHash4("sha256").update(content).digest("hex");
|
|
10763
10817
|
}
|
|
10764
|
-
function
|
|
10765
|
-
return process.env["HOME"] ||
|
|
10818
|
+
function homeDir3() {
|
|
10819
|
+
return process.env["HOME"] || homedir8();
|
|
10766
10820
|
}
|
|
10767
10821
|
function markerPayload(content, markerLine, markerIndex) {
|
|
10768
10822
|
const index = markerIndex ?? content.indexOf(markerLine);
|
|
@@ -10778,12 +10832,12 @@ function baseObservation(path) {
|
|
|
10778
10832
|
};
|
|
10779
10833
|
}
|
|
10780
10834
|
function observeCursorGlobalAuthority(options = {}) {
|
|
10781
|
-
const authorityPath =
|
|
10835
|
+
const authorityPath = resolve7(join11(options.home ?? homeDir3(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10782
10836
|
const readFile2 = options.readFile ?? ((path) => readFileSync2(path, "utf8"));
|
|
10783
10837
|
return observeCursorGlobalAuthorityPath(authorityPath, readFile2);
|
|
10784
10838
|
}
|
|
10785
10839
|
function observeCursorGlobalAuthorityAtPath(authorityPath) {
|
|
10786
|
-
return observeCursorGlobalAuthorityPath(
|
|
10840
|
+
return observeCursorGlobalAuthorityPath(resolve7(authorityPath), (path) => readFileSync2(path, "utf8"));
|
|
10787
10841
|
}
|
|
10788
10842
|
function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
10789
10843
|
const base = baseObservation(authorityPath);
|
|
@@ -10928,7 +10982,7 @@ function observeCursorGlobalAuthorityPath(authorityPath, readFile2) {
|
|
|
10928
10982
|
};
|
|
10929
10983
|
}
|
|
10930
10984
|
function isCursorGlobalAuthorityPath(path) {
|
|
10931
|
-
return
|
|
10985
|
+
return resolve7(path) === resolve7(join11(homeDir3(), CURSOR_GLOBAL_AUTHORITY_RELATIVE_PATH));
|
|
10932
10986
|
}
|
|
10933
10987
|
function stampCursorGlobalAuthorityMarker(content) {
|
|
10934
10988
|
const existing = content.match(CURSOR_GLOBAL_AUTHORITY_MARKER_PATTERN);
|
|
@@ -10984,16 +11038,16 @@ var init_cursor_authority = __esm(() => {
|
|
|
10984
11038
|
// src/lib/session-authority.ts
|
|
10985
11039
|
import { createHash as createHash5 } from "crypto";
|
|
10986
11040
|
import { lstatSync as lstatSync3, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
|
|
10987
|
-
import { homedir as
|
|
10988
|
-
import { join as
|
|
11041
|
+
import { homedir as homedir9 } from "os";
|
|
11042
|
+
import { join as join12, resolve as resolve8 } from "path";
|
|
10989
11043
|
function sha2565(content) {
|
|
10990
11044
|
return createHash5("sha256").update(content).digest("hex");
|
|
10991
11045
|
}
|
|
10992
11046
|
function configHomeDir() {
|
|
10993
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
11047
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir9();
|
|
10994
11048
|
}
|
|
10995
11049
|
function normalizeOwnedTargetPath(p) {
|
|
10996
|
-
const expanded = p.startsWith("~/") ?
|
|
11050
|
+
const expanded = p.startsWith("~/") ? resolve8(configHomeDir(), p.slice(2)) : resolve8(p);
|
|
10997
11051
|
try {
|
|
10998
11052
|
return realpathSync(expanded);
|
|
10999
11053
|
} catch {
|
|
@@ -11001,7 +11055,7 @@ function normalizeOwnedTargetPath(p) {
|
|
|
11001
11055
|
}
|
|
11002
11056
|
}
|
|
11003
11057
|
function detectClaudeAuthorityConflicts(targetHome, ownedAuthorities = []) {
|
|
11004
|
-
const authorityPath =
|
|
11058
|
+
const authorityPath = resolve8(join12(targetHome, CLAUDE_LEGACY_AUTHORITY_RELATIVE_PATH));
|
|
11005
11059
|
let stat;
|
|
11006
11060
|
try {
|
|
11007
11061
|
stat = lstatSync3(authorityPath);
|
|
@@ -11090,9 +11144,9 @@ var init_session_authority = __esm(() => {
|
|
|
11090
11144
|
|
|
11091
11145
|
// src/lib/session-render.ts
|
|
11092
11146
|
import { createHash as createHash6 } from "crypto";
|
|
11093
|
-
import { existsSync as
|
|
11094
|
-
import { homedir as
|
|
11095
|
-
import { basename as basename4, dirname as
|
|
11147
|
+
import { existsSync as existsSync10, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
11148
|
+
import { homedir as homedir10 } from "os";
|
|
11149
|
+
import { basename as basename4, dirname as dirname4, extname as extname2, isAbsolute as isAbsolute3, join as join13, parse as parse2, posix as posix2, relative as relative2, resolve as resolve9 } from "path";
|
|
11096
11150
|
function normalizeSessionInstructionLayer(value) {
|
|
11097
11151
|
if (value === "provider")
|
|
11098
11152
|
return "tool";
|
|
@@ -11178,13 +11232,13 @@ function yamlQuote2(value) {
|
|
|
11178
11232
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
11179
11233
|
}
|
|
11180
11234
|
function defaultTargetHome(tool, profile, sessionId) {
|
|
11181
|
-
const home = process.env["HOME"] ||
|
|
11182
|
-
return
|
|
11235
|
+
const home = process.env["HOME"] || homedir10();
|
|
11236
|
+
return join13(home, ".hasna", "accounts", "profiles", tool, slug(profile));
|
|
11183
11237
|
}
|
|
11184
11238
|
function joinTarget(targetHome, relativePath) {
|
|
11185
11239
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
11186
11240
|
const safeRelativePath2 = assertSafeRelativePath(relativePath);
|
|
11187
|
-
return
|
|
11241
|
+
return join13(safeTargetHome, ...safeRelativePath2.split("/"));
|
|
11188
11242
|
}
|
|
11189
11243
|
function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
11190
11244
|
const safeTargetHome = assertSafeTargetRoot(targetHome);
|
|
@@ -11753,7 +11807,7 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
|
|
|
11753
11807
|
...sources.flatMap((source) => source.resolvedRules.map((rule) => rule.id))
|
|
11754
11808
|
]);
|
|
11755
11809
|
const existingConfigPath = joinTarget(targetHome, adapter.configFile);
|
|
11756
|
-
const selectedConfig =
|
|
11810
|
+
const selectedConfig = existsSync10(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
|
|
11757
11811
|
const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
|
|
11758
11812
|
const config = {
|
|
11759
11813
|
...selectedConfig,
|
|
@@ -11942,7 +11996,7 @@ function adapterFor(input) {
|
|
|
11942
11996
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
11943
11997
|
}
|
|
11944
11998
|
function getHomeDir() {
|
|
11945
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
11999
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir10();
|
|
11946
12000
|
}
|
|
11947
12001
|
function cleanSessionPathInput(path) {
|
|
11948
12002
|
const trimmed = path.trim();
|
|
@@ -11957,16 +12011,16 @@ function resolveSessionPath(path) {
|
|
|
11957
12011
|
throw new Error("Session render path cannot be empty.");
|
|
11958
12012
|
const home = getHomeDir();
|
|
11959
12013
|
if (cleaned === "~")
|
|
11960
|
-
return
|
|
12014
|
+
return resolve9(home);
|
|
11961
12015
|
if (cleaned.startsWith("~/"))
|
|
11962
|
-
return
|
|
12016
|
+
return resolve9(home, cleaned.slice(2));
|
|
11963
12017
|
if (cleaned === "{{HOME}}" || cleaned === "${HOME}")
|
|
11964
|
-
return
|
|
12018
|
+
return resolve9(home);
|
|
11965
12019
|
if (cleaned.startsWith("{{HOME}}/"))
|
|
11966
|
-
return
|
|
12020
|
+
return resolve9(home, cleaned.slice("{{HOME}}/".length));
|
|
11967
12021
|
if (cleaned.startsWith("${HOME}/"))
|
|
11968
|
-
return
|
|
11969
|
-
return
|
|
12022
|
+
return resolve9(home, cleaned.slice("${HOME}/".length));
|
|
12023
|
+
return resolve9(cleaned);
|
|
11970
12024
|
}
|
|
11971
12025
|
function assertSafeRelativePath(relativePath) {
|
|
11972
12026
|
if (!relativePath.trim())
|
|
@@ -11982,7 +12036,7 @@ function assertSafeRelativePath(relativePath) {
|
|
|
11982
12036
|
function assertSafeTargetRoot(targetHome) {
|
|
11983
12037
|
if (!isAbsolute3(targetHome))
|
|
11984
12038
|
throw new Error(`Session render target must be an absolute path: ${targetHome}`);
|
|
11985
|
-
const normalized =
|
|
12039
|
+
const normalized = resolve9(targetHome);
|
|
11986
12040
|
if (normalized === parse2(normalized).root) {
|
|
11987
12041
|
throw new Error(`Session render target cannot be the filesystem root: ${targetHome}`);
|
|
11988
12042
|
}
|
|
@@ -12218,7 +12272,7 @@ function planSessionRender(input) {
|
|
|
12218
12272
|
sourceId: input.providerConfig.sourceId,
|
|
12219
12273
|
selectedPayloadSha256: sha2566(input.providerConfig.content),
|
|
12220
12274
|
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2566(input.providerConfig.content),
|
|
12221
|
-
selected: !
|
|
12275
|
+
selected: !existsSync10(joinTarget(targetHome, adapter.configFile))
|
|
12222
12276
|
}
|
|
12223
12277
|
} : {},
|
|
12224
12278
|
...projectContext ? {
|
|
@@ -12479,7 +12533,7 @@ function layerFromIdentityKind(kind, exportShape) {
|
|
|
12479
12533
|
function contentFromIdentitySourcePaths(sourcePaths, exportPath, sourceId) {
|
|
12480
12534
|
if (sourcePaths.length === 0 || !exportPath)
|
|
12481
12535
|
return;
|
|
12482
|
-
const baseDir3 =
|
|
12536
|
+
const baseDir3 = dirname4(resolveSessionPath(exportPath));
|
|
12483
12537
|
const contents = [];
|
|
12484
12538
|
for (const sourcePath of sourcePaths) {
|
|
12485
12539
|
const content = readIdentitySourcePath(sourcePath, baseDir3, sourceId);
|
|
@@ -12497,7 +12551,7 @@ ${item.content.trimEnd()}`).join(`
|
|
|
12497
12551
|
}
|
|
12498
12552
|
function readIdentitySourcePath(sourcePath, baseDir3, sourceId) {
|
|
12499
12553
|
const resolvedPath = resolveIdentitySourcePath(sourcePath.path, baseDir3, sourceId);
|
|
12500
|
-
if (!
|
|
12554
|
+
if (!existsSync10(resolvedPath)) {
|
|
12501
12555
|
if (sourcePath.required) {
|
|
12502
12556
|
throw new Error(`Required identity instruction source path not found for ${sourceId}: ${sourcePath.path}`);
|
|
12503
12557
|
}
|
|
@@ -12520,8 +12574,8 @@ function resolveIdentitySourcePath(path, baseDir3, sourceId) {
|
|
|
12520
12574
|
throw new Error(`Identity instruction source path cannot be empty for ${sourceId}.`);
|
|
12521
12575
|
if (cleaned.includes("\\"))
|
|
12522
12576
|
throw new Error(`Identity instruction source path must use POSIX separators for ${sourceId}: ${path}`);
|
|
12523
|
-
const resolvedPath = isAbsolute3(cleaned) ?
|
|
12524
|
-
if (!pathIsInside(resolvedPath,
|
|
12577
|
+
const resolvedPath = isAbsolute3(cleaned) ? resolve9(cleaned) : resolve9(baseDir3, cleaned);
|
|
12578
|
+
if (!pathIsInside(resolvedPath, resolve9(baseDir3))) {
|
|
12525
12579
|
throw new Error(`Identity instruction source path escapes export directory for ${sourceId}: ${path}`);
|
|
12526
12580
|
}
|
|
12527
12581
|
return resolvedPath;
|
|
@@ -14187,8 +14241,8 @@ var init_config_store = __esm(() => {
|
|
|
14187
14241
|
});
|
|
14188
14242
|
|
|
14189
14243
|
// src/lib/session-render-ownership.ts
|
|
14190
|
-
import { existsSync as
|
|
14191
|
-
import { dirname as
|
|
14244
|
+
import { existsSync as existsSync11, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
14245
|
+
import { dirname as dirname5, join as join14, parse as parse3, relative as relative3, sep } from "path";
|
|
14192
14246
|
function toSegments(absolutePath2) {
|
|
14193
14247
|
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
14194
14248
|
}
|
|
@@ -14207,7 +14261,7 @@ function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
|
14207
14261
|
function readManifestRelativePaths(manifestPath) {
|
|
14208
14262
|
let stats;
|
|
14209
14263
|
try {
|
|
14210
|
-
if (!
|
|
14264
|
+
if (!existsSync11(manifestPath))
|
|
14211
14265
|
return null;
|
|
14212
14266
|
stats = statSync4(manifestPath);
|
|
14213
14267
|
} catch {
|
|
@@ -14234,16 +14288,16 @@ function readManifestRelativePaths(manifestPath) {
|
|
|
14234
14288
|
}
|
|
14235
14289
|
function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
14236
14290
|
const root = parse3(absolutePath2).root;
|
|
14237
|
-
let home =
|
|
14291
|
+
let home = dirname5(absolutePath2);
|
|
14238
14292
|
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
14239
|
-
const manifestPath =
|
|
14293
|
+
const manifestPath = join14(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
14240
14294
|
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
14241
14295
|
if (relativePaths) {
|
|
14242
14296
|
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
14243
14297
|
if (relativePaths.has(claimed))
|
|
14244
14298
|
return true;
|
|
14245
14299
|
}
|
|
14246
|
-
const parent =
|
|
14300
|
+
const parent = dirname5(home);
|
|
14247
14301
|
if (parent === home || home === root)
|
|
14248
14302
|
break;
|
|
14249
14303
|
home = parent;
|
|
@@ -14271,17 +14325,17 @@ __export(exports_apply, {
|
|
|
14271
14325
|
applyConfigs: () => applyConfigs,
|
|
14272
14326
|
applyConfig: () => applyConfig
|
|
14273
14327
|
});
|
|
14274
|
-
import { existsSync as
|
|
14275
|
-
import { basename as basename5, dirname as
|
|
14276
|
-
import { homedir as
|
|
14328
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
14329
|
+
import { basename as basename5, dirname as dirname6, join as join15, resolve as resolve10 } from "path";
|
|
14330
|
+
import { homedir as homedir11 } from "os";
|
|
14277
14331
|
function getConfigHome() {
|
|
14278
|
-
return process.env["CONFIGS_HOME"] || process.env["HOME"] ||
|
|
14332
|
+
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir11();
|
|
14279
14333
|
}
|
|
14280
14334
|
function expandPath(p) {
|
|
14281
14335
|
if (p.startsWith("~/")) {
|
|
14282
|
-
return
|
|
14336
|
+
return resolve10(getConfigHome(), p.slice(2));
|
|
14283
14337
|
}
|
|
14284
|
-
return
|
|
14338
|
+
return resolve10(p);
|
|
14285
14339
|
}
|
|
14286
14340
|
function normalizeTargetPath(p) {
|
|
14287
14341
|
const expanded = expandPath(p);
|
|
@@ -14291,14 +14345,14 @@ function normalizeTargetPath(p) {
|
|
|
14291
14345
|
let current = expanded;
|
|
14292
14346
|
const missingSegments = [];
|
|
14293
14347
|
while (true) {
|
|
14294
|
-
if (
|
|
14348
|
+
if (existsSync12(current)) {
|
|
14295
14349
|
try {
|
|
14296
|
-
return
|
|
14350
|
+
return resolve10(realpathSync3(current), ...missingSegments);
|
|
14297
14351
|
} catch {
|
|
14298
14352
|
return expanded;
|
|
14299
14353
|
}
|
|
14300
14354
|
}
|
|
14301
|
-
const parent =
|
|
14355
|
+
const parent = dirname6(current);
|
|
14302
14356
|
const name = basename5(current);
|
|
14303
14357
|
if (parent === current)
|
|
14304
14358
|
return expanded;
|
|
@@ -14322,11 +14376,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
14322
14376
|
}
|
|
14323
14377
|
const path = expandPath(renderedTargetPath);
|
|
14324
14378
|
const renderedForTarget = isCursorGlobalAuthorityPath(path) ? stampCursorGlobalAuthorityMarker(renderedContent) : renderedContent;
|
|
14325
|
-
const previousContent =
|
|
14379
|
+
const previousContent = existsSync12(path) ? readFileSync6(path, "utf-8") : null;
|
|
14326
14380
|
const changed = previousContent !== renderedForTarget;
|
|
14327
14381
|
if (!opts.dryRun) {
|
|
14328
|
-
const dir =
|
|
14329
|
-
if (!
|
|
14382
|
+
const dir = dirname6(path);
|
|
14383
|
+
if (!existsSync12(dir)) {
|
|
14330
14384
|
mkdirSync3(dir, { recursive: true });
|
|
14331
14385
|
}
|
|
14332
14386
|
if (previousContent !== null && changed) {
|
|
@@ -14360,7 +14414,7 @@ function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
|
14360
14414
|
let current;
|
|
14361
14415
|
try {
|
|
14362
14416
|
const path = expandPath(targetPath);
|
|
14363
|
-
if (!
|
|
14417
|
+
if (!existsSync12(path))
|
|
14364
14418
|
return [];
|
|
14365
14419
|
current = readFileSync6(path, "utf-8");
|
|
14366
14420
|
} catch {
|
|
@@ -14686,7 +14740,7 @@ function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
|
14686
14740
|
getConfigHome(),
|
|
14687
14741
|
opts.vars?.["HOME_DIR"]
|
|
14688
14742
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
14689
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
14743
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join15(home, ...relativePath.split("/"))))))
|
|
14690
14744
|
return true;
|
|
14691
14745
|
return sessionRenderOwnsPath(normalized);
|
|
14692
14746
|
}
|
|
@@ -14704,20 +14758,20 @@ var init_apply = __esm(() => {
|
|
|
14704
14758
|
});
|
|
14705
14759
|
|
|
14706
14760
|
// src/lib/sync-dir.ts
|
|
14707
|
-
import { existsSync as
|
|
14708
|
-
import { join as
|
|
14709
|
-
import { homedir as
|
|
14761
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
14762
|
+
import { join as join16, relative as relative4 } from "path";
|
|
14763
|
+
import { homedir as homedir12 } from "os";
|
|
14710
14764
|
function shouldSkip(p) {
|
|
14711
14765
|
return SKIP.some((s) => p.includes(s));
|
|
14712
14766
|
}
|
|
14713
14767
|
async function syncFromDir(dir, opts = {}) {
|
|
14714
14768
|
const store = opts.store ?? resolveConfigStore();
|
|
14715
14769
|
const absDir = expandPath(dir);
|
|
14716
|
-
if (!
|
|
14770
|
+
if (!existsSync13(absDir))
|
|
14717
14771
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
14718
|
-
const files = opts.recursive !== false ? walkDir(absDir) :
|
|
14772
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join16(absDir, f)).filter((f) => statSync5(f).isFile());
|
|
14719
14773
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
14720
|
-
const home =
|
|
14774
|
+
const home = homedir12();
|
|
14721
14775
|
const allConfigs = await store.listConfigs();
|
|
14722
14776
|
for (const file of files) {
|
|
14723
14777
|
if (shouldSkip(file)) {
|
|
@@ -14752,7 +14806,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
14752
14806
|
}
|
|
14753
14807
|
async function syncToDir(dir, opts = {}) {
|
|
14754
14808
|
const store = opts.store ?? resolveConfigStore();
|
|
14755
|
-
const home =
|
|
14809
|
+
const home = homedir12();
|
|
14756
14810
|
const absDir = expandPath(dir);
|
|
14757
14811
|
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
14758
14812
|
const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
@@ -14775,8 +14829,8 @@ async function syncToDir(dir, opts = {}) {
|
|
|
14775
14829
|
return result;
|
|
14776
14830
|
}
|
|
14777
14831
|
function walkDir(dir, files = []) {
|
|
14778
|
-
for (const entry of
|
|
14779
|
-
const full =
|
|
14832
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
14833
|
+
const full = join16(dir, entry.name);
|
|
14780
14834
|
if (shouldSkip(full))
|
|
14781
14835
|
continue;
|
|
14782
14836
|
if (entry.isDirectory())
|
|
@@ -14811,8 +14865,8 @@ __export(exports_sync, {
|
|
|
14811
14865
|
KNOWN_CONFIGS: () => KNOWN_CONFIGS,
|
|
14812
14866
|
CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
|
|
14813
14867
|
});
|
|
14814
|
-
import { existsSync as
|
|
14815
|
-
import { basename as basename6, extname as extname3, join as
|
|
14868
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
|
|
14869
|
+
import { basename as basename6, extname as extname3, join as join17 } from "path";
|
|
14816
14870
|
function claudeRuleOutputs(fileName) {
|
|
14817
14871
|
const stem = basename6(fileName, extname3(fileName));
|
|
14818
14872
|
return [
|
|
@@ -14851,7 +14905,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
14851
14905
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
14852
14906
|
}
|
|
14853
14907
|
function hasClaudePromptSource() {
|
|
14854
|
-
return
|
|
14908
|
+
return existsSync14(expandPath("~/.claude/CLAUDE.md"));
|
|
14855
14909
|
}
|
|
14856
14910
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
14857
14911
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -14859,7 +14913,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
14859
14913
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
14860
14914
|
return false;
|
|
14861
14915
|
const stem = basename6(absoluteTargetPath, ".mdc");
|
|
14862
|
-
return
|
|
14916
|
+
return existsSync14(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync14(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
14863
14917
|
}
|
|
14864
14918
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
14865
14919
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -14876,8 +14930,8 @@ async function syncProject(opts) {
|
|
|
14876
14930
|
const allConfigs = await store.listConfigs();
|
|
14877
14931
|
const machine = detectMachineContext();
|
|
14878
14932
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
14879
|
-
const abs =
|
|
14880
|
-
if (!
|
|
14933
|
+
const abs = join17(absDir, pf.file);
|
|
14934
|
+
if (!existsSync14(abs))
|
|
14881
14935
|
continue;
|
|
14882
14936
|
try {
|
|
14883
14937
|
const rawContent = readFileSync8(abs, "utf-8");
|
|
@@ -14909,19 +14963,19 @@ async function syncProject(opts) {
|
|
|
14909
14963
|
}
|
|
14910
14964
|
}
|
|
14911
14965
|
for (const ruleDir of [
|
|
14912
|
-
{ dir:
|
|
14913
|
-
{ dir:
|
|
14914
|
-
{ dir:
|
|
14915
|
-
{ dir:
|
|
14916
|
-
{ dir:
|
|
14917
|
-
{ dir:
|
|
14918
|
-
{ dir:
|
|
14966
|
+
{ dir: join17(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
14967
|
+
{ dir: join17(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
|
|
14968
|
+
{ dir: join17(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
|
|
14969
|
+
{ dir: join17(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
|
|
14970
|
+
{ dir: join17(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
|
|
14971
|
+
{ dir: join17(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
|
|
14972
|
+
{ dir: join17(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
|
|
14919
14973
|
]) {
|
|
14920
|
-
if (!
|
|
14974
|
+
if (!existsSync14(ruleDir.dir))
|
|
14921
14975
|
continue;
|
|
14922
|
-
const mdFiles =
|
|
14976
|
+
const mdFiles = readdirSync3(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
14923
14977
|
for (const f of mdFiles) {
|
|
14924
|
-
const abs =
|
|
14978
|
+
const abs = join17(ruleDir.dir, f);
|
|
14925
14979
|
const raw = readFileSync8(abs, "utf-8");
|
|
14926
14980
|
const redacted = redactContent(raw, "markdown");
|
|
14927
14981
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
@@ -14961,14 +15015,14 @@ async function syncKnown(opts = {}) {
|
|
|
14961
15015
|
for (const known of targets) {
|
|
14962
15016
|
if (known.rulesDir) {
|
|
14963
15017
|
const absDir = expandPath(known.rulesDir);
|
|
14964
|
-
if (!
|
|
15018
|
+
if (!existsSync14(absDir)) {
|
|
14965
15019
|
result.skipped.push(known.rulesDir);
|
|
14966
15020
|
continue;
|
|
14967
15021
|
}
|
|
14968
15022
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
14969
|
-
const ruleFiles =
|
|
15023
|
+
const ruleFiles = readdirSync3(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
14970
15024
|
for (const f of ruleFiles) {
|
|
14971
|
-
const abs2 =
|
|
15025
|
+
const abs2 = join17(absDir, f);
|
|
14972
15026
|
const targetPath = abs2.replace(home, "~");
|
|
14973
15027
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
14974
15028
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
@@ -15002,7 +15056,7 @@ async function syncKnown(opts = {}) {
|
|
|
15002
15056
|
continue;
|
|
15003
15057
|
}
|
|
15004
15058
|
const abs = expandPath(known.path);
|
|
15005
|
-
if (!
|
|
15059
|
+
if (!existsSync14(abs)) {
|
|
15006
15060
|
result.skipped.push(known.path);
|
|
15007
15061
|
continue;
|
|
15008
15062
|
}
|
|
@@ -15108,7 +15162,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
|
15108
15162
|
}
|
|
15109
15163
|
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
15110
15164
|
const path = expandPath(targetPath);
|
|
15111
|
-
if (!
|
|
15165
|
+
if (!existsSync14(path))
|
|
15112
15166
|
return `(file not found on disk: ${path})`;
|
|
15113
15167
|
const diskContent = readFileSync8(path, "utf-8");
|
|
15114
15168
|
if (diskContent === expectedContent)
|
|
@@ -15343,16 +15397,16 @@ __export(exports_package_manager_guard, {
|
|
|
15343
15397
|
scanPackageManagerSecrets: () => scanPackageManagerSecrets
|
|
15344
15398
|
});
|
|
15345
15399
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15346
|
-
import { existsSync as
|
|
15347
|
-
import { homedir as
|
|
15348
|
-
import { basename as basename7, dirname as
|
|
15400
|
+
import { existsSync as existsSync23, lstatSync as lstatSync7, readdirSync as readdirSync6, readFileSync as readFileSync16 } from "fs";
|
|
15401
|
+
import { homedir as homedir15 } from "os";
|
|
15402
|
+
import { basename as basename7, dirname as dirname11, isAbsolute as isAbsolute5, join as join25, relative as relative7, resolve as resolve15 } from "path";
|
|
15349
15403
|
function scanPackageManagerSecrets(options = {}) {
|
|
15350
|
-
const cwd = options.cwd ?
|
|
15351
|
-
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) =>
|
|
15404
|
+
const cwd = options.cwd ? resolve15(options.cwd) : process.cwd();
|
|
15405
|
+
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve15(cwd, root));
|
|
15352
15406
|
const findings = [];
|
|
15353
15407
|
let scannedFiles = 0;
|
|
15354
15408
|
for (const root of roots) {
|
|
15355
|
-
if (!
|
|
15409
|
+
if (!existsSync23(root))
|
|
15356
15410
|
continue;
|
|
15357
15411
|
const stat = lstatSync7(root);
|
|
15358
15412
|
if (stat.isFile()) {
|
|
@@ -15362,7 +15416,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15362
15416
|
if (text === null)
|
|
15363
15417
|
continue;
|
|
15364
15418
|
scannedFiles++;
|
|
15365
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
15419
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname11(root)));
|
|
15366
15420
|
continue;
|
|
15367
15421
|
}
|
|
15368
15422
|
if (!stat.isDirectory())
|
|
@@ -15379,10 +15433,10 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15379
15433
|
}
|
|
15380
15434
|
}
|
|
15381
15435
|
if (options.includeHome) {
|
|
15382
|
-
const home =
|
|
15436
|
+
const home = homedir15();
|
|
15383
15437
|
for (const name of HOME_FILES) {
|
|
15384
|
-
const file =
|
|
15385
|
-
if (!
|
|
15438
|
+
const file = join25(home, name);
|
|
15439
|
+
if (!existsSync23(file))
|
|
15386
15440
|
continue;
|
|
15387
15441
|
const text = readTextFile(file);
|
|
15388
15442
|
if (text === null)
|
|
@@ -15402,16 +15456,16 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
15402
15456
|
function collectRepoFiles(root) {
|
|
15403
15457
|
const out = [];
|
|
15404
15458
|
const visit = (dir) => {
|
|
15405
|
-
for (const entry of
|
|
15459
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
15406
15460
|
if (entry.isDirectory()) {
|
|
15407
15461
|
if (SKIP_DIRS.has(entry.name))
|
|
15408
15462
|
continue;
|
|
15409
|
-
visit(
|
|
15463
|
+
visit(join25(dir, entry.name));
|
|
15410
15464
|
continue;
|
|
15411
15465
|
}
|
|
15412
15466
|
if (!entry.isFile())
|
|
15413
15467
|
continue;
|
|
15414
|
-
const file =
|
|
15468
|
+
const file = join25(dir, entry.name);
|
|
15415
15469
|
if (shouldScanRepoFile(file))
|
|
15416
15470
|
out.push(file);
|
|
15417
15471
|
}
|
|
@@ -15649,7 +15703,7 @@ function trackedFiles(root) {
|
|
|
15649
15703
|
}
|
|
15650
15704
|
function isTrackedFile(file) {
|
|
15651
15705
|
try {
|
|
15652
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
15706
|
+
const repoRoot = execFileSync2("git", ["-C", dirname11(file), "rev-parse", "--show-toplevel"], {
|
|
15653
15707
|
encoding: "utf-8",
|
|
15654
15708
|
stdio: ["ignore", "pipe", "ignore"]
|
|
15655
15709
|
}).trim();
|
|
@@ -15679,7 +15733,7 @@ function stripInlineComment(value) {
|
|
|
15679
15733
|
return value.replace(/\s[#;].*$/, "").trim();
|
|
15680
15734
|
}
|
|
15681
15735
|
function displayPath(file, root) {
|
|
15682
|
-
const home =
|
|
15736
|
+
const home = homedir15();
|
|
15683
15737
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
15684
15738
|
return "~/" + toPosix(relative7(home, file));
|
|
15685
15739
|
if (isAbsolute5(root) && file.startsWith(root + "/"))
|
|
@@ -17379,9 +17433,9 @@ var {
|
|
|
17379
17433
|
// src/cli/index.tsx
|
|
17380
17434
|
init_apply();
|
|
17381
17435
|
import chalk from "chalk";
|
|
17382
|
-
import { existsSync as
|
|
17383
|
-
import { homedir as
|
|
17384
|
-
import { basename as basename8, join as
|
|
17436
|
+
import { existsSync as existsSync24, lstatSync as lstatSync8, readFileSync as readFileSync17, readSync, writeSync } from "fs";
|
|
17437
|
+
import { homedir as homedir16 } from "os";
|
|
17438
|
+
import { basename as basename8, join as join26, resolve as resolve16 } from "path";
|
|
17385
17439
|
|
|
17386
17440
|
// src/lib/config-target-identity.ts
|
|
17387
17441
|
init_apply();
|
|
@@ -17427,15 +17481,15 @@ init_redact();
|
|
|
17427
17481
|
|
|
17428
17482
|
// src/lib/export.ts
|
|
17429
17483
|
init_config_store();
|
|
17430
|
-
import { existsSync as
|
|
17431
|
-
import { join as
|
|
17484
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
17485
|
+
import { join as join18, resolve as resolve11 } from "path";
|
|
17432
17486
|
import { tmpdir } from "os";
|
|
17433
17487
|
async function exportConfigs(outputPath, opts = {}) {
|
|
17434
17488
|
const store = opts.store ?? resolveConfigStore();
|
|
17435
17489
|
const configs = await store.listConfigs(opts.filter);
|
|
17436
|
-
const absOutput =
|
|
17437
|
-
const tmpDir =
|
|
17438
|
-
const contentsDir =
|
|
17490
|
+
const absOutput = resolve11(outputPath);
|
|
17491
|
+
const tmpDir = join18(tmpdir(), `configs-export-${Date.now()}`);
|
|
17492
|
+
const contentsDir = join18(tmpDir, "contents");
|
|
17439
17493
|
try {
|
|
17440
17494
|
mkdirSync4(contentsDir, { recursive: true });
|
|
17441
17495
|
const manifest = {
|
|
@@ -17443,10 +17497,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17443
17497
|
exported_at: new Date().toISOString(),
|
|
17444
17498
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
17445
17499
|
};
|
|
17446
|
-
writeFileSync3(
|
|
17500
|
+
writeFileSync3(join18(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
17447
17501
|
for (const config of configs) {
|
|
17448
17502
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
17449
|
-
writeFileSync3(
|
|
17503
|
+
writeFileSync3(join18(contentsDir, fileName), config.content, "utf-8");
|
|
17450
17504
|
}
|
|
17451
17505
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
17452
17506
|
stdout: "pipe",
|
|
@@ -17459,7 +17513,7 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17459
17513
|
}
|
|
17460
17514
|
return { path: absOutput, count: configs.length };
|
|
17461
17515
|
} finally {
|
|
17462
|
-
if (
|
|
17516
|
+
if (existsSync15(tmpDir)) {
|
|
17463
17517
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
17464
17518
|
}
|
|
17465
17519
|
}
|
|
@@ -17467,14 +17521,14 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
17467
17521
|
|
|
17468
17522
|
// src/lib/import.ts
|
|
17469
17523
|
init_config_store();
|
|
17470
|
-
import { existsSync as
|
|
17471
|
-
import { join as
|
|
17524
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync4 } from "fs";
|
|
17525
|
+
import { join as join19, resolve as resolve12 } from "path";
|
|
17472
17526
|
import { tmpdir as tmpdir2 } from "os";
|
|
17473
17527
|
async function importConfigs(bundlePath, opts = {}) {
|
|
17474
17528
|
const store = opts.store ?? resolveConfigStore();
|
|
17475
17529
|
const conflict = opts.conflict ?? "skip";
|
|
17476
|
-
const absPath =
|
|
17477
|
-
const tmpDir =
|
|
17530
|
+
const absPath = resolve12(bundlePath);
|
|
17531
|
+
const tmpDir = join19(tmpdir2(), `configs-import-${Date.now()}`);
|
|
17478
17532
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
17479
17533
|
try {
|
|
17480
17534
|
mkdirSync5(tmpDir, { recursive: true });
|
|
@@ -17487,15 +17541,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
17487
17541
|
const stderr = await new Response(proc.stderr).text();
|
|
17488
17542
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
17489
17543
|
}
|
|
17490
|
-
const manifestPath =
|
|
17491
|
-
if (!
|
|
17544
|
+
const manifestPath = join19(tmpDir, "manifest.json");
|
|
17545
|
+
if (!existsSync16(manifestPath))
|
|
17492
17546
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
17493
17547
|
const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
|
|
17494
17548
|
for (const meta of manifest.configs) {
|
|
17495
17549
|
try {
|
|
17496
17550
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
17497
|
-
const contentFile =
|
|
17498
|
-
const content =
|
|
17551
|
+
const contentFile = join19(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
17552
|
+
const content = existsSync16(contentFile) ? readFileSync9(contentFile, "utf-8") : "";
|
|
17499
17553
|
let existing = null;
|
|
17500
17554
|
try {
|
|
17501
17555
|
existing = await store.getConfig(meta.slug);
|
|
@@ -17529,7 +17583,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
17529
17583
|
}
|
|
17530
17584
|
return result;
|
|
17531
17585
|
} finally {
|
|
17532
|
-
if (
|
|
17586
|
+
if (existsSync16(tmpDir)) {
|
|
17533
17587
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
17534
17588
|
}
|
|
17535
17589
|
}
|
|
@@ -17541,19 +17595,20 @@ init_machine();
|
|
|
17541
17595
|
|
|
17542
17596
|
// src/lib/session-apply.ts
|
|
17543
17597
|
init_project_context();
|
|
17598
|
+
init_session_render_state();
|
|
17544
17599
|
init_session_render();
|
|
17545
17600
|
init_cursor_authority();
|
|
17546
17601
|
init_session_authority();
|
|
17547
17602
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
|
|
17548
17603
|
import {
|
|
17549
|
-
existsSync as
|
|
17604
|
+
existsSync as existsSync17,
|
|
17550
17605
|
lstatSync as lstatSync4,
|
|
17551
17606
|
mkdirSync as mkdirSync6,
|
|
17552
17607
|
readFileSync as readFileSync10,
|
|
17553
|
-
readdirSync as
|
|
17608
|
+
readdirSync as readdirSync4,
|
|
17554
17609
|
statSync as statSync6
|
|
17555
17610
|
} from "fs";
|
|
17556
|
-
import { dirname as
|
|
17611
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join20, parse as parse4, relative as relative5, resolve as resolve13 } from "path";
|
|
17557
17612
|
|
|
17558
17613
|
class SessionApplyError extends Error {
|
|
17559
17614
|
constructor(message) {
|
|
@@ -17685,13 +17740,13 @@ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthoritie
|
|
|
17685
17740
|
throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
|
|
17686
17741
|
}
|
|
17687
17742
|
function ensureSessionTargetHome(targetHome) {
|
|
17688
|
-
if (!
|
|
17743
|
+
if (!existsSync17(targetHome))
|
|
17689
17744
|
mkdirSync6(targetHome, { recursive: true, mode: 448 });
|
|
17690
17745
|
assertSafeTargetHome(targetHome);
|
|
17691
17746
|
}
|
|
17692
17747
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
17693
17748
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
17694
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome,
|
|
17749
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve13(manifestPath)), safeTargetHome) : resolve13(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
17695
17750
|
const checkedAt = new Date().toISOString();
|
|
17696
17751
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
17697
17752
|
if (!previousManifest) {
|
|
@@ -17708,7 +17763,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
17708
17763
|
const drifted = [];
|
|
17709
17764
|
for (const file of previousManifest.files) {
|
|
17710
17765
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
17711
|
-
if (!
|
|
17766
|
+
if (!existsSync17(target)) {
|
|
17712
17767
|
missing.push({
|
|
17713
17768
|
path: target,
|
|
17714
17769
|
relativePath: file.relativePath,
|
|
@@ -17741,12 +17796,17 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
17741
17796
|
function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
17742
17797
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
17743
17798
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
17744
|
-
const resolvedSnapshotPath =
|
|
17745
|
-
const
|
|
17746
|
-
|
|
17747
|
-
|
|
17799
|
+
const resolvedSnapshotPath = resolve13(snapshotPath);
|
|
17800
|
+
const snapshotDir = getSessionRenderSnapshotDir(targetHome);
|
|
17801
|
+
const snapshotDirRelative = relative5(snapshotDir, resolvedSnapshotPath);
|
|
17802
|
+
const insideSnapshotDir = snapshotDirRelative !== ".." && !snapshotDirRelative.startsWith("../") && !isAbsolute4(snapshotDirRelative);
|
|
17803
|
+
if (!insideSnapshotDir) {
|
|
17804
|
+
const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
|
|
17805
|
+
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute4(snapshotRelativePath)) {
|
|
17806
|
+
throw new SessionApplyError("Session snapshot must be stored inside its session-render snapshot location.");
|
|
17807
|
+
}
|
|
17748
17808
|
}
|
|
17749
|
-
assertNoSymlinkSegments2(targetHome, resolvedSnapshotPath);
|
|
17809
|
+
assertNoSymlinkSegments2(sessionRenderSnapshotWorkspaceRoot(targetHome), resolvedSnapshotPath);
|
|
17750
17810
|
const guard = observeProjectContextSessionGuard({
|
|
17751
17811
|
tool: snapshot.tool,
|
|
17752
17812
|
target_home: targetHome,
|
|
@@ -17859,8 +17919,8 @@ function requiredRestoreHash(file) {
|
|
|
17859
17919
|
return file.previousSha256;
|
|
17860
17920
|
}
|
|
17861
17921
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
17862
|
-
const resolved =
|
|
17863
|
-
if (!
|
|
17922
|
+
const resolved = resolve13(snapshotPath);
|
|
17923
|
+
if (!existsSync17(resolved))
|
|
17864
17924
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
17865
17925
|
const stat = lstatSync4(resolved);
|
|
17866
17926
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -17940,7 +18000,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
17940
18000
|
}
|
|
17941
18001
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
17942
18002
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
17943
|
-
const manifestPath =
|
|
18003
|
+
const manifestPath = resolve13(snapshot.manifestPath);
|
|
17944
18004
|
const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
|
|
17945
18005
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
17946
18006
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
@@ -17957,7 +18017,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
17957
18017
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is invalid: ${snapshotPath}`);
|
|
17958
18018
|
}
|
|
17959
18019
|
const appliedManifest = parsedManifest;
|
|
17960
|
-
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" ||
|
|
18020
|
+
if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve13(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
|
|
17961
18021
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest does not match its snapshot: ${snapshotPath}`);
|
|
17962
18022
|
}
|
|
17963
18023
|
const afterFiles = [];
|
|
@@ -18041,9 +18101,9 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
18041
18101
|
if (!Number.isFinite(createdAtMs)) {
|
|
18042
18102
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
18043
18103
|
}
|
|
18044
|
-
for (const entry of
|
|
18045
|
-
const candidatePath =
|
|
18046
|
-
if (candidatePath ===
|
|
18104
|
+
for (const entry of readdirSync4(dirname7(snapshotPath))) {
|
|
18105
|
+
const candidatePath = resolve13(dirname7(snapshotPath), entry);
|
|
18106
|
+
if (candidatePath === resolve13(snapshotPath) || !entry.endsWith(".json"))
|
|
18047
18107
|
continue;
|
|
18048
18108
|
const candidateStat = lstatSync4(candidatePath);
|
|
18049
18109
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
@@ -18051,7 +18111,7 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
18051
18111
|
try {
|
|
18052
18112
|
const candidate = JSON.parse(readFileSync10(candidatePath, "utf8"));
|
|
18053
18113
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
18054
|
-
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" &&
|
|
18114
|
+
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve13(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
|
|
18055
18115
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
18056
18116
|
}
|
|
18057
18117
|
} catch (error) {
|
|
@@ -18120,14 +18180,14 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
18120
18180
|
}
|
|
18121
18181
|
function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
18122
18182
|
const path = resolveManifestRelativePath(relativePath, targetHome);
|
|
18123
|
-
if (
|
|
18183
|
+
if (resolve13(recordedPath) !== path) {
|
|
18124
18184
|
throw new SessionApplyError(`Session snapshot file path mismatch for ${relativePath}`);
|
|
18125
18185
|
}
|
|
18126
18186
|
return path;
|
|
18127
18187
|
}
|
|
18128
18188
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
18129
18189
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
18130
|
-
const previousContent =
|
|
18190
|
+
const previousContent = existsSync17(target) ? readFileSync10(target, "utf-8") : null;
|
|
18131
18191
|
const previousSha256 = previousContent === null ? null : sha2568(previousContent);
|
|
18132
18192
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
18133
18193
|
const changed = previousContent !== file.content;
|
|
@@ -18226,7 +18286,7 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
18226
18286
|
}
|
|
18227
18287
|
function planStaleFileResult(file, targetHome, options) {
|
|
18228
18288
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
18229
|
-
if (!
|
|
18289
|
+
if (!existsSync17(target))
|
|
18230
18290
|
return null;
|
|
18231
18291
|
const previousContent = readFileSync10(target, "utf-8");
|
|
18232
18292
|
const previousSha256 = sha2568(previousContent);
|
|
@@ -18273,19 +18333,19 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
18273
18333
|
return previousHashes.get(file.relativePath) === previousSha256;
|
|
18274
18334
|
}
|
|
18275
18335
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
18276
|
-
const target =
|
|
18336
|
+
const target = resolve13(targetHome, ...file.relativePath.split("/"));
|
|
18277
18337
|
const rel = relative5(targetHome, target);
|
|
18278
18338
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
18279
18339
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
18280
18340
|
}
|
|
18281
|
-
if (
|
|
18341
|
+
if (resolve13(file.path) !== target) {
|
|
18282
18342
|
throw new SessionApplyError(`Session file path mismatch for ${file.relativePath}: ${file.path}`);
|
|
18283
18343
|
}
|
|
18284
18344
|
assertNoSymlinkSegments2(targetHome, target);
|
|
18285
18345
|
return target;
|
|
18286
18346
|
}
|
|
18287
18347
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
18288
|
-
const target =
|
|
18348
|
+
const target = resolve13(targetHome, ...relativePath.split(/[\\/]+/));
|
|
18289
18349
|
const rel = relative5(targetHome, target);
|
|
18290
18350
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute4(rel)) {
|
|
18291
18351
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
@@ -18294,7 +18354,7 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
18294
18354
|
return target;
|
|
18295
18355
|
}
|
|
18296
18356
|
function readPreviousManifest(path) {
|
|
18297
|
-
if (!
|
|
18357
|
+
if (!existsSync17(path))
|
|
18298
18358
|
return null;
|
|
18299
18359
|
try {
|
|
18300
18360
|
const parsed = JSON.parse(readFileSync10(path, "utf-8"));
|
|
@@ -18336,7 +18396,7 @@ function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
|
18336
18396
|
}
|
|
18337
18397
|
function currentSessionFileHash(path, targetHome) {
|
|
18338
18398
|
assertNoSymlinkSegments2(targetHome, path);
|
|
18339
|
-
if (!
|
|
18399
|
+
if (!existsSync17(path))
|
|
18340
18400
|
return null;
|
|
18341
18401
|
const stat = lstatSync4(path);
|
|
18342
18402
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -18351,7 +18411,7 @@ function requiredPreviousHash(result) {
|
|
|
18351
18411
|
return result.previousSha256;
|
|
18352
18412
|
}
|
|
18353
18413
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
18354
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) =>
|
|
18414
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync17(result.path)).map((result) => {
|
|
18355
18415
|
const content = readFileSync10(result.path, "utf-8");
|
|
18356
18416
|
return {
|
|
18357
18417
|
path: result.path,
|
|
@@ -18370,7 +18430,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18370
18430
|
};
|
|
18371
18431
|
}
|
|
18372
18432
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
18373
|
-
const snapshotPath =
|
|
18433
|
+
const snapshotPath = join20(getSessionRenderSnapshotDir(targetHome), `${timestamp}-${randomUUID7()}.json`);
|
|
18374
18434
|
const afterFiles = results.map((result) => {
|
|
18375
18435
|
if (result.action === "conflict") {
|
|
18376
18436
|
throw new SessionApplyError(`Cannot snapshot unresolved conflict: ${result.relativePath}`);
|
|
@@ -18400,7 +18460,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18400
18460
|
path: snapshotPath,
|
|
18401
18461
|
content: `${JSON.stringify(snapshot, null, 2)}
|
|
18402
18462
|
`,
|
|
18403
|
-
workspace_root: targetHome,
|
|
18463
|
+
workspace_root: sessionRenderSnapshotWorkspaceRoot(targetHome),
|
|
18404
18464
|
default_mode: 384,
|
|
18405
18465
|
expected_hash: null,
|
|
18406
18466
|
max_observed_bytes: null,
|
|
@@ -18418,12 +18478,12 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
18418
18478
|
function assertSafeTargetHome(targetHome) {
|
|
18419
18479
|
if (!isAbsolute4(targetHome))
|
|
18420
18480
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
18421
|
-
const normalized =
|
|
18481
|
+
const normalized = resolve13(targetHome);
|
|
18422
18482
|
if (normalized === parse4(normalized).root) {
|
|
18423
18483
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
18424
18484
|
}
|
|
18425
18485
|
assertNoSymlinkAncestors2(normalized);
|
|
18426
|
-
if (
|
|
18486
|
+
if (existsSync17(normalized) && lstatSync4(normalized).isSymbolicLink()) {
|
|
18427
18487
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
18428
18488
|
}
|
|
18429
18489
|
return normalized;
|
|
@@ -18433,20 +18493,20 @@ function assertNoSymlinkSegments2(root, target) {
|
|
|
18433
18493
|
const rel = relative5(root, target);
|
|
18434
18494
|
let current = root;
|
|
18435
18495
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18436
|
-
current =
|
|
18437
|
-
if (
|
|
18496
|
+
current = join20(current, segment);
|
|
18497
|
+
if (existsSync17(current) && lstatSync4(current).isSymbolicLink()) {
|
|
18438
18498
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
18439
18499
|
}
|
|
18440
18500
|
}
|
|
18441
18501
|
}
|
|
18442
18502
|
function assertNoSymlinkAncestors2(path) {
|
|
18443
|
-
const normalized =
|
|
18503
|
+
const normalized = resolve13(path);
|
|
18444
18504
|
const parsed = parse4(normalized);
|
|
18445
18505
|
let current = parsed.root;
|
|
18446
18506
|
const rel = relative5(parsed.root, normalized);
|
|
18447
18507
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
18448
|
-
current =
|
|
18449
|
-
if (!
|
|
18508
|
+
current = join20(current, segment);
|
|
18509
|
+
if (!existsSync17(current))
|
|
18450
18510
|
return;
|
|
18451
18511
|
if (lstatSync4(current).isSymbolicLink()) {
|
|
18452
18512
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
@@ -18497,9 +18557,9 @@ function formatGlobalSourceCoverageWarnings(result) {
|
|
|
18497
18557
|
// src/lib/station-profile.ts
|
|
18498
18558
|
init_raw_store_root();
|
|
18499
18559
|
import { spawnSync } from "child_process";
|
|
18500
|
-
import { existsSync as
|
|
18501
|
-
import { arch as osArch, homedir as
|
|
18502
|
-
import { dirname as
|
|
18560
|
+
import { existsSync as existsSync18, lstatSync as lstatSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
18561
|
+
import { arch as osArch, homedir as homedir13, hostname as osHostname, platform as osPlatform, userInfo as osUserInfo } from "os";
|
|
18562
|
+
import { dirname as dirname8, join as join21 } from "path";
|
|
18503
18563
|
var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
|
|
18504
18564
|
var STATION_PROFILE_SOURCE_ID = "station-profile";
|
|
18505
18565
|
var STATION_PROFILE_LAYER = "machine";
|
|
@@ -18509,21 +18569,21 @@ var STATION_PROFILE_FULL_NAMES_MAX = 6;
|
|
|
18509
18569
|
var STATION_PROFILE_PRIMARY_SCOPE = "@hasna";
|
|
18510
18570
|
var MACHINES_MANIFEST_PATH_ENV = "HASNA_MACHINES_MANIFEST_PATH";
|
|
18511
18571
|
var BUN_INSTALL_ENV = "BUN_INSTALL";
|
|
18512
|
-
function
|
|
18513
|
-
return env["HOME"] || env["USERPROFILE"] ||
|
|
18572
|
+
function homeDir4(env = process.env) {
|
|
18573
|
+
return env["HOME"] || env["USERPROFILE"] || homedir13();
|
|
18514
18574
|
}
|
|
18515
18575
|
function getStationProfileCachePath(env = process.env) {
|
|
18516
|
-
return
|
|
18576
|
+
return join21(getRawStoreRoot(env), STATION_PROFILE_CACHE_FILENAME);
|
|
18517
18577
|
}
|
|
18518
18578
|
function getMachinesManifestPath(env = process.env) {
|
|
18519
|
-
return env[MACHINES_MANIFEST_PATH_ENV] ||
|
|
18579
|
+
return env[MACHINES_MANIFEST_PATH_ENV] || join21(homeDir4(env), ".hasna", "machines", "machines.json");
|
|
18520
18580
|
}
|
|
18521
18581
|
function getBunGlobalModulesDir(env = process.env) {
|
|
18522
|
-
return
|
|
18582
|
+
return join21(env[BUN_INSTALL_ENV] || join21(homeDir4(env), ".bun"), "install", "global", "node_modules");
|
|
18523
18583
|
}
|
|
18524
18584
|
function readMachinesManifest(path) {
|
|
18525
18585
|
try {
|
|
18526
|
-
if (!
|
|
18586
|
+
if (!existsSync18(path))
|
|
18527
18587
|
return null;
|
|
18528
18588
|
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
18529
18589
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -18579,9 +18639,9 @@ function probeMachineStatus(machineId) {
|
|
|
18579
18639
|
function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
18580
18640
|
const hostname2 = osHostname();
|
|
18581
18641
|
const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
|
|
18582
|
-
const home =
|
|
18642
|
+
const home = homeDir4(env);
|
|
18583
18643
|
const platform = stringField(record, "platform") ?? osPlatform();
|
|
18584
|
-
const workspacePath = stringField(record, "workspacePath") ??
|
|
18644
|
+
const workspacePath = stringField(record, "workspacePath") ?? join21(home, platform === "darwin" ? "Workspace" : "workspace");
|
|
18585
18645
|
const machine = {
|
|
18586
18646
|
id: stringField(record, "id") ?? hostname2,
|
|
18587
18647
|
hostname: stringField(record, "hostname") ?? hostname2,
|
|
@@ -18598,9 +18658,9 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
|
|
|
18598
18658
|
return machine;
|
|
18599
18659
|
}
|
|
18600
18660
|
function scopedPackageNames(modulesDir, scope) {
|
|
18601
|
-
const scopeDir =
|
|
18661
|
+
const scopeDir = join21(modulesDir, scope);
|
|
18602
18662
|
try {
|
|
18603
|
-
if (!
|
|
18663
|
+
if (!existsSync18(scopeDir))
|
|
18604
18664
|
return null;
|
|
18605
18665
|
return readdirNames(scopeDir).sort();
|
|
18606
18666
|
} catch {
|
|
@@ -18608,9 +18668,9 @@ function scopedPackageNames(modulesDir, scope) {
|
|
|
18608
18668
|
}
|
|
18609
18669
|
}
|
|
18610
18670
|
function readdirNames(dir) {
|
|
18611
|
-
return
|
|
18671
|
+
return readdirSync5(dir).filter((name) => {
|
|
18612
18672
|
try {
|
|
18613
|
-
return lstatSync5(
|
|
18673
|
+
return lstatSync5(join21(dir, name)).isDirectory();
|
|
18614
18674
|
} catch {
|
|
18615
18675
|
return false;
|
|
18616
18676
|
}
|
|
@@ -18620,7 +18680,7 @@ function resolveStationProfilePackages(env = process.env) {
|
|
|
18620
18680
|
const modulesDir = getBunGlobalModulesDir(env);
|
|
18621
18681
|
let scopeDirs;
|
|
18622
18682
|
try {
|
|
18623
|
-
if (!
|
|
18683
|
+
if (!existsSync18(modulesDir))
|
|
18624
18684
|
return null;
|
|
18625
18685
|
scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
|
|
18626
18686
|
} catch {
|
|
@@ -18695,9 +18755,9 @@ function refreshStationProfile(options = {}) {
|
|
|
18695
18755
|
const path = getStationProfileCachePath(env);
|
|
18696
18756
|
const generatedAt = new Date().toISOString();
|
|
18697
18757
|
if (!options.dryRun) {
|
|
18698
|
-
const existing =
|
|
18758
|
+
const existing = existsSync18(path) ? readFileSync11(path, "utf8") : null;
|
|
18699
18759
|
if (existing !== content) {
|
|
18700
|
-
mkdirSync7(
|
|
18760
|
+
mkdirSync7(dirname8(path), { recursive: true });
|
|
18701
18761
|
writeFileSync4(path, content, "utf8");
|
|
18702
18762
|
}
|
|
18703
18763
|
}
|
|
@@ -18714,7 +18774,7 @@ function refreshStationProfile(options = {}) {
|
|
|
18714
18774
|
function readStationProfile(env = process.env) {
|
|
18715
18775
|
const path = getStationProfileCachePath(env);
|
|
18716
18776
|
try {
|
|
18717
|
-
if (!
|
|
18777
|
+
if (!existsSync18(path))
|
|
18718
18778
|
return null;
|
|
18719
18779
|
return readFileSync11(path, "utf8");
|
|
18720
18780
|
} catch {
|
|
@@ -19053,7 +19113,7 @@ init_codewith_shared_todos_storage_standard();
|
|
|
19053
19113
|
import { createHash as createHash9 } from "crypto";
|
|
19054
19114
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
19055
19115
|
import {
|
|
19056
|
-
existsSync as
|
|
19116
|
+
existsSync as existsSync19,
|
|
19057
19117
|
lstatSync as lstatSync6,
|
|
19058
19118
|
mkdirSync as mkdirSync8,
|
|
19059
19119
|
readFileSync as readFileSync12,
|
|
@@ -19061,8 +19121,8 @@ import {
|
|
|
19061
19121
|
rmSync as rmSync5,
|
|
19062
19122
|
writeFileSync as writeFileSync5
|
|
19063
19123
|
} from "fs";
|
|
19064
|
-
import { homedir as
|
|
19065
|
-
import { dirname as
|
|
19124
|
+
import { homedir as homedir14 } from "os";
|
|
19125
|
+
import { dirname as dirname9, join as join22, parse as parse5, relative as relative6, resolve as resolve14 } from "path";
|
|
19066
19126
|
var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
|
|
19067
19127
|
var INBOX_SKILL_MARKERS = [
|
|
19068
19128
|
[".claude", "skills", "inbox", "SKILL.md"],
|
|
@@ -19083,13 +19143,13 @@ function lstatOrNull(path) {
|
|
|
19083
19143
|
}
|
|
19084
19144
|
}
|
|
19085
19145
|
function findSymlinkedAncestor(path) {
|
|
19086
|
-
const normalized =
|
|
19146
|
+
const normalized = resolve14(path);
|
|
19087
19147
|
const parsed = parse5(normalized);
|
|
19088
19148
|
let current = parsed.root;
|
|
19089
19149
|
const rel = relative6(parsed.root, normalized);
|
|
19090
19150
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
19091
|
-
current =
|
|
19092
|
-
if (!
|
|
19151
|
+
current = join22(current, segment);
|
|
19152
|
+
if (!existsSync19(current))
|
|
19093
19153
|
return null;
|
|
19094
19154
|
if (lstatSync6(current).isSymbolicLink())
|
|
19095
19155
|
return current;
|
|
@@ -19106,11 +19166,11 @@ function packagedInboxSkillPath(explicitPath) {
|
|
|
19106
19166
|
if (explicitPath)
|
|
19107
19167
|
return explicitPath;
|
|
19108
19168
|
const candidates = [
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
|
|
19169
|
+
join22(import.meta.dir, "..", "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
19170
|
+
join22(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
|
|
19171
|
+
join22(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
|
|
19112
19172
|
];
|
|
19113
|
-
const found = candidates.find((candidate) =>
|
|
19173
|
+
const found = candidates.find((candidate) => existsSync19(candidate));
|
|
19114
19174
|
if (!found) {
|
|
19115
19175
|
throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
|
|
19116
19176
|
}
|
|
@@ -19159,8 +19219,8 @@ function compareVersions(left, right) {
|
|
|
19159
19219
|
}
|
|
19160
19220
|
return 0;
|
|
19161
19221
|
}
|
|
19162
|
-
function inspectSkillMarkers(
|
|
19163
|
-
return INBOX_SKILL_MARKERS.map((parts) =>
|
|
19222
|
+
function inspectSkillMarkers(homeDir5) {
|
|
19223
|
+
return INBOX_SKILL_MARKERS.map((parts) => join22(homeDir5, ...parts)).map((path) => {
|
|
19164
19224
|
const stat = lstatOrNull(path);
|
|
19165
19225
|
if (!stat)
|
|
19166
19226
|
return null;
|
|
@@ -19176,9 +19236,9 @@ function inspectSkillMarkers(homeDir4) {
|
|
|
19176
19236
|
}).filter((snapshot) => snapshot !== null);
|
|
19177
19237
|
}
|
|
19178
19238
|
function inspectInbox(options) {
|
|
19179
|
-
const
|
|
19239
|
+
const homeDir5 = options.homeDir ?? homedir14();
|
|
19180
19240
|
const runtimeCommand = options.conversationsCommand ?? "conversations";
|
|
19181
|
-
const snapshots = inspectSkillMarkers(
|
|
19241
|
+
const snapshots = inspectSkillMarkers(homeDir5);
|
|
19182
19242
|
const skillPresent = snapshots.length > 0;
|
|
19183
19243
|
let canonicalContent = null;
|
|
19184
19244
|
let canonicalSha256 = null;
|
|
@@ -19204,7 +19264,7 @@ function inspectInbox(options) {
|
|
|
19204
19264
|
let reason = "skill not installed";
|
|
19205
19265
|
if (skillPresent) {
|
|
19206
19266
|
const nonRegular = snapshots.some((snapshot) => !snapshot.regular);
|
|
19207
|
-
const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(
|
|
19267
|
+
const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname9(path))).find((found) => found !== null);
|
|
19208
19268
|
if (nonRegular)
|
|
19209
19269
|
reason = "managed skill target is not a regular file";
|
|
19210
19270
|
else if (symlinkAncestor)
|
|
@@ -19291,10 +19351,10 @@ function cleanup(path) {
|
|
|
19291
19351
|
rmSync5(path, { force: true });
|
|
19292
19352
|
}
|
|
19293
19353
|
function writeAtomic(path, content, mode) {
|
|
19294
|
-
assertNoSymlinkAncestors3(
|
|
19354
|
+
assertNoSymlinkAncestors3(dirname9(path));
|
|
19295
19355
|
const tempPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
19296
19356
|
try {
|
|
19297
|
-
mkdirSync8(
|
|
19357
|
+
mkdirSync8(dirname9(path), { recursive: true, mode: 493 });
|
|
19298
19358
|
writeFileSync5(tempPath, content, { mode, flag: "wx" });
|
|
19299
19359
|
renameSync2(tempPath, path);
|
|
19300
19360
|
} finally {
|
|
@@ -19370,7 +19430,7 @@ async function reconcileManagedSkillRuntimes(options = {}) {
|
|
|
19370
19430
|
dry_run: dryRun
|
|
19371
19431
|
};
|
|
19372
19432
|
}
|
|
19373
|
-
const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(
|
|
19433
|
+
const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname9(path))).find((found) => found !== null);
|
|
19374
19434
|
if (symlinkedAncestor) {
|
|
19375
19435
|
return {
|
|
19376
19436
|
runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
|
|
@@ -19453,28 +19513,28 @@ init_project_context();
|
|
|
19453
19513
|
init_config_store();
|
|
19454
19514
|
init_apply();
|
|
19455
19515
|
init_config_agents();
|
|
19456
|
-
import { existsSync as
|
|
19516
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "fs";
|
|
19457
19517
|
|
|
19458
19518
|
// src/lib/package-version.ts
|
|
19459
|
-
import { existsSync as
|
|
19460
|
-
import { dirname as
|
|
19519
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13 } from "fs";
|
|
19520
|
+
import { dirname as dirname10, join as join23 } from "path";
|
|
19461
19521
|
import { fileURLToPath } from "url";
|
|
19462
19522
|
var cached = null;
|
|
19463
19523
|
function getPackageVersion() {
|
|
19464
19524
|
if (cached)
|
|
19465
19525
|
return cached;
|
|
19466
19526
|
try {
|
|
19467
|
-
let dir =
|
|
19527
|
+
let dir = dirname10(fileURLToPath(import.meta.url));
|
|
19468
19528
|
for (let i = 0;i < 8; i++) {
|
|
19469
|
-
const pkgPath =
|
|
19470
|
-
if (
|
|
19529
|
+
const pkgPath = join23(dir, "package.json");
|
|
19530
|
+
if (existsSync20(pkgPath)) {
|
|
19471
19531
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
19472
19532
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
19473
19533
|
cached = pkg.version;
|
|
19474
19534
|
return cached;
|
|
19475
19535
|
}
|
|
19476
19536
|
}
|
|
19477
|
-
const parent =
|
|
19537
|
+
const parent = dirname10(dir);
|
|
19478
19538
|
if (parent === dir)
|
|
19479
19539
|
break;
|
|
19480
19540
|
dir = parent;
|
|
@@ -19531,7 +19591,7 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
|
|
|
19531
19591
|
continue;
|
|
19532
19592
|
knownTargets += 1;
|
|
19533
19593
|
const targetPath = expandPath(config.target_path);
|
|
19534
|
-
if (!
|
|
19594
|
+
if (!existsSync21(targetPath)) {
|
|
19535
19595
|
missingTargets += 1;
|
|
19536
19596
|
continue;
|
|
19537
19597
|
}
|
|
@@ -19631,8 +19691,8 @@ init_config_store();
|
|
|
19631
19691
|
|
|
19632
19692
|
// src/lib/provider-context.ts
|
|
19633
19693
|
import { createHash as createHash10 } from "crypto";
|
|
19634
|
-
import { existsSync as
|
|
19635
|
-
import { join as
|
|
19694
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
19695
|
+
import { join as join24 } from "path";
|
|
19636
19696
|
var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
|
|
19637
19697
|
var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
|
|
19638
19698
|
var PROVIDER_CONTEXT_SCHEMA = "hasna.instructions.provider-context/v1";
|
|
@@ -19777,17 +19837,17 @@ function resolveAndRenderProviderContext(opts) {
|
|
|
19777
19837
|
const recordedEndpoint = originAccepted ? `${opts.origin.host}${opts.origin.pathPrefix || ""}` : null;
|
|
19778
19838
|
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;
|
|
19779
19839
|
const content = renderProviderFragment(entry);
|
|
19780
|
-
const dir =
|
|
19781
|
-
if (!
|
|
19840
|
+
const dir = join24(opts.homeDir, PROVIDER_CONTEXT_DIR);
|
|
19841
|
+
if (!existsSync22(dir))
|
|
19782
19842
|
mkdirSync9(dir, { recursive: true });
|
|
19783
19843
|
const filename = `${entry ? entry.key : "invariant"}.md`;
|
|
19784
|
-
const fragmentPath2 =
|
|
19844
|
+
const fragmentPath2 = join24(dir, filename);
|
|
19785
19845
|
const fragmentSha256 = sha25610(content);
|
|
19786
19846
|
writeFileSync6(fragmentPath2, content, "utf8");
|
|
19787
|
-
const manifestPath =
|
|
19847
|
+
const manifestPath = join24(dir, PROVIDER_CONTEXT_MANIFEST);
|
|
19788
19848
|
let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
|
|
19789
19849
|
try {
|
|
19790
|
-
if (
|
|
19850
|
+
if (existsSync22(manifestPath)) {
|
|
19791
19851
|
const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
19792
19852
|
if (parsed && typeof parsed === "object")
|
|
19793
19853
|
manifest = parsed;
|
|
@@ -19931,7 +19991,7 @@ function parseSessionSource(value, order) {
|
|
|
19931
19991
|
if (!path)
|
|
19932
19992
|
throw new Error(`Invalid --source "${value}" (expected path or id=path)`);
|
|
19933
19993
|
const absPath = resolveSessionPath(path);
|
|
19934
|
-
if (!
|
|
19994
|
+
if (!existsSync24(absPath))
|
|
19935
19995
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
19936
19996
|
const content = readSessionInstructionSourceFile(absPath);
|
|
19937
19997
|
const source = sourceFromFilePath(absPath, content, order);
|
|
@@ -20012,7 +20072,7 @@ async function collectSessionSources(opts, tool, store) {
|
|
|
20012
20072
|
}
|
|
20013
20073
|
for (const value of opts.identityExport ?? []) {
|
|
20014
20074
|
const path = resolveSessionPath(value);
|
|
20015
|
-
if (!
|
|
20075
|
+
if (!existsSync24(path))
|
|
20016
20076
|
throw new Error(`Identity instruction export not found: ${path}`);
|
|
20017
20077
|
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
20018
20078
|
sources.push(...sourcesFromIdentityExport(parsed, { path, tool, orderOffset: sources.length }));
|
|
@@ -20146,7 +20206,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
20146
20206
|
if (value === "-")
|
|
20147
20207
|
return { json: readBoundedProjectContextStdin() };
|
|
20148
20208
|
const path = resolveSessionPath(value);
|
|
20149
|
-
if (!
|
|
20209
|
+
if (!existsSync24(path)) {
|
|
20150
20210
|
if (allowMissing)
|
|
20151
20211
|
return {};
|
|
20152
20212
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_MISSING", `bundle file not found: ${path}`);
|
|
@@ -20317,8 +20377,8 @@ program.command("tag <id>").description("Add or remove tags on a stored config (
|
|
|
20317
20377
|
console.log(chalk.green("\u2713") + ` Tags on ${chalk.bold(updated.name)} ${chalk.dim(`(${updated.slug})`)}: ${nextTags.join(", ") || chalk.dim("(none)")}`);
|
|
20318
20378
|
});
|
|
20319
20379
|
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) => {
|
|
20320
|
-
const abs =
|
|
20321
|
-
if (!
|
|
20380
|
+
const abs = resolve16(filePath);
|
|
20381
|
+
if (!existsSync24(abs)) {
|
|
20322
20382
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
20323
20383
|
process.exit(1);
|
|
20324
20384
|
}
|
|
@@ -20326,7 +20386,7 @@ program.command("add <path>").description("Ingest a file into the config DB").op
|
|
|
20326
20386
|
const storedFmt = detectFormat(abs);
|
|
20327
20387
|
const fmt = redactFormatForTarget(abs, storedFmt);
|
|
20328
20388
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
20329
|
-
const targetPath = abs.startsWith(
|
|
20389
|
+
const targetPath = abs.startsWith(homedir16()) ? abs.replace(homedir16(), "~") : abs;
|
|
20330
20390
|
const name = opts.name || filePath.split("/").pop();
|
|
20331
20391
|
const store = resolveConfigStore();
|
|
20332
20392
|
const allConfigs = await store.listConfigs();
|
|
@@ -20494,14 +20554,14 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
20494
20554
|
if (opts.project) {
|
|
20495
20555
|
const dir = typeof opts.project === "string" ? opts.project : process.cwd();
|
|
20496
20556
|
if (opts.all) {
|
|
20497
|
-
const { readdirSync:
|
|
20557
|
+
const { readdirSync: readdirSync7 } = await import("fs");
|
|
20498
20558
|
const absDir = expandPath(dir);
|
|
20499
|
-
const entries =
|
|
20559
|
+
const entries = readdirSync7(absDir, { withFileTypes: true });
|
|
20500
20560
|
let totalAdded = 0, totalUpdated = 0, totalUnchanged = 0, projects = 0;
|
|
20501
20561
|
for (const entry of entries) {
|
|
20502
20562
|
if (!entry.isDirectory())
|
|
20503
20563
|
continue;
|
|
20504
|
-
const projDir =
|
|
20564
|
+
const projDir = join26(absDir, entry.name);
|
|
20505
20565
|
const hasAgentConfig = [
|
|
20506
20566
|
"CLAUDE.md",
|
|
20507
20567
|
".mcp.json",
|
|
@@ -20514,7 +20574,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
20514
20574
|
".aicopilot",
|
|
20515
20575
|
".cursor",
|
|
20516
20576
|
".agents"
|
|
20517
|
-
].some((marker) =>
|
|
20577
|
+
].some((marker) => existsSync24(join26(projDir, marker)));
|
|
20518
20578
|
if (!hasAgentConfig)
|
|
20519
20579
|
continue;
|
|
20520
20580
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun, store });
|
|
@@ -20565,7 +20625,7 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
20565
20625
|
});
|
|
20566
20626
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
20567
20627
|
const store = resolveConfigStore();
|
|
20568
|
-
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
20628
|
+
const dbPath = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join26(getRawStoreRoot(), "instructions.db");
|
|
20569
20629
|
const stats = await store.getConfigStats();
|
|
20570
20630
|
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
20571
20631
|
console.log(chalk.cyan(isApiTransport() ? "API:" : "DB:") + " " + dbPath);
|
|
@@ -21444,7 +21504,7 @@ mcpCmd.command("install").alias("add").description("Install configs MCP server i
|
|
|
21444
21504
|
} else if (target === "codex") {
|
|
21445
21505
|
const { appendFileSync, existsSync: ex } = await import("fs");
|
|
21446
21506
|
const { join: j } = await import("path");
|
|
21447
|
-
const configPath = j(
|
|
21507
|
+
const configPath = j(homedir16(), ".codex", "config.toml");
|
|
21448
21508
|
const block = `
|
|
21449
21509
|
[mcp_servers.configs]
|
|
21450
21510
|
command = "${mcpBinary}"
|
|
@@ -21462,7 +21522,7 @@ args = []
|
|
|
21462
21522
|
} else if (target === "antigravity") {
|
|
21463
21523
|
const { mkdirSync: md, readFileSync: rf, writeFileSync: wf, existsSync: ex } = await import("fs");
|
|
21464
21524
|
const { dirname: dn, join: j } = await import("path");
|
|
21465
|
-
const configPath = j(
|
|
21525
|
+
const configPath = j(homedir16(), ".gemini", "config", "mcp_config.json");
|
|
21466
21526
|
let settings = {};
|
|
21467
21527
|
if (ex(configPath)) {
|
|
21468
21528
|
try {
|
|
@@ -21546,7 +21606,7 @@ DB stats:`));
|
|
|
21546
21606
|
if (count > 0)
|
|
21547
21607
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
21548
21608
|
}
|
|
21549
|
-
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
21609
|
+
const location = isApiTransport() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join26(getRawStoreRoot(), "instructions.db");
|
|
21550
21610
|
console.log(chalk.dim(`
|
|
21551
21611
|
${isApiTransport() ? "API" : "DB"}: ${location}`));
|
|
21552
21612
|
});
|
|
@@ -21607,10 +21667,10 @@ managedSkillsCmd.command("apply").option("--dry-run", "preview without writing")
|
|
|
21607
21667
|
});
|
|
21608
21668
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
21609
21669
|
const { mkdirSync: mk } = await import("fs");
|
|
21610
|
-
const backupDir =
|
|
21670
|
+
const backupDir = join26(getRawStoreRoot(), "backups");
|
|
21611
21671
|
mk(backupDir, { recursive: true });
|
|
21612
21672
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
21613
|
-
const outPath =
|
|
21673
|
+
const outPath = join26(backupDir, `configs-${ts}.tar.gz`);
|
|
21614
21674
|
const result = await exportConfigs(outPath, { store: resolveConfigStore() });
|
|
21615
21675
|
const { statSync: st } = await import("fs");
|
|
21616
21676
|
const size = st(outPath).size;
|
|
@@ -21638,9 +21698,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
21638
21698
|
console.log(chalk.cyan("Known files on disk:"));
|
|
21639
21699
|
for (const k of KNOWN_CONFIGS) {
|
|
21640
21700
|
if (k.rulesDir) {
|
|
21641
|
-
|
|
21701
|
+
existsSync24(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail2(`${k.rulesDir}/ not found`);
|
|
21642
21702
|
} else {
|
|
21643
|
-
|
|
21703
|
+
existsSync24(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail2(`${k.path} not found`);
|
|
21644
21704
|
}
|
|
21645
21705
|
}
|
|
21646
21706
|
const allConfigs = await store.listConfigs();
|
|
@@ -21794,16 +21854,16 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21794
21854
|
for (const k of KNOWN_CONFIGS) {
|
|
21795
21855
|
if (k.rulesDir) {
|
|
21796
21856
|
const absDir = expandPath2(k.rulesDir);
|
|
21797
|
-
if (!
|
|
21857
|
+
if (!existsSync24(absDir))
|
|
21798
21858
|
continue;
|
|
21799
|
-
const { readdirSync:
|
|
21800
|
-
for (const f of
|
|
21801
|
-
const abs =
|
|
21859
|
+
const { readdirSync: readdirSync7 } = await import("fs");
|
|
21860
|
+
for (const f of readdirSync7(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
21861
|
+
const abs = join26(absDir, f);
|
|
21802
21862
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21803
21863
|
}
|
|
21804
21864
|
} else {
|
|
21805
21865
|
const abs = expandPath2(k.path);
|
|
21806
|
-
if (
|
|
21866
|
+
if (existsSync24(abs))
|
|
21807
21867
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21808
21868
|
}
|
|
21809
21869
|
}
|
|
@@ -21811,7 +21871,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21811
21871
|
const tick = async () => {
|
|
21812
21872
|
let changed = 0;
|
|
21813
21873
|
for (const [abs, oldMtime] of mtimes) {
|
|
21814
|
-
if (!
|
|
21874
|
+
if (!existsSync24(abs))
|
|
21815
21875
|
continue;
|
|
21816
21876
|
const newMtime = st(abs).mtimeMs;
|
|
21817
21877
|
if (newMtime !== oldMtime) {
|
|
@@ -21823,10 +21883,10 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21823
21883
|
for (const k of KNOWN_CONFIGS) {
|
|
21824
21884
|
if (k.rulesDir) {
|
|
21825
21885
|
const absDir = expandPath2(k.rulesDir);
|
|
21826
|
-
if (!
|
|
21886
|
+
if (!existsSync24(absDir))
|
|
21827
21887
|
continue;
|
|
21828
21888
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
21829
|
-
const abs =
|
|
21889
|
+
const abs = join26(absDir, f);
|
|
21830
21890
|
if (!mtimes.has(abs)) {
|
|
21831
21891
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21832
21892
|
changed++;
|
|
@@ -21834,7 +21894,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
21834
21894
|
}
|
|
21835
21895
|
} else {
|
|
21836
21896
|
const abs = expandPath2(k.path);
|
|
21837
|
-
if (
|
|
21897
|
+
if (existsSync24(abs) && !mtimes.has(abs)) {
|
|
21838
21898
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
21839
21899
|
changed++;
|
|
21840
21900
|
}
|
|
@@ -21862,7 +21922,7 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
21862
21922
|
if (!c.target_path)
|
|
21863
21923
|
continue;
|
|
21864
21924
|
const abs = expandPath(c.target_path);
|
|
21865
|
-
if (!
|
|
21925
|
+
if (!existsSync24(abs)) {
|
|
21866
21926
|
missing++;
|
|
21867
21927
|
continue;
|
|
21868
21928
|
}
|
|
@@ -21933,7 +21993,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
21933
21993
|
if (!c.target_path)
|
|
21934
21994
|
continue;
|
|
21935
21995
|
const abs = expandPath(c.target_path);
|
|
21936
|
-
if (!
|
|
21996
|
+
if (!existsSync24(abs)) {
|
|
21937
21997
|
if (printed < maxPrinted) {
|
|
21938
21998
|
if (opts.dryRun) {
|
|
21939
21999
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${truncateMiddle(c.target_path, 88)})`)}`);
|
|
@@ -22079,13 +22139,13 @@ providerContextCmd.command("resolve").description("Resolve the endpoint to a pro
|
|
|
22079
22139
|
try {
|
|
22080
22140
|
const rawEndpoint = opts.endpoint ?? process.env["ANTHROPIC_BASE_URL"] ?? "";
|
|
22081
22141
|
const rawModel = opts.model ?? process.env["ANTHROPIC_MODEL"] ?? "";
|
|
22082
|
-
const
|
|
22142
|
+
const homeDir5 = opts.home ?? homedir16();
|
|
22083
22143
|
const origin = normalizeEndpointOrigin(rawEndpoint);
|
|
22084
22144
|
const resolution = resolveAndRenderProviderContext({
|
|
22085
22145
|
origin,
|
|
22086
22146
|
rawEndpoint,
|
|
22087
22147
|
rawModel,
|
|
22088
|
-
homeDir:
|
|
22148
|
+
homeDir: homeDir5
|
|
22089
22149
|
});
|
|
22090
22150
|
if (opts.json) {
|
|
22091
22151
|
printJson({
|