@hasna/instructions 0.4.14 → 0.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +72 -20
- package/dist/index.js +63 -12
- package/dist/lib/apply-secret-placeholder-guard.test.d.ts +2 -0
- package/dist/lib/apply-secret-placeholder-guard.test.d.ts.map +1 -0
- package/dist/lib/apply.d.ts +1 -1
- package/dist/lib/apply.d.ts.map +1 -1
- package/dist/mcp/index.js +67 -17
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -2641,9 +2641,6 @@ function templateizeMachineContent(content, machine) {
|
|
|
2641
2641
|
next = next.replace(/~\/workspace/g, "{{WORKSPACE_ROOT}}").replace(/~\/Workspace/g, "{{WORKSPACE_ROOT}}").replace(/\/opt\/homebrew\/bin\/bun/g, "{{BUN_PATH}}");
|
|
2642
2642
|
return { content: next, changed: next !== content };
|
|
2643
2643
|
}
|
|
2644
|
-
function renderMachineAwareContent(content, variables) {
|
|
2645
|
-
return isTemplate(content) ? renderTemplate(content, variables) : content;
|
|
2646
|
-
}
|
|
2647
2644
|
function renderMachineAwareContentPreview(content, variables) {
|
|
2648
2645
|
return isTemplate(content) ? renderTemplatePreview(content, variables) : { content, unresolved: [] };
|
|
2649
2646
|
}
|
|
@@ -11577,8 +11574,9 @@ function normalizeTargetPath(p) {
|
|
|
11577
11574
|
}
|
|
11578
11575
|
}
|
|
11579
11576
|
async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
11580
|
-
const
|
|
11581
|
-
const
|
|
11577
|
+
const variables = opts.vars ?? {};
|
|
11578
|
+
const renderedTarget = renderForApply(targetPath, variables);
|
|
11579
|
+
const rendered = renderForApply(content, variables);
|
|
11582
11580
|
const renderedTargetPath = renderedTarget.content;
|
|
11583
11581
|
const renderedContent = rendered.content;
|
|
11584
11582
|
const targetAgent = meta.agent ?? config.agent;
|
|
@@ -11617,13 +11615,42 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
11617
11615
|
...meta
|
|
11618
11616
|
};
|
|
11619
11617
|
}
|
|
11620
|
-
function renderForApply(content, variables
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11618
|
+
function renderForApply(content, variables) {
|
|
11619
|
+
return renderMachineAwareContentPreview(content, variables);
|
|
11620
|
+
}
|
|
11621
|
+
function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
11622
|
+
const secretTokens = parseTemplateVars(renderedContent).filter(isSecretVarName);
|
|
11623
|
+
if (secretTokens.length === 0)
|
|
11624
|
+
return [];
|
|
11625
|
+
let current;
|
|
11626
|
+
try {
|
|
11627
|
+
const path = expandPath(targetPath);
|
|
11628
|
+
if (!existsSync7(path))
|
|
11629
|
+
return [];
|
|
11630
|
+
current = readFileSync4(path, "utf-8");
|
|
11631
|
+
} catch {
|
|
11632
|
+
return secretTokens;
|
|
11633
|
+
}
|
|
11634
|
+
if (current === renderedContent)
|
|
11635
|
+
return [];
|
|
11636
|
+
if (scanSecrets(current, format).length > 0)
|
|
11637
|
+
return secretTokens;
|
|
11638
|
+
return secretTokens.filter((name) => countPlaceholder(current, name) < countPlaceholder(renderedContent, name));
|
|
11639
|
+
}
|
|
11640
|
+
function countPlaceholder(content, name) {
|
|
11641
|
+
return content.match(new RegExp(`\\{\\{${name}(?::[^}]*)?\\}\\}`, "g"))?.length ?? 0;
|
|
11642
|
+
}
|
|
11643
|
+
async function resolveApplyVariables(opts) {
|
|
11644
|
+
const machine = detectMachineContext();
|
|
11645
|
+
let base;
|
|
11646
|
+
try {
|
|
11647
|
+
const store = opts.store ?? resolveConfigStore();
|
|
11648
|
+
const profile = await store.resolveProfileForMachine(machine);
|
|
11649
|
+
base = resolveProfileVariables(profile, machine);
|
|
11650
|
+
} catch {
|
|
11651
|
+
base = machineContextToVariables(machine);
|
|
11652
|
+
}
|
|
11653
|
+
return { ...base, ...opts.vars ?? {} };
|
|
11627
11654
|
}
|
|
11628
11655
|
function isAntigravityRuleTarget(agent, targetPath) {
|
|
11629
11656
|
return agent === "antigravity" && /\.(md|mdc|markdown)$/i.test(targetPath);
|
|
@@ -11708,7 +11735,8 @@ async function applyConfigs(configs, opts = {}) {
|
|
|
11708
11735
|
return report.results;
|
|
11709
11736
|
}
|
|
11710
11737
|
async function applyConfigsWithReport(configs, opts = {}) {
|
|
11711
|
-
const
|
|
11738
|
+
const effective = { ...opts, vars: await resolveApplyVariables(opts) };
|
|
11739
|
+
const prepared = prepareConfigBatch(configs, effective);
|
|
11712
11740
|
if (prepared.failures.length > 0) {
|
|
11713
11741
|
return {
|
|
11714
11742
|
results: [],
|
|
@@ -11722,7 +11750,7 @@ async function applyConfigsWithReport(configs, opts = {}) {
|
|
|
11722
11750
|
if (config.kind === "reference" || isRetiredOrUnsupportedConfigAgent(config.agent))
|
|
11723
11751
|
continue;
|
|
11724
11752
|
try {
|
|
11725
|
-
results.push(await applyPreparedConfig(config,
|
|
11753
|
+
results.push(await applyPreparedConfig(config, effective));
|
|
11726
11754
|
} catch (error) {
|
|
11727
11755
|
failures.push({
|
|
11728
11756
|
config_id: config.id,
|
|
@@ -11762,7 +11790,27 @@ function prepareConfigBatch(configs, opts) {
|
|
|
11762
11790
|
}
|
|
11763
11791
|
return false;
|
|
11764
11792
|
});
|
|
11765
|
-
const
|
|
11793
|
+
const renderSafeConfigs = activeConfigs.filter((config) => {
|
|
11794
|
+
const behavior = canonicalConfigApplyBehavior(config, opts, activeConfigs);
|
|
11795
|
+
const targets = [
|
|
11796
|
+
...behavior.primary ? [{ path: behavior.primary.path, content: behavior.primary.content }] : [],
|
|
11797
|
+
...behavior.outputs.map((output) => ({ path: output.path, content: output.content }))
|
|
11798
|
+
];
|
|
11799
|
+
const blocked = [...new Set(targets.flatMap((target) => wouldDestroyACredential(target.path, target.content, config.format)))].sort();
|
|
11800
|
+
if (blocked.length === 0)
|
|
11801
|
+
return true;
|
|
11802
|
+
for (const target of targets) {
|
|
11803
|
+
skipped.push({
|
|
11804
|
+
config_id: config.id,
|
|
11805
|
+
config_slug: config.slug,
|
|
11806
|
+
path: target.path,
|
|
11807
|
+
owner: "unresolved-secret-placeholder",
|
|
11808
|
+
reason: `no value for ${blocked.map((name) => `{{${name}}}`).join(", ")} and the file holds something else there; writing the placeholder would overwrite a live credential`
|
|
11809
|
+
});
|
|
11810
|
+
}
|
|
11811
|
+
return false;
|
|
11812
|
+
});
|
|
11813
|
+
const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(renderSafeConfigs, opts);
|
|
11766
11814
|
for (const duplicate of duplicates) {
|
|
11767
11815
|
for (const target of configTargets(duplicate, opts, activeConfigs)) {
|
|
11768
11816
|
skipped.push({
|
|
@@ -11914,6 +11962,8 @@ var init_apply = __esm(() => {
|
|
|
11914
11962
|
init_machine();
|
|
11915
11963
|
init_session_render();
|
|
11916
11964
|
init_session_render_ownership();
|
|
11965
|
+
init_redact();
|
|
11966
|
+
init_template();
|
|
11917
11967
|
init_transforms();
|
|
11918
11968
|
});
|
|
11919
11969
|
|
|
@@ -15538,6 +15588,9 @@ program.command("apply <id>").description("Apply a config to its target_path and
|
|
|
15538
15588
|
const status = opts.dryRun ? chalk.yellow("[dry-run]") : result.primary_changed ? chalk.green("\u2713") : chalk.dim("=");
|
|
15539
15589
|
const change = result.primary_changed ? "changed" : "unchanged";
|
|
15540
15590
|
console.log(`${status} ${result.path} ${chalk.dim(`(${change})`)}`);
|
|
15591
|
+
if (result.unresolved_template_vars?.length) {
|
|
15592
|
+
console.log(` ${chalk.yellow("!")} left unexpanded: ${result.unresolved_template_vars.map((name) => `{{${name}}}`).join(", ")}` + ` ${chalk.dim("(no value for these; secret placeholders are preserved on purpose)")}`);
|
|
15593
|
+
}
|
|
15541
15594
|
for (const output of result.outputs ?? []) {
|
|
15542
15595
|
const outputStatus = opts.dryRun ? chalk.yellow("[dry-run]") : output.primary_changed ? chalk.green("\u2713") : chalk.dim("=");
|
|
15543
15596
|
const outputChange = output.primary_changed ? "changed" : "unchanged";
|
|
@@ -15818,11 +15871,10 @@ profileCmd.command("apply [id]").description("Apply all configs in a profile to
|
|
|
15818
15871
|
for (const skipped of report.skipped) {
|
|
15819
15872
|
console.log(`${chalk.dim("[owned]")} ${skipped.path} ${chalk.dim(skipped.owner)}`);
|
|
15820
15873
|
}
|
|
15821
|
-
|
|
15822
|
-
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
}
|
|
15874
|
+
const unresolved = [...new Set(results.flatMap((result) => result.unresolved_template_vars ?? []))];
|
|
15875
|
+
if (unresolved.length > 0) {
|
|
15876
|
+
const where = opts.dryRun ? "would be left unexpanded" : "left unexpanded";
|
|
15877
|
+
console.log(chalk.yellow(`Unresolved secret/runtime template references ${where}: ${unresolved.join(", ")}`));
|
|
15826
15878
|
}
|
|
15827
15879
|
for (const failure of report.failures) {
|
|
15828
15880
|
console.error(chalk.red(`[failed] ${failure.config_slug}: ${failure.message}`));
|
package/dist/index.js
CHANGED
|
@@ -9591,8 +9591,9 @@ function normalizeTargetPath(p) {
|
|
|
9591
9591
|
}
|
|
9592
9592
|
}
|
|
9593
9593
|
async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
9594
|
-
const
|
|
9595
|
-
const
|
|
9594
|
+
const variables = opts.vars ?? {};
|
|
9595
|
+
const renderedTarget = renderForApply(targetPath, variables);
|
|
9596
|
+
const rendered = renderForApply(content, variables);
|
|
9596
9597
|
const renderedTargetPath = renderedTarget.content;
|
|
9597
9598
|
const renderedContent = rendered.content;
|
|
9598
9599
|
const targetAgent = meta.agent ?? config.agent;
|
|
@@ -9631,13 +9632,42 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
9631
9632
|
...meta
|
|
9632
9633
|
};
|
|
9633
9634
|
}
|
|
9634
|
-
function renderForApply(content, variables
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9635
|
+
function renderForApply(content, variables) {
|
|
9636
|
+
return renderMachineAwareContentPreview(content, variables);
|
|
9637
|
+
}
|
|
9638
|
+
function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
9639
|
+
const secretTokens = parseTemplateVars(renderedContent).filter(isSecretVarName);
|
|
9640
|
+
if (secretTokens.length === 0)
|
|
9641
|
+
return [];
|
|
9642
|
+
let current;
|
|
9643
|
+
try {
|
|
9644
|
+
const path = expandPath(targetPath);
|
|
9645
|
+
if (!existsSync6(path))
|
|
9646
|
+
return [];
|
|
9647
|
+
current = readFileSync4(path, "utf-8");
|
|
9648
|
+
} catch {
|
|
9649
|
+
return secretTokens;
|
|
9650
|
+
}
|
|
9651
|
+
if (current === renderedContent)
|
|
9652
|
+
return [];
|
|
9653
|
+
if (scanSecrets(current, format).length > 0)
|
|
9654
|
+
return secretTokens;
|
|
9655
|
+
return secretTokens.filter((name) => countPlaceholder(current, name) < countPlaceholder(renderedContent, name));
|
|
9656
|
+
}
|
|
9657
|
+
function countPlaceholder(content, name) {
|
|
9658
|
+
return content.match(new RegExp(`\\{\\{${name}(?::[^}]*)?\\}\\}`, "g"))?.length ?? 0;
|
|
9659
|
+
}
|
|
9660
|
+
async function resolveApplyVariables(opts) {
|
|
9661
|
+
const machine = detectMachineContext();
|
|
9662
|
+
let base;
|
|
9663
|
+
try {
|
|
9664
|
+
const store = opts.store ?? resolveConfigStore();
|
|
9665
|
+
const profile = await store.resolveProfileForMachine(machine);
|
|
9666
|
+
base = resolveProfileVariables(profile, machine);
|
|
9667
|
+
} catch {
|
|
9668
|
+
base = machineContextToVariables(machine);
|
|
9669
|
+
}
|
|
9670
|
+
return { ...base, ...opts.vars ?? {} };
|
|
9641
9671
|
}
|
|
9642
9672
|
function isAntigravityRuleTarget(agent, targetPath) {
|
|
9643
9673
|
return agent === "antigravity" && /\.(md|mdc|markdown)$/i.test(targetPath);
|
|
@@ -9722,7 +9752,8 @@ async function applyConfigs(configs, opts = {}) {
|
|
|
9722
9752
|
return report.results;
|
|
9723
9753
|
}
|
|
9724
9754
|
async function applyConfigsWithReport(configs, opts = {}) {
|
|
9725
|
-
const
|
|
9755
|
+
const effective = { ...opts, vars: await resolveApplyVariables(opts) };
|
|
9756
|
+
const prepared = prepareConfigBatch(configs, effective);
|
|
9726
9757
|
if (prepared.failures.length > 0) {
|
|
9727
9758
|
return {
|
|
9728
9759
|
results: [],
|
|
@@ -9736,7 +9767,7 @@ async function applyConfigsWithReport(configs, opts = {}) {
|
|
|
9736
9767
|
if (config.kind === "reference" || isRetiredOrUnsupportedConfigAgent(config.agent))
|
|
9737
9768
|
continue;
|
|
9738
9769
|
try {
|
|
9739
|
-
results.push(await applyPreparedConfig(config,
|
|
9770
|
+
results.push(await applyPreparedConfig(config, effective));
|
|
9740
9771
|
} catch (error) {
|
|
9741
9772
|
failures.push({
|
|
9742
9773
|
config_id: config.id,
|
|
@@ -9776,7 +9807,27 @@ function prepareConfigBatch(configs, opts) {
|
|
|
9776
9807
|
}
|
|
9777
9808
|
return false;
|
|
9778
9809
|
});
|
|
9779
|
-
const
|
|
9810
|
+
const renderSafeConfigs = activeConfigs.filter((config) => {
|
|
9811
|
+
const behavior = canonicalConfigApplyBehavior(config, opts, activeConfigs);
|
|
9812
|
+
const targets = [
|
|
9813
|
+
...behavior.primary ? [{ path: behavior.primary.path, content: behavior.primary.content }] : [],
|
|
9814
|
+
...behavior.outputs.map((output) => ({ path: output.path, content: output.content }))
|
|
9815
|
+
];
|
|
9816
|
+
const blocked = [...new Set(targets.flatMap((target) => wouldDestroyACredential(target.path, target.content, config.format)))].sort();
|
|
9817
|
+
if (blocked.length === 0)
|
|
9818
|
+
return true;
|
|
9819
|
+
for (const target of targets) {
|
|
9820
|
+
skipped.push({
|
|
9821
|
+
config_id: config.id,
|
|
9822
|
+
config_slug: config.slug,
|
|
9823
|
+
path: target.path,
|
|
9824
|
+
owner: "unresolved-secret-placeholder",
|
|
9825
|
+
reason: `no value for ${blocked.map((name) => `{{${name}}}`).join(", ")} and the file holds something else there; writing the placeholder would overwrite a live credential`
|
|
9826
|
+
});
|
|
9827
|
+
}
|
|
9828
|
+
return false;
|
|
9829
|
+
});
|
|
9830
|
+
const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(renderSafeConfigs, opts);
|
|
9780
9831
|
for (const duplicate of duplicates) {
|
|
9781
9832
|
for (const target of configTargets(duplicate, opts, activeConfigs)) {
|
|
9782
9833
|
skipped.push({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-secret-placeholder-guard.test.d.ts","sourceRoot":"","sources":["../../src/lib/apply-secret-placeholder-guard.test.ts"],"names":[],"mappings":""}
|
package/dist/lib/apply.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface ConfigApplySkippedTarget {
|
|
|
23
23
|
config_id: string;
|
|
24
24
|
config_slug: string;
|
|
25
25
|
path: string;
|
|
26
|
-
owner: typeof SESSION_RENDERER_OWNER_ID | "equivalent-profile-config" | "retired-provider-config";
|
|
26
|
+
owner: typeof SESSION_RENDERER_OWNER_ID | "equivalent-profile-config" | "retired-provider-config" | "unresolved-secret-placeholder";
|
|
27
27
|
reason: string;
|
|
28
28
|
}
|
|
29
29
|
export interface ConfigApplyPreviewFailure {
|
package/dist/lib/apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAA8B,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQ1D,OAAO,EAGL,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,yBAAyB,GAAG,2BAA2B,GAAG,yBAAyB,GAAG,+BAA+B,CAAC;IACpI,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,QAAQ,EAAE,yBAAyB,EAAE,CAAC;CACvC;AA8PD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,CAAC,CA+BtB;AA2ED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CAMxB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAiC7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAM,GACtC,OAAO,CAAC,kBAAkB,CAAC,CAE7B"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -578,9 +578,6 @@ function templateizeMachineContent(content, machine) {
|
|
|
578
578
|
next = next.replace(/~\/workspace/g, "{{WORKSPACE_ROOT}}").replace(/~\/Workspace/g, "{{WORKSPACE_ROOT}}").replace(/\/opt\/homebrew\/bin\/bun/g, "{{BUN_PATH}}");
|
|
579
579
|
return { content: next, changed: next !== content };
|
|
580
580
|
}
|
|
581
|
-
function renderMachineAwareContent(content, variables) {
|
|
582
|
-
return isTemplate(content) ? renderTemplate(content, variables) : content;
|
|
583
|
-
}
|
|
584
581
|
function renderMachineAwareContentPreview(content, variables) {
|
|
585
582
|
return isTemplate(content) ? renderTemplatePreview(content, variables) : { content, unresolved: [] };
|
|
586
583
|
}
|
|
@@ -5894,8 +5891,9 @@ function normalizeTargetPath(p) {
|
|
|
5894
5891
|
}
|
|
5895
5892
|
}
|
|
5896
5893
|
async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
5897
|
-
const
|
|
5898
|
-
const
|
|
5894
|
+
const variables = opts.vars ?? {};
|
|
5895
|
+
const renderedTarget = renderForApply(targetPath, variables);
|
|
5896
|
+
const rendered = renderForApply(content, variables);
|
|
5899
5897
|
const renderedTargetPath = renderedTarget.content;
|
|
5900
5898
|
const renderedContent = rendered.content;
|
|
5901
5899
|
const targetAgent = meta.agent ?? config.agent;
|
|
@@ -5934,13 +5932,42 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
|
|
|
5934
5932
|
...meta
|
|
5935
5933
|
};
|
|
5936
5934
|
}
|
|
5937
|
-
function renderForApply(content, variables
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5935
|
+
function renderForApply(content, variables) {
|
|
5936
|
+
return renderMachineAwareContentPreview(content, variables);
|
|
5937
|
+
}
|
|
5938
|
+
function wouldDestroyACredential(targetPath, renderedContent, format) {
|
|
5939
|
+
const secretTokens = parseTemplateVars(renderedContent).filter(isSecretVarName);
|
|
5940
|
+
if (secretTokens.length === 0)
|
|
5941
|
+
return [];
|
|
5942
|
+
let current;
|
|
5943
|
+
try {
|
|
5944
|
+
const path = expandPath(targetPath);
|
|
5945
|
+
if (!existsSync4(path))
|
|
5946
|
+
return [];
|
|
5947
|
+
current = readFileSync2(path, "utf-8");
|
|
5948
|
+
} catch {
|
|
5949
|
+
return secretTokens;
|
|
5950
|
+
}
|
|
5951
|
+
if (current === renderedContent)
|
|
5952
|
+
return [];
|
|
5953
|
+
if (scanSecrets(current, format).length > 0)
|
|
5954
|
+
return secretTokens;
|
|
5955
|
+
return secretTokens.filter((name) => countPlaceholder(current, name) < countPlaceholder(renderedContent, name));
|
|
5956
|
+
}
|
|
5957
|
+
function countPlaceholder(content, name) {
|
|
5958
|
+
return content.match(new RegExp(`\\{\\{${name}(?::[^}]*)?\\}\\}`, "g"))?.length ?? 0;
|
|
5959
|
+
}
|
|
5960
|
+
async function resolveApplyVariables(opts) {
|
|
5961
|
+
const machine = detectMachineContext();
|
|
5962
|
+
let base;
|
|
5963
|
+
try {
|
|
5964
|
+
const store = opts.store ?? resolveConfigStore();
|
|
5965
|
+
const profile = await store.resolveProfileForMachine(machine);
|
|
5966
|
+
base = resolveProfileVariables(profile, machine);
|
|
5967
|
+
} catch {
|
|
5968
|
+
base = machineContextToVariables(machine);
|
|
5969
|
+
}
|
|
5970
|
+
return { ...base, ...opts.vars ?? {} };
|
|
5944
5971
|
}
|
|
5945
5972
|
function isAntigravityRuleTarget(agent, targetPath) {
|
|
5946
5973
|
return agent === "antigravity" && /\.(md|mdc|markdown)$/i.test(targetPath);
|
|
@@ -6025,7 +6052,8 @@ async function applyConfigs(configs, opts = {}) {
|
|
|
6025
6052
|
return report.results;
|
|
6026
6053
|
}
|
|
6027
6054
|
async function applyConfigsWithReport(configs, opts = {}) {
|
|
6028
|
-
const
|
|
6055
|
+
const effective = { ...opts, vars: await resolveApplyVariables(opts) };
|
|
6056
|
+
const prepared = prepareConfigBatch(configs, effective);
|
|
6029
6057
|
if (prepared.failures.length > 0) {
|
|
6030
6058
|
return {
|
|
6031
6059
|
results: [],
|
|
@@ -6039,7 +6067,7 @@ async function applyConfigsWithReport(configs, opts = {}) {
|
|
|
6039
6067
|
if (config.kind === "reference" || isRetiredOrUnsupportedConfigAgent(config.agent))
|
|
6040
6068
|
continue;
|
|
6041
6069
|
try {
|
|
6042
|
-
results.push(await applyPreparedConfig(config,
|
|
6070
|
+
results.push(await applyPreparedConfig(config, effective));
|
|
6043
6071
|
} catch (error) {
|
|
6044
6072
|
failures.push({
|
|
6045
6073
|
config_id: config.id,
|
|
@@ -6079,7 +6107,27 @@ function prepareConfigBatch(configs, opts) {
|
|
|
6079
6107
|
}
|
|
6080
6108
|
return false;
|
|
6081
6109
|
});
|
|
6082
|
-
const
|
|
6110
|
+
const renderSafeConfigs = activeConfigs.filter((config) => {
|
|
6111
|
+
const behavior = canonicalConfigApplyBehavior(config, opts, activeConfigs);
|
|
6112
|
+
const targets = [
|
|
6113
|
+
...behavior.primary ? [{ path: behavior.primary.path, content: behavior.primary.content }] : [],
|
|
6114
|
+
...behavior.outputs.map((output) => ({ path: output.path, content: output.content }))
|
|
6115
|
+
];
|
|
6116
|
+
const blocked = [...new Set(targets.flatMap((target) => wouldDestroyACredential(target.path, target.content, config.format)))].sort();
|
|
6117
|
+
if (blocked.length === 0)
|
|
6118
|
+
return true;
|
|
6119
|
+
for (const target of targets) {
|
|
6120
|
+
skipped.push({
|
|
6121
|
+
config_id: config.id,
|
|
6122
|
+
config_slug: config.slug,
|
|
6123
|
+
path: target.path,
|
|
6124
|
+
owner: "unresolved-secret-placeholder",
|
|
6125
|
+
reason: `no value for ${blocked.map((name) => `{{${name}}}`).join(", ")} and the file holds something else there; writing the placeholder would overwrite a live credential`
|
|
6126
|
+
});
|
|
6127
|
+
}
|
|
6128
|
+
return false;
|
|
6129
|
+
});
|
|
6130
|
+
const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(renderSafeConfigs, opts);
|
|
6083
6131
|
for (const duplicate of duplicates) {
|
|
6084
6132
|
for (const target of configTargets(duplicate, opts, activeConfigs)) {
|
|
6085
6133
|
skipped.push({
|
|
@@ -6231,6 +6279,8 @@ var init_apply = __esm(() => {
|
|
|
6231
6279
|
init_machine();
|
|
6232
6280
|
init_session_render();
|
|
6233
6281
|
init_session_render_ownership();
|
|
6282
|
+
init_redact();
|
|
6283
|
+
init_template();
|
|
6234
6284
|
init_transforms();
|
|
6235
6285
|
});
|
|
6236
6286
|
|
|
@@ -6858,7 +6908,7 @@ var init_sync_dir = __esm(() => {
|
|
|
6858
6908
|
var require_package = __commonJS((exports, module) => {
|
|
6859
6909
|
module.exports = {
|
|
6860
6910
|
name: "@hasna/instructions",
|
|
6861
|
-
version: "0.4.
|
|
6911
|
+
version: "0.4.15",
|
|
6862
6912
|
description: "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
6863
6913
|
type: "module",
|
|
6864
6914
|
main: "dist/index.js",
|
|
@@ -6897,7 +6947,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
6897
6947
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
6898
6948
|
"dev:serve": "bun run src/server/index.ts",
|
|
6899
6949
|
seed: "bun run scripts/seed.ts",
|
|
6900
|
-
prepublishOnly: "bun run build",
|
|
6950
|
+
prepublishOnly: "bun run scripts/check-publish-hold.ts && bun run build",
|
|
6901
6951
|
postinstall: "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true"
|
|
6902
6952
|
},
|
|
6903
6953
|
keywords: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/instructions",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.15",
|
|
4
4
|
"description": "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
40
40
|
"dev:serve": "bun run src/server/index.ts",
|
|
41
41
|
"seed": "bun run scripts/seed.ts",
|
|
42
|
-
"prepublishOnly": "bun run build",
|
|
42
|
+
"prepublishOnly": "bun run scripts/check-publish-hold.ts && bun run build",
|
|
43
43
|
"postinstall": "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true"
|
|
44
44
|
},
|
|
45
45
|
"keywords": [
|