@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 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 renderedTarget = opts.vars ? renderForApply(targetPath, opts.vars, opts.dryRun === true) : { content: targetPath, unresolved: [] };
11581
- const rendered = opts.vars ? renderForApply(content, opts.vars, opts.dryRun === true) : { content, unresolved: [] };
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, preview) {
11621
- if (preview)
11622
- return renderMachineAwareContentPreview(content, variables);
11623
- return {
11624
- content: renderMachineAwareContent(content, variables),
11625
- unresolved: []
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 prepared = prepareConfigBatch(configs, opts);
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, opts));
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 { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(activeConfigs, opts);
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
- if (opts.dryRun) {
15822
- const unresolved = [...new Set(results.flatMap((result) => result.unresolved_template_vars ?? []))];
15823
- if (unresolved.length > 0) {
15824
- console.log(chalk.yellow(`Unresolved secret/runtime template references preserved in preview: ${unresolved.join(", ")}`));
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 renderedTarget = opts.vars ? renderForApply(targetPath, opts.vars, opts.dryRun === true) : { content: targetPath, unresolved: [] };
9595
- const rendered = opts.vars ? renderForApply(content, opts.vars, opts.dryRun === true) : { content, unresolved: [] };
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, preview) {
9635
- if (preview)
9636
- return renderMachineAwareContentPreview(content, variables);
9637
- return {
9638
- content: renderMachineAwareContent(content, variables),
9639
- unresolved: []
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 prepared = prepareConfigBatch(configs, opts);
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, opts));
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 { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(activeConfigs, opts);
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=apply-secret-placeholder-guard.test.d.ts.map
@@ -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":""}
@@ -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 {
@@ -1 +1 @@
1
- {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAgB,MAAM,mBAAmB,CAAC;AAE3E,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAGL,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAI7B,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,CAAC;IAClG,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;AAuHD,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,CA4B7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAM,GACtC,OAAO,CAAC,kBAAkB,CAAC,CAE7B"}
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 renderedTarget = opts.vars ? renderForApply(targetPath, opts.vars, opts.dryRun === true) : { content: targetPath, unresolved: [] };
5898
- const rendered = opts.vars ? renderForApply(content, opts.vars, opts.dryRun === true) : { content, unresolved: [] };
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, preview) {
5938
- if (preview)
5939
- return renderMachineAwareContentPreview(content, variables);
5940
- return {
5941
- content: renderMachineAwareContent(content, variables),
5942
- unresolved: []
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 prepared = prepareConfigBatch(configs, opts);
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, opts));
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 { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(activeConfigs, opts);
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.14",
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.14",
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": [