@hasna/instructions 0.4.8 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/cli/index.js +513 -232
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +481 -200
- package/dist/lib/apply.d.ts +7 -0
- package/dist/lib/apply.d.ts.map +1 -1
- package/dist/lib/global-agent-rules-standard.d.ts +106 -0
- package/dist/lib/global-agent-rules-standard.d.ts.map +1 -1
- package/dist/lib/global-agent-rules-stored-content.test.d.ts +2 -0
- package/dist/lib/global-agent-rules-stored-content.test.d.ts.map +1 -0
- package/dist/lib/project-context-prepared-file.test.d.ts +2 -0
- package/dist/lib/project-context-prepared-file.test.d.ts.map +1 -0
- package/dist/lib/project-context.d.ts +10 -0
- package/dist/lib/project-context.d.ts.map +1 -1
- package/dist/lib/session-render-contract.d.ts +10 -0
- package/dist/lib/session-render-contract.d.ts.map +1 -1
- package/dist/lib/session-render-ownership.d.ts +23 -0
- package/dist/lib/session-render-ownership.d.ts.map +1 -0
- package/dist/lib/session-render-ownership.test.d.ts +2 -0
- package/dist/lib/session-render-ownership.test.d.ts.map +1 -0
- package/dist/lib/session-render.d.ts +24 -1
- package/dist/lib/session-render.d.ts.map +1 -1
- package/dist/lib/test-temp-root.d.ts +7 -0
- package/dist/lib/test-temp-root.d.ts.map +1 -0
- package/dist/mcp/index.js +214 -39
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3220,37 +3220,123 @@ var init_config_agents = __esm(() => {
|
|
|
3220
3220
|
});
|
|
3221
3221
|
|
|
3222
3222
|
// src/lib/global-agent-rules-standard.ts
|
|
3223
|
-
|
|
3224
|
-
|
|
3223
|
+
import { createHash } from "crypto";
|
|
3224
|
+
function parseAgentOperatingRulesVersion(content) {
|
|
3225
|
+
return content ? AGENT_OPERATING_RULES_SENTINEL_PATTERN.exec(content)?.[1] ?? null : null;
|
|
3226
|
+
}
|
|
3227
|
+
function compareAgentOperatingRulesVersions(left, right) {
|
|
3228
|
+
const leftParts = left.split(".").map(Number);
|
|
3229
|
+
const rightParts = right.split(".").map(Number);
|
|
3230
|
+
for (let i = 0;i < 3; i++) {
|
|
3231
|
+
const diff = (leftParts[i] ?? 0) - (rightParts[i] ?? 0);
|
|
3232
|
+
if (diff !== 0)
|
|
3233
|
+
return diff;
|
|
3234
|
+
}
|
|
3235
|
+
return 0;
|
|
3236
|
+
}
|
|
3237
|
+
function payloadDate(content) {
|
|
3238
|
+
const canonical = new RegExp(AGENT_OPERATING_RULES_HEADING_PATTERN.source, "m").exec(content)?.[1];
|
|
3239
|
+
if (canonical)
|
|
3240
|
+
return canonical;
|
|
3241
|
+
const heading = /^#[^\S\n].*$/m.exec(content)?.[0];
|
|
3242
|
+
return heading ? /\b([0-9]{4}-[0-9]{2}-[0-9]{2})\b/.exec(heading)?.[1] ?? null : null;
|
|
3243
|
+
}
|
|
3244
|
+
function sha256(content) {
|
|
3245
|
+
return createHash("sha256").update(content).digest("hex");
|
|
3246
|
+
}
|
|
3247
|
+
function resolveAgentOperatingRulesPayload(storedContent) {
|
|
3248
|
+
const stored = storedContent ?? "";
|
|
3249
|
+
const storedVersion = parseAgentOperatingRulesVersion(stored);
|
|
3250
|
+
const baselineOrder = storedVersion === null ? null : compareAgentOperatingRulesVersions(storedVersion, AGENT_OPERATING_RULES_VERSION);
|
|
3251
|
+
const storedIsCurrent = baselineOrder !== null && (baselineOrder > 0 || baselineOrder === 0 && sha256(stored) === AGENT_OPERATING_RULES_PAYLOAD_SHA256);
|
|
3252
|
+
const content = storedIsCurrent ? stored : GLOBAL_AGENT_RULES_STANDARD_CONTENT;
|
|
3253
|
+
const origin = storedIsCurrent ? "stored-config" : "embedded-baseline";
|
|
3254
|
+
const matchesEmbeddedBaseline = content === GLOBAL_AGENT_RULES_STANDARD_CONTENT;
|
|
3255
|
+
const integrity = matchesEmbeddedBaseline ? "pinned-digest" : "unverified-self-declared";
|
|
3256
|
+
const version = storedIsCurrent ? storedVersion : AGENT_OPERATING_RULES_VERSION;
|
|
3257
|
+
const payloadSha256 = matchesEmbeddedBaseline ? AGENT_OPERATING_RULES_PAYLOAD_SHA256 : sha256(content);
|
|
3258
|
+
const sourceSetVersion = matchesEmbeddedBaseline ? AGENT_OPERATING_RULES_SOURCE_SET_VERSION : payloadDate(content);
|
|
3259
|
+
const upstreamPin = matchesEmbeddedBaseline ? {
|
|
3260
|
+
upstreamRepository: AGENT_OPERATING_RULES_UPSTREAM.repository,
|
|
3261
|
+
upstreamCommit: AGENT_OPERATING_RULES_UPSTREAM.commit,
|
|
3262
|
+
upstreamPath: AGENT_OPERATING_RULES_UPSTREAM.path,
|
|
3263
|
+
upstreamFileSha256: AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256
|
|
3264
|
+
} : {};
|
|
3265
|
+
const policyReference = content.includes(SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE) ? { policyReference: SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE } : {};
|
|
3266
|
+
return {
|
|
3267
|
+
content,
|
|
3268
|
+
version,
|
|
3269
|
+
origin,
|
|
3270
|
+
matchesEmbeddedBaseline,
|
|
3271
|
+
integrity,
|
|
3272
|
+
provenance: {
|
|
3273
|
+
source: AGENT_OPERATING_RULES_PROVENANCE.source,
|
|
3274
|
+
payloadOrigin: origin,
|
|
3275
|
+
payloadIntegrity: integrity,
|
|
3276
|
+
...upstreamPin,
|
|
3277
|
+
upstreamExportId: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
3278
|
+
upstreamSourceId: AGENT_OPERATING_RULES_SOURCE_ID,
|
|
3279
|
+
selectedPayloadSha256: payloadSha256,
|
|
3280
|
+
rulesVersion: version,
|
|
3281
|
+
sourceSetVersion,
|
|
3282
|
+
...policyReference
|
|
3283
|
+
},
|
|
3284
|
+
metadata: {
|
|
3285
|
+
sourceSet: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
3286
|
+
role: AGENT_OPERATING_RULES_METADATA.role,
|
|
3287
|
+
payloadOrigin: origin,
|
|
3288
|
+
payloadIntegrity: integrity,
|
|
3289
|
+
rulesVersion: version,
|
|
3290
|
+
sourceSetVersion,
|
|
3291
|
+
plan: GLOBAL_AGENT_RULES_STANDARD_SLUG,
|
|
3292
|
+
contentSha256: payloadSha256,
|
|
3293
|
+
selectedPayloadSha256: payloadSha256,
|
|
3294
|
+
...matchesEmbeddedBaseline ? { upstreamFileSha256: AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 } : {},
|
|
3295
|
+
upstreamExportId: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
3296
|
+
upstreamSourceId: AGENT_OPERATING_RULES_SOURCE_ID,
|
|
3297
|
+
sentinel: AGENT_OPERATING_RULES_METADATA.sentinel,
|
|
3298
|
+
...policyReference.policyReference ? { policyReferences: { incidentRecovery: SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE } } : {}
|
|
3299
|
+
}
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
function standardConfigInput(payload) {
|
|
3303
|
+
return {
|
|
3225
3304
|
name: "Global Agent Rules Standard",
|
|
3226
3305
|
category: "rules",
|
|
3227
3306
|
agent: "global",
|
|
3228
3307
|
format: "markdown",
|
|
3229
|
-
content:
|
|
3308
|
+
content: payload.content,
|
|
3230
3309
|
kind: "reference",
|
|
3231
|
-
description: `Managed Hasna agent operating rules v${
|
|
3310
|
+
description: payload.matchesEmbeddedBaseline ? `Managed Hasna agent operating rules v${payload.version}; accepted source ${AGENT_OPERATING_RULES_UPSTREAM.repository}@${AGENT_OPERATING_RULES_UPSTREAM.commit}:${AGENT_OPERATING_RULES_UPSTREAM.path}` : `Managed Hasna agent operating rules v${payload.version}; stored payload sha256 ${payload.metadata["contentSha256"]}`,
|
|
3232
3311
|
tags: [
|
|
3233
3312
|
"global-agent-rules",
|
|
3234
3313
|
"system-prompt",
|
|
3235
3314
|
"coding-agent-rules",
|
|
3236
3315
|
"agent-operating-rules",
|
|
3237
|
-
`rules-version:${
|
|
3238
|
-
`source-commit:${AGENT_OPERATING_RULES_UPSTREAM.commit}`
|
|
3316
|
+
`rules-version:${payload.version}`,
|
|
3317
|
+
...payload.matchesEmbeddedBaseline ? [`source-commit:${AGENT_OPERATING_RULES_UPSTREAM.commit}`] : []
|
|
3239
3318
|
]
|
|
3240
3319
|
};
|
|
3320
|
+
}
|
|
3321
|
+
async function ensureGlobalAgentRulesStandardConfig(store = resolveConfigStore()) {
|
|
3322
|
+
let existing;
|
|
3241
3323
|
try {
|
|
3242
|
-
|
|
3243
|
-
if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
|
|
3244
|
-
return await store.updateConfig(existing.id, input);
|
|
3245
|
-
}
|
|
3246
|
-
return existing;
|
|
3324
|
+
existing = await store.getConfig(GLOBAL_AGENT_RULES_STANDARD_SLUG);
|
|
3247
3325
|
} catch {
|
|
3248
|
-
return await store.createConfig(
|
|
3326
|
+
return await store.createConfig(standardConfigInput(resolveAgentOperatingRulesPayload(null)));
|
|
3327
|
+
}
|
|
3328
|
+
const payload = resolveAgentOperatingRulesPayload(existing.content);
|
|
3329
|
+
const input = standardConfigInput(payload);
|
|
3330
|
+
if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
|
|
3331
|
+
return await store.updateConfig(existing.id, input);
|
|
3249
3332
|
}
|
|
3333
|
+
return existing;
|
|
3250
3334
|
}
|
|
3251
|
-
var GLOBAL_AGENT_RULES_STANDARD_SLUG = "global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_SET_ID = "hasna-global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_ID = "hasna-agent-operating-rules", AGENT_OPERATING_RULES_VERSION = "1.1.6", AGENT_OPERATING_RULES_SOURCE_SET_VERSION = "2026-07-23", AGENT_OPERATING_RULES_SENTINEL = "<!-- hasna:agent-operating-rules v=1.1.6 -->", AGENT_OPERATING_RULES_PAYLOAD_SHA256 = "8b236086b82e94490516e0b00dffa03fb5f6841b68d95f80fc3e3c8fb7087420", AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 = "b8e89cdb49e207e5b497ac51384d67022b94fe5645cc9273db60384eb2c2fb32", SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE = "hasna-agent-operating-rules/scoped-operational-control/v1", AGENT_OPERATING_RULES_UPSTREAM, SCOPED_OPERATIONAL_CONTROL_POLICY, AGENT_OPERATING_RULES_PROVENANCE, AGENT_OPERATING_RULES_METADATA, NO_BRITTLE_HARDCODING_RULE = "Do not hardcode brittle values, paths, provider names, config, business logic, environment-specific IDs, or one-off mappings when a source-of-truth, schema/config-driven, package-owned, reusable, or cleaner abstraction exists. This is especially strict in medium and large applications. Explicit constants, fixtures, tests, and temporary compatibility shims are allowed only when scoped, named, and justified.", GLOBAL_AGENT_RULES_STANDARD_CONTENT;
|
|
3335
|
+
var GLOBAL_AGENT_RULES_STANDARD_SLUG = "global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_SET_ID = "hasna-global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_ID = "hasna-agent-operating-rules", AGENT_OPERATING_RULES_ROLE = "agent-operating-rules", AGENT_OPERATING_RULES_VERSION = "1.1.6", AGENT_OPERATING_RULES_SOURCE_SET_VERSION = "2026-07-23", AGENT_OPERATING_RULES_SENTINEL = "<!-- hasna:agent-operating-rules v=1.1.6 -->", AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY = "hasna:agent-operating-rules", AGENT_OPERATING_RULES_SENTINEL_PATTERN, AGENT_OPERATING_RULES_HEADING_PATTERN, AGENT_OPERATING_RULES_PAYLOAD_SHA256 = "8b236086b82e94490516e0b00dffa03fb5f6841b68d95f80fc3e3c8fb7087420", AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 = "b8e89cdb49e207e5b497ac51384d67022b94fe5645cc9273db60384eb2c2fb32", SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE = "hasna-agent-operating-rules/scoped-operational-control/v1", AGENT_OPERATING_RULES_UPSTREAM, SCOPED_OPERATIONAL_CONTROL_POLICY, AGENT_OPERATING_RULES_PROVENANCE, AGENT_OPERATING_RULES_METADATA, NO_BRITTLE_HARDCODING_RULE = "Do not hardcode brittle values, paths, provider names, config, business logic, environment-specific IDs, or one-off mappings when a source-of-truth, schema/config-driven, package-owned, reusable, or cleaner abstraction exists. This is especially strict in medium and large applications. Explicit constants, fixtures, tests, and temporary compatibility shims are allowed only when scoped, named, and justified.", GLOBAL_AGENT_RULES_STANDARD_CONTENT;
|
|
3252
3336
|
var init_global_agent_rules_standard = __esm(() => {
|
|
3253
3337
|
init_config_store();
|
|
3338
|
+
AGENT_OPERATING_RULES_SENTINEL_PATTERN = /<!--\s*hasna:agent-operating-rules\s+v=([0-9]+\.[0-9]+\.[0-9]+)\s*-->/i;
|
|
3339
|
+
AGENT_OPERATING_RULES_HEADING_PATTERN = /^#\s*Hasna Agent Operating Rules\s+\u2014\s+v[0-9]+\.[0-9]+\.[0-9]+\s+\(([0-9]{4}-[0-9]{2}-[0-9]{2})\)/;
|
|
3254
3340
|
AGENT_OPERATING_RULES_UPSTREAM = {
|
|
3255
3341
|
repository: "hasnaxyz/iapp-identities",
|
|
3256
3342
|
commit: "48168c549cc2945053a4498a9a2b11888419bc94",
|
|
@@ -3279,7 +3365,7 @@ var init_global_agent_rules_standard = __esm(() => {
|
|
|
3279
3365
|
};
|
|
3280
3366
|
AGENT_OPERATING_RULES_METADATA = {
|
|
3281
3367
|
sourceSet: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
3282
|
-
role:
|
|
3368
|
+
role: AGENT_OPERATING_RULES_ROLE,
|
|
3283
3369
|
rulesVersion: AGENT_OPERATING_RULES_VERSION,
|
|
3284
3370
|
sourceSetVersion: AGENT_OPERATING_RULES_SOURCE_SET_VERSION,
|
|
3285
3371
|
plan: GLOBAL_AGENT_RULES_STANDARD_SLUG,
|
|
@@ -3288,7 +3374,7 @@ var init_global_agent_rules_standard = __esm(() => {
|
|
|
3288
3374
|
upstreamFileSha256: AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256,
|
|
3289
3375
|
upstreamExportId: AGENT_OPERATING_RULES_SOURCE_SET_ID,
|
|
3290
3376
|
upstreamSourceId: AGENT_OPERATING_RULES_SOURCE_ID,
|
|
3291
|
-
sentinel:
|
|
3377
|
+
sentinel: AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY,
|
|
3292
3378
|
policyReferences: {
|
|
3293
3379
|
incidentRecovery: SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE
|
|
3294
3380
|
}
|
|
@@ -7480,8 +7566,11 @@ var init_redact = __esm(() => {
|
|
|
7480
7566
|
});
|
|
7481
7567
|
|
|
7482
7568
|
// src/lib/session-render-contract.ts
|
|
7483
|
-
var CODEWITH_NATIVE_IMPORTS_ENV = "HASNA_CONFIGS_CODEWITH_NATIVE_IMPORTS", SESSION_RENDER_MANAGED_MARKER = "Managed by @hasna/configs session render", SESSION_RENDER_SCHEMA = "hasna.configs.session-render/v1", SESSION_INSTRUCTION_LAYERS;
|
|
7569
|
+
var CODEWITH_NATIVE_IMPORTS_ENV = "HASNA_CONFIGS_CODEWITH_NATIVE_IMPORTS", SESSION_RENDER_MANAGED_MARKER = "Managed by @hasna/configs session render", SESSION_RENDER_SCHEMA = "hasna.configs.session-render/v1", SESSION_RENDER_MANAGED_NAMESPACE = ".hasna", SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR, SESSION_RENDER_MANIFEST_RELATIVE_PATH, SESSION_RENDER_SNAPSHOT_RELATIVE_DIR, SESSION_INSTRUCTION_LAYERS;
|
|
7484
7570
|
var init_session_render_contract = __esm(() => {
|
|
7571
|
+
SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR = `${SESSION_RENDER_MANAGED_NAMESPACE}/instructions`;
|
|
7572
|
+
SESSION_RENDER_MANIFEST_RELATIVE_PATH = `${SESSION_RENDER_MANAGED_NAMESPACE}/session-render-manifest.json`;
|
|
7573
|
+
SESSION_RENDER_SNAPSHOT_RELATIVE_DIR = `${SESSION_RENDER_MANAGED_NAMESPACE}/session-render-snapshots`;
|
|
7485
7574
|
SESSION_INSTRUCTION_LAYERS = [
|
|
7486
7575
|
"global",
|
|
7487
7576
|
"tool",
|
|
@@ -7498,7 +7587,7 @@ var init_session_render_contract = __esm(() => {
|
|
|
7498
7587
|
});
|
|
7499
7588
|
|
|
7500
7589
|
// src/lib/project-context.ts
|
|
7501
|
-
import { createHash, randomUUID as randomUUID6 } from "crypto";
|
|
7590
|
+
import { createHash as createHash2, randomUUID as randomUUID6 } from "crypto";
|
|
7502
7591
|
import { execFileSync } from "child_process";
|
|
7503
7592
|
import { dlopen, FFIType } from "bun:ffi";
|
|
7504
7593
|
import {
|
|
@@ -7520,7 +7609,7 @@ import {
|
|
|
7520
7609
|
import { basename, dirname, isAbsolute, join as join4, parse, relative, resolve } from "path";
|
|
7521
7610
|
function computeProjectContextSourceHash(value) {
|
|
7522
7611
|
const normalized = removeHashForFingerprint(value);
|
|
7523
|
-
return `sha256:${
|
|
7612
|
+
return `sha256:${sha2562(stableStringify(normalized))}`;
|
|
7524
7613
|
}
|
|
7525
7614
|
function parseProjectContextBundle(input) {
|
|
7526
7615
|
let encoded;
|
|
@@ -7690,7 +7779,7 @@ function composeProjectContextSessionRender(input) {
|
|
|
7690
7779
|
const files = input.files.map((file) => file === index ? {
|
|
7691
7780
|
...file,
|
|
7692
7781
|
content,
|
|
7693
|
-
sha256:
|
|
7782
|
+
sha256: sha2562(content),
|
|
7694
7783
|
sourceIds: [...new Set([...file.sourceIds, "project-context-bundle"])]
|
|
7695
7784
|
} : file);
|
|
7696
7785
|
if (observedHashes.some((observed) => currentFileHash(observed.path, workspaceRoot) !== observed.sha256)) {
|
|
@@ -7891,7 +7980,7 @@ function assertRenderedOutputsStable(plan, cacheContent, sessionOutput) {
|
|
|
7891
7980
|
sessionOutput
|
|
7892
7981
|
];
|
|
7893
7982
|
for (const output of outputs) {
|
|
7894
|
-
if (currentFileHash(output.path, plan.workspace_root) !==
|
|
7983
|
+
if (currentFileHash(output.path, plan.workspace_root) !== sha2562(output.content)) {
|
|
7895
7984
|
throw new ProjectContextHashRace(`managed path changed before manifest commit: ${relativePosix(plan.workspace_root, output.path)}`);
|
|
7896
7985
|
}
|
|
7897
7986
|
}
|
|
@@ -8111,7 +8200,7 @@ function findLegacyCodewithWorkspaceSection(workspaceRoot, runtime, content, bun
|
|
|
8111
8200
|
return null;
|
|
8112
8201
|
const files = Array.isArray(manifest["files"]) ? manifest["files"] : [];
|
|
8113
8202
|
const codewith = files.find((file) => isRecord(file) && file["relativePath"] === "CODEWITH.md");
|
|
8114
|
-
if (!isRecord(codewith) || codewith["sha256"] !==
|
|
8203
|
+
if (!isRecord(codewith) || codewith["sha256"] !== sha2562(content)) {
|
|
8115
8204
|
throw new ProjectContextError("MANAGED_BLOCK_CONFLICT", "legacy /dev/fd session manifest does not match CODEWITH.md");
|
|
8116
8205
|
}
|
|
8117
8206
|
const section = /^## Workspace\r?\n/gm.exec(content);
|
|
@@ -8181,14 +8270,14 @@ function buildManifest(plan, now3) {
|
|
|
8181
8270
|
path: plan.fragment_path,
|
|
8182
8271
|
relativePath: PROJECT_CONTEXT_FRAGMENT_PATH,
|
|
8183
8272
|
role: "fragment",
|
|
8184
|
-
sha256:
|
|
8273
|
+
sha256: sha2562(plan.fragment),
|
|
8185
8274
|
sourceIds: ["project-context-bundle"]
|
|
8186
8275
|
},
|
|
8187
8276
|
{
|
|
8188
8277
|
path: plan.target_path,
|
|
8189
8278
|
relativePath: plan.target_relative_path,
|
|
8190
8279
|
role: "index",
|
|
8191
|
-
sha256:
|
|
8280
|
+
sha256: sha2562(plan.target_content),
|
|
8192
8281
|
sourceIds: ["project-context-bundle"]
|
|
8193
8282
|
}
|
|
8194
8283
|
];
|
|
@@ -8265,7 +8354,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8265
8354
|
path: plan.target_path,
|
|
8266
8355
|
relativePath: targetRelativePath,
|
|
8267
8356
|
role: "index",
|
|
8268
|
-
sha256:
|
|
8357
|
+
sha256: sha2562(plan.target_content),
|
|
8269
8358
|
sourceIds: [...new Set([...previousSourceIds ?? [], "project-context-bundle"])]
|
|
8270
8359
|
};
|
|
8271
8360
|
const targetOwner = isRecord(existing["targetOwner"]) ? existing["targetOwner"] : {};
|
|
@@ -8293,7 +8382,7 @@ function buildSessionCompatibilityManifest(plan, now3) {
|
|
|
8293
8382
|
blockers: [],
|
|
8294
8383
|
generatedAt: now3.toISOString(),
|
|
8295
8384
|
env: sanitizeLegacyEnvironment(existing["env"]),
|
|
8296
|
-
sourceHash:
|
|
8385
|
+
sourceHash: sha2562(stableStringify({ previous: typeof existing["sourceHash"] === "string" ? existing["sourceHash"] : null, projectContext: plan.bundle.hash })),
|
|
8297
8386
|
sources,
|
|
8298
8387
|
skippedSources: sanitizeLegacySkippedSources(existing["skippedSources"]),
|
|
8299
8388
|
files: [...files.filter((file) => file["relativePath"] !== targetRelativePath), updatedTarget],
|
|
@@ -8496,7 +8585,7 @@ function projectContextManifestSource(cachePath, runtime, bundle) {
|
|
|
8496
8585
|
nonOverridable: true,
|
|
8497
8586
|
replacementScope: "project-context",
|
|
8498
8587
|
rules: [],
|
|
8499
|
-
renderedPayloadSha256:
|
|
8588
|
+
renderedPayloadSha256: sha2562(JSON.stringify(bundle)),
|
|
8500
8589
|
provenance: {
|
|
8501
8590
|
schema: PROJECT_CONTEXT_SCHEMA,
|
|
8502
8591
|
projectId: bundle.project.id,
|
|
@@ -8618,7 +8707,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8618
8707
|
let fd = null;
|
|
8619
8708
|
let preserveTemp = false;
|
|
8620
8709
|
let directoryChanged = false;
|
|
8621
|
-
const desiredHash =
|
|
8710
|
+
const desiredHash = sha2562(content);
|
|
8622
8711
|
try {
|
|
8623
8712
|
fd = anchoredOpenExclusive(directory, tempName, previousMode);
|
|
8624
8713
|
writeFileSync(fd, content, { encoding: "utf8" });
|
|
@@ -8627,7 +8716,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8627
8716
|
fd = null;
|
|
8628
8717
|
beforeInstall?.(tempPath);
|
|
8629
8718
|
assertManagedDirectoryStable(dir, workspaceRoot, directory.identity);
|
|
8630
|
-
if (
|
|
8719
|
+
if (anchoredPreparedObservation(directory, tempName, path, "before installation").hash !== desiredHash) {
|
|
8631
8720
|
throw new ProjectContextHashRace(`prepared bytes changed before installation: ${relativePosix(workspaceRoot, path)}`);
|
|
8632
8721
|
}
|
|
8633
8722
|
if (expectedHash === undefined) {
|
|
@@ -8636,8 +8725,8 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8636
8725
|
}
|
|
8637
8726
|
directoryChanged = true;
|
|
8638
8727
|
} else if (expectedHash === null) {
|
|
8639
|
-
const prepared =
|
|
8640
|
-
if (
|
|
8728
|
+
const prepared = anchoredPreparedObservation(directory, tempName, path, "before creation");
|
|
8729
|
+
if (anchoredFileObservation(directory, targetName) !== null) {
|
|
8641
8730
|
throw new ProjectContextHashRace(`managed path appeared before creation: ${relativePosix(workspaceRoot, path)}`);
|
|
8642
8731
|
}
|
|
8643
8732
|
if (!directory.ops.linkat(directory.fd, tempName, directory.fd, targetName)) {
|
|
@@ -8645,7 +8734,8 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8645
8734
|
}
|
|
8646
8735
|
directoryChanged = true;
|
|
8647
8736
|
const installed = anchoredFileObservation(directory, targetName);
|
|
8648
|
-
|
|
8737
|
+
const stagedHash = anchoredFileHash(directory, tempName);
|
|
8738
|
+
if (!installed || installed.dev !== prepared.dev || installed.ino !== prepared.ino || stagedHash !== desiredHash || installed.hash !== desiredHash) {
|
|
8649
8739
|
directory.ops.unlinkat(directory.fd, tempName);
|
|
8650
8740
|
preserveTemp = true;
|
|
8651
8741
|
throw new ProjectContextHashRace(`prepared bytes changed during creation: ${relativePosix(workspaceRoot, path)}`);
|
|
@@ -8662,7 +8752,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8662
8752
|
if (anchoredFileHash(directory, targetName) !== expectedHash) {
|
|
8663
8753
|
throw new ProjectContextHashRace(`managed path changed before atomic replacement: ${relativePosix(workspaceRoot, path)}`);
|
|
8664
8754
|
}
|
|
8665
|
-
if (
|
|
8755
|
+
if (anchoredPreparedObservation(directory, tempName, path, "before atomic replacement").hash !== desiredHash) {
|
|
8666
8756
|
throw new ProjectContextHashRace(`prepared bytes changed before atomic replacement: ${relativePosix(workspaceRoot, path)}`);
|
|
8667
8757
|
}
|
|
8668
8758
|
atomicExchangeEntries(directory.fd, tempName, targetName);
|
|
@@ -8728,7 +8818,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8728
8818
|
}
|
|
8729
8819
|
}
|
|
8730
8820
|
function atomicWritePortable(path, content, workspaceRoot, defaultMode, expectedHash, beforeInstall, maxObservedBytes, allowReplacement = false) {
|
|
8731
|
-
const desiredHash =
|
|
8821
|
+
const desiredHash = sha2562(content);
|
|
8732
8822
|
const currentHash = portableFileHash(path, workspaceRoot, maxObservedBytes);
|
|
8733
8823
|
if (expectedHash === undefined && currentHash === desiredHash)
|
|
8734
8824
|
return;
|
|
@@ -8759,7 +8849,7 @@ function atomicWritePortable(path, content, workspaceRoot, defaultMode, expected
|
|
|
8759
8849
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
8760
8850
|
assertNoSymlinkSegments(workspaceRoot, tempPath);
|
|
8761
8851
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8762
|
-
if (
|
|
8852
|
+
if (portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, "before portable creation") !== desiredHash || portableFileHash(path, workspaceRoot, maxObservedBytes) !== null) {
|
|
8763
8853
|
throw new ProjectContextHashRace(`managed path changed before portable creation: ${relativePosix(workspaceRoot, path)}`);
|
|
8764
8854
|
}
|
|
8765
8855
|
const prepared = lstatSync(tempPath);
|
|
@@ -8802,7 +8892,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8802
8892
|
const dir = dirname(path);
|
|
8803
8893
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8804
8894
|
const tempPath = join4(dir, `.project-context-${randomUUID6()}.tmp`);
|
|
8805
|
-
const desiredHash =
|
|
8895
|
+
const desiredHash = sha2562(content);
|
|
8806
8896
|
let fd = null;
|
|
8807
8897
|
let tempIdentity = null;
|
|
8808
8898
|
try {
|
|
@@ -8817,7 +8907,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8817
8907
|
assertManagedDirectoryStable(dir, workspaceRoot, directoryIdentity);
|
|
8818
8908
|
assertNoSymlinkSegments(workspaceRoot, tempPath);
|
|
8819
8909
|
assertNoSymlinkSegments(workspaceRoot, path);
|
|
8820
|
-
if (portableFileHash(path, workspaceRoot, maxObservedBytes) !== expectedHash ||
|
|
8910
|
+
if (portableFileHash(path, workspaceRoot, maxObservedBytes) !== expectedHash || portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, "before portable replacement") !== desiredHash) {
|
|
8821
8911
|
throw new ProjectContextHashRace(`managed path changed before portable replacement: ${relativePosix(workspaceRoot, path)}`);
|
|
8822
8912
|
}
|
|
8823
8913
|
renameSync(tempPath, path);
|
|
@@ -8841,6 +8931,20 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8841
8931
|
throw error;
|
|
8842
8932
|
}
|
|
8843
8933
|
}
|
|
8934
|
+
function portablePreparedHash(tempPath, path, workspaceRoot, maxObservedBytes, stage) {
|
|
8935
|
+
const unreadable = (cause) => new ProjectContextError("PROJECT_CONTEXT_PREPARED_FILE_UNREADABLE", `the prepared managed file could not be read back ${stage}: ${relativePosix(workspaceRoot, path)}`, { staged_path: tempPath, stage, ...cause === undefined ? {} : { cause } });
|
|
8936
|
+
let hash;
|
|
8937
|
+
try {
|
|
8938
|
+
hash = portableFileHash(tempPath, workspaceRoot, maxObservedBytes);
|
|
8939
|
+
} catch (error) {
|
|
8940
|
+
if (error instanceof ProjectContextError || error instanceof ProjectContextHashRace)
|
|
8941
|
+
throw error;
|
|
8942
|
+
throw unreadable(error.message);
|
|
8943
|
+
}
|
|
8944
|
+
if (hash === null)
|
|
8945
|
+
throw unreadable();
|
|
8946
|
+
return hash;
|
|
8947
|
+
}
|
|
8844
8948
|
function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
8845
8949
|
if (maxObservedBytes === undefined)
|
|
8846
8950
|
return currentFileHash(path, workspaceRoot);
|
|
@@ -8853,10 +8957,10 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
8853
8957
|
if (maxObservedBytes !== null && stat.size > maxObservedBytes) {
|
|
8854
8958
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePosix(workspaceRoot, path)}`);
|
|
8855
8959
|
}
|
|
8856
|
-
return
|
|
8960
|
+
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
8857
8961
|
}
|
|
8858
8962
|
function writeProjectContextCoordinatedFile(input) {
|
|
8859
|
-
atomicWriteFile(resolve(input.path), input.content, assertSafeWorkspaceRoot(input.workspace_root), input.default_mode ?? 420, input.expected_hash, undefined, false,
|
|
8963
|
+
atomicWriteFile(resolve(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);
|
|
8860
8964
|
}
|
|
8861
8965
|
function removeProjectContextCoordinatedFile(input) {
|
|
8862
8966
|
const workspaceRoot = assertSafeWorkspaceRoot(input.workspace_root);
|
|
@@ -9000,15 +9104,43 @@ function openAnchoredDirectory(path, workspaceRoot, providedOps, maxObservedByte
|
|
|
9000
9104
|
}
|
|
9001
9105
|
}
|
|
9002
9106
|
function anchoredOpenExclusive(directory, name, mode) {
|
|
9003
|
-
const
|
|
9004
|
-
|
|
9107
|
+
const requestedMode = mode & 4095;
|
|
9108
|
+
let fd;
|
|
9109
|
+
try {
|
|
9110
|
+
fd = openSync(join4(directory.path, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, requestedMode);
|
|
9111
|
+
} catch {
|
|
9005
9112
|
throw new ProjectContextHashRace(`could not create prepared managed file in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
9006
|
-
|
|
9007
|
-
|
|
9113
|
+
}
|
|
9114
|
+
try {
|
|
9115
|
+
const opened = fstatSync(fd);
|
|
9116
|
+
if (!opened.isFile()) {
|
|
9117
|
+
throw new ProjectContextHashRace("prepared managed output is not a regular file");
|
|
9118
|
+
}
|
|
9119
|
+
if (!isPreparedManagedFileModeUsable(requestedMode, opened.mode)) {
|
|
9120
|
+
throw new ProjectContextError("PROJECT_CONTEXT_PREPARED_FILE_MODE_REJECTED", `the platform created the prepared managed file with an unusable mode in ${relativePosix(directory.workspaceRoot, directory.path)}`, { requested_mode: modeLiteral(requestedMode), observed_mode: modeLiteral(opened.mode) });
|
|
9121
|
+
}
|
|
9122
|
+
assertManagedDirectoryStable(directory.path, directory.workspaceRoot, directory.identity);
|
|
9123
|
+
const anchored = anchoredFileObservation(directory, name);
|
|
9124
|
+
if (!anchored || anchored.dev !== opened.dev || anchored.ino !== opened.ino) {
|
|
9125
|
+
throw new ProjectContextHashRace(`prepared managed file is not the one anchored in ${relativePosix(directory.workspaceRoot, directory.path)}`);
|
|
9126
|
+
}
|
|
9127
|
+
return fd;
|
|
9128
|
+
} catch (error) {
|
|
9008
9129
|
closeSync(fd);
|
|
9009
|
-
throw
|
|
9130
|
+
throw error;
|
|
9010
9131
|
}
|
|
9011
|
-
|
|
9132
|
+
}
|
|
9133
|
+
function modeLiteral(mode) {
|
|
9134
|
+
return `0o${(mode & 4095).toString(8).padStart(3, "0")}`;
|
|
9135
|
+
}
|
|
9136
|
+
function isPreparedManagedFileModeUsable(requestedMode, observedMode) {
|
|
9137
|
+
const requested = requestedMode & 4095;
|
|
9138
|
+
const observed = observedMode & 4095;
|
|
9139
|
+
if ((observed & ~requested) !== 0)
|
|
9140
|
+
return false;
|
|
9141
|
+
if ((requested & 256) !== 0 && (observed & 256) === 0)
|
|
9142
|
+
return false;
|
|
9143
|
+
return true;
|
|
9012
9144
|
}
|
|
9013
9145
|
function anchoredFileObservation(directory, name) {
|
|
9014
9146
|
const fd = directory.ops.openat(directory.fd, name, constants.O_RDONLY | constants.O_NOFOLLOW, 0);
|
|
@@ -9026,7 +9158,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
9026
9158
|
return {
|
|
9027
9159
|
dev: stat.dev,
|
|
9028
9160
|
ino: stat.ino,
|
|
9029
|
-
hash:
|
|
9161
|
+
hash: createHash2("sha256").update(readFileSync(fd)).digest("hex"),
|
|
9030
9162
|
mode: stat.mode & 511
|
|
9031
9163
|
};
|
|
9032
9164
|
} finally {
|
|
@@ -9036,6 +9168,13 @@ function anchoredFileObservation(directory, name) {
|
|
|
9036
9168
|
function anchoredFileHash(directory, name) {
|
|
9037
9169
|
return anchoredFileObservation(directory, name)?.hash ?? null;
|
|
9038
9170
|
}
|
|
9171
|
+
function anchoredPreparedObservation(directory, name, path, stage) {
|
|
9172
|
+
const observed = anchoredFileObservation(directory, name);
|
|
9173
|
+
if (!observed) {
|
|
9174
|
+
throw new ProjectContextError("PROJECT_CONTEXT_PREPARED_FILE_UNREADABLE", `the prepared managed file could not be read back ${stage}: ${relativePosix(directory.workspaceRoot, path)}`, { staged_name: name, stage });
|
|
9175
|
+
}
|
|
9176
|
+
return observed;
|
|
9177
|
+
}
|
|
9039
9178
|
function captureManagedDirectoryIdentity(path, workspaceRoot) {
|
|
9040
9179
|
assertNoSymlinkSegments(workspaceRoot, join4(path, ".project-context-directory-guard"));
|
|
9041
9180
|
let stat;
|
|
@@ -9204,7 +9343,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9204
9343
|
process_start_id: processStartIdentityLookup(process.pid)
|
|
9205
9344
|
})}
|
|
9206
9345
|
`;
|
|
9207
|
-
openedContentHash =
|
|
9346
|
+
openedContentHash = sha2562(content);
|
|
9208
9347
|
writeFileSync(fd, content);
|
|
9209
9348
|
fsyncSync(fd);
|
|
9210
9349
|
try {
|
|
@@ -9255,7 +9394,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
9255
9394
|
const current = lstatSync(lockPath);
|
|
9256
9395
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
9257
9396
|
return;
|
|
9258
|
-
if (expectedHash !== undefined &&
|
|
9397
|
+
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
9259
9398
|
return;
|
|
9260
9399
|
rmSync2(lockPath);
|
|
9261
9400
|
fsyncDirectory(resolve(lockPath, ".."));
|
|
@@ -9272,7 +9411,7 @@ function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentity
|
|
|
9272
9411
|
} catch {
|
|
9273
9412
|
return null;
|
|
9274
9413
|
}
|
|
9275
|
-
const contentHash =
|
|
9414
|
+
const contentHash = sha2562(content);
|
|
9276
9415
|
if (currentFileHash(lockPath, workspaceRoot) !== contentHash)
|
|
9277
9416
|
return null;
|
|
9278
9417
|
let pid = null;
|
|
@@ -9434,7 +9573,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9434
9573
|
created_at: new Date().toISOString()
|
|
9435
9574
|
})}
|
|
9436
9575
|
`;
|
|
9437
|
-
releaseHash =
|
|
9576
|
+
releaseHash = sha2562(releaseContent);
|
|
9438
9577
|
writeFileSync(releaseFd, releaseContent);
|
|
9439
9578
|
fsyncSync(releaseFd);
|
|
9440
9579
|
closeSync(releaseFd);
|
|
@@ -9673,7 +9812,7 @@ function currentFileHash(path, workspaceRoot) {
|
|
|
9673
9812
|
return null;
|
|
9674
9813
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
9675
9814
|
const maxBytes = relativePath === ".hasna/session-render-manifest.json" || relativePath === ".codewith/.hasna/session-render-manifest.json" ? SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES : 256 * 1024;
|
|
9676
|
-
return
|
|
9815
|
+
return sha2562(readUtf8RegularFile(path, workspaceRoot, maxBytes));
|
|
9677
9816
|
}
|
|
9678
9817
|
function hashesStillMatch(expected, workspaceRoot) {
|
|
9679
9818
|
for (const [path, hash] of expected) {
|
|
@@ -9842,8 +9981,8 @@ function removeHashForFingerprint(value) {
|
|
|
9842
9981
|
}
|
|
9843
9982
|
return copy;
|
|
9844
9983
|
}
|
|
9845
|
-
function
|
|
9846
|
-
return
|
|
9984
|
+
function sha2562(content) {
|
|
9985
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
9847
9986
|
}
|
|
9848
9987
|
function isRecord(value) {
|
|
9849
9988
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -10110,7 +10249,7 @@ function applyTransform(source, output, context = {}) {
|
|
|
10110
10249
|
var init_transforms = () => {};
|
|
10111
10250
|
|
|
10112
10251
|
// src/lib/session-render.ts
|
|
10113
|
-
import { createHash as
|
|
10252
|
+
import { createHash as createHash3 } from "crypto";
|
|
10114
10253
|
import { existsSync as existsSync5, readFileSync as readFileSync2, realpathSync, statSync as statSync2 } from "fs";
|
|
10115
10254
|
import { homedir as homedir3 } from "os";
|
|
10116
10255
|
import { basename as basename3, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute2, join as join5, parse as parse2, posix, relative as relative2, resolve as resolve2 } from "path";
|
|
@@ -10130,11 +10269,11 @@ function ensureTrailingNewline3(content) {
|
|
|
10130
10269
|
`) ? content : `${content}
|
|
10131
10270
|
`;
|
|
10132
10271
|
}
|
|
10133
|
-
function
|
|
10134
|
-
return
|
|
10272
|
+
function sha2563(content) {
|
|
10273
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
10135
10274
|
}
|
|
10136
10275
|
function fingerprint(value) {
|
|
10137
|
-
return
|
|
10276
|
+
return sha2563(JSON.stringify(value));
|
|
10138
10277
|
}
|
|
10139
10278
|
function canonicalFingerprintValue(value) {
|
|
10140
10279
|
if (Array.isArray(value))
|
|
@@ -10200,18 +10339,59 @@ function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
|
10200
10339
|
relativePath: safeRelativePath,
|
|
10201
10340
|
role,
|
|
10202
10341
|
content: normalizedContent,
|
|
10203
|
-
sha256:
|
|
10342
|
+
sha256: sha2563(normalizedContent),
|
|
10204
10343
|
sourceIds
|
|
10205
10344
|
};
|
|
10206
10345
|
}
|
|
10346
|
+
function claimsAgentOperatingRulesPolicy(source, content) {
|
|
10347
|
+
if (!AGENT_OPERATING_RULES_SENTINEL_PATTERN.test(content))
|
|
10348
|
+
return false;
|
|
10349
|
+
if (source.nonOverridable === true)
|
|
10350
|
+
return true;
|
|
10351
|
+
if (source.id === GLOBAL_AGENT_RULES_STANDARD_SLUG || source.id === AGENT_OPERATING_RULES_SOURCE_ID)
|
|
10352
|
+
return true;
|
|
10353
|
+
if (source.metadata?.["role"] === AGENT_OPERATING_RULES_ROLE)
|
|
10354
|
+
return true;
|
|
10355
|
+
return AGENT_OPERATING_RULES_HEADING_PATTERN.test(content.trimStart());
|
|
10356
|
+
}
|
|
10357
|
+
function applyAgentOperatingRulesFloor(source, content) {
|
|
10358
|
+
const unchanged = {
|
|
10359
|
+
content,
|
|
10360
|
+
provenance: source.provenance ?? null,
|
|
10361
|
+
metadata: source.metadata ?? null
|
|
10362
|
+
};
|
|
10363
|
+
if (!claimsAgentOperatingRulesPolicy(source, content))
|
|
10364
|
+
return unchanged;
|
|
10365
|
+
const payload = resolveAgentOperatingRulesPayload(content);
|
|
10366
|
+
if (payload.content === content) {
|
|
10367
|
+
return {
|
|
10368
|
+
content,
|
|
10369
|
+
provenance: { ...source.provenance ?? {}, payloadIntegrity: payload.integrity },
|
|
10370
|
+
metadata: { ...source.metadata ?? {}, payloadIntegrity: payload.integrity }
|
|
10371
|
+
};
|
|
10372
|
+
}
|
|
10373
|
+
const floored = {
|
|
10374
|
+
payloadFloorApplied: true,
|
|
10375
|
+
flooredFromRulesVersion: parseAgentOperatingRulesVersion(content),
|
|
10376
|
+
flooredFromPayloadSha256: sha2563(content)
|
|
10377
|
+
};
|
|
10378
|
+
return {
|
|
10379
|
+
content: payload.content,
|
|
10380
|
+
provenance: { ...source.provenance ?? {}, ...payload.provenance, ...floored },
|
|
10381
|
+
metadata: { ...source.metadata ?? {}, ...payload.metadata, ...floored }
|
|
10382
|
+
};
|
|
10383
|
+
}
|
|
10207
10384
|
function normalizeSources(sources, tool, allowEmptySources) {
|
|
10208
10385
|
const normalized = sources.map((source, index) => {
|
|
10209
10386
|
if (!source.id.trim())
|
|
10210
10387
|
throw new Error("Session instruction source id is required.");
|
|
10211
|
-
const
|
|
10388
|
+
const floored = applyAgentOperatingRulesFloor(source, source.content ?? "");
|
|
10389
|
+
const content = filterProviderOnlyBlocks(floored.content, tool);
|
|
10212
10390
|
const normalized2 = {
|
|
10213
10391
|
...source,
|
|
10214
10392
|
content,
|
|
10393
|
+
provenance: floored.provenance,
|
|
10394
|
+
metadata: floored.metadata,
|
|
10215
10395
|
normalizedId: slug(source.id),
|
|
10216
10396
|
resolvedLabel: source.label ?? source.id,
|
|
10217
10397
|
resolvedLayer: source.layer === undefined ? "agent" : normalizeSessionInstructionLayer(source.layer),
|
|
@@ -10234,30 +10414,36 @@ function deduplicateSemanticPolicySources(sources) {
|
|
|
10234
10414
|
const selected = [];
|
|
10235
10415
|
const policySources = new Map;
|
|
10236
10416
|
for (const source of sources) {
|
|
10237
|
-
const sentinel = source.content.match(
|
|
10417
|
+
const sentinel = source.content.match(AGENT_OPERATING_RULES_SENTINEL_PATTERN);
|
|
10238
10418
|
if (!sentinel) {
|
|
10239
10419
|
selected.push(source);
|
|
10240
10420
|
continue;
|
|
10241
10421
|
}
|
|
10242
|
-
const
|
|
10422
|
+
const version = sentinel[1];
|
|
10423
|
+
const key = AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY;
|
|
10243
10424
|
const normalizedContent = source.content.replace(/\r\n/g, `
|
|
10244
10425
|
`).trim();
|
|
10245
10426
|
const existing = policySources.get(key);
|
|
10246
10427
|
if (!existing) {
|
|
10247
|
-
policySources.set(key, { index: selected.length, normalizedContent });
|
|
10428
|
+
policySources.set(key, { index: selected.length, version, normalizedContent });
|
|
10248
10429
|
selected.push(source);
|
|
10249
10430
|
continue;
|
|
10250
10431
|
}
|
|
10251
|
-
|
|
10252
|
-
|
|
10432
|
+
const versionOrder = compareAgentOperatingRulesVersions(version, existing.version);
|
|
10433
|
+
if (versionOrder === 0 && existing.normalizedContent !== normalizedContent) {
|
|
10434
|
+
throw new Error(`Conflicting semantic policy sources declare ${key}/v${version} with different content.`);
|
|
10253
10435
|
}
|
|
10254
10436
|
const current = selected[existing.index];
|
|
10255
|
-
|
|
10437
|
+
const priorityOrder = semanticPolicySourcePriority(source) - semanticPolicySourcePriority(current);
|
|
10438
|
+
if (priorityOrder < 0)
|
|
10439
|
+
continue;
|
|
10440
|
+
if (priorityOrder === 0 && versionOrder <= 0)
|
|
10256
10441
|
continue;
|
|
10257
10442
|
selected[existing.index] = {
|
|
10258
10443
|
...source,
|
|
10259
10444
|
resolvedOrder: current.resolvedOrder
|
|
10260
10445
|
};
|
|
10446
|
+
policySources.set(key, { index: existing.index, version, normalizedContent });
|
|
10261
10447
|
}
|
|
10262
10448
|
return selected;
|
|
10263
10449
|
}
|
|
@@ -10267,7 +10453,7 @@ function semanticPolicySourcePriority(source) {
|
|
|
10267
10453
|
priority += 4;
|
|
10268
10454
|
if (source.id === GLOBAL_AGENT_RULES_STANDARD_SLUG)
|
|
10269
10455
|
priority += 2;
|
|
10270
|
-
if (source.metadata?.["role"] ===
|
|
10456
|
+
if (source.metadata?.["role"] === AGENT_OPERATING_RULES_ROLE)
|
|
10271
10457
|
priority += 1;
|
|
10272
10458
|
return priority;
|
|
10273
10459
|
}
|
|
@@ -10756,7 +10942,7 @@ function planSessionRender(input) {
|
|
|
10756
10942
|
globs: rule.globs ?? [],
|
|
10757
10943
|
hash: rule.hash ?? null
|
|
10758
10944
|
})),
|
|
10759
|
-
renderedPayloadSha256:
|
|
10945
|
+
renderedPayloadSha256: sha2563(source.content),
|
|
10760
10946
|
provenance: source.provenance ?? null,
|
|
10761
10947
|
metadata: source.metadata ?? null
|
|
10762
10948
|
})),
|
|
@@ -10774,8 +10960,8 @@ function planSessionRender(input) {
|
|
|
10774
10960
|
...input.providerConfig ? {
|
|
10775
10961
|
providerConfig: {
|
|
10776
10962
|
sourceId: input.providerConfig.sourceId,
|
|
10777
|
-
selectedPayloadSha256:
|
|
10778
|
-
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ??
|
|
10963
|
+
selectedPayloadSha256: sha2563(input.providerConfig.content),
|
|
10964
|
+
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2563(input.providerConfig.content),
|
|
10779
10965
|
selected: !existsSync5(joinTarget(targetHome, adapter.configFile))
|
|
10780
10966
|
}
|
|
10781
10967
|
} : {},
|
|
@@ -10819,15 +11005,16 @@ function sourceFromFilePath(path, content, order = 0) {
|
|
|
10819
11005
|
}
|
|
10820
11006
|
function sourceFromConfig(config, order = 0, layer) {
|
|
10821
11007
|
const isAgentOperatingRules = config.slug === GLOBAL_AGENT_RULES_STANDARD_SLUG;
|
|
11008
|
+
const rules = isAgentOperatingRules ? resolveAgentOperatingRulesPayload(config.content) : null;
|
|
10822
11009
|
return {
|
|
10823
11010
|
id: config.slug,
|
|
10824
11011
|
label: config.name,
|
|
10825
|
-
content:
|
|
11012
|
+
content: rules ? rules.content : config.content,
|
|
10826
11013
|
layer: layer ?? (config.agent === "global" ? "global" : "agent"),
|
|
10827
11014
|
order,
|
|
10828
11015
|
path: config.target_path ?? undefined,
|
|
10829
|
-
provenance:
|
|
10830
|
-
...
|
|
11016
|
+
provenance: rules ? {
|
|
11017
|
+
...rules.provenance,
|
|
10831
11018
|
configSlug: config.slug,
|
|
10832
11019
|
configAgent: config.agent
|
|
10833
11020
|
} : {
|
|
@@ -10835,7 +11022,7 @@ function sourceFromConfig(config, order = 0, layer) {
|
|
|
10835
11022
|
configSlug: config.slug,
|
|
10836
11023
|
configAgent: config.agent
|
|
10837
11024
|
},
|
|
10838
|
-
metadata:
|
|
11025
|
+
metadata: rules ? { ...rules.metadata } : null,
|
|
10839
11026
|
nonOverridable: isAgentOperatingRules
|
|
10840
11027
|
};
|
|
10841
11028
|
}
|
|
@@ -11121,7 +11308,7 @@ function asStringArray(value) {
|
|
|
11121
11308
|
return [];
|
|
11122
11309
|
return value.filter((item) => typeof item === "string");
|
|
11123
11310
|
}
|
|
11124
|
-
var RAW_STORE_ROOT_ENV = "HASNA_CONFIGS_HOME", ANTIGRAVITY_RULE_FILE_CHAR_LIMIT = 12000, SESSION_RENDERER_OWNER_ID = "instructions-session-renderer", SESSION_RENDER_TOOLS, SESSION_RENDER_PROFILE_ENTRYPOINTS, SESSION_RENDER_OWNED_CONFIG_TARGETS, CODEWITH_FLATTENED_ADAPTER, CODEWITH_NATIVE_ADAPTER, SESSION_TOOL_ADAPTERS, SESSION_LAYER_RANK;
|
|
11311
|
+
var RAW_STORE_ROOT_ENV = "HASNA_CONFIGS_HOME", ANTIGRAVITY_RULE_FILE_CHAR_LIMIT = 12000, SESSION_RENDERER_OWNER_ID = "instructions-session-renderer", SESSION_RENDER_TOOLS, SESSION_RENDER_PROFILE_ENTRYPOINTS, SESSION_RENDER_OWNED_CONFIG_TARGETS, CODEWITH_FLATTENED_ADAPTER, CODEWITH_NATIVE_ADAPTER, SESSION_TOOL_ADAPTERS, SESSION_RENDER_MANAGED_DIRS, SESSION_RENDER_SHARED_MANAGED_DIRS, SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS, SESSION_LAYER_RANK;
|
|
11125
11312
|
var init_session_render = __esm(() => {
|
|
11126
11313
|
init_global_agent_rules_standard();
|
|
11127
11314
|
init_project_context();
|
|
@@ -11154,7 +11341,7 @@ var init_session_render = __esm(() => {
|
|
|
11154
11341
|
tool: "codewith",
|
|
11155
11342
|
mode: "flattened-markdown",
|
|
11156
11343
|
indexFile: "CODEWITH.md",
|
|
11157
|
-
managedDir:
|
|
11344
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11158
11345
|
envVar: "CODEWITH_HOME",
|
|
11159
11346
|
nativeImports: false,
|
|
11160
11347
|
description: "Codewith CODEWITH.md flattened until native @ imports are implemented in Codewith."
|
|
@@ -11163,7 +11350,7 @@ var init_session_render = __esm(() => {
|
|
|
11163
11350
|
tool: "codewith",
|
|
11164
11351
|
mode: "native-imports",
|
|
11165
11352
|
indexFile: "CODEWITH.md",
|
|
11166
|
-
managedDir:
|
|
11353
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11167
11354
|
envVar: "CODEWITH_HOME",
|
|
11168
11355
|
nativeImports: true,
|
|
11169
11356
|
description: "Codewith CODEWITH.md with gated @ imports into managed fragments."
|
|
@@ -11173,7 +11360,7 @@ var init_session_render = __esm(() => {
|
|
|
11173
11360
|
tool: "claude",
|
|
11174
11361
|
mode: "native-imports",
|
|
11175
11362
|
indexFile: "CLAUDE.md",
|
|
11176
|
-
managedDir:
|
|
11363
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11177
11364
|
envVar: "CLAUDE_CONFIG_DIR",
|
|
11178
11365
|
nativeImports: true,
|
|
11179
11366
|
description: "Claude Code CLAUDE.md with @ imports into managed fragments."
|
|
@@ -11182,7 +11369,7 @@ var init_session_render = __esm(() => {
|
|
|
11182
11369
|
tool: "codex",
|
|
11183
11370
|
mode: "flattened-markdown",
|
|
11184
11371
|
indexFile: "AGENTS.md",
|
|
11185
|
-
managedDir:
|
|
11372
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11186
11373
|
envVar: "CODEX_HOME",
|
|
11187
11374
|
nativeImports: false,
|
|
11188
11375
|
description: "Codex AGENTS.md flattened instruction file."
|
|
@@ -11199,7 +11386,7 @@ var init_session_render = __esm(() => {
|
|
|
11199
11386
|
mode: "opencode-instructions",
|
|
11200
11387
|
indexFile: "AGENTS.md",
|
|
11201
11388
|
configFile: "opencode.json",
|
|
11202
|
-
managedDir:
|
|
11389
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11203
11390
|
envVar: "OPENCODE_CONFIG_DIR",
|
|
11204
11391
|
nativeImports: false,
|
|
11205
11392
|
description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments."
|
|
@@ -11208,7 +11395,7 @@ var init_session_render = __esm(() => {
|
|
|
11208
11395
|
tool: "aicopilot",
|
|
11209
11396
|
mode: "flattened-markdown",
|
|
11210
11397
|
indexFile: "AICOPILOT.md",
|
|
11211
|
-
managedDir:
|
|
11398
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11212
11399
|
envVar: "AICOPILOT_CONFIG_DIR",
|
|
11213
11400
|
nativeImports: false,
|
|
11214
11401
|
description: "AI Copilot AICOPILOT.md flattened instruction file."
|
|
@@ -11231,6 +11418,19 @@ var init_session_render = __esm(() => {
|
|
|
11231
11418
|
},
|
|
11232
11419
|
codewith: CODEWITH_FLATTENED_ADAPTER
|
|
11233
11420
|
};
|
|
11421
|
+
SESSION_RENDER_MANAGED_DIRS = [
|
|
11422
|
+
...new Set([
|
|
11423
|
+
CODEWITH_FLATTENED_ADAPTER,
|
|
11424
|
+
CODEWITH_NATIVE_ADAPTER,
|
|
11425
|
+
...Object.values(SESSION_TOOL_ADAPTERS)
|
|
11426
|
+
].map((adapter) => adapter.managedDir))
|
|
11427
|
+
];
|
|
11428
|
+
SESSION_RENDER_SHARED_MANAGED_DIRS = [".cursor/rules"];
|
|
11429
|
+
SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS = [
|
|
11430
|
+
...SESSION_RENDER_MANAGED_DIRS.filter((dir) => !SESSION_RENDER_SHARED_MANAGED_DIRS.includes(dir)),
|
|
11431
|
+
SESSION_RENDER_MANIFEST_RELATIVE_PATH,
|
|
11432
|
+
SESSION_RENDER_SNAPSHOT_RELATIVE_DIR
|
|
11433
|
+
];
|
|
11234
11434
|
SESSION_LAYER_RANK = {
|
|
11235
11435
|
global: 10,
|
|
11236
11436
|
tool: 20,
|
|
@@ -11246,6 +11446,80 @@ var init_session_render = __esm(() => {
|
|
|
11246
11446
|
};
|
|
11247
11447
|
});
|
|
11248
11448
|
|
|
11449
|
+
// src/lib/session-render-ownership.ts
|
|
11450
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
11451
|
+
import { dirname as dirname3, join as join6, parse as parse3, relative as relative3, sep } from "path";
|
|
11452
|
+
function toSegments(absolutePath2) {
|
|
11453
|
+
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
11454
|
+
}
|
|
11455
|
+
function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
11456
|
+
const segments = toSegments(absolutePath2);
|
|
11457
|
+
return MANAGED_PATH_SEGMENTS.some((managed) => {
|
|
11458
|
+
if (managed.length === 0 || managed.length > segments.length)
|
|
11459
|
+
return false;
|
|
11460
|
+
for (let start = 0;start + managed.length <= segments.length; start += 1) {
|
|
11461
|
+
if (managed.every((segment, offset) => segments[start + offset] === segment))
|
|
11462
|
+
return true;
|
|
11463
|
+
}
|
|
11464
|
+
return false;
|
|
11465
|
+
});
|
|
11466
|
+
}
|
|
11467
|
+
function readManifestRelativePaths(manifestPath) {
|
|
11468
|
+
let stats;
|
|
11469
|
+
try {
|
|
11470
|
+
if (!existsSync6(manifestPath))
|
|
11471
|
+
return null;
|
|
11472
|
+
stats = statSync3(manifestPath);
|
|
11473
|
+
} catch {
|
|
11474
|
+
return null;
|
|
11475
|
+
}
|
|
11476
|
+
const cached = manifestCache.get(manifestPath);
|
|
11477
|
+
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
|
|
11478
|
+
return cached.relativePaths;
|
|
11479
|
+
}
|
|
11480
|
+
let manifest;
|
|
11481
|
+
try {
|
|
11482
|
+
manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
|
|
11483
|
+
} catch {
|
|
11484
|
+
return null;
|
|
11485
|
+
}
|
|
11486
|
+
if (manifest?.schema !== SESSION_RENDER_SCHEMA || !Array.isArray(manifest.files))
|
|
11487
|
+
return null;
|
|
11488
|
+
const writerId = manifest.targetOwner?.writer?.id;
|
|
11489
|
+
if (writerId !== undefined && writerId !== SESSION_RENDERER_OWNER_ID)
|
|
11490
|
+
return null;
|
|
11491
|
+
const relativePaths = new Set(manifest.files.map((file) => file?.relativePath).filter((relativePath) => typeof relativePath === "string").map((relativePath) => relativePath.replaceAll("\\", "/")));
|
|
11492
|
+
manifestCache.set(manifestPath, { mtimeMs: stats.mtimeMs, size: stats.size, relativePaths });
|
|
11493
|
+
return relativePaths;
|
|
11494
|
+
}
|
|
11495
|
+
function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
11496
|
+
const root = parse3(absolutePath2).root;
|
|
11497
|
+
let home = dirname3(absolutePath2);
|
|
11498
|
+
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
11499
|
+
const manifestPath = join6(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
11500
|
+
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
11501
|
+
if (relativePaths) {
|
|
11502
|
+
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
11503
|
+
if (relativePaths.has(claimed))
|
|
11504
|
+
return true;
|
|
11505
|
+
}
|
|
11506
|
+
const parent = dirname3(home);
|
|
11507
|
+
if (parent === home || home === root)
|
|
11508
|
+
break;
|
|
11509
|
+
home = parent;
|
|
11510
|
+
}
|
|
11511
|
+
return false;
|
|
11512
|
+
}
|
|
11513
|
+
function sessionRenderOwnsPath(absolutePath2) {
|
|
11514
|
+
return pathIsSessionRenderManagedDir(absolutePath2) || sessionRenderManifestClaimsPath(absolutePath2);
|
|
11515
|
+
}
|
|
11516
|
+
var MANIFEST_ANCESTOR_LIMIT = 24, MANAGED_PATH_SEGMENTS, manifestCache;
|
|
11517
|
+
var init_session_render_ownership = __esm(() => {
|
|
11518
|
+
init_session_render();
|
|
11519
|
+
MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
|
|
11520
|
+
manifestCache = new Map;
|
|
11521
|
+
});
|
|
11522
|
+
|
|
11249
11523
|
// src/lib/apply.ts
|
|
11250
11524
|
var exports_apply = {};
|
|
11251
11525
|
__export(exports_apply, {
|
|
@@ -11257,8 +11531,8 @@ __export(exports_apply, {
|
|
|
11257
11531
|
applyConfigs: () => applyConfigs,
|
|
11258
11532
|
applyConfig: () => applyConfig
|
|
11259
11533
|
});
|
|
11260
|
-
import { existsSync as
|
|
11261
|
-
import { basename as basename4, dirname as
|
|
11534
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
11535
|
+
import { basename as basename4, dirname as dirname4, join as join7, resolve as resolve3 } from "path";
|
|
11262
11536
|
import { homedir as homedir4 } from "os";
|
|
11263
11537
|
function getConfigHome() {
|
|
11264
11538
|
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir4();
|
|
@@ -11277,14 +11551,14 @@ function normalizeTargetPath(p) {
|
|
|
11277
11551
|
let current = expanded;
|
|
11278
11552
|
const missingSegments = [];
|
|
11279
11553
|
while (true) {
|
|
11280
|
-
if (
|
|
11554
|
+
if (existsSync7(current)) {
|
|
11281
11555
|
try {
|
|
11282
11556
|
return resolve3(realpathSync2(current), ...missingSegments);
|
|
11283
11557
|
} catch {
|
|
11284
11558
|
return expanded;
|
|
11285
11559
|
}
|
|
11286
11560
|
}
|
|
11287
|
-
const parent =
|
|
11561
|
+
const parent = dirname4(current);
|
|
11288
11562
|
const name = basename4(current);
|
|
11289
11563
|
if (parent === current)
|
|
11290
11564
|
return expanded;
|
|
@@ -11306,11 +11580,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
11306
11580
|
throw new ConfigApplyError(`Antigravity rule file ${renderedTargetPath} is ${renderedContent.length} characters; split it before applying because Antigravity limits rule files to ${ANTIGRAVITY_RULE_FILE_CHAR_LIMIT} characters.`);
|
|
11307
11581
|
}
|
|
11308
11582
|
const path = expandPath(renderedTargetPath);
|
|
11309
|
-
const previousContent =
|
|
11583
|
+
const previousContent = existsSync7(path) ? readFileSync4(path, "utf-8") : null;
|
|
11310
11584
|
const changed = previousContent !== renderedContent;
|
|
11311
11585
|
if (!opts.dryRun) {
|
|
11312
|
-
const dir =
|
|
11313
|
-
if (!
|
|
11586
|
+
const dir = dirname4(path);
|
|
11587
|
+
if (!existsSync7(dir)) {
|
|
11314
11588
|
mkdirSync3(dir, { recursive: true });
|
|
11315
11589
|
}
|
|
11316
11590
|
if (previousContent !== null && changed) {
|
|
@@ -11613,13 +11887,15 @@ function sessionRendererOwnsTarget(targetPath, opts) {
|
|
|
11613
11887
|
return sessionRendererOwnsCanonicalTarget(canonicalApplyTargetPath(targetPath, opts), opts);
|
|
11614
11888
|
}
|
|
11615
11889
|
function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
11890
|
+
if (opts.allowSessionRendererOwned)
|
|
11891
|
+
return false;
|
|
11616
11892
|
const homes = new Set([
|
|
11617
11893
|
getConfigHome(),
|
|
11618
11894
|
opts.vars?.["HOME_DIR"]
|
|
11619
11895
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
11620
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
11896
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join7(home, ...relativePath.split("/"))))))
|
|
11621
11897
|
return true;
|
|
11622
|
-
return normalized
|
|
11898
|
+
return sessionRenderOwnsPath(normalized);
|
|
11623
11899
|
}
|
|
11624
11900
|
var init_apply = __esm(() => {
|
|
11625
11901
|
init_types();
|
|
@@ -11627,12 +11903,13 @@ var init_apply = __esm(() => {
|
|
|
11627
11903
|
init_config_agents();
|
|
11628
11904
|
init_machine();
|
|
11629
11905
|
init_session_render();
|
|
11906
|
+
init_session_render_ownership();
|
|
11630
11907
|
init_transforms();
|
|
11631
11908
|
});
|
|
11632
11909
|
|
|
11633
11910
|
// src/lib/sync-dir.ts
|
|
11634
|
-
import { existsSync as
|
|
11635
|
-
import { join as
|
|
11911
|
+
import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
11912
|
+
import { join as join8, relative as relative4 } from "path";
|
|
11636
11913
|
import { homedir as homedir5 } from "os";
|
|
11637
11914
|
function shouldSkip(p) {
|
|
11638
11915
|
return SKIP.some((s) => p.includes(s));
|
|
@@ -11640,9 +11917,9 @@ function shouldSkip(p) {
|
|
|
11640
11917
|
async function syncFromDir(dir, opts = {}) {
|
|
11641
11918
|
const store = opts.store ?? resolveConfigStore();
|
|
11642
11919
|
const absDir = expandPath(dir);
|
|
11643
|
-
if (!
|
|
11920
|
+
if (!existsSync8(absDir))
|
|
11644
11921
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
11645
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) =>
|
|
11922
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join8(absDir, f)).filter((f) => statSync4(f).isFile());
|
|
11646
11923
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
11647
11924
|
const home = homedir5();
|
|
11648
11925
|
const allConfigs = await store.listConfigs();
|
|
@@ -11652,7 +11929,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
11652
11929
|
continue;
|
|
11653
11930
|
}
|
|
11654
11931
|
try {
|
|
11655
|
-
const content =
|
|
11932
|
+
const content = readFileSync5(file, "utf-8");
|
|
11656
11933
|
if (content.length > 500000) {
|
|
11657
11934
|
result.skipped.push(file + " (too large)");
|
|
11658
11935
|
continue;
|
|
@@ -11661,7 +11938,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
11661
11938
|
const existing = allConfigs.find((c) => c.target_path === targetPath);
|
|
11662
11939
|
if (!existing) {
|
|
11663
11940
|
if (!opts.dryRun)
|
|
11664
|
-
await store.createConfig({ name:
|
|
11941
|
+
await store.createConfig({ name: relative4(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content });
|
|
11665
11942
|
result.added++;
|
|
11666
11943
|
} else if (existing.content !== content) {
|
|
11667
11944
|
if (!opts.dryRun)
|
|
@@ -11702,7 +11979,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
11702
11979
|
}
|
|
11703
11980
|
function walkDir(dir, files = []) {
|
|
11704
11981
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
11705
|
-
const full =
|
|
11982
|
+
const full = join8(dir, entry.name);
|
|
11706
11983
|
if (shouldSkip(full))
|
|
11707
11984
|
continue;
|
|
11708
11985
|
if (entry.isDirectory())
|
|
@@ -11736,8 +12013,8 @@ __export(exports_sync, {
|
|
|
11736
12013
|
KNOWN_CONFIGS: () => KNOWN_CONFIGS,
|
|
11737
12014
|
CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
|
|
11738
12015
|
});
|
|
11739
|
-
import { existsSync as
|
|
11740
|
-
import { basename as basename5, extname as extname3, join as
|
|
12016
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
12017
|
+
import { basename as basename5, extname as extname3, join as join9 } from "path";
|
|
11741
12018
|
function claudeRuleOutputs(fileName) {
|
|
11742
12019
|
const stem = basename5(fileName, extname3(fileName));
|
|
11743
12020
|
return [
|
|
@@ -11769,7 +12046,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
11769
12046
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
11770
12047
|
}
|
|
11771
12048
|
function hasClaudePromptSource() {
|
|
11772
|
-
return
|
|
12049
|
+
return existsSync9(expandPath("~/.claude/CLAUDE.md"));
|
|
11773
12050
|
}
|
|
11774
12051
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
11775
12052
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -11777,7 +12054,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
11777
12054
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
11778
12055
|
return false;
|
|
11779
12056
|
const stem = basename5(absoluteTargetPath, ".mdc");
|
|
11780
|
-
return
|
|
12057
|
+
return existsSync9(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync9(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
11781
12058
|
}
|
|
11782
12059
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
11783
12060
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -11794,11 +12071,11 @@ async function syncProject(opts) {
|
|
|
11794
12071
|
const allConfigs = await store.listConfigs();
|
|
11795
12072
|
const machine = detectMachineContext();
|
|
11796
12073
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
11797
|
-
const abs =
|
|
11798
|
-
if (!
|
|
12074
|
+
const abs = join9(absDir, pf.file);
|
|
12075
|
+
if (!existsSync9(abs))
|
|
11799
12076
|
continue;
|
|
11800
12077
|
try {
|
|
11801
|
-
const rawContent =
|
|
12078
|
+
const rawContent = readFileSync6(abs, "utf-8");
|
|
11802
12079
|
if (rawContent.length > 500000) {
|
|
11803
12080
|
result.skipped.push(pf.file);
|
|
11804
12081
|
continue;
|
|
@@ -11827,15 +12104,15 @@ async function syncProject(opts) {
|
|
|
11827
12104
|
}
|
|
11828
12105
|
}
|
|
11829
12106
|
for (const ruleDir of [
|
|
11830
|
-
{ dir:
|
|
11831
|
-
{ dir:
|
|
12107
|
+
{ dir: join9(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
12108
|
+
{ dir: join9(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" }
|
|
11832
12109
|
]) {
|
|
11833
|
-
if (!
|
|
12110
|
+
if (!existsSync9(ruleDir.dir))
|
|
11834
12111
|
continue;
|
|
11835
12112
|
const mdFiles = readdirSync2(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
11836
12113
|
for (const f of mdFiles) {
|
|
11837
|
-
const abs =
|
|
11838
|
-
const raw =
|
|
12114
|
+
const abs = join9(ruleDir.dir, f);
|
|
12115
|
+
const raw = readFileSync6(abs, "utf-8");
|
|
11839
12116
|
const redacted = redactContent(raw, "markdown");
|
|
11840
12117
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
11841
12118
|
const content = machineAware.content;
|
|
@@ -11874,20 +12151,20 @@ async function syncKnown(opts = {}) {
|
|
|
11874
12151
|
for (const known of targets) {
|
|
11875
12152
|
if (known.rulesDir) {
|
|
11876
12153
|
const absDir = expandPath(known.rulesDir);
|
|
11877
|
-
if (!
|
|
12154
|
+
if (!existsSync9(absDir)) {
|
|
11878
12155
|
result.skipped.push(known.rulesDir);
|
|
11879
12156
|
continue;
|
|
11880
12157
|
}
|
|
11881
12158
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
11882
12159
|
const ruleFiles = readdirSync2(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
11883
12160
|
for (const f of ruleFiles) {
|
|
11884
|
-
const abs2 =
|
|
12161
|
+
const abs2 = join9(absDir, f);
|
|
11885
12162
|
const targetPath = abs2.replace(home, "~");
|
|
11886
12163
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
11887
12164
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
11888
12165
|
continue;
|
|
11889
12166
|
}
|
|
11890
|
-
const raw =
|
|
12167
|
+
const raw = readFileSync6(abs2, "utf-8");
|
|
11891
12168
|
const redacted = redactContent(raw, "markdown");
|
|
11892
12169
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
11893
12170
|
const content = machineAware.content;
|
|
@@ -11915,12 +12192,12 @@ async function syncKnown(opts = {}) {
|
|
|
11915
12192
|
continue;
|
|
11916
12193
|
}
|
|
11917
12194
|
const abs = expandPath(known.path);
|
|
11918
|
-
if (!
|
|
12195
|
+
if (!existsSync9(abs)) {
|
|
11919
12196
|
result.skipped.push(known.path);
|
|
11920
12197
|
continue;
|
|
11921
12198
|
}
|
|
11922
12199
|
try {
|
|
11923
|
-
const rawContent =
|
|
12200
|
+
const rawContent = readFileSync6(abs, "utf-8");
|
|
11924
12201
|
if (rawContent.length > 500000) {
|
|
11925
12202
|
result.skipped.push(known.path + " (too large)");
|
|
11926
12203
|
continue;
|
|
@@ -12008,9 +12285,9 @@ async function syncToDisk(opts = {}) {
|
|
|
12008
12285
|
}
|
|
12009
12286
|
function buildDiff(expectedContent, targetPath) {
|
|
12010
12287
|
const path = expandPath(targetPath);
|
|
12011
|
-
if (!
|
|
12288
|
+
if (!existsSync9(path))
|
|
12012
12289
|
return `(file not found on disk: ${path})`;
|
|
12013
|
-
const diskContent =
|
|
12290
|
+
const diskContent = readFileSync6(path, "utf-8");
|
|
12014
12291
|
if (diskContent === expectedContent)
|
|
12015
12292
|
return "(no diff \u2014 identical)";
|
|
12016
12293
|
const stored = expectedContent.split(`
|
|
@@ -12195,16 +12472,16 @@ __export(exports_package_manager_guard, {
|
|
|
12195
12472
|
scanPackageManagerSecrets: () => scanPackageManagerSecrets
|
|
12196
12473
|
});
|
|
12197
12474
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12198
|
-
import { existsSync as
|
|
12475
|
+
import { existsSync as existsSync15, lstatSync as lstatSync3, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
12199
12476
|
import { homedir as homedir6 } from "os";
|
|
12200
|
-
import { basename as basename6, dirname as
|
|
12477
|
+
import { basename as basename6, dirname as dirname7, isAbsolute as isAbsolute4, join as join14, relative as relative6, resolve as resolve7 } from "path";
|
|
12201
12478
|
function scanPackageManagerSecrets(options = {}) {
|
|
12202
12479
|
const cwd = options.cwd ? resolve7(options.cwd) : process.cwd();
|
|
12203
12480
|
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve7(cwd, root));
|
|
12204
12481
|
const findings = [];
|
|
12205
12482
|
let scannedFiles = 0;
|
|
12206
12483
|
for (const root of roots) {
|
|
12207
|
-
if (!
|
|
12484
|
+
if (!existsSync15(root))
|
|
12208
12485
|
continue;
|
|
12209
12486
|
const stat = lstatSync3(root);
|
|
12210
12487
|
if (stat.isFile()) {
|
|
@@ -12214,14 +12491,14 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
12214
12491
|
if (text === null)
|
|
12215
12492
|
continue;
|
|
12216
12493
|
scannedFiles++;
|
|
12217
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
12494
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname7(root)));
|
|
12218
12495
|
continue;
|
|
12219
12496
|
}
|
|
12220
12497
|
if (!stat.isDirectory())
|
|
12221
12498
|
continue;
|
|
12222
12499
|
const tracked = trackedFiles(root);
|
|
12223
12500
|
for (const file of collectRepoFiles(root)) {
|
|
12224
|
-
const rel = toPosix(
|
|
12501
|
+
const rel = toPosix(relative6(root, file));
|
|
12225
12502
|
const isTracked = tracked.has(rel);
|
|
12226
12503
|
const text = readTextFile(file);
|
|
12227
12504
|
if (text === null)
|
|
@@ -12233,8 +12510,8 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
12233
12510
|
if (options.includeHome) {
|
|
12234
12511
|
const home = homedir6();
|
|
12235
12512
|
for (const name of HOME_FILES) {
|
|
12236
|
-
const file =
|
|
12237
|
-
if (!
|
|
12513
|
+
const file = join14(home, name);
|
|
12514
|
+
if (!existsSync15(file))
|
|
12238
12515
|
continue;
|
|
12239
12516
|
const text = readTextFile(file);
|
|
12240
12517
|
if (text === null)
|
|
@@ -12258,12 +12535,12 @@ function collectRepoFiles(root) {
|
|
|
12258
12535
|
if (entry.isDirectory()) {
|
|
12259
12536
|
if (SKIP_DIRS.has(entry.name))
|
|
12260
12537
|
continue;
|
|
12261
|
-
visit(
|
|
12538
|
+
visit(join14(dir, entry.name));
|
|
12262
12539
|
continue;
|
|
12263
12540
|
}
|
|
12264
12541
|
if (!entry.isFile())
|
|
12265
12542
|
continue;
|
|
12266
|
-
const file =
|
|
12543
|
+
const file = join14(dir, entry.name);
|
|
12267
12544
|
if (shouldScanRepoFile(file))
|
|
12268
12545
|
out.push(file);
|
|
12269
12546
|
}
|
|
@@ -12301,7 +12578,7 @@ function readTextFile(file) {
|
|
|
12301
12578
|
const stat = lstatSync3(file);
|
|
12302
12579
|
if (!stat.isFile() || stat.size > 5000000)
|
|
12303
12580
|
return null;
|
|
12304
|
-
const buf =
|
|
12581
|
+
const buf = readFileSync11(file);
|
|
12305
12582
|
if (buf.includes(0))
|
|
12306
12583
|
return null;
|
|
12307
12584
|
return buf.toString("utf-8");
|
|
@@ -12501,11 +12778,11 @@ function trackedFiles(root) {
|
|
|
12501
12778
|
}
|
|
12502
12779
|
function isTrackedFile(file) {
|
|
12503
12780
|
try {
|
|
12504
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
12781
|
+
const repoRoot = execFileSync2("git", ["-C", dirname7(file), "rev-parse", "--show-toplevel"], {
|
|
12505
12782
|
encoding: "utf-8",
|
|
12506
12783
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12507
12784
|
}).trim();
|
|
12508
|
-
const rel = toPosix(
|
|
12785
|
+
const rel = toPosix(relative6(repoRoot, file));
|
|
12509
12786
|
execFileSync2("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", rel], {
|
|
12510
12787
|
stdio: ["ignore", "ignore", "ignore"]
|
|
12511
12788
|
});
|
|
@@ -12533,11 +12810,11 @@ function stripInlineComment(value) {
|
|
|
12533
12810
|
function displayPath(file, root) {
|
|
12534
12811
|
const home = homedir6();
|
|
12535
12812
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
12536
|
-
return "~/" + toPosix(
|
|
12813
|
+
return "~/" + toPosix(relative6(home, file));
|
|
12537
12814
|
if (isAbsolute4(root) && file.startsWith(root + "/"))
|
|
12538
|
-
return toPosix(
|
|
12815
|
+
return toPosix(relative6(root, file));
|
|
12539
12816
|
if (file === home || file.startsWith(home + "/"))
|
|
12540
|
-
return "~/" + toPosix(
|
|
12817
|
+
return "~/" + toPosix(relative6(home, file));
|
|
12541
12818
|
return file;
|
|
12542
12819
|
}
|
|
12543
12820
|
function toPosix(path) {
|
|
@@ -13286,21 +13563,21 @@ init_apply();
|
|
|
13286
13563
|
init_sync();
|
|
13287
13564
|
init_redact();
|
|
13288
13565
|
import chalk from "chalk";
|
|
13289
|
-
import { existsSync as
|
|
13566
|
+
import { existsSync as existsSync16, lstatSync as lstatSync4, readFileSync as readFileSync12, readSync, writeSync } from "fs";
|
|
13290
13567
|
import { homedir as homedir7 } from "os";
|
|
13291
|
-
import { basename as basename7, join as
|
|
13568
|
+
import { basename as basename7, join as join15, resolve as resolve8 } from "path";
|
|
13292
13569
|
|
|
13293
13570
|
// src/lib/export.ts
|
|
13294
13571
|
init_config_store();
|
|
13295
|
-
import { existsSync as
|
|
13296
|
-
import { join as
|
|
13572
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
13573
|
+
import { join as join10, resolve as resolve4 } from "path";
|
|
13297
13574
|
import { tmpdir } from "os";
|
|
13298
13575
|
async function exportConfigs(outputPath, opts = {}) {
|
|
13299
13576
|
const store = opts.store ?? resolveConfigStore();
|
|
13300
13577
|
const configs = await store.listConfigs(opts.filter);
|
|
13301
13578
|
const absOutput = resolve4(outputPath);
|
|
13302
|
-
const tmpDir =
|
|
13303
|
-
const contentsDir =
|
|
13579
|
+
const tmpDir = join10(tmpdir(), `configs-export-${Date.now()}`);
|
|
13580
|
+
const contentsDir = join10(tmpDir, "contents");
|
|
13304
13581
|
try {
|
|
13305
13582
|
mkdirSync4(contentsDir, { recursive: true });
|
|
13306
13583
|
const manifest = {
|
|
@@ -13308,10 +13585,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
13308
13585
|
exported_at: new Date().toISOString(),
|
|
13309
13586
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
13310
13587
|
};
|
|
13311
|
-
writeFileSync3(
|
|
13588
|
+
writeFileSync3(join10(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
13312
13589
|
for (const config of configs) {
|
|
13313
13590
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
13314
|
-
writeFileSync3(
|
|
13591
|
+
writeFileSync3(join10(contentsDir, fileName), config.content, "utf-8");
|
|
13315
13592
|
}
|
|
13316
13593
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
13317
13594
|
stdout: "pipe",
|
|
@@ -13324,7 +13601,7 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
13324
13601
|
}
|
|
13325
13602
|
return { path: absOutput, count: configs.length };
|
|
13326
13603
|
} finally {
|
|
13327
|
-
if (
|
|
13604
|
+
if (existsSync10(tmpDir)) {
|
|
13328
13605
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
13329
13606
|
}
|
|
13330
13607
|
}
|
|
@@ -13332,14 +13609,14 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
13332
13609
|
|
|
13333
13610
|
// src/lib/import.ts
|
|
13334
13611
|
init_config_store();
|
|
13335
|
-
import { existsSync as
|
|
13336
|
-
import { join as
|
|
13612
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync as rmSync4 } from "fs";
|
|
13613
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
13337
13614
|
import { tmpdir as tmpdir2 } from "os";
|
|
13338
13615
|
async function importConfigs(bundlePath, opts = {}) {
|
|
13339
13616
|
const store = opts.store ?? resolveConfigStore();
|
|
13340
13617
|
const conflict = opts.conflict ?? "skip";
|
|
13341
13618
|
const absPath = resolve5(bundlePath);
|
|
13342
|
-
const tmpDir =
|
|
13619
|
+
const tmpDir = join11(tmpdir2(), `configs-import-${Date.now()}`);
|
|
13343
13620
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
13344
13621
|
try {
|
|
13345
13622
|
mkdirSync5(tmpDir, { recursive: true });
|
|
@@ -13352,15 +13629,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
13352
13629
|
const stderr = await new Response(proc.stderr).text();
|
|
13353
13630
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
13354
13631
|
}
|
|
13355
|
-
const manifestPath =
|
|
13356
|
-
if (!
|
|
13632
|
+
const manifestPath = join11(tmpDir, "manifest.json");
|
|
13633
|
+
if (!existsSync11(manifestPath))
|
|
13357
13634
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
13358
|
-
const manifest = JSON.parse(
|
|
13635
|
+
const manifest = JSON.parse(readFileSync7(manifestPath, "utf-8"));
|
|
13359
13636
|
for (const meta of manifest.configs) {
|
|
13360
13637
|
try {
|
|
13361
13638
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
13362
|
-
const contentFile =
|
|
13363
|
-
const content =
|
|
13639
|
+
const contentFile = join11(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
13640
|
+
const content = existsSync11(contentFile) ? readFileSync7(contentFile, "utf-8") : "";
|
|
13364
13641
|
let existing = null;
|
|
13365
13642
|
try {
|
|
13366
13643
|
existing = await store.getConfig(meta.slug);
|
|
@@ -13394,7 +13671,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
13394
13671
|
}
|
|
13395
13672
|
return result;
|
|
13396
13673
|
} finally {
|
|
13397
|
-
if (
|
|
13674
|
+
if (existsSync11(tmpDir)) {
|
|
13398
13675
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
13399
13676
|
}
|
|
13400
13677
|
}
|
|
@@ -13407,16 +13684,16 @@ init_machine();
|
|
|
13407
13684
|
// src/lib/session-apply.ts
|
|
13408
13685
|
init_project_context();
|
|
13409
13686
|
init_session_render();
|
|
13410
|
-
import { createHash as
|
|
13687
|
+
import { createHash as createHash4, randomUUID as randomUUID7 } from "crypto";
|
|
13411
13688
|
import {
|
|
13412
|
-
existsSync as
|
|
13689
|
+
existsSync as existsSync12,
|
|
13413
13690
|
lstatSync as lstatSync2,
|
|
13414
13691
|
mkdirSync as mkdirSync6,
|
|
13415
|
-
readFileSync as
|
|
13692
|
+
readFileSync as readFileSync8,
|
|
13416
13693
|
readdirSync as readdirSync3,
|
|
13417
|
-
statSync as
|
|
13694
|
+
statSync as statSync5
|
|
13418
13695
|
} from "fs";
|
|
13419
|
-
import { dirname as
|
|
13696
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join12, parse as parse4, relative as relative5, resolve as resolve6 } from "path";
|
|
13420
13697
|
|
|
13421
13698
|
class SessionApplyError extends Error {
|
|
13422
13699
|
constructor(message) {
|
|
@@ -13497,13 +13774,13 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
13497
13774
|
};
|
|
13498
13775
|
}
|
|
13499
13776
|
function ensureSessionTargetHome(targetHome) {
|
|
13500
|
-
if (!
|
|
13777
|
+
if (!existsSync12(targetHome))
|
|
13501
13778
|
mkdirSync6(targetHome, { recursive: true, mode: 448 });
|
|
13502
13779
|
assertSafeTargetHome(targetHome);
|
|
13503
13780
|
}
|
|
13504
13781
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
13505
13782
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
13506
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(
|
|
13783
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve6(manifestPath)), safeTargetHome) : resolve6(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
13507
13784
|
const checkedAt = new Date().toISOString();
|
|
13508
13785
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
13509
13786
|
if (!previousManifest) {
|
|
@@ -13520,7 +13797,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
13520
13797
|
const drifted = [];
|
|
13521
13798
|
for (const file of previousManifest.files) {
|
|
13522
13799
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
13523
|
-
if (!
|
|
13800
|
+
if (!existsSync12(target)) {
|
|
13524
13801
|
missing.push({
|
|
13525
13802
|
path: target,
|
|
13526
13803
|
relativePath: file.relativePath,
|
|
@@ -13530,7 +13807,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
13530
13807
|
});
|
|
13531
13808
|
continue;
|
|
13532
13809
|
}
|
|
13533
|
-
const actualSha256 =
|
|
13810
|
+
const actualSha256 = sha2564(readFileSync8(target, "utf-8"));
|
|
13534
13811
|
if (actualSha256 !== file.sha256) {
|
|
13535
13812
|
drifted.push({
|
|
13536
13813
|
path: target,
|
|
@@ -13554,7 +13831,7 @@ function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
|
13554
13831
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
13555
13832
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
13556
13833
|
const resolvedSnapshotPath = resolve6(snapshotPath);
|
|
13557
|
-
const snapshotRelativePath =
|
|
13834
|
+
const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
|
|
13558
13835
|
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute3(snapshotRelativePath)) {
|
|
13559
13836
|
throw new SessionApplyError("Session snapshot must be stored inside its target home.");
|
|
13560
13837
|
}
|
|
@@ -13672,18 +13949,18 @@ function requiredRestoreHash(file) {
|
|
|
13672
13949
|
}
|
|
13673
13950
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
13674
13951
|
const resolved = resolve6(snapshotPath);
|
|
13675
|
-
if (!
|
|
13952
|
+
if (!existsSync12(resolved))
|
|
13676
13953
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
13677
13954
|
const stat = lstatSync2(resolved);
|
|
13678
13955
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
13679
13956
|
throw new SessionApplyError(`Session snapshot is not a regular file: ${snapshotPath}`);
|
|
13680
13957
|
}
|
|
13681
|
-
if (
|
|
13958
|
+
if (statSync5(resolved).size > 32 * 1024 * 1024) {
|
|
13682
13959
|
throw new SessionApplyError(`Session snapshot exceeds the 32 MiB restore limit: ${snapshotPath}`);
|
|
13683
13960
|
}
|
|
13684
13961
|
let parsed;
|
|
13685
13962
|
try {
|
|
13686
|
-
parsed = JSON.parse(
|
|
13963
|
+
parsed = JSON.parse(readFileSync8(resolved, "utf8"));
|
|
13687
13964
|
} catch {
|
|
13688
13965
|
throw new SessionApplyError(`Session snapshot is not valid JSON: ${snapshotPath}`);
|
|
13689
13966
|
}
|
|
@@ -13705,7 +13982,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
13705
13982
|
const previousManifest = snapshot.previousManifest;
|
|
13706
13983
|
const previousFiles = new Map;
|
|
13707
13984
|
for (const file of snapshot.files) {
|
|
13708
|
-
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" ||
|
|
13985
|
+
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha2564(file.content) !== file.sha256) {
|
|
13709
13986
|
throw new SessionApplyError(`Session snapshot previous file metadata is invalid: ${snapshotPath}`);
|
|
13710
13987
|
}
|
|
13711
13988
|
resolveSnapshotFilePath(file.relativePath, file.path, targetHome);
|
|
@@ -13753,7 +14030,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
13753
14030
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
13754
14031
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
13755
14032
|
const manifestPath = resolve6(snapshot.manifestPath);
|
|
13756
|
-
const manifestRelativePath =
|
|
14033
|
+
const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
|
|
13757
14034
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
13758
14035
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
13759
14036
|
if (manifestSha256 === null) {
|
|
@@ -13761,7 +14038,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
13761
14038
|
}
|
|
13762
14039
|
let parsedManifest;
|
|
13763
14040
|
try {
|
|
13764
|
-
parsedManifest = JSON.parse(
|
|
14041
|
+
parsedManifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
|
|
13765
14042
|
} catch {
|
|
13766
14043
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is not valid JSON: ${snapshotPath}`);
|
|
13767
14044
|
}
|
|
@@ -13853,15 +14130,15 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
13853
14130
|
if (!Number.isFinite(createdAtMs)) {
|
|
13854
14131
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
13855
14132
|
}
|
|
13856
|
-
for (const entry of readdirSync3(
|
|
13857
|
-
const candidatePath = resolve6(
|
|
14133
|
+
for (const entry of readdirSync3(dirname5(snapshotPath))) {
|
|
14134
|
+
const candidatePath = resolve6(dirname5(snapshotPath), entry);
|
|
13858
14135
|
if (candidatePath === resolve6(snapshotPath) || !entry.endsWith(".json"))
|
|
13859
14136
|
continue;
|
|
13860
14137
|
const candidateStat = lstatSync2(candidatePath);
|
|
13861
14138
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
13862
14139
|
continue;
|
|
13863
14140
|
try {
|
|
13864
|
-
const candidate = JSON.parse(
|
|
14141
|
+
const candidate = JSON.parse(readFileSync8(candidatePath, "utf8"));
|
|
13865
14142
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
13866
14143
|
if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve6(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
|
|
13867
14144
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
@@ -13921,7 +14198,7 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
13921
14198
|
return "create";
|
|
13922
14199
|
}
|
|
13923
14200
|
if (file.role === "manifest" && previousManifest) {
|
|
13924
|
-
const previousManifestSha256 =
|
|
14201
|
+
const previousManifestSha256 = sha2564(`${JSON.stringify(previousManifest, null, 2)}
|
|
13925
14202
|
`);
|
|
13926
14203
|
if (previousManifestSha256 !== file.sha256) {
|
|
13927
14204
|
throw new SessionApplyError(`Cannot infer legacy v1 manifest action without a before-image: ${file.relativePath}`);
|
|
@@ -13939,8 +14216,8 @@ function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
|
13939
14216
|
}
|
|
13940
14217
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
13941
14218
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
13942
|
-
const previousContent =
|
|
13943
|
-
const previousSha256 = previousContent === null ? null :
|
|
14219
|
+
const previousContent = existsSync12(target) ? readFileSync8(target, "utf-8") : null;
|
|
14220
|
+
const previousSha256 = previousContent === null ? null : sha2564(previousContent);
|
|
13944
14221
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
13945
14222
|
const changed = previousContent !== file.content;
|
|
13946
14223
|
if (previousContent !== null && !options.force && !previouslyManaged) {
|
|
@@ -14022,10 +14299,10 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
14022
14299
|
}
|
|
14023
14300
|
function planStaleFileResult(file, targetHome, options) {
|
|
14024
14301
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
14025
|
-
if (!
|
|
14302
|
+
if (!existsSync12(target))
|
|
14026
14303
|
return null;
|
|
14027
|
-
const previousContent =
|
|
14028
|
-
const previousSha256 =
|
|
14304
|
+
const previousContent = readFileSync8(target, "utf-8");
|
|
14305
|
+
const previousSha256 = sha2564(previousContent);
|
|
14029
14306
|
if (!options.force && previousSha256 !== file.sha256) {
|
|
14030
14307
|
return {
|
|
14031
14308
|
path: target,
|
|
@@ -14070,7 +14347,7 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
14070
14347
|
}
|
|
14071
14348
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
14072
14349
|
const target = resolve6(targetHome, ...file.relativePath.split("/"));
|
|
14073
|
-
const rel =
|
|
14350
|
+
const rel = relative5(targetHome, target);
|
|
14074
14351
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute3(rel)) {
|
|
14075
14352
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
14076
14353
|
}
|
|
@@ -14082,7 +14359,7 @@ function resolvePlannedFilePath(plan, file, targetHome) {
|
|
|
14082
14359
|
}
|
|
14083
14360
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
14084
14361
|
const target = resolve6(targetHome, ...relativePath.split(/[\\/]+/));
|
|
14085
|
-
const rel =
|
|
14362
|
+
const rel = relative5(targetHome, target);
|
|
14086
14363
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute3(rel)) {
|
|
14087
14364
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
14088
14365
|
}
|
|
@@ -14090,10 +14367,10 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
14090
14367
|
return target;
|
|
14091
14368
|
}
|
|
14092
14369
|
function readPreviousManifest(path) {
|
|
14093
|
-
if (!
|
|
14370
|
+
if (!existsSync12(path))
|
|
14094
14371
|
return null;
|
|
14095
14372
|
try {
|
|
14096
|
-
const parsed = JSON.parse(
|
|
14373
|
+
const parsed = JSON.parse(readFileSync8(path, "utf-8"));
|
|
14097
14374
|
if (parsed.schema !== SESSION_RENDER_SCHEMA)
|
|
14098
14375
|
return null;
|
|
14099
14376
|
if (!Array.isArray(parsed.files))
|
|
@@ -14127,18 +14404,18 @@ function applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, a
|
|
|
14127
14404
|
function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
14128
14405
|
const actualHash = currentSessionFileHash(path, targetHome);
|
|
14129
14406
|
if (actualHash !== expectedHash) {
|
|
14130
|
-
throw new SessionApplyError(`Session apply path changed after planning: ${
|
|
14407
|
+
throw new SessionApplyError(`Session apply path changed after planning: ${relative5(targetHome, path)}`);
|
|
14131
14408
|
}
|
|
14132
14409
|
}
|
|
14133
14410
|
function currentSessionFileHash(path, targetHome) {
|
|
14134
14411
|
assertNoSymlinkSegments2(targetHome, path);
|
|
14135
|
-
if (!
|
|
14412
|
+
if (!existsSync12(path))
|
|
14136
14413
|
return null;
|
|
14137
14414
|
const stat = lstatSync2(path);
|
|
14138
14415
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
14139
14416
|
throw new SessionApplyError(`Session apply path is not a regular file: ${path}`);
|
|
14140
14417
|
}
|
|
14141
|
-
return
|
|
14418
|
+
return sha2564(readFileSync8(path, "utf-8"));
|
|
14142
14419
|
}
|
|
14143
14420
|
function requiredPreviousHash(result) {
|
|
14144
14421
|
if (result.previousSha256 === null) {
|
|
@@ -14147,13 +14424,13 @@ function requiredPreviousHash(result) {
|
|
|
14147
14424
|
return result.previousSha256;
|
|
14148
14425
|
}
|
|
14149
14426
|
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
14150
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) =>
|
|
14151
|
-
const content =
|
|
14427
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync12(result.path)).map((result) => {
|
|
14428
|
+
const content = readFileSync8(result.path, "utf-8");
|
|
14152
14429
|
return {
|
|
14153
14430
|
path: result.path,
|
|
14154
14431
|
relativePath: result.relativePath,
|
|
14155
14432
|
role: result.role,
|
|
14156
|
-
sha256:
|
|
14433
|
+
sha256: sha2564(content),
|
|
14157
14434
|
content
|
|
14158
14435
|
};
|
|
14159
14436
|
});
|
|
@@ -14204,42 +14481,42 @@ function assertSafeTargetHome(targetHome) {
|
|
|
14204
14481
|
if (!isAbsolute3(targetHome))
|
|
14205
14482
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
14206
14483
|
const normalized = resolve6(targetHome);
|
|
14207
|
-
if (normalized ===
|
|
14484
|
+
if (normalized === parse4(normalized).root) {
|
|
14208
14485
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
14209
14486
|
}
|
|
14210
14487
|
assertNoSymlinkAncestors2(normalized);
|
|
14211
|
-
if (
|
|
14488
|
+
if (existsSync12(normalized) && lstatSync2(normalized).isSymbolicLink()) {
|
|
14212
14489
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
14213
14490
|
}
|
|
14214
14491
|
return normalized;
|
|
14215
14492
|
}
|
|
14216
14493
|
function assertNoSymlinkSegments2(root, target) {
|
|
14217
14494
|
assertNoSymlinkAncestors2(root);
|
|
14218
|
-
const rel =
|
|
14495
|
+
const rel = relative5(root, target);
|
|
14219
14496
|
let current = root;
|
|
14220
14497
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14221
|
-
current =
|
|
14222
|
-
if (
|
|
14498
|
+
current = join12(current, segment);
|
|
14499
|
+
if (existsSync12(current) && lstatSync2(current).isSymbolicLink()) {
|
|
14223
14500
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
14224
14501
|
}
|
|
14225
14502
|
}
|
|
14226
14503
|
}
|
|
14227
14504
|
function assertNoSymlinkAncestors2(path) {
|
|
14228
14505
|
const normalized = resolve6(path);
|
|
14229
|
-
const parsed =
|
|
14506
|
+
const parsed = parse4(normalized);
|
|
14230
14507
|
let current = parsed.root;
|
|
14231
|
-
const rel =
|
|
14508
|
+
const rel = relative5(parsed.root, normalized);
|
|
14232
14509
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14233
|
-
current =
|
|
14234
|
-
if (!
|
|
14510
|
+
current = join12(current, segment);
|
|
14511
|
+
if (!existsSync12(current))
|
|
14235
14512
|
return;
|
|
14236
14513
|
if (lstatSync2(current).isSymbolicLink()) {
|
|
14237
14514
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
14238
14515
|
}
|
|
14239
14516
|
}
|
|
14240
14517
|
}
|
|
14241
|
-
function
|
|
14242
|
-
return
|
|
14518
|
+
function sha2564(content) {
|
|
14519
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
14243
14520
|
}
|
|
14244
14521
|
|
|
14245
14522
|
// src/cli/index.tsx
|
|
@@ -14557,28 +14834,28 @@ init_project_context();
|
|
|
14557
14834
|
init_config_store();
|
|
14558
14835
|
init_apply();
|
|
14559
14836
|
init_config_agents();
|
|
14560
|
-
import { existsSync as
|
|
14837
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
|
|
14561
14838
|
|
|
14562
14839
|
// src/lib/package-version.ts
|
|
14563
|
-
import { existsSync as
|
|
14564
|
-
import { dirname as
|
|
14840
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
14841
|
+
import { dirname as dirname6, join as join13 } from "path";
|
|
14565
14842
|
import { fileURLToPath } from "url";
|
|
14566
14843
|
var cached = null;
|
|
14567
14844
|
function getPackageVersion() {
|
|
14568
14845
|
if (cached)
|
|
14569
14846
|
return cached;
|
|
14570
14847
|
try {
|
|
14571
|
-
let dir =
|
|
14848
|
+
let dir = dirname6(fileURLToPath(import.meta.url));
|
|
14572
14849
|
for (let i = 0;i < 8; i++) {
|
|
14573
|
-
const pkgPath =
|
|
14574
|
-
if (
|
|
14575
|
-
const pkg = JSON.parse(
|
|
14850
|
+
const pkgPath = join13(dir, "package.json");
|
|
14851
|
+
if (existsSync13(pkgPath)) {
|
|
14852
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
14576
14853
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
14577
14854
|
cached = pkg.version;
|
|
14578
14855
|
return cached;
|
|
14579
14856
|
}
|
|
14580
14857
|
}
|
|
14581
|
-
const parent =
|
|
14858
|
+
const parent = dirname6(dir);
|
|
14582
14859
|
if (parent === dir)
|
|
14583
14860
|
break;
|
|
14584
14861
|
dir = parent;
|
|
@@ -14635,11 +14912,11 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
14635
14912
|
continue;
|
|
14636
14913
|
knownTargets += 1;
|
|
14637
14914
|
const targetPath = expandPath(config.target_path);
|
|
14638
|
-
if (!
|
|
14915
|
+
if (!existsSync14(targetPath)) {
|
|
14639
14916
|
missingTargets += 1;
|
|
14640
14917
|
continue;
|
|
14641
14918
|
}
|
|
14642
|
-
const disk =
|
|
14919
|
+
const disk = readFileSync10(targetPath, "utf-8");
|
|
14643
14920
|
const { content: redactedDisk } = redactContent(disk, config.format);
|
|
14644
14921
|
if (redactedDisk !== config.content) {
|
|
14645
14922
|
driftedTargets += 1;
|
|
@@ -14865,9 +15142,9 @@ function parseSessionSource(value, order, replaceIds) {
|
|
|
14865
15142
|
if (!path)
|
|
14866
15143
|
throw new Error(`Invalid --source "${value}" (expected path or id=path)`);
|
|
14867
15144
|
const absPath = resolveSessionPath(path);
|
|
14868
|
-
if (!
|
|
15145
|
+
if (!existsSync16(absPath))
|
|
14869
15146
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
14870
|
-
const content =
|
|
15147
|
+
const content = readFileSync12(absPath, "utf-8");
|
|
14871
15148
|
const source = sourceFromFilePath(absPath, content, order);
|
|
14872
15149
|
const resolvedId = id || source.id || basename7(absPath);
|
|
14873
15150
|
return {
|
|
@@ -14903,9 +15180,9 @@ async function collectSessionSources(opts, tool, store) {
|
|
|
14903
15180
|
}
|
|
14904
15181
|
for (const value of opts.identityExport ?? []) {
|
|
14905
15182
|
const path = resolveSessionPath(value);
|
|
14906
|
-
if (!
|
|
15183
|
+
if (!existsSync16(path))
|
|
14907
15184
|
throw new Error(`Identity instruction export not found: ${path}`);
|
|
14908
|
-
const parsed = JSON.parse(
|
|
15185
|
+
const parsed = JSON.parse(readFileSync12(path, "utf-8"));
|
|
14909
15186
|
sources.push(...sourcesFromIdentityExport(parsed, { path, tool, orderOffset: sources.length }));
|
|
14910
15187
|
}
|
|
14911
15188
|
return sources.map((source) => replaceIds.has(source.id) ? { ...source, merge: "replace" } : source);
|
|
@@ -14935,7 +15212,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
14935
15212
|
if (value === "-")
|
|
14936
15213
|
return { json: readBoundedProjectContextStdin() };
|
|
14937
15214
|
const path = resolveSessionPath(value);
|
|
14938
|
-
if (!
|
|
15215
|
+
if (!existsSync16(path)) {
|
|
14939
15216
|
if (allowMissing)
|
|
14940
15217
|
return {};
|
|
14941
15218
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_MISSING", `bundle file not found: ${path}`);
|
|
@@ -14947,7 +15224,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
14947
15224
|
if (stat.size > PROJECT_CONTEXT_MAX_INPUT_BYTES) {
|
|
14948
15225
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_TOO_LARGE", `bundle exceeds ${PROJECT_CONTEXT_MAX_INPUT_BYTES} bytes`);
|
|
14949
15226
|
}
|
|
14950
|
-
return { json:
|
|
15227
|
+
return { json: readFileSync12(path, "utf8"), sourcePath: path };
|
|
14951
15228
|
}
|
|
14952
15229
|
function readBoundedProjectContextStdin() {
|
|
14953
15230
|
const chunks = [];
|
|
@@ -15075,11 +15352,11 @@ program.command("show <id>").alias("inspect").description("Show a config's conte
|
|
|
15075
15352
|
});
|
|
15076
15353
|
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)").action(async (filePath, opts) => {
|
|
15077
15354
|
const abs = resolve8(filePath);
|
|
15078
|
-
if (!
|
|
15355
|
+
if (!existsSync16(abs)) {
|
|
15079
15356
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
15080
15357
|
process.exit(1);
|
|
15081
15358
|
}
|
|
15082
|
-
const rawContent =
|
|
15359
|
+
const rawContent = readFileSync12(abs, "utf-8");
|
|
15083
15360
|
const fmt = detectFormat(abs);
|
|
15084
15361
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
15085
15362
|
const targetPath = abs.startsWith(homedir7()) ? abs.replace(homedir7(), "~") : abs;
|
|
@@ -15117,11 +15394,15 @@ program.command("delete <id>").alias("rm").description("Delete a config record (
|
|
|
15117
15394
|
process.exit(1);
|
|
15118
15395
|
}
|
|
15119
15396
|
});
|
|
15120
|
-
program.command("apply <id>").description("Apply a config to its target_path and output targets on disk").option("--dry-run", "preview without writing").option("--force", "overwrite even if unchanged").action(async (id, opts) => {
|
|
15397
|
+
program.command("apply <id>").description("Apply a config to its target_path and output targets on disk").option("--dry-run", "preview without writing").option("--force", "overwrite even if unchanged").option("--allow-renderer-owned", "write even when the target is owned by the Instructions session renderer (opt-in; normally use `instructions session apply`)").action(async (id, opts) => {
|
|
15121
15398
|
try {
|
|
15122
15399
|
const store = resolveConfigStore();
|
|
15123
15400
|
const config = await store.getConfig(id);
|
|
15124
|
-
const report = await applyConfigsWithReport([config], {
|
|
15401
|
+
const report = await applyConfigsWithReport([config], {
|
|
15402
|
+
dryRun: opts.dryRun,
|
|
15403
|
+
store,
|
|
15404
|
+
allowSessionRendererOwned: opts.allowRendererOwned
|
|
15405
|
+
});
|
|
15125
15406
|
if (report.failures.length > 0) {
|
|
15126
15407
|
throw new Error(report.failures.map((failure) => failure.message).join("; "));
|
|
15127
15408
|
}
|
|
@@ -15199,7 +15480,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
15199
15480
|
for (const entry of entries) {
|
|
15200
15481
|
if (!entry.isDirectory())
|
|
15201
15482
|
continue;
|
|
15202
|
-
const projDir =
|
|
15483
|
+
const projDir = join15(absDir, entry.name);
|
|
15203
15484
|
const hasAgentConfig = [
|
|
15204
15485
|
"CLAUDE.md",
|
|
15205
15486
|
".mcp.json",
|
|
@@ -15212,7 +15493,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
15212
15493
|
".aicopilot",
|
|
15213
15494
|
".cursor",
|
|
15214
15495
|
".agents"
|
|
15215
|
-
].some((marker) =>
|
|
15496
|
+
].some((marker) => existsSync16(join15(projDir, marker)));
|
|
15216
15497
|
if (!hasAgentConfig)
|
|
15217
15498
|
continue;
|
|
15218
15499
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun, store });
|
|
@@ -15263,7 +15544,7 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
15263
15544
|
});
|
|
15264
15545
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
15265
15546
|
const store = resolveConfigStore();
|
|
15266
|
-
const dbPath = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
15547
|
+
const dbPath = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join15(homedir7(), ".hasna", "instructions", "instructions.db");
|
|
15267
15548
|
const stats = await store.getConfigStats();
|
|
15268
15549
|
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
15269
15550
|
console.log(chalk.cyan(isCloudMode() ? "API:" : "DB:") + " " + dbPath);
|
|
@@ -15889,7 +16170,7 @@ command = "${mcpBinary}"
|
|
|
15889
16170
|
args = []
|
|
15890
16171
|
`;
|
|
15891
16172
|
if (ex(configPath)) {
|
|
15892
|
-
const content =
|
|
16173
|
+
const content = readFileSync12(configPath, "utf-8");
|
|
15893
16174
|
if (content.includes("[mcp_servers.configs]")) {
|
|
15894
16175
|
console.log(chalk.dim("= Already installed in Codex"));
|
|
15895
16176
|
continue;
|
|
@@ -15983,7 +16264,7 @@ DB stats:`));
|
|
|
15983
16264
|
if (count > 0)
|
|
15984
16265
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
15985
16266
|
}
|
|
15986
|
-
const location = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
16267
|
+
const location = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join15(homedir7(), ".hasna", "instructions", "instructions.db");
|
|
15987
16268
|
console.log(chalk.dim(`
|
|
15988
16269
|
${isCloudMode() ? "API" : "DB"}: ${location}`));
|
|
15989
16270
|
});
|
|
@@ -16005,10 +16286,10 @@ program.command("status").description("Health check: total configs, drift from d
|
|
|
16005
16286
|
});
|
|
16006
16287
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
16007
16288
|
const { mkdirSync: mk } = await import("fs");
|
|
16008
|
-
const backupDir =
|
|
16289
|
+
const backupDir = join15(homedir7(), ".hasna", "instructions", "backups");
|
|
16009
16290
|
mk(backupDir, { recursive: true });
|
|
16010
16291
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
16011
|
-
const outPath =
|
|
16292
|
+
const outPath = join15(backupDir, `configs-${ts}.tar.gz`);
|
|
16012
16293
|
const result = await exportConfigs(outPath, { store: resolveConfigStore() });
|
|
16013
16294
|
const { statSync: st } = await import("fs");
|
|
16014
16295
|
const size = st(outPath).size;
|
|
@@ -16036,9 +16317,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
16036
16317
|
console.log(chalk.cyan("Known files on disk:"));
|
|
16037
16318
|
for (const k of KNOWN_CONFIGS) {
|
|
16038
16319
|
if (k.rulesDir) {
|
|
16039
|
-
|
|
16320
|
+
existsSync16(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail(`${k.rulesDir}/ not found`);
|
|
16040
16321
|
} else {
|
|
16041
|
-
|
|
16322
|
+
existsSync16(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail(`${k.path} not found`);
|
|
16042
16323
|
}
|
|
16043
16324
|
}
|
|
16044
16325
|
const allConfigs = await store.listConfigs();
|
|
@@ -16163,16 +16444,16 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16163
16444
|
for (const k of KNOWN_CONFIGS) {
|
|
16164
16445
|
if (k.rulesDir) {
|
|
16165
16446
|
const absDir = expandPath2(k.rulesDir);
|
|
16166
|
-
if (!
|
|
16447
|
+
if (!existsSync16(absDir))
|
|
16167
16448
|
continue;
|
|
16168
16449
|
const { readdirSync: readdirSync5 } = await import("fs");
|
|
16169
16450
|
for (const f of readdirSync5(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
16170
|
-
const abs =
|
|
16451
|
+
const abs = join15(absDir, f);
|
|
16171
16452
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16172
16453
|
}
|
|
16173
16454
|
} else {
|
|
16174
16455
|
const abs = expandPath2(k.path);
|
|
16175
|
-
if (
|
|
16456
|
+
if (existsSync16(abs))
|
|
16176
16457
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16177
16458
|
}
|
|
16178
16459
|
}
|
|
@@ -16180,7 +16461,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16180
16461
|
const tick = async () => {
|
|
16181
16462
|
let changed = 0;
|
|
16182
16463
|
for (const [abs, oldMtime] of mtimes) {
|
|
16183
|
-
if (!
|
|
16464
|
+
if (!existsSync16(abs))
|
|
16184
16465
|
continue;
|
|
16185
16466
|
const newMtime = st(abs).mtimeMs;
|
|
16186
16467
|
if (newMtime !== oldMtime) {
|
|
@@ -16192,10 +16473,10 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16192
16473
|
for (const k of KNOWN_CONFIGS) {
|
|
16193
16474
|
if (k.rulesDir) {
|
|
16194
16475
|
const absDir = expandPath2(k.rulesDir);
|
|
16195
|
-
if (!
|
|
16476
|
+
if (!existsSync16(absDir))
|
|
16196
16477
|
continue;
|
|
16197
16478
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
16198
|
-
const abs =
|
|
16479
|
+
const abs = join15(absDir, f);
|
|
16199
16480
|
if (!mtimes.has(abs)) {
|
|
16200
16481
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16201
16482
|
changed++;
|
|
@@ -16203,7 +16484,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16203
16484
|
}
|
|
16204
16485
|
} else {
|
|
16205
16486
|
const abs = expandPath2(k.path);
|
|
16206
|
-
if (
|
|
16487
|
+
if (existsSync16(abs) && !mtimes.has(abs)) {
|
|
16207
16488
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16208
16489
|
changed++;
|
|
16209
16490
|
}
|
|
@@ -16231,11 +16512,11 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
16231
16512
|
if (!c.target_path)
|
|
16232
16513
|
continue;
|
|
16233
16514
|
const abs = expandPath(c.target_path);
|
|
16234
|
-
if (!
|
|
16515
|
+
if (!existsSync16(abs)) {
|
|
16235
16516
|
missing++;
|
|
16236
16517
|
continue;
|
|
16237
16518
|
}
|
|
16238
|
-
const disk =
|
|
16519
|
+
const disk = readFileSync12(abs, "utf-8");
|
|
16239
16520
|
const { content: redactedDisk } = redactContent(disk, c.format);
|
|
16240
16521
|
if (redactedDisk !== c.content)
|
|
16241
16522
|
drifted++;
|
|
@@ -16276,7 +16557,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
16276
16557
|
if (!c.target_path)
|
|
16277
16558
|
continue;
|
|
16278
16559
|
const abs = expandPath(c.target_path);
|
|
16279
|
-
if (!
|
|
16560
|
+
if (!existsSync16(abs)) {
|
|
16280
16561
|
if (printed < maxPrinted) {
|
|
16281
16562
|
if (opts.dryRun) {
|
|
16282
16563
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${truncateMiddle(c.target_path, 88)})`)}`);
|