@hasna/instructions 0.4.8 → 0.4.9
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 +449 -218
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +417 -186
- 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/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/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" });
|
|
@@ -8728,7 +8817,7 @@ function atomicWriteFile(path, content, workspaceRoot, defaultMode, expectedHash
|
|
|
8728
8817
|
}
|
|
8729
8818
|
}
|
|
8730
8819
|
function atomicWritePortable(path, content, workspaceRoot, defaultMode, expectedHash, beforeInstall, maxObservedBytes, allowReplacement = false) {
|
|
8731
|
-
const desiredHash =
|
|
8820
|
+
const desiredHash = sha2562(content);
|
|
8732
8821
|
const currentHash = portableFileHash(path, workspaceRoot, maxObservedBytes);
|
|
8733
8822
|
if (expectedHash === undefined && currentHash === desiredHash)
|
|
8734
8823
|
return;
|
|
@@ -8802,7 +8891,7 @@ function atomicWritePortableReplacement(path, content, workspaceRoot, expectedHa
|
|
|
8802
8891
|
const dir = dirname(path);
|
|
8803
8892
|
const directoryIdentity = captureManagedDirectoryIdentity(dir, workspaceRoot);
|
|
8804
8893
|
const tempPath = join4(dir, `.project-context-${randomUUID6()}.tmp`);
|
|
8805
|
-
const desiredHash =
|
|
8894
|
+
const desiredHash = sha2562(content);
|
|
8806
8895
|
let fd = null;
|
|
8807
8896
|
let tempIdentity = null;
|
|
8808
8897
|
try {
|
|
@@ -8853,7 +8942,7 @@ function portableFileHash(path, workspaceRoot, maxObservedBytes) {
|
|
|
8853
8942
|
if (maxObservedBytes !== null && stat.size > maxObservedBytes) {
|
|
8854
8943
|
throw new ProjectContextHashRace(`managed output exceeds the safe read limit: ${relativePosix(workspaceRoot, path)}`);
|
|
8855
8944
|
}
|
|
8856
|
-
return
|
|
8945
|
+
return createHash2("sha256").update(readFileSync(path)).digest("hex");
|
|
8857
8946
|
}
|
|
8858
8947
|
function writeProjectContextCoordinatedFile(input) {
|
|
8859
8948
|
atomicWriteFile(resolve(input.path), input.content, assertSafeWorkspaceRoot(input.workspace_root), input.default_mode ?? 420, input.expected_hash, undefined, false, undefined, input.force_portable_file_ops ?? false, input.max_observed_bytes, input.allow_portable_replacement ?? false);
|
|
@@ -9026,7 +9115,7 @@ function anchoredFileObservation(directory, name) {
|
|
|
9026
9115
|
return {
|
|
9027
9116
|
dev: stat.dev,
|
|
9028
9117
|
ino: stat.ino,
|
|
9029
|
-
hash:
|
|
9118
|
+
hash: createHash2("sha256").update(readFileSync(fd)).digest("hex"),
|
|
9030
9119
|
mode: stat.mode & 511
|
|
9031
9120
|
};
|
|
9032
9121
|
} finally {
|
|
@@ -9204,7 +9293,7 @@ function acquireWorkspaceLock(workspaceRoot, lockPath, afterOpen, beforeStaleRem
|
|
|
9204
9293
|
process_start_id: processStartIdentityLookup(process.pid)
|
|
9205
9294
|
})}
|
|
9206
9295
|
`;
|
|
9207
|
-
openedContentHash =
|
|
9296
|
+
openedContentHash = sha2562(content);
|
|
9208
9297
|
writeFileSync(fd, content);
|
|
9209
9298
|
fsyncSync(fd);
|
|
9210
9299
|
try {
|
|
@@ -9255,7 +9344,7 @@ function removeOwnedLockByInode(lockPath, identity, expectedHash) {
|
|
|
9255
9344
|
const current = lstatSync(lockPath);
|
|
9256
9345
|
if (current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino)
|
|
9257
9346
|
return;
|
|
9258
|
-
if (expectedHash !== undefined &&
|
|
9347
|
+
if (expectedHash !== undefined && sha2562(readFileSync(lockPath, "utf8")) !== expectedHash)
|
|
9259
9348
|
return;
|
|
9260
9349
|
rmSync2(lockPath);
|
|
9261
9350
|
fsyncDirectory(resolve(lockPath, ".."));
|
|
@@ -9272,7 +9361,7 @@ function observeStaleWorkspaceLock(lockPath, workspaceRoot, processStartIdentity
|
|
|
9272
9361
|
} catch {
|
|
9273
9362
|
return null;
|
|
9274
9363
|
}
|
|
9275
|
-
const contentHash =
|
|
9364
|
+
const contentHash = sha2562(content);
|
|
9276
9365
|
if (currentFileHash(lockPath, workspaceRoot) !== contentHash)
|
|
9277
9366
|
return null;
|
|
9278
9367
|
let pid = null;
|
|
@@ -9434,7 +9523,7 @@ function releaseWorkspaceLock(lockPath, lock, workspaceRoot) {
|
|
|
9434
9523
|
created_at: new Date().toISOString()
|
|
9435
9524
|
})}
|
|
9436
9525
|
`;
|
|
9437
|
-
releaseHash =
|
|
9526
|
+
releaseHash = sha2562(releaseContent);
|
|
9438
9527
|
writeFileSync(releaseFd, releaseContent);
|
|
9439
9528
|
fsyncSync(releaseFd);
|
|
9440
9529
|
closeSync(releaseFd);
|
|
@@ -9673,7 +9762,7 @@ function currentFileHash(path, workspaceRoot) {
|
|
|
9673
9762
|
return null;
|
|
9674
9763
|
const relativePath = relativePosix(workspaceRoot, path);
|
|
9675
9764
|
const maxBytes = relativePath === ".hasna/session-render-manifest.json" || relativePath === ".codewith/.hasna/session-render-manifest.json" ? SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES : 256 * 1024;
|
|
9676
|
-
return
|
|
9765
|
+
return sha2562(readUtf8RegularFile(path, workspaceRoot, maxBytes));
|
|
9677
9766
|
}
|
|
9678
9767
|
function hashesStillMatch(expected, workspaceRoot) {
|
|
9679
9768
|
for (const [path, hash] of expected) {
|
|
@@ -9842,8 +9931,8 @@ function removeHashForFingerprint(value) {
|
|
|
9842
9931
|
}
|
|
9843
9932
|
return copy;
|
|
9844
9933
|
}
|
|
9845
|
-
function
|
|
9846
|
-
return
|
|
9934
|
+
function sha2562(content) {
|
|
9935
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
9847
9936
|
}
|
|
9848
9937
|
function isRecord(value) {
|
|
9849
9938
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -10110,7 +10199,7 @@ function applyTransform(source, output, context = {}) {
|
|
|
10110
10199
|
var init_transforms = () => {};
|
|
10111
10200
|
|
|
10112
10201
|
// src/lib/session-render.ts
|
|
10113
|
-
import { createHash as
|
|
10202
|
+
import { createHash as createHash3 } from "crypto";
|
|
10114
10203
|
import { existsSync as existsSync5, readFileSync as readFileSync2, realpathSync, statSync as statSync2 } from "fs";
|
|
10115
10204
|
import { homedir as homedir3 } from "os";
|
|
10116
10205
|
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 +10219,11 @@ function ensureTrailingNewline3(content) {
|
|
|
10130
10219
|
`) ? content : `${content}
|
|
10131
10220
|
`;
|
|
10132
10221
|
}
|
|
10133
|
-
function
|
|
10134
|
-
return
|
|
10222
|
+
function sha2563(content) {
|
|
10223
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
10135
10224
|
}
|
|
10136
10225
|
function fingerprint(value) {
|
|
10137
|
-
return
|
|
10226
|
+
return sha2563(JSON.stringify(value));
|
|
10138
10227
|
}
|
|
10139
10228
|
function canonicalFingerprintValue(value) {
|
|
10140
10229
|
if (Array.isArray(value))
|
|
@@ -10200,18 +10289,59 @@ function makeFile(targetHome, relativePath, role, content, sourceIds) {
|
|
|
10200
10289
|
relativePath: safeRelativePath,
|
|
10201
10290
|
role,
|
|
10202
10291
|
content: normalizedContent,
|
|
10203
|
-
sha256:
|
|
10292
|
+
sha256: sha2563(normalizedContent),
|
|
10204
10293
|
sourceIds
|
|
10205
10294
|
};
|
|
10206
10295
|
}
|
|
10296
|
+
function claimsAgentOperatingRulesPolicy(source, content) {
|
|
10297
|
+
if (!AGENT_OPERATING_RULES_SENTINEL_PATTERN.test(content))
|
|
10298
|
+
return false;
|
|
10299
|
+
if (source.nonOverridable === true)
|
|
10300
|
+
return true;
|
|
10301
|
+
if (source.id === GLOBAL_AGENT_RULES_STANDARD_SLUG || source.id === AGENT_OPERATING_RULES_SOURCE_ID)
|
|
10302
|
+
return true;
|
|
10303
|
+
if (source.metadata?.["role"] === AGENT_OPERATING_RULES_ROLE)
|
|
10304
|
+
return true;
|
|
10305
|
+
return AGENT_OPERATING_RULES_HEADING_PATTERN.test(content.trimStart());
|
|
10306
|
+
}
|
|
10307
|
+
function applyAgentOperatingRulesFloor(source, content) {
|
|
10308
|
+
const unchanged = {
|
|
10309
|
+
content,
|
|
10310
|
+
provenance: source.provenance ?? null,
|
|
10311
|
+
metadata: source.metadata ?? null
|
|
10312
|
+
};
|
|
10313
|
+
if (!claimsAgentOperatingRulesPolicy(source, content))
|
|
10314
|
+
return unchanged;
|
|
10315
|
+
const payload = resolveAgentOperatingRulesPayload(content);
|
|
10316
|
+
if (payload.content === content) {
|
|
10317
|
+
return {
|
|
10318
|
+
content,
|
|
10319
|
+
provenance: { ...source.provenance ?? {}, payloadIntegrity: payload.integrity },
|
|
10320
|
+
metadata: { ...source.metadata ?? {}, payloadIntegrity: payload.integrity }
|
|
10321
|
+
};
|
|
10322
|
+
}
|
|
10323
|
+
const floored = {
|
|
10324
|
+
payloadFloorApplied: true,
|
|
10325
|
+
flooredFromRulesVersion: parseAgentOperatingRulesVersion(content),
|
|
10326
|
+
flooredFromPayloadSha256: sha2563(content)
|
|
10327
|
+
};
|
|
10328
|
+
return {
|
|
10329
|
+
content: payload.content,
|
|
10330
|
+
provenance: { ...source.provenance ?? {}, ...payload.provenance, ...floored },
|
|
10331
|
+
metadata: { ...source.metadata ?? {}, ...payload.metadata, ...floored }
|
|
10332
|
+
};
|
|
10333
|
+
}
|
|
10207
10334
|
function normalizeSources(sources, tool, allowEmptySources) {
|
|
10208
10335
|
const normalized = sources.map((source, index) => {
|
|
10209
10336
|
if (!source.id.trim())
|
|
10210
10337
|
throw new Error("Session instruction source id is required.");
|
|
10211
|
-
const
|
|
10338
|
+
const floored = applyAgentOperatingRulesFloor(source, source.content ?? "");
|
|
10339
|
+
const content = filterProviderOnlyBlocks(floored.content, tool);
|
|
10212
10340
|
const normalized2 = {
|
|
10213
10341
|
...source,
|
|
10214
10342
|
content,
|
|
10343
|
+
provenance: floored.provenance,
|
|
10344
|
+
metadata: floored.metadata,
|
|
10215
10345
|
normalizedId: slug(source.id),
|
|
10216
10346
|
resolvedLabel: source.label ?? source.id,
|
|
10217
10347
|
resolvedLayer: source.layer === undefined ? "agent" : normalizeSessionInstructionLayer(source.layer),
|
|
@@ -10234,30 +10364,36 @@ function deduplicateSemanticPolicySources(sources) {
|
|
|
10234
10364
|
const selected = [];
|
|
10235
10365
|
const policySources = new Map;
|
|
10236
10366
|
for (const source of sources) {
|
|
10237
|
-
const sentinel = source.content.match(
|
|
10367
|
+
const sentinel = source.content.match(AGENT_OPERATING_RULES_SENTINEL_PATTERN);
|
|
10238
10368
|
if (!sentinel) {
|
|
10239
10369
|
selected.push(source);
|
|
10240
10370
|
continue;
|
|
10241
10371
|
}
|
|
10242
|
-
const
|
|
10372
|
+
const version = sentinel[1];
|
|
10373
|
+
const key = AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY;
|
|
10243
10374
|
const normalizedContent = source.content.replace(/\r\n/g, `
|
|
10244
10375
|
`).trim();
|
|
10245
10376
|
const existing = policySources.get(key);
|
|
10246
10377
|
if (!existing) {
|
|
10247
|
-
policySources.set(key, { index: selected.length, normalizedContent });
|
|
10378
|
+
policySources.set(key, { index: selected.length, version, normalizedContent });
|
|
10248
10379
|
selected.push(source);
|
|
10249
10380
|
continue;
|
|
10250
10381
|
}
|
|
10251
|
-
|
|
10252
|
-
|
|
10382
|
+
const versionOrder = compareAgentOperatingRulesVersions(version, existing.version);
|
|
10383
|
+
if (versionOrder === 0 && existing.normalizedContent !== normalizedContent) {
|
|
10384
|
+
throw new Error(`Conflicting semantic policy sources declare ${key}/v${version} with different content.`);
|
|
10253
10385
|
}
|
|
10254
10386
|
const current = selected[existing.index];
|
|
10255
|
-
|
|
10387
|
+
const priorityOrder = semanticPolicySourcePriority(source) - semanticPolicySourcePriority(current);
|
|
10388
|
+
if (priorityOrder < 0)
|
|
10389
|
+
continue;
|
|
10390
|
+
if (priorityOrder === 0 && versionOrder <= 0)
|
|
10256
10391
|
continue;
|
|
10257
10392
|
selected[existing.index] = {
|
|
10258
10393
|
...source,
|
|
10259
10394
|
resolvedOrder: current.resolvedOrder
|
|
10260
10395
|
};
|
|
10396
|
+
policySources.set(key, { index: existing.index, version, normalizedContent });
|
|
10261
10397
|
}
|
|
10262
10398
|
return selected;
|
|
10263
10399
|
}
|
|
@@ -10267,7 +10403,7 @@ function semanticPolicySourcePriority(source) {
|
|
|
10267
10403
|
priority += 4;
|
|
10268
10404
|
if (source.id === GLOBAL_AGENT_RULES_STANDARD_SLUG)
|
|
10269
10405
|
priority += 2;
|
|
10270
|
-
if (source.metadata?.["role"] ===
|
|
10406
|
+
if (source.metadata?.["role"] === AGENT_OPERATING_RULES_ROLE)
|
|
10271
10407
|
priority += 1;
|
|
10272
10408
|
return priority;
|
|
10273
10409
|
}
|
|
@@ -10756,7 +10892,7 @@ function planSessionRender(input) {
|
|
|
10756
10892
|
globs: rule.globs ?? [],
|
|
10757
10893
|
hash: rule.hash ?? null
|
|
10758
10894
|
})),
|
|
10759
|
-
renderedPayloadSha256:
|
|
10895
|
+
renderedPayloadSha256: sha2563(source.content),
|
|
10760
10896
|
provenance: source.provenance ?? null,
|
|
10761
10897
|
metadata: source.metadata ?? null
|
|
10762
10898
|
})),
|
|
@@ -10774,8 +10910,8 @@ function planSessionRender(input) {
|
|
|
10774
10910
|
...input.providerConfig ? {
|
|
10775
10911
|
providerConfig: {
|
|
10776
10912
|
sourceId: input.providerConfig.sourceId,
|
|
10777
|
-
selectedPayloadSha256:
|
|
10778
|
-
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ??
|
|
10913
|
+
selectedPayloadSha256: sha2563(input.providerConfig.content),
|
|
10914
|
+
renderedPayloadSha256: files.find((file) => file.relativePath === adapter.configFile)?.sha256 ?? sha2563(input.providerConfig.content),
|
|
10779
10915
|
selected: !existsSync5(joinTarget(targetHome, adapter.configFile))
|
|
10780
10916
|
}
|
|
10781
10917
|
} : {},
|
|
@@ -10819,15 +10955,16 @@ function sourceFromFilePath(path, content, order = 0) {
|
|
|
10819
10955
|
}
|
|
10820
10956
|
function sourceFromConfig(config, order = 0, layer) {
|
|
10821
10957
|
const isAgentOperatingRules = config.slug === GLOBAL_AGENT_RULES_STANDARD_SLUG;
|
|
10958
|
+
const rules = isAgentOperatingRules ? resolveAgentOperatingRulesPayload(config.content) : null;
|
|
10822
10959
|
return {
|
|
10823
10960
|
id: config.slug,
|
|
10824
10961
|
label: config.name,
|
|
10825
|
-
content:
|
|
10962
|
+
content: rules ? rules.content : config.content,
|
|
10826
10963
|
layer: layer ?? (config.agent === "global" ? "global" : "agent"),
|
|
10827
10964
|
order,
|
|
10828
10965
|
path: config.target_path ?? undefined,
|
|
10829
|
-
provenance:
|
|
10830
|
-
...
|
|
10966
|
+
provenance: rules ? {
|
|
10967
|
+
...rules.provenance,
|
|
10831
10968
|
configSlug: config.slug,
|
|
10832
10969
|
configAgent: config.agent
|
|
10833
10970
|
} : {
|
|
@@ -10835,7 +10972,7 @@ function sourceFromConfig(config, order = 0, layer) {
|
|
|
10835
10972
|
configSlug: config.slug,
|
|
10836
10973
|
configAgent: config.agent
|
|
10837
10974
|
},
|
|
10838
|
-
metadata:
|
|
10975
|
+
metadata: rules ? { ...rules.metadata } : null,
|
|
10839
10976
|
nonOverridable: isAgentOperatingRules
|
|
10840
10977
|
};
|
|
10841
10978
|
}
|
|
@@ -11121,7 +11258,7 @@ function asStringArray(value) {
|
|
|
11121
11258
|
return [];
|
|
11122
11259
|
return value.filter((item) => typeof item === "string");
|
|
11123
11260
|
}
|
|
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;
|
|
11261
|
+
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
11262
|
var init_session_render = __esm(() => {
|
|
11126
11263
|
init_global_agent_rules_standard();
|
|
11127
11264
|
init_project_context();
|
|
@@ -11154,7 +11291,7 @@ var init_session_render = __esm(() => {
|
|
|
11154
11291
|
tool: "codewith",
|
|
11155
11292
|
mode: "flattened-markdown",
|
|
11156
11293
|
indexFile: "CODEWITH.md",
|
|
11157
|
-
managedDir:
|
|
11294
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11158
11295
|
envVar: "CODEWITH_HOME",
|
|
11159
11296
|
nativeImports: false,
|
|
11160
11297
|
description: "Codewith CODEWITH.md flattened until native @ imports are implemented in Codewith."
|
|
@@ -11163,7 +11300,7 @@ var init_session_render = __esm(() => {
|
|
|
11163
11300
|
tool: "codewith",
|
|
11164
11301
|
mode: "native-imports",
|
|
11165
11302
|
indexFile: "CODEWITH.md",
|
|
11166
|
-
managedDir:
|
|
11303
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11167
11304
|
envVar: "CODEWITH_HOME",
|
|
11168
11305
|
nativeImports: true,
|
|
11169
11306
|
description: "Codewith CODEWITH.md with gated @ imports into managed fragments."
|
|
@@ -11173,7 +11310,7 @@ var init_session_render = __esm(() => {
|
|
|
11173
11310
|
tool: "claude",
|
|
11174
11311
|
mode: "native-imports",
|
|
11175
11312
|
indexFile: "CLAUDE.md",
|
|
11176
|
-
managedDir:
|
|
11313
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11177
11314
|
envVar: "CLAUDE_CONFIG_DIR",
|
|
11178
11315
|
nativeImports: true,
|
|
11179
11316
|
description: "Claude Code CLAUDE.md with @ imports into managed fragments."
|
|
@@ -11182,7 +11319,7 @@ var init_session_render = __esm(() => {
|
|
|
11182
11319
|
tool: "codex",
|
|
11183
11320
|
mode: "flattened-markdown",
|
|
11184
11321
|
indexFile: "AGENTS.md",
|
|
11185
|
-
managedDir:
|
|
11322
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11186
11323
|
envVar: "CODEX_HOME",
|
|
11187
11324
|
nativeImports: false,
|
|
11188
11325
|
description: "Codex AGENTS.md flattened instruction file."
|
|
@@ -11199,7 +11336,7 @@ var init_session_render = __esm(() => {
|
|
|
11199
11336
|
mode: "opencode-instructions",
|
|
11200
11337
|
indexFile: "AGENTS.md",
|
|
11201
11338
|
configFile: "opencode.json",
|
|
11202
|
-
managedDir:
|
|
11339
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11203
11340
|
envVar: "OPENCODE_CONFIG_DIR",
|
|
11204
11341
|
nativeImports: false,
|
|
11205
11342
|
description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments."
|
|
@@ -11208,7 +11345,7 @@ var init_session_render = __esm(() => {
|
|
|
11208
11345
|
tool: "aicopilot",
|
|
11209
11346
|
mode: "flattened-markdown",
|
|
11210
11347
|
indexFile: "AICOPILOT.md",
|
|
11211
|
-
managedDir:
|
|
11348
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
11212
11349
|
envVar: "AICOPILOT_CONFIG_DIR",
|
|
11213
11350
|
nativeImports: false,
|
|
11214
11351
|
description: "AI Copilot AICOPILOT.md flattened instruction file."
|
|
@@ -11231,6 +11368,19 @@ var init_session_render = __esm(() => {
|
|
|
11231
11368
|
},
|
|
11232
11369
|
codewith: CODEWITH_FLATTENED_ADAPTER
|
|
11233
11370
|
};
|
|
11371
|
+
SESSION_RENDER_MANAGED_DIRS = [
|
|
11372
|
+
...new Set([
|
|
11373
|
+
CODEWITH_FLATTENED_ADAPTER,
|
|
11374
|
+
CODEWITH_NATIVE_ADAPTER,
|
|
11375
|
+
...Object.values(SESSION_TOOL_ADAPTERS)
|
|
11376
|
+
].map((adapter) => adapter.managedDir))
|
|
11377
|
+
];
|
|
11378
|
+
SESSION_RENDER_SHARED_MANAGED_DIRS = [".cursor/rules"];
|
|
11379
|
+
SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS = [
|
|
11380
|
+
...SESSION_RENDER_MANAGED_DIRS.filter((dir) => !SESSION_RENDER_SHARED_MANAGED_DIRS.includes(dir)),
|
|
11381
|
+
SESSION_RENDER_MANIFEST_RELATIVE_PATH,
|
|
11382
|
+
SESSION_RENDER_SNAPSHOT_RELATIVE_DIR
|
|
11383
|
+
];
|
|
11234
11384
|
SESSION_LAYER_RANK = {
|
|
11235
11385
|
global: 10,
|
|
11236
11386
|
tool: 20,
|
|
@@ -11246,6 +11396,80 @@ var init_session_render = __esm(() => {
|
|
|
11246
11396
|
};
|
|
11247
11397
|
});
|
|
11248
11398
|
|
|
11399
|
+
// src/lib/session-render-ownership.ts
|
|
11400
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
11401
|
+
import { dirname as dirname3, join as join6, parse as parse3, relative as relative3, sep } from "path";
|
|
11402
|
+
function toSegments(absolutePath2) {
|
|
11403
|
+
return absolutePath2.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
11404
|
+
}
|
|
11405
|
+
function pathIsSessionRenderManagedDir(absolutePath2) {
|
|
11406
|
+
const segments = toSegments(absolutePath2);
|
|
11407
|
+
return MANAGED_PATH_SEGMENTS.some((managed) => {
|
|
11408
|
+
if (managed.length === 0 || managed.length > segments.length)
|
|
11409
|
+
return false;
|
|
11410
|
+
for (let start = 0;start + managed.length <= segments.length; start += 1) {
|
|
11411
|
+
if (managed.every((segment, offset) => segments[start + offset] === segment))
|
|
11412
|
+
return true;
|
|
11413
|
+
}
|
|
11414
|
+
return false;
|
|
11415
|
+
});
|
|
11416
|
+
}
|
|
11417
|
+
function readManifestRelativePaths(manifestPath) {
|
|
11418
|
+
let stats;
|
|
11419
|
+
try {
|
|
11420
|
+
if (!existsSync6(manifestPath))
|
|
11421
|
+
return null;
|
|
11422
|
+
stats = statSync3(manifestPath);
|
|
11423
|
+
} catch {
|
|
11424
|
+
return null;
|
|
11425
|
+
}
|
|
11426
|
+
const cached = manifestCache.get(manifestPath);
|
|
11427
|
+
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
|
|
11428
|
+
return cached.relativePaths;
|
|
11429
|
+
}
|
|
11430
|
+
let manifest;
|
|
11431
|
+
try {
|
|
11432
|
+
manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
|
|
11433
|
+
} catch {
|
|
11434
|
+
return null;
|
|
11435
|
+
}
|
|
11436
|
+
if (manifest?.schema !== SESSION_RENDER_SCHEMA || !Array.isArray(manifest.files))
|
|
11437
|
+
return null;
|
|
11438
|
+
const writerId = manifest.targetOwner?.writer?.id;
|
|
11439
|
+
if (writerId !== undefined && writerId !== SESSION_RENDERER_OWNER_ID)
|
|
11440
|
+
return null;
|
|
11441
|
+
const relativePaths = new Set(manifest.files.map((file) => file?.relativePath).filter((relativePath) => typeof relativePath === "string").map((relativePath) => relativePath.replaceAll("\\", "/")));
|
|
11442
|
+
manifestCache.set(manifestPath, { mtimeMs: stats.mtimeMs, size: stats.size, relativePaths });
|
|
11443
|
+
return relativePaths;
|
|
11444
|
+
}
|
|
11445
|
+
function sessionRenderManifestClaimsPath(absolutePath2) {
|
|
11446
|
+
const root = parse3(absolutePath2).root;
|
|
11447
|
+
let home = dirname3(absolutePath2);
|
|
11448
|
+
for (let depth = 0;depth < MANIFEST_ANCESTOR_LIMIT; depth += 1) {
|
|
11449
|
+
const manifestPath = join6(home, ...SESSION_RENDER_MANIFEST_RELATIVE_PATH.split("/"));
|
|
11450
|
+
const relativePaths = readManifestRelativePaths(manifestPath);
|
|
11451
|
+
if (relativePaths) {
|
|
11452
|
+
const claimed = relative3(home, absolutePath2).split(sep).join("/");
|
|
11453
|
+
if (relativePaths.has(claimed))
|
|
11454
|
+
return true;
|
|
11455
|
+
}
|
|
11456
|
+
const parent = dirname3(home);
|
|
11457
|
+
if (parent === home || home === root)
|
|
11458
|
+
break;
|
|
11459
|
+
home = parent;
|
|
11460
|
+
}
|
|
11461
|
+
return false;
|
|
11462
|
+
}
|
|
11463
|
+
function sessionRenderOwnsPath(absolutePath2) {
|
|
11464
|
+
return pathIsSessionRenderManagedDir(absolutePath2) || sessionRenderManifestClaimsPath(absolutePath2);
|
|
11465
|
+
}
|
|
11466
|
+
var MANIFEST_ANCESTOR_LIMIT = 24, MANAGED_PATH_SEGMENTS, manifestCache;
|
|
11467
|
+
var init_session_render_ownership = __esm(() => {
|
|
11468
|
+
init_session_render();
|
|
11469
|
+
MANAGED_PATH_SEGMENTS = SESSION_RENDER_EXCLUSIVE_MANAGED_PATHS.map((managedPath) => managedPath.split("/").filter(Boolean));
|
|
11470
|
+
manifestCache = new Map;
|
|
11471
|
+
});
|
|
11472
|
+
|
|
11249
11473
|
// src/lib/apply.ts
|
|
11250
11474
|
var exports_apply = {};
|
|
11251
11475
|
__export(exports_apply, {
|
|
@@ -11257,8 +11481,8 @@ __export(exports_apply, {
|
|
|
11257
11481
|
applyConfigs: () => applyConfigs,
|
|
11258
11482
|
applyConfig: () => applyConfig
|
|
11259
11483
|
});
|
|
11260
|
-
import { existsSync as
|
|
11261
|
-
import { basename as basename4, dirname as
|
|
11484
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
11485
|
+
import { basename as basename4, dirname as dirname4, join as join7, resolve as resolve3 } from "path";
|
|
11262
11486
|
import { homedir as homedir4 } from "os";
|
|
11263
11487
|
function getConfigHome() {
|
|
11264
11488
|
return process.env["CONFIGS_HOME"] || process.env["HOME"] || homedir4();
|
|
@@ -11277,14 +11501,14 @@ function normalizeTargetPath(p) {
|
|
|
11277
11501
|
let current = expanded;
|
|
11278
11502
|
const missingSegments = [];
|
|
11279
11503
|
while (true) {
|
|
11280
|
-
if (
|
|
11504
|
+
if (existsSync7(current)) {
|
|
11281
11505
|
try {
|
|
11282
11506
|
return resolve3(realpathSync2(current), ...missingSegments);
|
|
11283
11507
|
} catch {
|
|
11284
11508
|
return expanded;
|
|
11285
11509
|
}
|
|
11286
11510
|
}
|
|
11287
|
-
const parent =
|
|
11511
|
+
const parent = dirname4(current);
|
|
11288
11512
|
const name = basename4(current);
|
|
11289
11513
|
if (parent === current)
|
|
11290
11514
|
return expanded;
|
|
@@ -11306,11 +11530,11 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
11306
11530
|
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
11531
|
}
|
|
11308
11532
|
const path = expandPath(renderedTargetPath);
|
|
11309
|
-
const previousContent =
|
|
11533
|
+
const previousContent = existsSync7(path) ? readFileSync4(path, "utf-8") : null;
|
|
11310
11534
|
const changed = previousContent !== renderedContent;
|
|
11311
11535
|
if (!opts.dryRun) {
|
|
11312
|
-
const dir =
|
|
11313
|
-
if (!
|
|
11536
|
+
const dir = dirname4(path);
|
|
11537
|
+
if (!existsSync7(dir)) {
|
|
11314
11538
|
mkdirSync3(dir, { recursive: true });
|
|
11315
11539
|
}
|
|
11316
11540
|
if (previousContent !== null && changed) {
|
|
@@ -11613,13 +11837,15 @@ function sessionRendererOwnsTarget(targetPath, opts) {
|
|
|
11613
11837
|
return sessionRendererOwnsCanonicalTarget(canonicalApplyTargetPath(targetPath, opts), opts);
|
|
11614
11838
|
}
|
|
11615
11839
|
function sessionRendererOwnsCanonicalTarget(normalized, opts) {
|
|
11840
|
+
if (opts.allowSessionRendererOwned)
|
|
11841
|
+
return false;
|
|
11616
11842
|
const homes = new Set([
|
|
11617
11843
|
getConfigHome(),
|
|
11618
11844
|
opts.vars?.["HOME_DIR"]
|
|
11619
11845
|
].filter((home) => typeof home === "string" && home.length > 0));
|
|
11620
|
-
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(
|
|
11846
|
+
if ([...homes].some((home) => SESSION_RENDER_OWNED_CONFIG_TARGETS.some((relativePath) => normalized === normalizeTargetPath(join7(home, ...relativePath.split("/"))))))
|
|
11621
11847
|
return true;
|
|
11622
|
-
return normalized
|
|
11848
|
+
return sessionRenderOwnsPath(normalized);
|
|
11623
11849
|
}
|
|
11624
11850
|
var init_apply = __esm(() => {
|
|
11625
11851
|
init_types();
|
|
@@ -11627,12 +11853,13 @@ var init_apply = __esm(() => {
|
|
|
11627
11853
|
init_config_agents();
|
|
11628
11854
|
init_machine();
|
|
11629
11855
|
init_session_render();
|
|
11856
|
+
init_session_render_ownership();
|
|
11630
11857
|
init_transforms();
|
|
11631
11858
|
});
|
|
11632
11859
|
|
|
11633
11860
|
// src/lib/sync-dir.ts
|
|
11634
|
-
import { existsSync as
|
|
11635
|
-
import { join as
|
|
11861
|
+
import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
11862
|
+
import { join as join8, relative as relative4 } from "path";
|
|
11636
11863
|
import { homedir as homedir5 } from "os";
|
|
11637
11864
|
function shouldSkip(p) {
|
|
11638
11865
|
return SKIP.some((s) => p.includes(s));
|
|
@@ -11640,9 +11867,9 @@ function shouldSkip(p) {
|
|
|
11640
11867
|
async function syncFromDir(dir, opts = {}) {
|
|
11641
11868
|
const store = opts.store ?? resolveConfigStore();
|
|
11642
11869
|
const absDir = expandPath(dir);
|
|
11643
|
-
if (!
|
|
11870
|
+
if (!existsSync8(absDir))
|
|
11644
11871
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
11645
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) =>
|
|
11872
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join8(absDir, f)).filter((f) => statSync4(f).isFile());
|
|
11646
11873
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
11647
11874
|
const home = homedir5();
|
|
11648
11875
|
const allConfigs = await store.listConfigs();
|
|
@@ -11652,7 +11879,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
11652
11879
|
continue;
|
|
11653
11880
|
}
|
|
11654
11881
|
try {
|
|
11655
|
-
const content =
|
|
11882
|
+
const content = readFileSync5(file, "utf-8");
|
|
11656
11883
|
if (content.length > 500000) {
|
|
11657
11884
|
result.skipped.push(file + " (too large)");
|
|
11658
11885
|
continue;
|
|
@@ -11661,7 +11888,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
11661
11888
|
const existing = allConfigs.find((c) => c.target_path === targetPath);
|
|
11662
11889
|
if (!existing) {
|
|
11663
11890
|
if (!opts.dryRun)
|
|
11664
|
-
await store.createConfig({ name:
|
|
11891
|
+
await store.createConfig({ name: relative4(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content });
|
|
11665
11892
|
result.added++;
|
|
11666
11893
|
} else if (existing.content !== content) {
|
|
11667
11894
|
if (!opts.dryRun)
|
|
@@ -11702,7 +11929,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
11702
11929
|
}
|
|
11703
11930
|
function walkDir(dir, files = []) {
|
|
11704
11931
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
11705
|
-
const full =
|
|
11932
|
+
const full = join8(dir, entry.name);
|
|
11706
11933
|
if (shouldSkip(full))
|
|
11707
11934
|
continue;
|
|
11708
11935
|
if (entry.isDirectory())
|
|
@@ -11736,8 +11963,8 @@ __export(exports_sync, {
|
|
|
11736
11963
|
KNOWN_CONFIGS: () => KNOWN_CONFIGS,
|
|
11737
11964
|
CLAUDE_PROMPT_OUTPUTS: () => CLAUDE_PROMPT_OUTPUTS
|
|
11738
11965
|
});
|
|
11739
|
-
import { existsSync as
|
|
11740
|
-
import { basename as basename5, extname as extname3, join as
|
|
11966
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
11967
|
+
import { basename as basename5, extname as extname3, join as join9 } from "path";
|
|
11741
11968
|
function claudeRuleOutputs(fileName) {
|
|
11742
11969
|
const stem = basename5(fileName, extname3(fileName));
|
|
11743
11970
|
return [
|
|
@@ -11769,7 +11996,7 @@ function isGeneratedOutputTarget2(config, owners) {
|
|
|
11769
11996
|
return !!ownerIds && !ownerIds.has(config.id);
|
|
11770
11997
|
}
|
|
11771
11998
|
function hasClaudePromptSource() {
|
|
11772
|
-
return
|
|
11999
|
+
return existsSync9(expandPath("~/.claude/CLAUDE.md"));
|
|
11773
12000
|
}
|
|
11774
12001
|
function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
11775
12002
|
const absoluteTargetPath = expandPath(targetPath);
|
|
@@ -11777,7 +12004,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
11777
12004
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
11778
12005
|
return false;
|
|
11779
12006
|
const stem = basename5(absoluteTargetPath, ".mdc");
|
|
11780
|
-
return
|
|
12007
|
+
return existsSync9(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync9(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
11781
12008
|
}
|
|
11782
12009
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
11783
12010
|
const normalizedTargetPath = normalizeTargetPath(targetPath);
|
|
@@ -11794,11 +12021,11 @@ async function syncProject(opts) {
|
|
|
11794
12021
|
const allConfigs = await store.listConfigs();
|
|
11795
12022
|
const machine = detectMachineContext();
|
|
11796
12023
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
11797
|
-
const abs =
|
|
11798
|
-
if (!
|
|
12024
|
+
const abs = join9(absDir, pf.file);
|
|
12025
|
+
if (!existsSync9(abs))
|
|
11799
12026
|
continue;
|
|
11800
12027
|
try {
|
|
11801
|
-
const rawContent =
|
|
12028
|
+
const rawContent = readFileSync6(abs, "utf-8");
|
|
11802
12029
|
if (rawContent.length > 500000) {
|
|
11803
12030
|
result.skipped.push(pf.file);
|
|
11804
12031
|
continue;
|
|
@@ -11827,15 +12054,15 @@ async function syncProject(opts) {
|
|
|
11827
12054
|
}
|
|
11828
12055
|
}
|
|
11829
12056
|
for (const ruleDir of [
|
|
11830
|
-
{ dir:
|
|
11831
|
-
{ dir:
|
|
12057
|
+
{ dir: join9(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
12058
|
+
{ dir: join9(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" }
|
|
11832
12059
|
]) {
|
|
11833
|
-
if (!
|
|
12060
|
+
if (!existsSync9(ruleDir.dir))
|
|
11834
12061
|
continue;
|
|
11835
12062
|
const mdFiles = readdirSync2(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
11836
12063
|
for (const f of mdFiles) {
|
|
11837
|
-
const abs =
|
|
11838
|
-
const raw =
|
|
12064
|
+
const abs = join9(ruleDir.dir, f);
|
|
12065
|
+
const raw = readFileSync6(abs, "utf-8");
|
|
11839
12066
|
const redacted = redactContent(raw, "markdown");
|
|
11840
12067
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
11841
12068
|
const content = machineAware.content;
|
|
@@ -11874,20 +12101,20 @@ async function syncKnown(opts = {}) {
|
|
|
11874
12101
|
for (const known of targets) {
|
|
11875
12102
|
if (known.rulesDir) {
|
|
11876
12103
|
const absDir = expandPath(known.rulesDir);
|
|
11877
|
-
if (!
|
|
12104
|
+
if (!existsSync9(absDir)) {
|
|
11878
12105
|
result.skipped.push(known.rulesDir);
|
|
11879
12106
|
continue;
|
|
11880
12107
|
}
|
|
11881
12108
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
11882
12109
|
const ruleFiles = readdirSync2(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
11883
12110
|
for (const f of ruleFiles) {
|
|
11884
|
-
const abs2 =
|
|
12111
|
+
const abs2 = join9(absDir, f);
|
|
11885
12112
|
const targetPath = abs2.replace(home, "~");
|
|
11886
12113
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
11887
12114
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
11888
12115
|
continue;
|
|
11889
12116
|
}
|
|
11890
|
-
const raw =
|
|
12117
|
+
const raw = readFileSync6(abs2, "utf-8");
|
|
11891
12118
|
const redacted = redactContent(raw, "markdown");
|
|
11892
12119
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
11893
12120
|
const content = machineAware.content;
|
|
@@ -11915,12 +12142,12 @@ async function syncKnown(opts = {}) {
|
|
|
11915
12142
|
continue;
|
|
11916
12143
|
}
|
|
11917
12144
|
const abs = expandPath(known.path);
|
|
11918
|
-
if (!
|
|
12145
|
+
if (!existsSync9(abs)) {
|
|
11919
12146
|
result.skipped.push(known.path);
|
|
11920
12147
|
continue;
|
|
11921
12148
|
}
|
|
11922
12149
|
try {
|
|
11923
|
-
const rawContent =
|
|
12150
|
+
const rawContent = readFileSync6(abs, "utf-8");
|
|
11924
12151
|
if (rawContent.length > 500000) {
|
|
11925
12152
|
result.skipped.push(known.path + " (too large)");
|
|
11926
12153
|
continue;
|
|
@@ -12008,9 +12235,9 @@ async function syncToDisk(opts = {}) {
|
|
|
12008
12235
|
}
|
|
12009
12236
|
function buildDiff(expectedContent, targetPath) {
|
|
12010
12237
|
const path = expandPath(targetPath);
|
|
12011
|
-
if (!
|
|
12238
|
+
if (!existsSync9(path))
|
|
12012
12239
|
return `(file not found on disk: ${path})`;
|
|
12013
|
-
const diskContent =
|
|
12240
|
+
const diskContent = readFileSync6(path, "utf-8");
|
|
12014
12241
|
if (diskContent === expectedContent)
|
|
12015
12242
|
return "(no diff \u2014 identical)";
|
|
12016
12243
|
const stored = expectedContent.split(`
|
|
@@ -12195,16 +12422,16 @@ __export(exports_package_manager_guard, {
|
|
|
12195
12422
|
scanPackageManagerSecrets: () => scanPackageManagerSecrets
|
|
12196
12423
|
});
|
|
12197
12424
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12198
|
-
import { existsSync as
|
|
12425
|
+
import { existsSync as existsSync15, lstatSync as lstatSync3, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
12199
12426
|
import { homedir as homedir6 } from "os";
|
|
12200
|
-
import { basename as basename6, dirname as
|
|
12427
|
+
import { basename as basename6, dirname as dirname7, isAbsolute as isAbsolute4, join as join14, relative as relative6, resolve as resolve7 } from "path";
|
|
12201
12428
|
function scanPackageManagerSecrets(options = {}) {
|
|
12202
12429
|
const cwd = options.cwd ? resolve7(options.cwd) : process.cwd();
|
|
12203
12430
|
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve7(cwd, root));
|
|
12204
12431
|
const findings = [];
|
|
12205
12432
|
let scannedFiles = 0;
|
|
12206
12433
|
for (const root of roots) {
|
|
12207
|
-
if (!
|
|
12434
|
+
if (!existsSync15(root))
|
|
12208
12435
|
continue;
|
|
12209
12436
|
const stat = lstatSync3(root);
|
|
12210
12437
|
if (stat.isFile()) {
|
|
@@ -12214,14 +12441,14 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
12214
12441
|
if (text === null)
|
|
12215
12442
|
continue;
|
|
12216
12443
|
scannedFiles++;
|
|
12217
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
12444
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname7(root)));
|
|
12218
12445
|
continue;
|
|
12219
12446
|
}
|
|
12220
12447
|
if (!stat.isDirectory())
|
|
12221
12448
|
continue;
|
|
12222
12449
|
const tracked = trackedFiles(root);
|
|
12223
12450
|
for (const file of collectRepoFiles(root)) {
|
|
12224
|
-
const rel = toPosix(
|
|
12451
|
+
const rel = toPosix(relative6(root, file));
|
|
12225
12452
|
const isTracked = tracked.has(rel);
|
|
12226
12453
|
const text = readTextFile(file);
|
|
12227
12454
|
if (text === null)
|
|
@@ -12233,8 +12460,8 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
12233
12460
|
if (options.includeHome) {
|
|
12234
12461
|
const home = homedir6();
|
|
12235
12462
|
for (const name of HOME_FILES) {
|
|
12236
|
-
const file =
|
|
12237
|
-
if (!
|
|
12463
|
+
const file = join14(home, name);
|
|
12464
|
+
if (!existsSync15(file))
|
|
12238
12465
|
continue;
|
|
12239
12466
|
const text = readTextFile(file);
|
|
12240
12467
|
if (text === null)
|
|
@@ -12258,12 +12485,12 @@ function collectRepoFiles(root) {
|
|
|
12258
12485
|
if (entry.isDirectory()) {
|
|
12259
12486
|
if (SKIP_DIRS.has(entry.name))
|
|
12260
12487
|
continue;
|
|
12261
|
-
visit(
|
|
12488
|
+
visit(join14(dir, entry.name));
|
|
12262
12489
|
continue;
|
|
12263
12490
|
}
|
|
12264
12491
|
if (!entry.isFile())
|
|
12265
12492
|
continue;
|
|
12266
|
-
const file =
|
|
12493
|
+
const file = join14(dir, entry.name);
|
|
12267
12494
|
if (shouldScanRepoFile(file))
|
|
12268
12495
|
out.push(file);
|
|
12269
12496
|
}
|
|
@@ -12301,7 +12528,7 @@ function readTextFile(file) {
|
|
|
12301
12528
|
const stat = lstatSync3(file);
|
|
12302
12529
|
if (!stat.isFile() || stat.size > 5000000)
|
|
12303
12530
|
return null;
|
|
12304
|
-
const buf =
|
|
12531
|
+
const buf = readFileSync11(file);
|
|
12305
12532
|
if (buf.includes(0))
|
|
12306
12533
|
return null;
|
|
12307
12534
|
return buf.toString("utf-8");
|
|
@@ -12501,11 +12728,11 @@ function trackedFiles(root) {
|
|
|
12501
12728
|
}
|
|
12502
12729
|
function isTrackedFile(file) {
|
|
12503
12730
|
try {
|
|
12504
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
12731
|
+
const repoRoot = execFileSync2("git", ["-C", dirname7(file), "rev-parse", "--show-toplevel"], {
|
|
12505
12732
|
encoding: "utf-8",
|
|
12506
12733
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12507
12734
|
}).trim();
|
|
12508
|
-
const rel = toPosix(
|
|
12735
|
+
const rel = toPosix(relative6(repoRoot, file));
|
|
12509
12736
|
execFileSync2("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", rel], {
|
|
12510
12737
|
stdio: ["ignore", "ignore", "ignore"]
|
|
12511
12738
|
});
|
|
@@ -12533,11 +12760,11 @@ function stripInlineComment(value) {
|
|
|
12533
12760
|
function displayPath(file, root) {
|
|
12534
12761
|
const home = homedir6();
|
|
12535
12762
|
if (root === home && (file === home || file.startsWith(home + "/")))
|
|
12536
|
-
return "~/" + toPosix(
|
|
12763
|
+
return "~/" + toPosix(relative6(home, file));
|
|
12537
12764
|
if (isAbsolute4(root) && file.startsWith(root + "/"))
|
|
12538
|
-
return toPosix(
|
|
12765
|
+
return toPosix(relative6(root, file));
|
|
12539
12766
|
if (file === home || file.startsWith(home + "/"))
|
|
12540
|
-
return "~/" + toPosix(
|
|
12767
|
+
return "~/" + toPosix(relative6(home, file));
|
|
12541
12768
|
return file;
|
|
12542
12769
|
}
|
|
12543
12770
|
function toPosix(path) {
|
|
@@ -13286,21 +13513,21 @@ init_apply();
|
|
|
13286
13513
|
init_sync();
|
|
13287
13514
|
init_redact();
|
|
13288
13515
|
import chalk from "chalk";
|
|
13289
|
-
import { existsSync as
|
|
13516
|
+
import { existsSync as existsSync16, lstatSync as lstatSync4, readFileSync as readFileSync12, readSync, writeSync } from "fs";
|
|
13290
13517
|
import { homedir as homedir7 } from "os";
|
|
13291
|
-
import { basename as basename7, join as
|
|
13518
|
+
import { basename as basename7, join as join15, resolve as resolve8 } from "path";
|
|
13292
13519
|
|
|
13293
13520
|
// src/lib/export.ts
|
|
13294
13521
|
init_config_store();
|
|
13295
|
-
import { existsSync as
|
|
13296
|
-
import { join as
|
|
13522
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
13523
|
+
import { join as join10, resolve as resolve4 } from "path";
|
|
13297
13524
|
import { tmpdir } from "os";
|
|
13298
13525
|
async function exportConfigs(outputPath, opts = {}) {
|
|
13299
13526
|
const store = opts.store ?? resolveConfigStore();
|
|
13300
13527
|
const configs = await store.listConfigs(opts.filter);
|
|
13301
13528
|
const absOutput = resolve4(outputPath);
|
|
13302
|
-
const tmpDir =
|
|
13303
|
-
const contentsDir =
|
|
13529
|
+
const tmpDir = join10(tmpdir(), `configs-export-${Date.now()}`);
|
|
13530
|
+
const contentsDir = join10(tmpDir, "contents");
|
|
13304
13531
|
try {
|
|
13305
13532
|
mkdirSync4(contentsDir, { recursive: true });
|
|
13306
13533
|
const manifest = {
|
|
@@ -13308,10 +13535,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
13308
13535
|
exported_at: new Date().toISOString(),
|
|
13309
13536
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
13310
13537
|
};
|
|
13311
|
-
writeFileSync3(
|
|
13538
|
+
writeFileSync3(join10(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
13312
13539
|
for (const config of configs) {
|
|
13313
13540
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
13314
|
-
writeFileSync3(
|
|
13541
|
+
writeFileSync3(join10(contentsDir, fileName), config.content, "utf-8");
|
|
13315
13542
|
}
|
|
13316
13543
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
13317
13544
|
stdout: "pipe",
|
|
@@ -13324,7 +13551,7 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
13324
13551
|
}
|
|
13325
13552
|
return { path: absOutput, count: configs.length };
|
|
13326
13553
|
} finally {
|
|
13327
|
-
if (
|
|
13554
|
+
if (existsSync10(tmpDir)) {
|
|
13328
13555
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
13329
13556
|
}
|
|
13330
13557
|
}
|
|
@@ -13332,14 +13559,14 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
13332
13559
|
|
|
13333
13560
|
// src/lib/import.ts
|
|
13334
13561
|
init_config_store();
|
|
13335
|
-
import { existsSync as
|
|
13336
|
-
import { join as
|
|
13562
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync as rmSync4 } from "fs";
|
|
13563
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
13337
13564
|
import { tmpdir as tmpdir2 } from "os";
|
|
13338
13565
|
async function importConfigs(bundlePath, opts = {}) {
|
|
13339
13566
|
const store = opts.store ?? resolveConfigStore();
|
|
13340
13567
|
const conflict = opts.conflict ?? "skip";
|
|
13341
13568
|
const absPath = resolve5(bundlePath);
|
|
13342
|
-
const tmpDir =
|
|
13569
|
+
const tmpDir = join11(tmpdir2(), `configs-import-${Date.now()}`);
|
|
13343
13570
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
13344
13571
|
try {
|
|
13345
13572
|
mkdirSync5(tmpDir, { recursive: true });
|
|
@@ -13352,15 +13579,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
13352
13579
|
const stderr = await new Response(proc.stderr).text();
|
|
13353
13580
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
13354
13581
|
}
|
|
13355
|
-
const manifestPath =
|
|
13356
|
-
if (!
|
|
13582
|
+
const manifestPath = join11(tmpDir, "manifest.json");
|
|
13583
|
+
if (!existsSync11(manifestPath))
|
|
13357
13584
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
13358
|
-
const manifest = JSON.parse(
|
|
13585
|
+
const manifest = JSON.parse(readFileSync7(manifestPath, "utf-8"));
|
|
13359
13586
|
for (const meta of manifest.configs) {
|
|
13360
13587
|
try {
|
|
13361
13588
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
13362
|
-
const contentFile =
|
|
13363
|
-
const content =
|
|
13589
|
+
const contentFile = join11(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
13590
|
+
const content = existsSync11(contentFile) ? readFileSync7(contentFile, "utf-8") : "";
|
|
13364
13591
|
let existing = null;
|
|
13365
13592
|
try {
|
|
13366
13593
|
existing = await store.getConfig(meta.slug);
|
|
@@ -13394,7 +13621,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
13394
13621
|
}
|
|
13395
13622
|
return result;
|
|
13396
13623
|
} finally {
|
|
13397
|
-
if (
|
|
13624
|
+
if (existsSync11(tmpDir)) {
|
|
13398
13625
|
rmSync4(tmpDir, { recursive: true, force: true });
|
|
13399
13626
|
}
|
|
13400
13627
|
}
|
|
@@ -13407,16 +13634,16 @@ init_machine();
|
|
|
13407
13634
|
// src/lib/session-apply.ts
|
|
13408
13635
|
init_project_context();
|
|
13409
13636
|
init_session_render();
|
|
13410
|
-
import { createHash as
|
|
13637
|
+
import { createHash as createHash4, randomUUID as randomUUID7 } from "crypto";
|
|
13411
13638
|
import {
|
|
13412
|
-
existsSync as
|
|
13639
|
+
existsSync as existsSync12,
|
|
13413
13640
|
lstatSync as lstatSync2,
|
|
13414
13641
|
mkdirSync as mkdirSync6,
|
|
13415
|
-
readFileSync as
|
|
13642
|
+
readFileSync as readFileSync8,
|
|
13416
13643
|
readdirSync as readdirSync3,
|
|
13417
|
-
statSync as
|
|
13644
|
+
statSync as statSync5
|
|
13418
13645
|
} from "fs";
|
|
13419
|
-
import { dirname as
|
|
13646
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join12, parse as parse4, relative as relative5, resolve as resolve6 } from "path";
|
|
13420
13647
|
|
|
13421
13648
|
class SessionApplyError extends Error {
|
|
13422
13649
|
constructor(message) {
|
|
@@ -13497,13 +13724,13 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
13497
13724
|
};
|
|
13498
13725
|
}
|
|
13499
13726
|
function ensureSessionTargetHome(targetHome) {
|
|
13500
|
-
if (!
|
|
13727
|
+
if (!existsSync12(targetHome))
|
|
13501
13728
|
mkdirSync6(targetHome, { recursive: true, mode: 448 });
|
|
13502
13729
|
assertSafeTargetHome(targetHome);
|
|
13503
13730
|
}
|
|
13504
13731
|
function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
13505
13732
|
const safeTargetHome = assertSafeTargetHome(targetHome);
|
|
13506
|
-
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(
|
|
13733
|
+
const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve6(manifestPath)), safeTargetHome) : resolve6(safeTargetHome, ".hasna", "session-render-manifest.json");
|
|
13507
13734
|
const checkedAt = new Date().toISOString();
|
|
13508
13735
|
const previousManifest = readPreviousManifest(resolvedManifestPath);
|
|
13509
13736
|
if (!previousManifest) {
|
|
@@ -13520,7 +13747,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
13520
13747
|
const drifted = [];
|
|
13521
13748
|
for (const file of previousManifest.files) {
|
|
13522
13749
|
const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
|
|
13523
|
-
if (!
|
|
13750
|
+
if (!existsSync12(target)) {
|
|
13524
13751
|
missing.push({
|
|
13525
13752
|
path: target,
|
|
13526
13753
|
relativePath: file.relativePath,
|
|
@@ -13530,7 +13757,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
|
|
|
13530
13757
|
});
|
|
13531
13758
|
continue;
|
|
13532
13759
|
}
|
|
13533
|
-
const actualSha256 =
|
|
13760
|
+
const actualSha256 = sha2564(readFileSync8(target, "utf-8"));
|
|
13534
13761
|
if (actualSha256 !== file.sha256) {
|
|
13535
13762
|
drifted.push({
|
|
13536
13763
|
path: target,
|
|
@@ -13554,7 +13781,7 @@ function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
|
|
|
13554
13781
|
const snapshot = readSessionRenderSnapshot(snapshotPath);
|
|
13555
13782
|
const targetHome = assertSafeTargetHome(snapshot.targetHome);
|
|
13556
13783
|
const resolvedSnapshotPath = resolve6(snapshotPath);
|
|
13557
|
-
const snapshotRelativePath =
|
|
13784
|
+
const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
|
|
13558
13785
|
if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute3(snapshotRelativePath)) {
|
|
13559
13786
|
throw new SessionApplyError("Session snapshot must be stored inside its target home.");
|
|
13560
13787
|
}
|
|
@@ -13672,18 +13899,18 @@ function requiredRestoreHash(file) {
|
|
|
13672
13899
|
}
|
|
13673
13900
|
function readSessionRenderSnapshot(snapshotPath) {
|
|
13674
13901
|
const resolved = resolve6(snapshotPath);
|
|
13675
|
-
if (!
|
|
13902
|
+
if (!existsSync12(resolved))
|
|
13676
13903
|
throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
|
|
13677
13904
|
const stat = lstatSync2(resolved);
|
|
13678
13905
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
13679
13906
|
throw new SessionApplyError(`Session snapshot is not a regular file: ${snapshotPath}`);
|
|
13680
13907
|
}
|
|
13681
|
-
if (
|
|
13908
|
+
if (statSync5(resolved).size > 32 * 1024 * 1024) {
|
|
13682
13909
|
throw new SessionApplyError(`Session snapshot exceeds the 32 MiB restore limit: ${snapshotPath}`);
|
|
13683
13910
|
}
|
|
13684
13911
|
let parsed;
|
|
13685
13912
|
try {
|
|
13686
|
-
parsed = JSON.parse(
|
|
13913
|
+
parsed = JSON.parse(readFileSync8(resolved, "utf8"));
|
|
13687
13914
|
} catch {
|
|
13688
13915
|
throw new SessionApplyError(`Session snapshot is not valid JSON: ${snapshotPath}`);
|
|
13689
13916
|
}
|
|
@@ -13705,7 +13932,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
13705
13932
|
const previousManifest = snapshot.previousManifest;
|
|
13706
13933
|
const previousFiles = new Map;
|
|
13707
13934
|
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" ||
|
|
13935
|
+
if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha2564(file.content) !== file.sha256) {
|
|
13709
13936
|
throw new SessionApplyError(`Session snapshot previous file metadata is invalid: ${snapshotPath}`);
|
|
13710
13937
|
}
|
|
13711
13938
|
resolveSnapshotFilePath(file.relativePath, file.path, targetHome);
|
|
@@ -13753,7 +13980,7 @@ function readSessionRenderSnapshot(snapshotPath) {
|
|
|
13753
13980
|
function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
|
|
13754
13981
|
assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
|
|
13755
13982
|
const manifestPath = resolve6(snapshot.manifestPath);
|
|
13756
|
-
const manifestRelativePath =
|
|
13983
|
+
const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
|
|
13757
13984
|
resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
|
|
13758
13985
|
const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
|
|
13759
13986
|
if (manifestSha256 === null) {
|
|
@@ -13761,7 +13988,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
|
|
|
13761
13988
|
}
|
|
13762
13989
|
let parsedManifest;
|
|
13763
13990
|
try {
|
|
13764
|
-
parsedManifest = JSON.parse(
|
|
13991
|
+
parsedManifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
|
|
13765
13992
|
} catch {
|
|
13766
13993
|
throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is not valid JSON: ${snapshotPath}`);
|
|
13767
13994
|
}
|
|
@@ -13853,15 +14080,15 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
|
|
|
13853
14080
|
if (!Number.isFinite(createdAtMs)) {
|
|
13854
14081
|
throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
|
|
13855
14082
|
}
|
|
13856
|
-
for (const entry of readdirSync3(
|
|
13857
|
-
const candidatePath = resolve6(
|
|
14083
|
+
for (const entry of readdirSync3(dirname5(snapshotPath))) {
|
|
14084
|
+
const candidatePath = resolve6(dirname5(snapshotPath), entry);
|
|
13858
14085
|
if (candidatePath === resolve6(snapshotPath) || !entry.endsWith(".json"))
|
|
13859
14086
|
continue;
|
|
13860
14087
|
const candidateStat = lstatSync2(candidatePath);
|
|
13861
14088
|
if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
|
|
13862
14089
|
continue;
|
|
13863
14090
|
try {
|
|
13864
|
-
const candidate = JSON.parse(
|
|
14091
|
+
const candidate = JSON.parse(readFileSync8(candidatePath, "utf8"));
|
|
13865
14092
|
const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
|
|
13866
14093
|
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
14094
|
throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
|
|
@@ -13921,7 +14148,7 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
|
|
|
13921
14148
|
return "create";
|
|
13922
14149
|
}
|
|
13923
14150
|
if (file.role === "manifest" && previousManifest) {
|
|
13924
|
-
const previousManifestSha256 =
|
|
14151
|
+
const previousManifestSha256 = sha2564(`${JSON.stringify(previousManifest, null, 2)}
|
|
13925
14152
|
`);
|
|
13926
14153
|
if (previousManifestSha256 !== file.sha256) {
|
|
13927
14154
|
throw new SessionApplyError(`Cannot infer legacy v1 manifest action without a before-image: ${file.relativePath}`);
|
|
@@ -13939,8 +14166,8 @@ function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
|
13939
14166
|
}
|
|
13940
14167
|
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
13941
14168
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
13942
|
-
const previousContent =
|
|
13943
|
-
const previousSha256 = previousContent === null ? null :
|
|
14169
|
+
const previousContent = existsSync12(target) ? readFileSync8(target, "utf-8") : null;
|
|
14170
|
+
const previousSha256 = previousContent === null ? null : sha2564(previousContent);
|
|
13944
14171
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
13945
14172
|
const changed = previousContent !== file.content;
|
|
13946
14173
|
if (previousContent !== null && !options.force && !previouslyManaged) {
|
|
@@ -14022,10 +14249,10 @@ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativ
|
|
|
14022
14249
|
}
|
|
14023
14250
|
function planStaleFileResult(file, targetHome, options) {
|
|
14024
14251
|
const target = resolveManifestRelativePath(file.relativePath, targetHome);
|
|
14025
|
-
if (!
|
|
14252
|
+
if (!existsSync12(target))
|
|
14026
14253
|
return null;
|
|
14027
|
-
const previousContent =
|
|
14028
|
-
const previousSha256 =
|
|
14254
|
+
const previousContent = readFileSync8(target, "utf-8");
|
|
14255
|
+
const previousSha256 = sha2564(previousContent);
|
|
14029
14256
|
if (!options.force && previousSha256 !== file.sha256) {
|
|
14030
14257
|
return {
|
|
14031
14258
|
path: target,
|
|
@@ -14070,7 +14297,7 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
|
|
|
14070
14297
|
}
|
|
14071
14298
|
function resolvePlannedFilePath(plan, file, targetHome) {
|
|
14072
14299
|
const target = resolve6(targetHome, ...file.relativePath.split("/"));
|
|
14073
|
-
const rel =
|
|
14300
|
+
const rel = relative5(targetHome, target);
|
|
14074
14301
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute3(rel)) {
|
|
14075
14302
|
throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
|
|
14076
14303
|
}
|
|
@@ -14082,7 +14309,7 @@ function resolvePlannedFilePath(plan, file, targetHome) {
|
|
|
14082
14309
|
}
|
|
14083
14310
|
function resolveManifestRelativePath(relativePath, targetHome) {
|
|
14084
14311
|
const target = resolve6(targetHome, ...relativePath.split(/[\\/]+/));
|
|
14085
|
-
const rel =
|
|
14312
|
+
const rel = relative5(targetHome, target);
|
|
14086
14313
|
if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute3(rel)) {
|
|
14087
14314
|
throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
|
|
14088
14315
|
}
|
|
@@ -14090,10 +14317,10 @@ function resolveManifestRelativePath(relativePath, targetHome) {
|
|
|
14090
14317
|
return target;
|
|
14091
14318
|
}
|
|
14092
14319
|
function readPreviousManifest(path) {
|
|
14093
|
-
if (!
|
|
14320
|
+
if (!existsSync12(path))
|
|
14094
14321
|
return null;
|
|
14095
14322
|
try {
|
|
14096
|
-
const parsed = JSON.parse(
|
|
14323
|
+
const parsed = JSON.parse(readFileSync8(path, "utf-8"));
|
|
14097
14324
|
if (parsed.schema !== SESSION_RENDER_SCHEMA)
|
|
14098
14325
|
return null;
|
|
14099
14326
|
if (!Array.isArray(parsed.files))
|
|
@@ -14127,18 +14354,18 @@ function applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, a
|
|
|
14127
14354
|
function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
|
|
14128
14355
|
const actualHash = currentSessionFileHash(path, targetHome);
|
|
14129
14356
|
if (actualHash !== expectedHash) {
|
|
14130
|
-
throw new SessionApplyError(`Session apply path changed after planning: ${
|
|
14357
|
+
throw new SessionApplyError(`Session apply path changed after planning: ${relative5(targetHome, path)}`);
|
|
14131
14358
|
}
|
|
14132
14359
|
}
|
|
14133
14360
|
function currentSessionFileHash(path, targetHome) {
|
|
14134
14361
|
assertNoSymlinkSegments2(targetHome, path);
|
|
14135
|
-
if (!
|
|
14362
|
+
if (!existsSync12(path))
|
|
14136
14363
|
return null;
|
|
14137
14364
|
const stat = lstatSync2(path);
|
|
14138
14365
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
14139
14366
|
throw new SessionApplyError(`Session apply path is not a regular file: ${path}`);
|
|
14140
14367
|
}
|
|
14141
|
-
return
|
|
14368
|
+
return sha2564(readFileSync8(path, "utf-8"));
|
|
14142
14369
|
}
|
|
14143
14370
|
function requiredPreviousHash(result) {
|
|
14144
14371
|
if (result.previousSha256 === null) {
|
|
@@ -14147,13 +14374,13 @@ function requiredPreviousHash(result) {
|
|
|
14147
14374
|
return result.previousSha256;
|
|
14148
14375
|
}
|
|
14149
14376
|
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 =
|
|
14377
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync12(result.path)).map((result) => {
|
|
14378
|
+
const content = readFileSync8(result.path, "utf-8");
|
|
14152
14379
|
return {
|
|
14153
14380
|
path: result.path,
|
|
14154
14381
|
relativePath: result.relativePath,
|
|
14155
14382
|
role: result.role,
|
|
14156
|
-
sha256:
|
|
14383
|
+
sha256: sha2564(content),
|
|
14157
14384
|
content
|
|
14158
14385
|
};
|
|
14159
14386
|
});
|
|
@@ -14204,42 +14431,42 @@ function assertSafeTargetHome(targetHome) {
|
|
|
14204
14431
|
if (!isAbsolute3(targetHome))
|
|
14205
14432
|
throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
|
|
14206
14433
|
const normalized = resolve6(targetHome);
|
|
14207
|
-
if (normalized ===
|
|
14434
|
+
if (normalized === parse4(normalized).root) {
|
|
14208
14435
|
throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
|
|
14209
14436
|
}
|
|
14210
14437
|
assertNoSymlinkAncestors2(normalized);
|
|
14211
|
-
if (
|
|
14438
|
+
if (existsSync12(normalized) && lstatSync2(normalized).isSymbolicLink()) {
|
|
14212
14439
|
throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
|
|
14213
14440
|
}
|
|
14214
14441
|
return normalized;
|
|
14215
14442
|
}
|
|
14216
14443
|
function assertNoSymlinkSegments2(root, target) {
|
|
14217
14444
|
assertNoSymlinkAncestors2(root);
|
|
14218
|
-
const rel =
|
|
14445
|
+
const rel = relative5(root, target);
|
|
14219
14446
|
let current = root;
|
|
14220
14447
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14221
|
-
current =
|
|
14222
|
-
if (
|
|
14448
|
+
current = join12(current, segment);
|
|
14449
|
+
if (existsSync12(current) && lstatSync2(current).isSymbolicLink()) {
|
|
14223
14450
|
throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
|
|
14224
14451
|
}
|
|
14225
14452
|
}
|
|
14226
14453
|
}
|
|
14227
14454
|
function assertNoSymlinkAncestors2(path) {
|
|
14228
14455
|
const normalized = resolve6(path);
|
|
14229
|
-
const parsed =
|
|
14456
|
+
const parsed = parse4(normalized);
|
|
14230
14457
|
let current = parsed.root;
|
|
14231
|
-
const rel =
|
|
14458
|
+
const rel = relative5(parsed.root, normalized);
|
|
14232
14459
|
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
14233
|
-
current =
|
|
14234
|
-
if (!
|
|
14460
|
+
current = join12(current, segment);
|
|
14461
|
+
if (!existsSync12(current))
|
|
14235
14462
|
return;
|
|
14236
14463
|
if (lstatSync2(current).isSymbolicLink()) {
|
|
14237
14464
|
throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
|
|
14238
14465
|
}
|
|
14239
14466
|
}
|
|
14240
14467
|
}
|
|
14241
|
-
function
|
|
14242
|
-
return
|
|
14468
|
+
function sha2564(content) {
|
|
14469
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
14243
14470
|
}
|
|
14244
14471
|
|
|
14245
14472
|
// src/cli/index.tsx
|
|
@@ -14557,28 +14784,28 @@ init_project_context();
|
|
|
14557
14784
|
init_config_store();
|
|
14558
14785
|
init_apply();
|
|
14559
14786
|
init_config_agents();
|
|
14560
|
-
import { existsSync as
|
|
14787
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
|
|
14561
14788
|
|
|
14562
14789
|
// src/lib/package-version.ts
|
|
14563
|
-
import { existsSync as
|
|
14564
|
-
import { dirname as
|
|
14790
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
14791
|
+
import { dirname as dirname6, join as join13 } from "path";
|
|
14565
14792
|
import { fileURLToPath } from "url";
|
|
14566
14793
|
var cached = null;
|
|
14567
14794
|
function getPackageVersion() {
|
|
14568
14795
|
if (cached)
|
|
14569
14796
|
return cached;
|
|
14570
14797
|
try {
|
|
14571
|
-
let dir =
|
|
14798
|
+
let dir = dirname6(fileURLToPath(import.meta.url));
|
|
14572
14799
|
for (let i = 0;i < 8; i++) {
|
|
14573
|
-
const pkgPath =
|
|
14574
|
-
if (
|
|
14575
|
-
const pkg = JSON.parse(
|
|
14800
|
+
const pkgPath = join13(dir, "package.json");
|
|
14801
|
+
if (existsSync13(pkgPath)) {
|
|
14802
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
14576
14803
|
if (pkg.name === "@hasna/instructions" && pkg.version) {
|
|
14577
14804
|
cached = pkg.version;
|
|
14578
14805
|
return cached;
|
|
14579
14806
|
}
|
|
14580
14807
|
}
|
|
14581
|
-
const parent =
|
|
14808
|
+
const parent = dirname6(dir);
|
|
14582
14809
|
if (parent === dir)
|
|
14583
14810
|
break;
|
|
14584
14811
|
dir = parent;
|
|
@@ -14635,11 +14862,11 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
14635
14862
|
continue;
|
|
14636
14863
|
knownTargets += 1;
|
|
14637
14864
|
const targetPath = expandPath(config.target_path);
|
|
14638
|
-
if (!
|
|
14865
|
+
if (!existsSync14(targetPath)) {
|
|
14639
14866
|
missingTargets += 1;
|
|
14640
14867
|
continue;
|
|
14641
14868
|
}
|
|
14642
|
-
const disk =
|
|
14869
|
+
const disk = readFileSync10(targetPath, "utf-8");
|
|
14643
14870
|
const { content: redactedDisk } = redactContent(disk, config.format);
|
|
14644
14871
|
if (redactedDisk !== config.content) {
|
|
14645
14872
|
driftedTargets += 1;
|
|
@@ -14865,9 +15092,9 @@ function parseSessionSource(value, order, replaceIds) {
|
|
|
14865
15092
|
if (!path)
|
|
14866
15093
|
throw new Error(`Invalid --source "${value}" (expected path or id=path)`);
|
|
14867
15094
|
const absPath = resolveSessionPath(path);
|
|
14868
|
-
if (!
|
|
15095
|
+
if (!existsSync16(absPath))
|
|
14869
15096
|
throw new Error(`Instruction source file not found: ${absPath}`);
|
|
14870
|
-
const content =
|
|
15097
|
+
const content = readFileSync12(absPath, "utf-8");
|
|
14871
15098
|
const source = sourceFromFilePath(absPath, content, order);
|
|
14872
15099
|
const resolvedId = id || source.id || basename7(absPath);
|
|
14873
15100
|
return {
|
|
@@ -14903,9 +15130,9 @@ async function collectSessionSources(opts, tool, store) {
|
|
|
14903
15130
|
}
|
|
14904
15131
|
for (const value of opts.identityExport ?? []) {
|
|
14905
15132
|
const path = resolveSessionPath(value);
|
|
14906
|
-
if (!
|
|
15133
|
+
if (!existsSync16(path))
|
|
14907
15134
|
throw new Error(`Identity instruction export not found: ${path}`);
|
|
14908
|
-
const parsed = JSON.parse(
|
|
15135
|
+
const parsed = JSON.parse(readFileSync12(path, "utf-8"));
|
|
14909
15136
|
sources.push(...sourcesFromIdentityExport(parsed, { path, tool, orderOffset: sources.length }));
|
|
14910
15137
|
}
|
|
14911
15138
|
return sources.map((source) => replaceIds.has(source.id) ? { ...source, merge: "replace" } : source);
|
|
@@ -14935,7 +15162,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
14935
15162
|
if (value === "-")
|
|
14936
15163
|
return { json: readBoundedProjectContextStdin() };
|
|
14937
15164
|
const path = resolveSessionPath(value);
|
|
14938
|
-
if (!
|
|
15165
|
+
if (!existsSync16(path)) {
|
|
14939
15166
|
if (allowMissing)
|
|
14940
15167
|
return {};
|
|
14941
15168
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_MISSING", `bundle file not found: ${path}`);
|
|
@@ -14947,7 +15174,7 @@ function readProjectContextBundleOption(value, allowMissing = false) {
|
|
|
14947
15174
|
if (stat.size > PROJECT_CONTEXT_MAX_INPUT_BYTES) {
|
|
14948
15175
|
throw new ProjectContextError("PROJECT_CONTEXT_INPUT_TOO_LARGE", `bundle exceeds ${PROJECT_CONTEXT_MAX_INPUT_BYTES} bytes`);
|
|
14949
15176
|
}
|
|
14950
|
-
return { json:
|
|
15177
|
+
return { json: readFileSync12(path, "utf8"), sourcePath: path };
|
|
14951
15178
|
}
|
|
14952
15179
|
function readBoundedProjectContextStdin() {
|
|
14953
15180
|
const chunks = [];
|
|
@@ -15075,11 +15302,11 @@ program.command("show <id>").alias("inspect").description("Show a config's conte
|
|
|
15075
15302
|
});
|
|
15076
15303
|
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
15304
|
const abs = resolve8(filePath);
|
|
15078
|
-
if (!
|
|
15305
|
+
if (!existsSync16(abs)) {
|
|
15079
15306
|
console.error(chalk.red(`File not found: ${abs}`));
|
|
15080
15307
|
process.exit(1);
|
|
15081
15308
|
}
|
|
15082
|
-
const rawContent =
|
|
15309
|
+
const rawContent = readFileSync12(abs, "utf-8");
|
|
15083
15310
|
const fmt = detectFormat(abs);
|
|
15084
15311
|
const { content, redacted, isTemplate: isTemplate2 } = redactContent(rawContent, fmt);
|
|
15085
15312
|
const targetPath = abs.startsWith(homedir7()) ? abs.replace(homedir7(), "~") : abs;
|
|
@@ -15117,11 +15344,15 @@ program.command("delete <id>").alias("rm").description("Delete a config record (
|
|
|
15117
15344
|
process.exit(1);
|
|
15118
15345
|
}
|
|
15119
15346
|
});
|
|
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) => {
|
|
15347
|
+
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
15348
|
try {
|
|
15122
15349
|
const store = resolveConfigStore();
|
|
15123
15350
|
const config = await store.getConfig(id);
|
|
15124
|
-
const report = await applyConfigsWithReport([config], {
|
|
15351
|
+
const report = await applyConfigsWithReport([config], {
|
|
15352
|
+
dryRun: opts.dryRun,
|
|
15353
|
+
store,
|
|
15354
|
+
allowSessionRendererOwned: opts.allowRendererOwned
|
|
15355
|
+
});
|
|
15125
15356
|
if (report.failures.length > 0) {
|
|
15126
15357
|
throw new Error(report.failures.map((failure) => failure.message).join("; "));
|
|
15127
15358
|
}
|
|
@@ -15199,7 +15430,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
15199
15430
|
for (const entry of entries) {
|
|
15200
15431
|
if (!entry.isDirectory())
|
|
15201
15432
|
continue;
|
|
15202
|
-
const projDir =
|
|
15433
|
+
const projDir = join15(absDir, entry.name);
|
|
15203
15434
|
const hasAgentConfig = [
|
|
15204
15435
|
"CLAUDE.md",
|
|
15205
15436
|
".mcp.json",
|
|
@@ -15212,7 +15443,7 @@ program.command("sync").description("Sync known AI coding configs from disk into
|
|
|
15212
15443
|
".aicopilot",
|
|
15213
15444
|
".cursor",
|
|
15214
15445
|
".agents"
|
|
15215
|
-
].some((marker) =>
|
|
15446
|
+
].some((marker) => existsSync16(join15(projDir, marker)));
|
|
15216
15447
|
if (!hasAgentConfig)
|
|
15217
15448
|
continue;
|
|
15218
15449
|
const result2 = await syncProject({ projectDir: projDir, dryRun: opts.dryRun, store });
|
|
@@ -15263,7 +15494,7 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
15263
15494
|
});
|
|
15264
15495
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
15265
15496
|
const store = resolveConfigStore();
|
|
15266
|
-
const dbPath = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
15497
|
+
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
15498
|
const stats = await store.getConfigStats();
|
|
15268
15499
|
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
15269
15500
|
console.log(chalk.cyan(isCloudMode() ? "API:" : "DB:") + " " + dbPath);
|
|
@@ -15889,7 +16120,7 @@ command = "${mcpBinary}"
|
|
|
15889
16120
|
args = []
|
|
15890
16121
|
`;
|
|
15891
16122
|
if (ex(configPath)) {
|
|
15892
|
-
const content =
|
|
16123
|
+
const content = readFileSync12(configPath, "utf-8");
|
|
15893
16124
|
if (content.includes("[mcp_servers.configs]")) {
|
|
15894
16125
|
console.log(chalk.dim("= Already installed in Codex"));
|
|
15895
16126
|
continue;
|
|
@@ -15983,7 +16214,7 @@ DB stats:`));
|
|
|
15983
16214
|
if (count > 0)
|
|
15984
16215
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
15985
16216
|
}
|
|
15986
|
-
const location = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] ||
|
|
16217
|
+
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
16218
|
console.log(chalk.dim(`
|
|
15988
16219
|
${isCloudMode() ? "API" : "DB"}: ${location}`));
|
|
15989
16220
|
});
|
|
@@ -16005,10 +16236,10 @@ program.command("status").description("Health check: total configs, drift from d
|
|
|
16005
16236
|
});
|
|
16006
16237
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
16007
16238
|
const { mkdirSync: mk } = await import("fs");
|
|
16008
|
-
const backupDir =
|
|
16239
|
+
const backupDir = join15(homedir7(), ".hasna", "instructions", "backups");
|
|
16009
16240
|
mk(backupDir, { recursive: true });
|
|
16010
16241
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
16011
|
-
const outPath =
|
|
16242
|
+
const outPath = join15(backupDir, `configs-${ts}.tar.gz`);
|
|
16012
16243
|
const result = await exportConfigs(outPath, { store: resolveConfigStore() });
|
|
16013
16244
|
const { statSync: st } = await import("fs");
|
|
16014
16245
|
const size = st(outPath).size;
|
|
@@ -16036,9 +16267,9 @@ program.command("doctor").description("Validate configs: syntax, permissions, mi
|
|
|
16036
16267
|
console.log(chalk.cyan("Known files on disk:"));
|
|
16037
16268
|
for (const k of KNOWN_CONFIGS) {
|
|
16038
16269
|
if (k.rulesDir) {
|
|
16039
|
-
|
|
16270
|
+
existsSync16(expandPath(k.rulesDir)) ? pass(`${k.rulesDir}/ exists`) : k.optional ? skip(`${k.rulesDir}/ (optional)`) : fail(`${k.rulesDir}/ not found`);
|
|
16040
16271
|
} else {
|
|
16041
|
-
|
|
16272
|
+
existsSync16(expandPath(k.path)) ? pass(k.path) : k.optional ? skip(`${k.path} (optional)`) : fail(`${k.path} not found`);
|
|
16042
16273
|
}
|
|
16043
16274
|
}
|
|
16044
16275
|
const allConfigs = await store.listConfigs();
|
|
@@ -16163,16 +16394,16 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16163
16394
|
for (const k of KNOWN_CONFIGS) {
|
|
16164
16395
|
if (k.rulesDir) {
|
|
16165
16396
|
const absDir = expandPath2(k.rulesDir);
|
|
16166
|
-
if (!
|
|
16397
|
+
if (!existsSync16(absDir))
|
|
16167
16398
|
continue;
|
|
16168
16399
|
const { readdirSync: readdirSync5 } = await import("fs");
|
|
16169
16400
|
for (const f of readdirSync5(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
16170
|
-
const abs =
|
|
16401
|
+
const abs = join15(absDir, f);
|
|
16171
16402
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16172
16403
|
}
|
|
16173
16404
|
} else {
|
|
16174
16405
|
const abs = expandPath2(k.path);
|
|
16175
|
-
if (
|
|
16406
|
+
if (existsSync16(abs))
|
|
16176
16407
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16177
16408
|
}
|
|
16178
16409
|
}
|
|
@@ -16180,7 +16411,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16180
16411
|
const tick = async () => {
|
|
16181
16412
|
let changed = 0;
|
|
16182
16413
|
for (const [abs, oldMtime] of mtimes) {
|
|
16183
|
-
if (!
|
|
16414
|
+
if (!existsSync16(abs))
|
|
16184
16415
|
continue;
|
|
16185
16416
|
const newMtime = st(abs).mtimeMs;
|
|
16186
16417
|
if (newMtime !== oldMtime) {
|
|
@@ -16192,10 +16423,10 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16192
16423
|
for (const k of KNOWN_CONFIGS) {
|
|
16193
16424
|
if (k.rulesDir) {
|
|
16194
16425
|
const absDir = expandPath2(k.rulesDir);
|
|
16195
|
-
if (!
|
|
16426
|
+
if (!existsSync16(absDir))
|
|
16196
16427
|
continue;
|
|
16197
16428
|
for (const f of rd(absDir).filter((f2) => f2.endsWith(".md"))) {
|
|
16198
|
-
const abs =
|
|
16429
|
+
const abs = join15(absDir, f);
|
|
16199
16430
|
if (!mtimes.has(abs)) {
|
|
16200
16431
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16201
16432
|
changed++;
|
|
@@ -16203,7 +16434,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
16203
16434
|
}
|
|
16204
16435
|
} else {
|
|
16205
16436
|
const abs = expandPath2(k.path);
|
|
16206
|
-
if (
|
|
16437
|
+
if (existsSync16(abs) && !mtimes.has(abs)) {
|
|
16207
16438
|
mtimes.set(abs, st(abs).mtimeMs);
|
|
16208
16439
|
changed++;
|
|
16209
16440
|
}
|
|
@@ -16231,11 +16462,11 @@ program.command("report").description("Summary of stored configs, drift, and eco
|
|
|
16231
16462
|
if (!c.target_path)
|
|
16232
16463
|
continue;
|
|
16233
16464
|
const abs = expandPath(c.target_path);
|
|
16234
|
-
if (!
|
|
16465
|
+
if (!existsSync16(abs)) {
|
|
16235
16466
|
missing++;
|
|
16236
16467
|
continue;
|
|
16237
16468
|
}
|
|
16238
|
-
const disk =
|
|
16469
|
+
const disk = readFileSync12(abs, "utf-8");
|
|
16239
16470
|
const { content: redactedDisk } = redactContent(disk, c.format);
|
|
16240
16471
|
if (redactedDisk !== c.content)
|
|
16241
16472
|
drifted++;
|
|
@@ -16276,7 +16507,7 @@ program.command("clean").description("Remove configs from DB whose target files
|
|
|
16276
16507
|
if (!c.target_path)
|
|
16277
16508
|
continue;
|
|
16278
16509
|
const abs = expandPath(c.target_path);
|
|
16279
|
-
if (!
|
|
16510
|
+
if (!existsSync16(abs)) {
|
|
16280
16511
|
if (printed < maxPrinted) {
|
|
16281
16512
|
if (opts.dryRun) {
|
|
16282
16513
|
console.log(chalk.yellow(" would remove:") + ` ${c.slug} ${chalk.dim(`(${truncateMiddle(c.target_path, 88)})`)}`);
|