@hasna/instructions 0.4.24 → 0.4.26
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 +188 -22
- package/dist/cli/session-apply-managed-input-bound.test.d.ts +2 -0
- package/dist/cli/session-apply-managed-input-bound.test.d.ts.map +1 -0
- package/dist/index.js +172 -20
- package/dist/lib/project-context.d.ts +1 -0
- package/dist/lib/project-context.d.ts.map +1 -1
- package/dist/mcp/index.js +21 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7930,6 +7930,9 @@ function computeProjectContextSourceHash(value) {
|
|
|
7930
7930
|
return `sha256:${sha2562(stableStringify(normalized))}`;
|
|
7931
7931
|
}
|
|
7932
7932
|
function parseProjectContextBundle(input) {
|
|
7933
|
+
return parseProjectContextBundleInternal(input, true, true);
|
|
7934
|
+
}
|
|
7935
|
+
function parseProjectContextBundleInternal(input, allowLegacyHash, normalizeLegacyHash = false) {
|
|
7933
7936
|
let encoded;
|
|
7934
7937
|
try {
|
|
7935
7938
|
const serialized = typeof input === "string" ? input : JSON.stringify(input);
|
|
@@ -7966,10 +7969,11 @@ function parseProjectContextBundle(input) {
|
|
|
7966
7969
|
validateIdentityConsistency(bundle);
|
|
7967
7970
|
rejectCredentialLikeBundle(bundle);
|
|
7968
7971
|
const expected = computeProjectContextSourceHash(bundle);
|
|
7969
|
-
|
|
7972
|
+
const matchesLegacyHash = bundle.hash !== expected && allowLegacyHash && bundle.hash === computeLegacyProjectContextSourceHash(bundle);
|
|
7973
|
+
if (bundle.hash !== expected && !matchesLegacyHash) {
|
|
7970
7974
|
throw new ProjectContextError("PROJECT_CONTEXT_HASH_MISMATCH", "bundle hash does not match its canonical allowlisted payload");
|
|
7971
7975
|
}
|
|
7972
|
-
return bundle;
|
|
7976
|
+
return matchesLegacyHash && normalizeLegacyHash ? { ...bundle, hash: expected } : bundle;
|
|
7973
7977
|
}
|
|
7974
7978
|
function planProjectContext(input) {
|
|
7975
7979
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
@@ -8217,6 +8221,8 @@ function applyProjectContext(options) {
|
|
|
8217
8221
|
content: `${JSON.stringify(sessionManifest, null, 2)}
|
|
8218
8222
|
`
|
|
8219
8223
|
};
|
|
8224
|
+
const manifestContent = `${JSON.stringify(buildManifest(plan, now3), null, 2)}
|
|
8225
|
+
`;
|
|
8220
8226
|
options.test_hooks?.before_compare?.({ attempt, plan });
|
|
8221
8227
|
if (!hashesStillMatch(plan.expected_hashes, workspaceRoot)) {
|
|
8222
8228
|
if (attempt === 0) {
|
|
@@ -8229,7 +8235,15 @@ function applyProjectContext(options) {
|
|
|
8229
8235
|
return resultForPlan(plan, true, raceRetries, null);
|
|
8230
8236
|
assertWorkspaceLockHeld(lockPath, lock, workspaceRoot);
|
|
8231
8237
|
try {
|
|
8232
|
-
|
|
8238
|
+
const metadataSnapshotPath = writeMetadataSnapshot(plan, now3);
|
|
8239
|
+
const rollbackSnapshotPath = writeProjectContextRollbackSnapshot(plan, now3, [
|
|
8240
|
+
{ path: plan.fragment_path, content: plan.fragment, role: "fragment" },
|
|
8241
|
+
{ path: plan.target_path, content: plan.target_content, role: "index" },
|
|
8242
|
+
{ path: plan.cache_path, content: cacheContent, role: "config" },
|
|
8243
|
+
{ path: sessionOutput.path, content: sessionOutput.content, role: "manifest" },
|
|
8244
|
+
{ path: plan.manifest_path, content: manifestContent, role: "manifest" }
|
|
8245
|
+
]);
|
|
8246
|
+
snapshotPath = rollbackSnapshotPath ?? metadataSnapshotPath;
|
|
8233
8247
|
atomicWriteFile(plan.fragment_path, plan.fragment, workspaceRoot, 420, expectedPlanHash(plan, plan.fragment_path), undefined, options.test_hooks?.atomic_exchange_unavailable, undefined, options.test_hooks?.portable_create_only);
|
|
8234
8248
|
options.test_hooks?.after_fragment?.({ attempt, plan });
|
|
8235
8249
|
assertWorkspaceLockHeld(lockPath, lock, workspaceRoot);
|
|
@@ -8243,9 +8257,7 @@ function applyProjectContext(options) {
|
|
|
8243
8257
|
options.test_hooks?.before_manifest?.({ attempt, plan });
|
|
8244
8258
|
assertWorkspaceLockHeld(lockPath, lock, workspaceRoot);
|
|
8245
8259
|
assertRenderedOutputsStable(plan, cacheContent, sessionOutput);
|
|
8246
|
-
|
|
8247
|
-
atomicWriteFile(plan.manifest_path, `${JSON.stringify(manifest, null, 2)}
|
|
8248
|
-
`, workspaceRoot, 384, expectedPlanHash(plan, plan.manifest_path), undefined, options.test_hooks?.atomic_exchange_unavailable, undefined, options.test_hooks?.portable_create_only);
|
|
8260
|
+
atomicWriteFile(plan.manifest_path, manifestContent, workspaceRoot, 384, expectedPlanHash(plan, plan.manifest_path), undefined, options.test_hooks?.atomic_exchange_unavailable, undefined, options.test_hooks?.portable_create_only);
|
|
8249
8261
|
return resultForPlan(plan, false, raceRetries, snapshotPath);
|
|
8250
8262
|
} catch (error) {
|
|
8251
8263
|
if (error instanceof ProjectContextHashRace && attempt === 0) {
|
|
@@ -8336,10 +8348,12 @@ function resolveBundleForApply(options, workspaceRoot, now3) {
|
|
|
8336
8348
|
if (cache.project_id !== options.expected_project_id || cache.bundle.project.id !== options.expected_project_id) {
|
|
8337
8349
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_MISMATCH", "cached project context belongs to a different project");
|
|
8338
8350
|
}
|
|
8339
|
-
const
|
|
8340
|
-
if (
|
|
8351
|
+
const cachedBundle = cache.bundle;
|
|
8352
|
+
if (cachedBundle.revision !== cache.revision || cachedBundle.hash !== cache.hash) {
|
|
8341
8353
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_INVALID", "cached revision or hash metadata is inconsistent");
|
|
8342
8354
|
}
|
|
8355
|
+
const canonicalHash = computeProjectContextSourceHash(cachedBundle);
|
|
8356
|
+
const bundle = cachedBundle.hash === canonicalHash ? cachedBundle : { ...cachedBundle, hash: canonicalHash };
|
|
8343
8357
|
const ageSeconds = Math.max(staleCacheAgeInSeconds(bundle.generated_at, now3, "bundle generated_at"), staleCacheAgeInSeconds(cache.cached_at, now3, "cache cached_at"));
|
|
8344
8358
|
const maxAge = normalizeMaxStaleAge(options.max_stale_age_seconds);
|
|
8345
8359
|
if (ageSeconds > maxAge) {
|
|
@@ -8436,11 +8450,13 @@ function parseManagedBlock(content, force) {
|
|
|
8436
8450
|
if (structurallyInvalid) {
|
|
8437
8451
|
if (!force)
|
|
8438
8452
|
throw new ProjectContextError("MANAGED_BLOCK_INVALID", "managed project-context markers are duplicate, nested, malformed, or unbalanced");
|
|
8453
|
+
const firstMarkerRange = markerCommentRange(markerLines[0]);
|
|
8454
|
+
const lastMarkerRange = markerCommentRange(markerLines[markerLines.length - 1]);
|
|
8439
8455
|
return {
|
|
8440
8456
|
block: null,
|
|
8441
8457
|
forceRange: {
|
|
8442
|
-
start: markerLines[0].start,
|
|
8443
|
-
end: lineContentEnd(markerLines[markerLines.length - 1])
|
|
8458
|
+
start: firstMarkerRange?.start ?? markerLines[0].start,
|
|
8459
|
+
end: lastMarkerRange?.end ?? lineContentEnd(markerLines[markerLines.length - 1])
|
|
8444
8460
|
}
|
|
8445
8461
|
};
|
|
8446
8462
|
}
|
|
@@ -8471,6 +8487,21 @@ function parseManagedBlock(content, force) {
|
|
|
8471
8487
|
forceRange: null
|
|
8472
8488
|
};
|
|
8473
8489
|
}
|
|
8490
|
+
function markerCommentRange(line) {
|
|
8491
|
+
const canonicalMarker = line.text.indexOf(PROJECT_CONTEXT_MANAGED_COMMENT);
|
|
8492
|
+
const legacyMarker = /@hasna\/configs project context/i.exec(line.text)?.index ?? -1;
|
|
8493
|
+
const marker = canonicalMarker >= 0 ? canonicalMarker : legacyMarker;
|
|
8494
|
+
if (marker < 0)
|
|
8495
|
+
return null;
|
|
8496
|
+
const commentStart = line.text.lastIndexOf("<!--", marker);
|
|
8497
|
+
const commentEnd = line.text.indexOf("-->", marker);
|
|
8498
|
+
if (commentStart < 0 || commentEnd < 0)
|
|
8499
|
+
return null;
|
|
8500
|
+
return {
|
|
8501
|
+
start: line.start + commentStart,
|
|
8502
|
+
end: line.start + commentEnd + "-->".length
|
|
8503
|
+
};
|
|
8504
|
+
}
|
|
8474
8505
|
function parseMarkerLine(text) {
|
|
8475
8506
|
const line = text.replace(/[\r\n]+$/, "");
|
|
8476
8507
|
const canonical = line.match(/^<!-- Managed by @hasna\/configs project context (BEGIN|END) id=([A-Za-z0-9][A-Za-z0-9._:@+-]*) revision=([A-Za-z0-9%._~+-]+) hash=(sha256:[a-f0-9]{64}) -->$/);
|
|
@@ -8492,8 +8523,14 @@ function parseMarkerLine(text) {
|
|
|
8492
8523
|
}
|
|
8493
8524
|
function replaceOrAppendManagedBlock(content, block, parsed, legacy) {
|
|
8494
8525
|
const range = parsed.block ?? parsed.forceRange ?? legacy;
|
|
8495
|
-
if (range)
|
|
8496
|
-
|
|
8526
|
+
if (range) {
|
|
8527
|
+
const before = content.slice(0, range.start);
|
|
8528
|
+
const after = content.slice(range.end);
|
|
8529
|
+
const eol2 = preferredEol(content);
|
|
8530
|
+
const beforeSeparator = before && !/[\r\n]$/.test(before) ? eol2 : "";
|
|
8531
|
+
const afterSeparator = after && !/^[\r\n]/.test(after) ? eol2 : "";
|
|
8532
|
+
return `${before}${beforeSeparator}${block}${afterSeparator}${after}`;
|
|
8533
|
+
}
|
|
8497
8534
|
if (!content)
|
|
8498
8535
|
return `${block}
|
|
8499
8536
|
`;
|
|
@@ -8537,13 +8574,18 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
8537
8574
|
}
|
|
8538
8575
|
function assertRevisionOrdering(plan, force) {
|
|
8539
8576
|
const observations = [];
|
|
8577
|
+
const cache = readProjectContextCache(plan.cache_path, plan.workspace_root);
|
|
8578
|
+
const canonicalCacheHash = cache === null ? null : computeProjectContextSourceHash(cache.bundle);
|
|
8579
|
+
const normalizePersistedHash = (revision, hash) => cache !== null && canonicalCacheHash !== null && revision === cache.revision && hash === cache.hash ? canonicalCacheHash : hash;
|
|
8540
8580
|
const manifest = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
8541
8581
|
if (manifest) {
|
|
8582
|
+
const manifestHashHasRecoveryProof = manifest.projectContext.hash === plan.bundle.hash || metadataSnapshotMatchesManifest(plan, manifest);
|
|
8583
|
+
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 && existsSync4(plan.fragment_path) && fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && manifestHashHasRecoveryProof;
|
|
8542
8584
|
observations.push({
|
|
8543
8585
|
source: "manifest",
|
|
8544
8586
|
id: manifest.projectContext.projectId,
|
|
8545
8587
|
revision: manifest.projectContext.revision,
|
|
8546
|
-
hash: manifest.projectContext.hash
|
|
8588
|
+
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)
|
|
8547
8589
|
});
|
|
8548
8590
|
const fragmentEntry = manifest.files.find((file) => file.relativePath === PROJECT_CONTEXT_FRAGMENT_PATH);
|
|
8549
8591
|
if (fragmentEntry && existsSync4(plan.fragment_path)) {
|
|
@@ -8553,11 +8595,16 @@ function assertRevisionOrdering(plan, force) {
|
|
|
8553
8595
|
}
|
|
8554
8596
|
}
|
|
8555
8597
|
}
|
|
8556
|
-
const cache = readProjectContextCache(plan.cache_path, plan.workspace_root);
|
|
8557
8598
|
if (cache)
|
|
8558
|
-
observations.push({ source: "cache", id: cache.project_id, revision: cache.revision, hash:
|
|
8559
|
-
if (plan.marker)
|
|
8560
|
-
observations.push({
|
|
8599
|
+
observations.push({ source: "cache", id: cache.project_id, revision: cache.revision, hash: canonicalCacheHash });
|
|
8600
|
+
if (plan.marker) {
|
|
8601
|
+
observations.push({
|
|
8602
|
+
source: "marker",
|
|
8603
|
+
id: plan.marker.id,
|
|
8604
|
+
revision: plan.marker.revision,
|
|
8605
|
+
hash: normalizePersistedHash(plan.marker.revision, plan.marker.hash)
|
|
8606
|
+
});
|
|
8607
|
+
}
|
|
8561
8608
|
for (const observation of observations) {
|
|
8562
8609
|
if (observation.id !== plan.bundle.project.id) {
|
|
8563
8610
|
throw new ProjectContextError("PROJECT_CONTEXT_IDENTITY_CONFLICT", `${observation.source} belongs to another project`);
|
|
@@ -8677,6 +8724,10 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8677
8724
|
};
|
|
8678
8725
|
const targetOwner = isRecord(existing["targetOwner"]) ? existing["targetOwner"] : {};
|
|
8679
8726
|
const adapterMode = plan.native_imports ? "native-imports" : "flattened-markdown";
|
|
8727
|
+
const existingProjectContext = isRecord(existing["projectContext"]) ? existing["projectContext"] : null;
|
|
8728
|
+
const existingProjectContextHash = existingProjectContext === null ? null : safeLegacyMetadataString(existingProjectContext["hash"], null);
|
|
8729
|
+
const existingSourceHash = typeof existing["sourceHash"] === "string" ? existing["sourceHash"] : null;
|
|
8730
|
+
const sourceHash = existingProjectContextHash === plan.bundle.hash && existingSourceHash !== null ? existingSourceHash : sha2562(stableStringify({ previous: existingSourceHash, projectContext: plan.bundle.hash }));
|
|
8680
8731
|
return credentialSafeSessionManifest({
|
|
8681
8732
|
schema: SESSION_RENDER_SCHEMA,
|
|
8682
8733
|
tool,
|
|
@@ -8700,7 +8751,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8700
8751
|
blockers: [],
|
|
8701
8752
|
generatedAt: now3.toISOString(),
|
|
8702
8753
|
env: sanitizeLegacyEnvironment(existing["env"]),
|
|
8703
|
-
sourceHash
|
|
8754
|
+
sourceHash,
|
|
8704
8755
|
sources,
|
|
8705
8756
|
skippedSources: sanitizeLegacySkippedSources(existing["skippedSources"]),
|
|
8706
8757
|
files: [...files.filter((file) => file["relativePath"] !== targetRelativePath), updatedTarget],
|
|
@@ -8956,6 +9007,77 @@ function writeMetadataSnapshot(plan, now3) {
|
|
|
8956
9007
|
`, plan.workspace_root, 384);
|
|
8957
9008
|
return snapshotPath;
|
|
8958
9009
|
}
|
|
9010
|
+
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
9011
|
+
const snapshotDir = resolve(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
9012
|
+
const snapshotPath = resolve(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
9013
|
+
if (!existsSync4(snapshotPath))
|
|
9014
|
+
return false;
|
|
9015
|
+
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
9016
|
+
const result = projectContextMetadataSnapshotSchema.safeParse(record);
|
|
9017
|
+
if (!result.success)
|
|
9018
|
+
return false;
|
|
9019
|
+
if (result.data.projectId !== manifest.projectContext.projectId || result.data.revision !== manifest.projectContext.revision || result.data.hash !== manifest.projectContext.hash || result.data.status !== manifest.projectContext.status)
|
|
9020
|
+
return false;
|
|
9021
|
+
const sortFiles = (files) => [...files].sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
9022
|
+
const snapshotFiles = sortFiles(result.data.files);
|
|
9023
|
+
const manifestFiles = sortFiles(manifest.files.map((file) => ({
|
|
9024
|
+
relativePath: file.relativePath,
|
|
9025
|
+
role: file.role,
|
|
9026
|
+
sha256: file.sha256
|
|
9027
|
+
})));
|
|
9028
|
+
return JSON.stringify(snapshotFiles) === JSON.stringify(manifestFiles);
|
|
9029
|
+
}
|
|
9030
|
+
function writeProjectContextRollbackSnapshot(plan, now3, outputs) {
|
|
9031
|
+
const targetExpectedHash = expectedPlanHash(plan, plan.target_path);
|
|
9032
|
+
if (targetExpectedHash === sha2562(plan.target_content))
|
|
9033
|
+
return null;
|
|
9034
|
+
const files = outputs.flatMap((output) => {
|
|
9035
|
+
const expectedHash = expectedPlanHash(plan, output.path);
|
|
9036
|
+
if (expectedHash === null)
|
|
9037
|
+
return [];
|
|
9038
|
+
const content = readUtf8RegularFile(output.path, plan.workspace_root, managedObservationMaxBytes(relativePosix(plan.workspace_root, output.path)));
|
|
9039
|
+
if (sha2562(content) !== expectedHash) {
|
|
9040
|
+
throw new ProjectContextHashRace(`managed path changed while creating rollback evidence: ${relativePosix(plan.workspace_root, output.path)}`);
|
|
9041
|
+
}
|
|
9042
|
+
return [{
|
|
9043
|
+
path: output.path,
|
|
9044
|
+
relativePath: relativePosix(plan.workspace_root, output.path),
|
|
9045
|
+
role: output.role,
|
|
9046
|
+
sha256: expectedHash,
|
|
9047
|
+
content
|
|
9048
|
+
}];
|
|
9049
|
+
});
|
|
9050
|
+
const afterFiles = outputs.map((output) => {
|
|
9051
|
+
const previousHash = expectedPlanHash(plan, output.path);
|
|
9052
|
+
const nextHash = sha2562(output.content);
|
|
9053
|
+
return {
|
|
9054
|
+
path: output.path,
|
|
9055
|
+
relativePath: relativePosix(plan.workspace_root, output.path),
|
|
9056
|
+
role: output.role,
|
|
9057
|
+
action: previousHash === null ? "create" : previousHash === nextHash ? "unchanged" : "update",
|
|
9058
|
+
sha256: nextHash
|
|
9059
|
+
};
|
|
9060
|
+
});
|
|
9061
|
+
const snapshotDir = resolve(plan.workspace_root, ...SESSION_RENDER_SNAPSHOT_RELATIVE_DIR.split("/"));
|
|
9062
|
+
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
9063
|
+
const timestamp = now3.toISOString().replace(/[:.]/g, "-");
|
|
9064
|
+
const snapshotPath = resolve(snapshotDir, `${timestamp}-${randomUUID6()}.json`);
|
|
9065
|
+
const snapshot = {
|
|
9066
|
+
schema: "hasna.configs.session-render-snapshot/v2",
|
|
9067
|
+
createdAt: now3.toISOString(),
|
|
9068
|
+
tool: manifestTool(plan.runtime),
|
|
9069
|
+
profile: "project-context",
|
|
9070
|
+
targetHome: plan.workspace_root,
|
|
9071
|
+
targetKind: "project-root",
|
|
9072
|
+
manifestPath: plan.manifest_path,
|
|
9073
|
+
previousManifest: null,
|
|
9074
|
+
files,
|
|
9075
|
+
afterFiles
|
|
9076
|
+
};
|
|
9077
|
+
atomicWriteFile(snapshotPath, `${JSON.stringify(snapshot, null, 2)}
|
|
9078
|
+
`, plan.workspace_root, 384, null);
|
|
9079
|
+
return snapshotPath;
|
|
9080
|
+
}
|
|
8959
9081
|
function readProjectContextManifest(path, workspaceRoot) {
|
|
8960
9082
|
if (!existsSync4(path))
|
|
8961
9083
|
return null;
|
|
@@ -8979,7 +9101,7 @@ function readProjectContextCache(path, workspaceRoot) {
|
|
|
8979
9101
|
if (!result.success) {
|
|
8980
9102
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_INVALID", "cache is malformed or incompatible");
|
|
8981
9103
|
}
|
|
8982
|
-
const bundle =
|
|
9104
|
+
const bundle = parseProjectContextBundleInternal(result.data.bundle, true);
|
|
8983
9105
|
if (result.data.project_id !== bundle.project.id || result.data.revision !== bundle.revision || result.data.hash !== bundle.hash) {
|
|
8984
9106
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_INVALID", "cache metadata does not match its bundle");
|
|
8985
9107
|
}
|
|
@@ -10292,19 +10414,30 @@ function removeHashForFingerprint(value) {
|
|
|
10292
10414
|
return value;
|
|
10293
10415
|
const copy = {};
|
|
10294
10416
|
for (const [key, item] of Object.entries(value)) {
|
|
10295
|
-
if (key === "hash")
|
|
10417
|
+
if (key === "generated_at" || key === "hash")
|
|
10296
10418
|
continue;
|
|
10297
10419
|
copy[key] = item;
|
|
10298
10420
|
}
|
|
10299
10421
|
return copy;
|
|
10300
10422
|
}
|
|
10423
|
+
function computeLegacyProjectContextSourceHash(value) {
|
|
10424
|
+
if (!isRecord(value))
|
|
10425
|
+
return `sha256:${sha2562(stableStringify(value))}`;
|
|
10426
|
+
const copy = {};
|
|
10427
|
+
for (const [key, item] of Object.entries(value)) {
|
|
10428
|
+
if (key === "hash")
|
|
10429
|
+
continue;
|
|
10430
|
+
copy[key] = item;
|
|
10431
|
+
}
|
|
10432
|
+
return `sha256:${sha2562(stableStringify(copy))}`;
|
|
10433
|
+
}
|
|
10301
10434
|
function sha2562(content) {
|
|
10302
10435
|
return createHash2("sha256").update(content).digest("hex");
|
|
10303
10436
|
}
|
|
10304
10437
|
function isRecord(value) {
|
|
10305
10438
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
10306
10439
|
}
|
|
10307
|
-
var PROJECT_CONTEXT_SCHEMA = "hasna.projects.project_context_bundle.v1", PROJECT_CONTEXT_MAX_INPUT_BYTES, PROJECT_CONTEXT_MAX_RENDERED_BYTES, PROJECT_CONTEXT_MAX_APPROX_TOKENS = 1000, PROJECT_CONTEXT_MAX_COMMANDS = 6, PROJECT_CONTEXT_MAX_WARNINGS = 3, PROJECT_CONTEXT_FRAGMENT_PATH = ".hasna/instructions/project-context.md", PROJECT_CONTEXT_MANIFEST_PATH = ".hasna/project-context-manifest.json", PROJECT_CONTEXT_CACHE_PATH = ".hasna/project-context-cache.json", PROJECT_CONTEXT_LOCK_PATH = ".hasna/project-context.lock", PROJECT_CONTEXT_SNAPSHOT_DIR = ".hasna/project-context-snapshots", PROJECT_CONTEXT_CACHE_SCHEMA = "hasna.instructions.project-context-cache/v1", PROJECT_CONTEXT_MANAGED_COMMENT = "Managed by @hasna/configs project context", SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES, FOREIGN_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_PATHS, SESSION_MANAGED_OUTPUT_WARN_BYTES, PROJECT_CONTEXT_LOCK_STALE_MS, LEGACY_CONFIGS_PACKAGE = "@hasna/configs", LEGACY_CONFIGS_COMPAT_VERSION = "0.2.45", LEGACY_CONFIGS_EXECUTABLE = "configs", PROJECT_KINDS, PROJECT_STATUSES, LINK_STATES, RESOLUTION_SOURCES, safeId, nullableId, producerSlug, producerName, safeOptionalDisplay, isoTimestamp, revisionSchema, hashSchema, absolutePath, commandArg, commandSchema, projectContextBundleSchema, storedManifestProjectContextSchema, storedManifestFileSchema, storedManifestObservationSchema, projectContextCacheSchema, ProjectContextError, ProjectContextHashRace, anchoredFsOps, atomicExchange, atomicExchangeLibraries;
|
|
10440
|
+
var PROJECT_CONTEXT_SCHEMA = "hasna.projects.project_context_bundle.v1", PROJECT_CONTEXT_MAX_INPUT_BYTES, PROJECT_CONTEXT_MAX_RENDERED_BYTES, PROJECT_CONTEXT_MAX_APPROX_TOKENS = 1000, PROJECT_CONTEXT_MAX_COMMANDS = 6, PROJECT_CONTEXT_MAX_WARNINGS = 3, PROJECT_CONTEXT_FRAGMENT_PATH = ".hasna/instructions/project-context.md", PROJECT_CONTEXT_MANIFEST_PATH = ".hasna/project-context-manifest.json", PROJECT_CONTEXT_CACHE_PATH = ".hasna/project-context-cache.json", PROJECT_CONTEXT_LOCK_PATH = ".hasna/project-context.lock", PROJECT_CONTEXT_SNAPSHOT_DIR = ".hasna/project-context-snapshots", PROJECT_CONTEXT_CACHE_SCHEMA = "hasna.instructions.project-context-cache/v1", PROJECT_CONTEXT_MANAGED_COMMENT = "Managed by @hasna/configs project context", SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES, FOREIGN_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_MAX_BYTES, SESSION_MANAGED_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_PATHS, SESSION_MANAGED_OUTPUT_WARN_BYTES, PROJECT_CONTEXT_LOCK_STALE_MS, LEGACY_CONFIGS_PACKAGE = "@hasna/configs", LEGACY_CONFIGS_COMPAT_VERSION = "0.2.45", LEGACY_CONFIGS_EXECUTABLE = "configs", PROJECT_KINDS, PROJECT_STATUSES, LINK_STATES, RESOLUTION_SOURCES, safeId, nullableId, producerSlug, producerName, safeOptionalDisplay, isoTimestamp, revisionSchema, hashSchema, absolutePath, commandArg, commandSchema, projectContextBundleSchema, storedManifestProjectContextSchema, storedManifestFileSchema, storedManifestObservationSchema, projectContextMetadataSnapshotSchema, projectContextCacheSchema, ProjectContextError, ProjectContextHashRace, anchoredFsOps, atomicExchange, atomicExchangeLibraries;
|
|
10308
10441
|
var init_project_context = __esm(() => {
|
|
10309
10442
|
init_zod();
|
|
10310
10443
|
init_redact();
|
|
@@ -10314,6 +10447,7 @@ var init_project_context = __esm(() => {
|
|
|
10314
10447
|
SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES = 8 * 1024 * 1024;
|
|
10315
10448
|
FOREIGN_INPUT_MAX_BYTES = 256 * 1024;
|
|
10316
10449
|
SESSION_MANAGED_OUTPUT_MAX_BYTES = SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES;
|
|
10450
|
+
SESSION_MANAGED_INPUT_MAX_BYTES = SESSION_MANAGED_OUTPUT_MAX_BYTES;
|
|
10317
10451
|
SESSION_MANAGED_OUTPUT_PATHS = [
|
|
10318
10452
|
".hasna/session-render-manifest.json",
|
|
10319
10453
|
".codewith/.hasna/session-render-manifest.json",
|
|
@@ -10443,6 +10577,25 @@ var init_project_context = __esm(() => {
|
|
|
10443
10577
|
});
|
|
10444
10578
|
}
|
|
10445
10579
|
});
|
|
10580
|
+
projectContextMetadataSnapshotSchema = exports_external.object({
|
|
10581
|
+
schema: exports_external.literal("hasna.configs.session-render-snapshot/v1"),
|
|
10582
|
+
kind: exports_external.literal("project-context-metadata"),
|
|
10583
|
+
createdAt: isoTimestamp,
|
|
10584
|
+
projectId: safeId,
|
|
10585
|
+
revision: revisionSchema,
|
|
10586
|
+
hash: hashSchema,
|
|
10587
|
+
status: exports_external.enum(["fresh", "stale-source", "stale-cache"]),
|
|
10588
|
+
files: exports_external.array(exports_external.object({
|
|
10589
|
+
relativePath: exports_external.enum([
|
|
10590
|
+
PROJECT_CONTEXT_FRAGMENT_PATH,
|
|
10591
|
+
"CLAUDE.md",
|
|
10592
|
+
".codewith/CODEWITH.md",
|
|
10593
|
+
"AGENTS.md"
|
|
10594
|
+
]),
|
|
10595
|
+
role: exports_external.enum(["fragment", "index"]),
|
|
10596
|
+
sha256: exports_external.string().regex(/^[a-f0-9]{64}$/)
|
|
10597
|
+
}).strict()).min(1).max(2)
|
|
10598
|
+
}).strict();
|
|
10446
10599
|
projectContextCacheSchema = exports_external.object({
|
|
10447
10600
|
schema: exports_external.literal(PROJECT_CONTEXT_CACHE_SCHEMA),
|
|
10448
10601
|
cached_at: isoTimestamp,
|
|
@@ -15778,7 +15931,7 @@ function parseSessionSource(value, order, replaceIds) {
|
|
|
15778
15931
|
const absPath = resolveSessionPath(path);
|
|
15779
15932
|
if (!existsSync16(absPath))
|
|
15780
15933
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
15781
|
-
const content =
|
|
15934
|
+
const content = readSessionInstructionSourceFile(absPath);
|
|
15782
15935
|
const source = sourceFromFilePath(absPath, content, order);
|
|
15783
15936
|
const resolvedId = id || source.id || basename7(absPath);
|
|
15784
15937
|
return {
|
|
@@ -15789,6 +15942,19 @@ function parseSessionSource(value, order, replaceIds) {
|
|
|
15789
15942
|
merge: replaceIds.has(resolvedId) ? "replace" : "append"
|
|
15790
15943
|
};
|
|
15791
15944
|
}
|
|
15945
|
+
function readSessionInstructionSourceFile(path) {
|
|
15946
|
+
const stat = lstatSync4(path);
|
|
15947
|
+
if (stat.isSymbolicLink()) {
|
|
15948
|
+
throw new Error("SESSION_SOURCE_SYMLINK_REJECTED: instruction source file must be a regular non-symlink file");
|
|
15949
|
+
}
|
|
15950
|
+
if (!stat.isFile()) {
|
|
15951
|
+
throw new Error(`SESSION_SOURCE_PATH_INVALID: instruction source path is not a regular file: ${path}`);
|
|
15952
|
+
}
|
|
15953
|
+
if (stat.size > SESSION_MANAGED_INPUT_MAX_BYTES) {
|
|
15954
|
+
throw new Error(`SESSION_SOURCE_INPUT_TOO_LARGE: instruction source file exceeds ${SESSION_MANAGED_INPUT_MAX_BYTES} bytes`);
|
|
15955
|
+
}
|
|
15956
|
+
return readFileSync12(path, "utf-8");
|
|
15957
|
+
}
|
|
15792
15958
|
function parseLayeredReference(value) {
|
|
15793
15959
|
const trimmed = value.trim();
|
|
15794
15960
|
const idx = trimmed.indexOf(":");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-apply-managed-input-bound.test.d.ts","sourceRoot":"","sources":["../../src/cli/session-apply-managed-input-bound.test.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -5972,6 +5972,25 @@ var storedManifestObservationSchema = exports_external.object({
|
|
|
5972
5972
|
});
|
|
5973
5973
|
}
|
|
5974
5974
|
});
|
|
5975
|
+
var projectContextMetadataSnapshotSchema = exports_external.object({
|
|
5976
|
+
schema: exports_external.literal("hasna.configs.session-render-snapshot/v1"),
|
|
5977
|
+
kind: exports_external.literal("project-context-metadata"),
|
|
5978
|
+
createdAt: isoTimestamp,
|
|
5979
|
+
projectId: safeId,
|
|
5980
|
+
revision: revisionSchema,
|
|
5981
|
+
hash: hashSchema,
|
|
5982
|
+
status: exports_external.enum(["fresh", "stale-source", "stale-cache"]),
|
|
5983
|
+
files: exports_external.array(exports_external.object({
|
|
5984
|
+
relativePath: exports_external.enum([
|
|
5985
|
+
PROJECT_CONTEXT_FRAGMENT_PATH,
|
|
5986
|
+
"CLAUDE.md",
|
|
5987
|
+
".codewith/CODEWITH.md",
|
|
5988
|
+
"AGENTS.md"
|
|
5989
|
+
]),
|
|
5990
|
+
role: exports_external.enum(["fragment", "index"]),
|
|
5991
|
+
sha256: exports_external.string().regex(/^[a-f0-9]{64}$/)
|
|
5992
|
+
}).strict()).min(1).max(2)
|
|
5993
|
+
}).strict();
|
|
5975
5994
|
var projectContextCacheSchema = exports_external.object({
|
|
5976
5995
|
schema: exports_external.literal(PROJECT_CONTEXT_CACHE_SCHEMA),
|
|
5977
5996
|
cached_at: isoTimestamp,
|
|
@@ -5999,6 +6018,9 @@ function computeProjectContextSourceHash(value) {
|
|
|
5999
6018
|
return `sha256:${sha2562(stableStringify(normalized))}`;
|
|
6000
6019
|
}
|
|
6001
6020
|
function parseProjectContextBundle(input) {
|
|
6021
|
+
return parseProjectContextBundleInternal(input, true, true);
|
|
6022
|
+
}
|
|
6023
|
+
function parseProjectContextBundleInternal(input, allowLegacyHash, normalizeLegacyHash = false) {
|
|
6002
6024
|
let encoded;
|
|
6003
6025
|
try {
|
|
6004
6026
|
const serialized = typeof input === "string" ? input : JSON.stringify(input);
|
|
@@ -6035,10 +6057,11 @@ function parseProjectContextBundle(input) {
|
|
|
6035
6057
|
validateIdentityConsistency(bundle);
|
|
6036
6058
|
rejectCredentialLikeBundle(bundle);
|
|
6037
6059
|
const expected = computeProjectContextSourceHash(bundle);
|
|
6038
|
-
|
|
6060
|
+
const matchesLegacyHash = bundle.hash !== expected && allowLegacyHash && bundle.hash === computeLegacyProjectContextSourceHash(bundle);
|
|
6061
|
+
if (bundle.hash !== expected && !matchesLegacyHash) {
|
|
6039
6062
|
throw new ProjectContextError("PROJECT_CONTEXT_HASH_MISMATCH", "bundle hash does not match its canonical allowlisted payload");
|
|
6040
6063
|
}
|
|
6041
|
-
return bundle;
|
|
6064
|
+
return matchesLegacyHash && normalizeLegacyHash ? { ...bundle, hash: expected } : bundle;
|
|
6042
6065
|
}
|
|
6043
6066
|
function planProjectContext(input) {
|
|
6044
6067
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
@@ -6286,6 +6309,8 @@ function applyProjectContext(options) {
|
|
|
6286
6309
|
content: `${JSON.stringify(sessionManifest, null, 2)}
|
|
6287
6310
|
`
|
|
6288
6311
|
};
|
|
6312
|
+
const manifestContent = `${JSON.stringify(buildManifest(plan, now2), null, 2)}
|
|
6313
|
+
`;
|
|
6289
6314
|
options.test_hooks?.before_compare?.({ attempt, plan });
|
|
6290
6315
|
if (!hashesStillMatch(plan.expected_hashes, workspaceRoot)) {
|
|
6291
6316
|
if (attempt === 0) {
|
|
@@ -6298,7 +6323,15 @@ function applyProjectContext(options) {
|
|
|
6298
6323
|
return resultForPlan(plan, true, raceRetries, null);
|
|
6299
6324
|
assertWorkspaceLockHeld(lockPath, lock, workspaceRoot);
|
|
6300
6325
|
try {
|
|
6301
|
-
|
|
6326
|
+
const metadataSnapshotPath = writeMetadataSnapshot(plan, now2);
|
|
6327
|
+
const rollbackSnapshotPath = writeProjectContextRollbackSnapshot(plan, now2, [
|
|
6328
|
+
{ path: plan.fragment_path, content: plan.fragment, role: "fragment" },
|
|
6329
|
+
{ path: plan.target_path, content: plan.target_content, role: "index" },
|
|
6330
|
+
{ path: plan.cache_path, content: cacheContent, role: "config" },
|
|
6331
|
+
{ path: sessionOutput.path, content: sessionOutput.content, role: "manifest" },
|
|
6332
|
+
{ path: plan.manifest_path, content: manifestContent, role: "manifest" }
|
|
6333
|
+
]);
|
|
6334
|
+
snapshotPath = rollbackSnapshotPath ?? metadataSnapshotPath;
|
|
6302
6335
|
atomicWriteFile(plan.fragment_path, plan.fragment, workspaceRoot, 420, expectedPlanHash(plan, plan.fragment_path), undefined, options.test_hooks?.atomic_exchange_unavailable, undefined, options.test_hooks?.portable_create_only);
|
|
6303
6336
|
options.test_hooks?.after_fragment?.({ attempt, plan });
|
|
6304
6337
|
assertWorkspaceLockHeld(lockPath, lock, workspaceRoot);
|
|
@@ -6312,9 +6345,7 @@ function applyProjectContext(options) {
|
|
|
6312
6345
|
options.test_hooks?.before_manifest?.({ attempt, plan });
|
|
6313
6346
|
assertWorkspaceLockHeld(lockPath, lock, workspaceRoot);
|
|
6314
6347
|
assertRenderedOutputsStable(plan, cacheContent, sessionOutput);
|
|
6315
|
-
|
|
6316
|
-
atomicWriteFile(plan.manifest_path, `${JSON.stringify(manifest, null, 2)}
|
|
6317
|
-
`, workspaceRoot, 384, expectedPlanHash(plan, plan.manifest_path), undefined, options.test_hooks?.atomic_exchange_unavailable, undefined, options.test_hooks?.portable_create_only);
|
|
6348
|
+
atomicWriteFile(plan.manifest_path, manifestContent, workspaceRoot, 384, expectedPlanHash(plan, plan.manifest_path), undefined, options.test_hooks?.atomic_exchange_unavailable, undefined, options.test_hooks?.portable_create_only);
|
|
6318
6349
|
return resultForPlan(plan, false, raceRetries, snapshotPath);
|
|
6319
6350
|
} catch (error) {
|
|
6320
6351
|
if (error instanceof ProjectContextHashRace && attempt === 0) {
|
|
@@ -6405,10 +6436,12 @@ function resolveBundleForApply(options, workspaceRoot, now2) {
|
|
|
6405
6436
|
if (cache.project_id !== options.expected_project_id || cache.bundle.project.id !== options.expected_project_id) {
|
|
6406
6437
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_ID_MISMATCH", "cached project context belongs to a different project");
|
|
6407
6438
|
}
|
|
6408
|
-
const
|
|
6409
|
-
if (
|
|
6439
|
+
const cachedBundle = cache.bundle;
|
|
6440
|
+
if (cachedBundle.revision !== cache.revision || cachedBundle.hash !== cache.hash) {
|
|
6410
6441
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_INVALID", "cached revision or hash metadata is inconsistent");
|
|
6411
6442
|
}
|
|
6443
|
+
const canonicalHash = computeProjectContextSourceHash(cachedBundle);
|
|
6444
|
+
const bundle = cachedBundle.hash === canonicalHash ? cachedBundle : { ...cachedBundle, hash: canonicalHash };
|
|
6412
6445
|
const ageSeconds = Math.max(staleCacheAgeInSeconds(bundle.generated_at, now2, "bundle generated_at"), staleCacheAgeInSeconds(cache.cached_at, now2, "cache cached_at"));
|
|
6413
6446
|
const maxAge = normalizeMaxStaleAge(options.max_stale_age_seconds);
|
|
6414
6447
|
if (ageSeconds > maxAge) {
|
|
@@ -6505,11 +6538,13 @@ function parseManagedBlock(content, force) {
|
|
|
6505
6538
|
if (structurallyInvalid) {
|
|
6506
6539
|
if (!force)
|
|
6507
6540
|
throw new ProjectContextError("MANAGED_BLOCK_INVALID", "managed project-context markers are duplicate, nested, malformed, or unbalanced");
|
|
6541
|
+
const firstMarkerRange = markerCommentRange(markerLines[0]);
|
|
6542
|
+
const lastMarkerRange = markerCommentRange(markerLines[markerLines.length - 1]);
|
|
6508
6543
|
return {
|
|
6509
6544
|
block: null,
|
|
6510
6545
|
forceRange: {
|
|
6511
|
-
start: markerLines[0].start,
|
|
6512
|
-
end: lineContentEnd(markerLines[markerLines.length - 1])
|
|
6546
|
+
start: firstMarkerRange?.start ?? markerLines[0].start,
|
|
6547
|
+
end: lastMarkerRange?.end ?? lineContentEnd(markerLines[markerLines.length - 1])
|
|
6513
6548
|
}
|
|
6514
6549
|
};
|
|
6515
6550
|
}
|
|
@@ -6540,6 +6575,21 @@ function parseManagedBlock(content, force) {
|
|
|
6540
6575
|
forceRange: null
|
|
6541
6576
|
};
|
|
6542
6577
|
}
|
|
6578
|
+
function markerCommentRange(line) {
|
|
6579
|
+
const canonicalMarker = line.text.indexOf(PROJECT_CONTEXT_MANAGED_COMMENT);
|
|
6580
|
+
const legacyMarker = /@hasna\/configs project context/i.exec(line.text)?.index ?? -1;
|
|
6581
|
+
const marker = canonicalMarker >= 0 ? canonicalMarker : legacyMarker;
|
|
6582
|
+
if (marker < 0)
|
|
6583
|
+
return null;
|
|
6584
|
+
const commentStart = line.text.lastIndexOf("<!--", marker);
|
|
6585
|
+
const commentEnd = line.text.indexOf("-->", marker);
|
|
6586
|
+
if (commentStart < 0 || commentEnd < 0)
|
|
6587
|
+
return null;
|
|
6588
|
+
return {
|
|
6589
|
+
start: line.start + commentStart,
|
|
6590
|
+
end: line.start + commentEnd + "-->".length
|
|
6591
|
+
};
|
|
6592
|
+
}
|
|
6543
6593
|
function parseMarkerLine(text) {
|
|
6544
6594
|
const line = text.replace(/[\r\n]+$/, "");
|
|
6545
6595
|
const canonical = line.match(/^<!-- Managed by @hasna\/configs project context (BEGIN|END) id=([A-Za-z0-9][A-Za-z0-9._:@+-]*) revision=([A-Za-z0-9%._~+-]+) hash=(sha256:[a-f0-9]{64}) -->$/);
|
|
@@ -6561,8 +6611,14 @@ function parseMarkerLine(text) {
|
|
|
6561
6611
|
}
|
|
6562
6612
|
function replaceOrAppendManagedBlock(content, block, parsed, legacy) {
|
|
6563
6613
|
const range = parsed.block ?? parsed.forceRange ?? legacy;
|
|
6564
|
-
if (range)
|
|
6565
|
-
|
|
6614
|
+
if (range) {
|
|
6615
|
+
const before = content.slice(0, range.start);
|
|
6616
|
+
const after = content.slice(range.end);
|
|
6617
|
+
const eol2 = preferredEol(content);
|
|
6618
|
+
const beforeSeparator = before && !/[\r\n]$/.test(before) ? eol2 : "";
|
|
6619
|
+
const afterSeparator = after && !/^[\r\n]/.test(after) ? eol2 : "";
|
|
6620
|
+
return `${before}${beforeSeparator}${block}${afterSeparator}${after}`;
|
|
6621
|
+
}
|
|
6566
6622
|
if (!content)
|
|
6567
6623
|
return `${block}
|
|
6568
6624
|
`;
|
|
@@ -6606,13 +6662,18 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
6606
6662
|
}
|
|
6607
6663
|
function assertRevisionOrdering(plan, force) {
|
|
6608
6664
|
const observations = [];
|
|
6665
|
+
const cache = readProjectContextCache(plan.cache_path, plan.workspace_root);
|
|
6666
|
+
const canonicalCacheHash = cache === null ? null : computeProjectContextSourceHash(cache.bundle);
|
|
6667
|
+
const normalizePersistedHash = (revision, hash) => cache !== null && canonicalCacheHash !== null && revision === cache.revision && hash === cache.hash ? canonicalCacheHash : hash;
|
|
6609
6668
|
const manifest = readProjectContextManifest(plan.manifest_path, plan.workspace_root);
|
|
6610
6669
|
if (manifest) {
|
|
6670
|
+
const manifestHashHasRecoveryProof = manifest.projectContext.hash === plan.bundle.hash || metadataSnapshotMatchesManifest(plan, manifest);
|
|
6671
|
+
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 && existsSync3(plan.fragment_path) && fragmentMatchesBundle(plan.fragment_path, plan.bundle, plan.workspace_root) && manifestHashHasRecoveryProof;
|
|
6611
6672
|
observations.push({
|
|
6612
6673
|
source: "manifest",
|
|
6613
6674
|
id: manifest.projectContext.projectId,
|
|
6614
6675
|
revision: manifest.projectContext.revision,
|
|
6615
|
-
hash: manifest.projectContext.hash
|
|
6676
|
+
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)
|
|
6616
6677
|
});
|
|
6617
6678
|
const fragmentEntry = manifest.files.find((file) => file.relativePath === PROJECT_CONTEXT_FRAGMENT_PATH);
|
|
6618
6679
|
if (fragmentEntry && existsSync3(plan.fragment_path)) {
|
|
@@ -6622,11 +6683,16 @@ function assertRevisionOrdering(plan, force) {
|
|
|
6622
6683
|
}
|
|
6623
6684
|
}
|
|
6624
6685
|
}
|
|
6625
|
-
const cache = readProjectContextCache(plan.cache_path, plan.workspace_root);
|
|
6626
6686
|
if (cache)
|
|
6627
|
-
observations.push({ source: "cache", id: cache.project_id, revision: cache.revision, hash:
|
|
6628
|
-
if (plan.marker)
|
|
6629
|
-
observations.push({
|
|
6687
|
+
observations.push({ source: "cache", id: cache.project_id, revision: cache.revision, hash: canonicalCacheHash });
|
|
6688
|
+
if (plan.marker) {
|
|
6689
|
+
observations.push({
|
|
6690
|
+
source: "marker",
|
|
6691
|
+
id: plan.marker.id,
|
|
6692
|
+
revision: plan.marker.revision,
|
|
6693
|
+
hash: normalizePersistedHash(plan.marker.revision, plan.marker.hash)
|
|
6694
|
+
});
|
|
6695
|
+
}
|
|
6630
6696
|
for (const observation of observations) {
|
|
6631
6697
|
if (observation.id !== plan.bundle.project.id) {
|
|
6632
6698
|
throw new ProjectContextError("PROJECT_CONTEXT_IDENTITY_CONFLICT", `${observation.source} belongs to another project`);
|
|
@@ -6746,6 +6812,10 @@ function buildSessionCompatibilityManifest(plan, now2) {
|
|
|
6746
6812
|
};
|
|
6747
6813
|
const targetOwner = isRecord(existing["targetOwner"]) ? existing["targetOwner"] : {};
|
|
6748
6814
|
const adapterMode = plan.native_imports ? "native-imports" : "flattened-markdown";
|
|
6815
|
+
const existingProjectContext = isRecord(existing["projectContext"]) ? existing["projectContext"] : null;
|
|
6816
|
+
const existingProjectContextHash = existingProjectContext === null ? null : safeLegacyMetadataString(existingProjectContext["hash"], null);
|
|
6817
|
+
const existingSourceHash = typeof existing["sourceHash"] === "string" ? existing["sourceHash"] : null;
|
|
6818
|
+
const sourceHash = existingProjectContextHash === plan.bundle.hash && existingSourceHash !== null ? existingSourceHash : sha2562(stableStringify({ previous: existingSourceHash, projectContext: plan.bundle.hash }));
|
|
6749
6819
|
return credentialSafeSessionManifest({
|
|
6750
6820
|
schema: SESSION_RENDER_SCHEMA,
|
|
6751
6821
|
tool,
|
|
@@ -6769,7 +6839,7 @@ function buildSessionCompatibilityManifest(plan, now2) {
|
|
|
6769
6839
|
blockers: [],
|
|
6770
6840
|
generatedAt: now2.toISOString(),
|
|
6771
6841
|
env: sanitizeLegacyEnvironment(existing["env"]),
|
|
6772
|
-
sourceHash
|
|
6842
|
+
sourceHash,
|
|
6773
6843
|
sources,
|
|
6774
6844
|
skippedSources: sanitizeLegacySkippedSources(existing["skippedSources"]),
|
|
6775
6845
|
files: [...files.filter((file) => file["relativePath"] !== targetRelativePath), updatedTarget],
|
|
@@ -7025,6 +7095,77 @@ function writeMetadataSnapshot(plan, now2) {
|
|
|
7025
7095
|
`, plan.workspace_root, 384);
|
|
7026
7096
|
return snapshotPath;
|
|
7027
7097
|
}
|
|
7098
|
+
function metadataSnapshotMatchesManifest(plan, manifest) {
|
|
7099
|
+
const snapshotDir = resolve(plan.workspace_root, ...PROJECT_CONTEXT_SNAPSHOT_DIR.split("/"));
|
|
7100
|
+
const snapshotPath = resolve(snapshotDir, `${safeFilename(manifest.projectContext.revision)}-${manifest.projectContext.hash.slice(-12)}.json`);
|
|
7101
|
+
if (!existsSync3(snapshotPath))
|
|
7102
|
+
return false;
|
|
7103
|
+
const record = readJsonRecord(snapshotPath, plan.workspace_root);
|
|
7104
|
+
const result = projectContextMetadataSnapshotSchema.safeParse(record);
|
|
7105
|
+
if (!result.success)
|
|
7106
|
+
return false;
|
|
7107
|
+
if (result.data.projectId !== manifest.projectContext.projectId || result.data.revision !== manifest.projectContext.revision || result.data.hash !== manifest.projectContext.hash || result.data.status !== manifest.projectContext.status)
|
|
7108
|
+
return false;
|
|
7109
|
+
const sortFiles = (files) => [...files].sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
7110
|
+
const snapshotFiles = sortFiles(result.data.files);
|
|
7111
|
+
const manifestFiles = sortFiles(manifest.files.map((file) => ({
|
|
7112
|
+
relativePath: file.relativePath,
|
|
7113
|
+
role: file.role,
|
|
7114
|
+
sha256: file.sha256
|
|
7115
|
+
})));
|
|
7116
|
+
return JSON.stringify(snapshotFiles) === JSON.stringify(manifestFiles);
|
|
7117
|
+
}
|
|
7118
|
+
function writeProjectContextRollbackSnapshot(plan, now2, outputs) {
|
|
7119
|
+
const targetExpectedHash = expectedPlanHash(plan, plan.target_path);
|
|
7120
|
+
if (targetExpectedHash === sha2562(plan.target_content))
|
|
7121
|
+
return null;
|
|
7122
|
+
const files = outputs.flatMap((output) => {
|
|
7123
|
+
const expectedHash = expectedPlanHash(plan, output.path);
|
|
7124
|
+
if (expectedHash === null)
|
|
7125
|
+
return [];
|
|
7126
|
+
const content = readUtf8RegularFile(output.path, plan.workspace_root, managedObservationMaxBytes(relativePosix(plan.workspace_root, output.path)));
|
|
7127
|
+
if (sha2562(content) !== expectedHash) {
|
|
7128
|
+
throw new ProjectContextHashRace(`managed path changed while creating rollback evidence: ${relativePosix(plan.workspace_root, output.path)}`);
|
|
7129
|
+
}
|
|
7130
|
+
return [{
|
|
7131
|
+
path: output.path,
|
|
7132
|
+
relativePath: relativePosix(plan.workspace_root, output.path),
|
|
7133
|
+
role: output.role,
|
|
7134
|
+
sha256: expectedHash,
|
|
7135
|
+
content
|
|
7136
|
+
}];
|
|
7137
|
+
});
|
|
7138
|
+
const afterFiles = outputs.map((output) => {
|
|
7139
|
+
const previousHash = expectedPlanHash(plan, output.path);
|
|
7140
|
+
const nextHash = sha2562(output.content);
|
|
7141
|
+
return {
|
|
7142
|
+
path: output.path,
|
|
7143
|
+
relativePath: relativePosix(plan.workspace_root, output.path),
|
|
7144
|
+
role: output.role,
|
|
7145
|
+
action: previousHash === null ? "create" : previousHash === nextHash ? "unchanged" : "update",
|
|
7146
|
+
sha256: nextHash
|
|
7147
|
+
};
|
|
7148
|
+
});
|
|
7149
|
+
const snapshotDir = resolve(plan.workspace_root, ...SESSION_RENDER_SNAPSHOT_RELATIVE_DIR.split("/"));
|
|
7150
|
+
ensureSafeDirectory(snapshotDir, plan.workspace_root, 448);
|
|
7151
|
+
const timestamp = now2.toISOString().replace(/[:.]/g, "-");
|
|
7152
|
+
const snapshotPath = resolve(snapshotDir, `${timestamp}-${randomUUID3()}.json`);
|
|
7153
|
+
const snapshot = {
|
|
7154
|
+
schema: "hasna.configs.session-render-snapshot/v2",
|
|
7155
|
+
createdAt: now2.toISOString(),
|
|
7156
|
+
tool: manifestTool(plan.runtime),
|
|
7157
|
+
profile: "project-context",
|
|
7158
|
+
targetHome: plan.workspace_root,
|
|
7159
|
+
targetKind: "project-root",
|
|
7160
|
+
manifestPath: plan.manifest_path,
|
|
7161
|
+
previousManifest: null,
|
|
7162
|
+
files,
|
|
7163
|
+
afterFiles
|
|
7164
|
+
};
|
|
7165
|
+
atomicWriteFile(snapshotPath, `${JSON.stringify(snapshot, null, 2)}
|
|
7166
|
+
`, plan.workspace_root, 384, null);
|
|
7167
|
+
return snapshotPath;
|
|
7168
|
+
}
|
|
7028
7169
|
function readProjectContextManifest(path, workspaceRoot) {
|
|
7029
7170
|
if (!existsSync3(path))
|
|
7030
7171
|
return null;
|
|
@@ -7048,7 +7189,7 @@ function readProjectContextCache(path, workspaceRoot) {
|
|
|
7048
7189
|
if (!result.success) {
|
|
7049
7190
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_INVALID", "cache is malformed or incompatible");
|
|
7050
7191
|
}
|
|
7051
|
-
const bundle =
|
|
7192
|
+
const bundle = parseProjectContextBundleInternal(result.data.bundle, true);
|
|
7052
7193
|
if (result.data.project_id !== bundle.project.id || result.data.revision !== bundle.revision || result.data.hash !== bundle.hash) {
|
|
7053
7194
|
throw new ProjectContextError("PROJECT_CONTEXT_CACHE_INVALID", "cache metadata does not match its bundle");
|
|
7054
7195
|
}
|
|
@@ -8364,12 +8505,23 @@ function removeHashForFingerprint(value) {
|
|
|
8364
8505
|
return value;
|
|
8365
8506
|
const copy = {};
|
|
8366
8507
|
for (const [key, item] of Object.entries(value)) {
|
|
8367
|
-
if (key === "hash")
|
|
8508
|
+
if (key === "generated_at" || key === "hash")
|
|
8368
8509
|
continue;
|
|
8369
8510
|
copy[key] = item;
|
|
8370
8511
|
}
|
|
8371
8512
|
return copy;
|
|
8372
8513
|
}
|
|
8514
|
+
function computeLegacyProjectContextSourceHash(value) {
|
|
8515
|
+
if (!isRecord(value))
|
|
8516
|
+
return `sha256:${sha2562(stableStringify(value))}`;
|
|
8517
|
+
const copy = {};
|
|
8518
|
+
for (const [key, item] of Object.entries(value)) {
|
|
8519
|
+
if (key === "hash")
|
|
8520
|
+
continue;
|
|
8521
|
+
copy[key] = item;
|
|
8522
|
+
}
|
|
8523
|
+
return `sha256:${sha2562(stableStringify(copy))}`;
|
|
8524
|
+
}
|
|
8373
8525
|
function sha2562(content) {
|
|
8374
8526
|
return createHash2("sha256").update(content).digest("hex");
|
|
8375
8527
|
}
|
|
@@ -15,6 +15,7 @@ export declare const PROJECT_CONTEXT_CACHE_SCHEMA: "hasna.instructions.project-c
|
|
|
15
15
|
export declare const PROJECT_CONTEXT_MANAGED_COMMENT = "Managed by @hasna/configs project context";
|
|
16
16
|
export declare const FOREIGN_INPUT_MAX_BYTES: number;
|
|
17
17
|
export declare const SESSION_MANAGED_OUTPUT_MAX_BYTES: number;
|
|
18
|
+
export declare const SESSION_MANAGED_INPUT_MAX_BYTES: number;
|
|
18
19
|
export declare const SESSION_MANAGED_OUTPUT_PATHS: string[];
|
|
19
20
|
/**
|
|
20
21
|
* Headroom threshold. A managed output past this is still written and still read, but the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-context.d.ts","sourceRoot":"","sources":["../../src/lib/project-context.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"project-context.d.ts","sourceRoot":"","sources":["../../src/lib/project-context.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -5729,7 +5729,7 @@ function isSafeSingleLine(value) {
|
|
|
5729
5729
|
function isSafeCommandArgument(value) {
|
|
5730
5730
|
return /^[A-Za-z0-9_./:@+=,-]+$/.test(value) && !value.includes("://") && !value.startsWith("-") || /^--[a-z][a-z0-9-]*$/.test(value);
|
|
5731
5731
|
}
|
|
5732
|
-
var PROJECT_CONTEXT_SCHEMA = "hasna.projects.project_context_bundle.v1", PROJECT_CONTEXT_MAX_INPUT_BYTES, PROJECT_CONTEXT_MAX_RENDERED_BYTES, PROJECT_CONTEXT_MAX_COMMANDS = 6, PROJECT_CONTEXT_FRAGMENT_PATH = ".hasna/instructions/project-context.md", PROJECT_CONTEXT_CACHE_SCHEMA = "hasna.instructions.project-context-cache/v1", SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES, FOREIGN_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_WARN_BYTES, PROJECT_CONTEXT_LOCK_STALE_MS, PROJECT_KINDS, PROJECT_STATUSES, LINK_STATES, RESOLUTION_SOURCES, safeId, nullableId, producerSlug, producerName, safeOptionalDisplay, isoTimestamp, revisionSchema, hashSchema, absolutePath, commandArg, commandSchema, projectContextBundleSchema, storedManifestProjectContextSchema, storedManifestFileSchema, storedManifestObservationSchema, projectContextCacheSchema;
|
|
5732
|
+
var PROJECT_CONTEXT_SCHEMA = "hasna.projects.project_context_bundle.v1", PROJECT_CONTEXT_MAX_INPUT_BYTES, PROJECT_CONTEXT_MAX_RENDERED_BYTES, PROJECT_CONTEXT_MAX_COMMANDS = 6, PROJECT_CONTEXT_FRAGMENT_PATH = ".hasna/instructions/project-context.md", PROJECT_CONTEXT_CACHE_SCHEMA = "hasna.instructions.project-context-cache/v1", SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES, FOREIGN_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_WARN_BYTES, PROJECT_CONTEXT_LOCK_STALE_MS, PROJECT_KINDS, PROJECT_STATUSES, LINK_STATES, RESOLUTION_SOURCES, safeId, nullableId, producerSlug, producerName, safeOptionalDisplay, isoTimestamp, revisionSchema, hashSchema, absolutePath, commandArg, commandSchema, projectContextBundleSchema, storedManifestProjectContextSchema, storedManifestFileSchema, storedManifestObservationSchema, projectContextMetadataSnapshotSchema, projectContextCacheSchema;
|
|
5733
5733
|
var init_project_context = __esm(() => {
|
|
5734
5734
|
init_zod();
|
|
5735
5735
|
init_redact();
|
|
@@ -5860,6 +5860,25 @@ var init_project_context = __esm(() => {
|
|
|
5860
5860
|
});
|
|
5861
5861
|
}
|
|
5862
5862
|
});
|
|
5863
|
+
projectContextMetadataSnapshotSchema = exports_external.object({
|
|
5864
|
+
schema: exports_external.literal("hasna.configs.session-render-snapshot/v1"),
|
|
5865
|
+
kind: exports_external.literal("project-context-metadata"),
|
|
5866
|
+
createdAt: isoTimestamp,
|
|
5867
|
+
projectId: safeId,
|
|
5868
|
+
revision: revisionSchema,
|
|
5869
|
+
hash: hashSchema,
|
|
5870
|
+
status: exports_external.enum(["fresh", "stale-source", "stale-cache"]),
|
|
5871
|
+
files: exports_external.array(exports_external.object({
|
|
5872
|
+
relativePath: exports_external.enum([
|
|
5873
|
+
PROJECT_CONTEXT_FRAGMENT_PATH,
|
|
5874
|
+
"CLAUDE.md",
|
|
5875
|
+
".codewith/CODEWITH.md",
|
|
5876
|
+
"AGENTS.md"
|
|
5877
|
+
]),
|
|
5878
|
+
role: exports_external.enum(["fragment", "index"]),
|
|
5879
|
+
sha256: exports_external.string().regex(/^[a-f0-9]{64}$/)
|
|
5880
|
+
}).strict()).min(1).max(2)
|
|
5881
|
+
}).strict();
|
|
5863
5882
|
projectContextCacheSchema = exports_external.object({
|
|
5864
5883
|
schema: exports_external.literal(PROJECT_CONTEXT_CACHE_SCHEMA),
|
|
5865
5884
|
cached_at: isoTimestamp,
|
|
@@ -7239,7 +7258,7 @@ var init_sync_dir = __esm(() => {
|
|
|
7239
7258
|
var require_package = __commonJS((exports, module) => {
|
|
7240
7259
|
module.exports = {
|
|
7241
7260
|
name: "@hasna/instructions",
|
|
7242
|
-
version: "0.4.
|
|
7261
|
+
version: "0.4.26",
|
|
7243
7262
|
description: "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
7244
7263
|
type: "module",
|
|
7245
7264
|
main: "dist/index.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/instructions",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.26",
|
|
4
4
|
"description": "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|