@hasna/instructions 0.4.14 → 0.4.16

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
  }
@@ -7532,6 +7529,14 @@ function reasonFor(key, value) {
7532
7529
  function isReferenceValue(value) {
7533
7530
  return /^\{\{[A-Z][A-Z0-9_]*\}\}$/.test(value) || /^\$\{[A-Z][A-Z0-9_]*\}$/.test(value) || /^\$[A-Z][A-Z0-9_]*$/.test(value) || /^%[A-Z][A-Z0-9_]*%$/.test(value);
7534
7531
  }
7532
+ function redactFormatForTarget(targetPath, storedFormat) {
7533
+ const p = targetPath.toLowerCase();
7534
+ if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
7535
+ return "shell";
7536
+ if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
7537
+ return "ini";
7538
+ return storedFormat;
7539
+ }
7535
7540
  function redactContent(content, format) {
7536
7541
  switch (format) {
7537
7542
  case "shell":
@@ -11577,8 +11582,9 @@ function normalizeTargetPath(p) {
11577
11582
  }
11578
11583
  }
11579
11584
  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: [] };
11585
+ const variables = opts.vars ?? {};
11586
+ const renderedTarget = renderForApply(targetPath, variables);
11587
+ const rendered = renderForApply(content, variables);
11582
11588
  const renderedTargetPath = renderedTarget.content;
11583
11589
  const renderedContent = rendered.content;
11584
11590
  const targetAgent = meta.agent ?? config.agent;
@@ -11617,13 +11623,42 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
11617
11623
  ...meta
11618
11624
  };
11619
11625
  }
11620
- function renderForApply(content, variables, preview) {
11621
- if (preview)
11622
- return renderMachineAwareContentPreview(content, variables);
11623
- return {
11624
- content: renderMachineAwareContent(content, variables),
11625
- unresolved: []
11626
- };
11626
+ function renderForApply(content, variables) {
11627
+ return renderMachineAwareContentPreview(content, variables);
11628
+ }
11629
+ function wouldDestroyACredential(targetPath, renderedContent, format) {
11630
+ const secretTokens = parseTemplateVars(renderedContent).filter(isSecretVarName);
11631
+ if (secretTokens.length === 0)
11632
+ return [];
11633
+ let current;
11634
+ try {
11635
+ const path = expandPath(targetPath);
11636
+ if (!existsSync7(path))
11637
+ return [];
11638
+ current = readFileSync4(path, "utf-8");
11639
+ } catch {
11640
+ return secretTokens;
11641
+ }
11642
+ if (current === renderedContent)
11643
+ return [];
11644
+ if (scanSecrets(current, redactFormatForTarget(targetPath, format)).length > 0)
11645
+ return secretTokens;
11646
+ return secretTokens.filter((name) => countPlaceholder(current, name) < countPlaceholder(renderedContent, name));
11647
+ }
11648
+ function countPlaceholder(content, name) {
11649
+ return content.match(new RegExp(`\\{\\{${name}(?::[^}]*)?\\}\\}`, "g"))?.length ?? 0;
11650
+ }
11651
+ async function resolveApplyVariables(opts) {
11652
+ const machine = detectMachineContext();
11653
+ let base;
11654
+ try {
11655
+ const store = opts.store ?? resolveConfigStore();
11656
+ const profile = await store.resolveProfileForMachine(machine);
11657
+ base = resolveProfileVariables(profile, machine);
11658
+ } catch {
11659
+ base = machineContextToVariables(machine);
11660
+ }
11661
+ return { ...base, ...opts.vars ?? {} };
11627
11662
  }
11628
11663
  function isAntigravityRuleTarget(agent, targetPath) {
11629
11664
  return agent === "antigravity" && /\.(md|mdc|markdown)$/i.test(targetPath);
@@ -11708,7 +11743,8 @@ async function applyConfigs(configs, opts = {}) {
11708
11743
  return report.results;
11709
11744
  }
11710
11745
  async function applyConfigsWithReport(configs, opts = {}) {
11711
- const prepared = prepareConfigBatch(configs, opts);
11746
+ const effective = { ...opts, vars: await resolveApplyVariables(opts) };
11747
+ const prepared = prepareConfigBatch(configs, effective);
11712
11748
  if (prepared.failures.length > 0) {
11713
11749
  return {
11714
11750
  results: [],
@@ -11722,7 +11758,7 @@ async function applyConfigsWithReport(configs, opts = {}) {
11722
11758
  if (config.kind === "reference" || isRetiredOrUnsupportedConfigAgent(config.agent))
11723
11759
  continue;
11724
11760
  try {
11725
- results.push(await applyPreparedConfig(config, opts));
11761
+ results.push(await applyPreparedConfig(config, effective));
11726
11762
  } catch (error) {
11727
11763
  failures.push({
11728
11764
  config_id: config.id,
@@ -11762,7 +11798,27 @@ function prepareConfigBatch(configs, opts) {
11762
11798
  }
11763
11799
  return false;
11764
11800
  });
11765
- const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(activeConfigs, opts);
11801
+ const renderSafeConfigs = activeConfigs.filter((config) => {
11802
+ const behavior = canonicalConfigApplyBehavior(config, opts, activeConfigs);
11803
+ const targets = [
11804
+ ...behavior.primary ? [{ path: behavior.primary.path, content: behavior.primary.content }] : [],
11805
+ ...behavior.outputs.map((output) => ({ path: output.path, content: output.content }))
11806
+ ];
11807
+ const blocked = [...new Set(targets.flatMap((target) => wouldDestroyACredential(target.path, target.content, config.format)))].sort();
11808
+ if (blocked.length === 0)
11809
+ return true;
11810
+ for (const target of targets) {
11811
+ skipped.push({
11812
+ config_id: config.id,
11813
+ config_slug: config.slug,
11814
+ path: target.path,
11815
+ owner: "unresolved-secret-placeholder",
11816
+ reason: `no value for ${blocked.map((name) => `{{${name}}}`).join(", ")} and the file holds something else there; writing the placeholder would overwrite a live credential`
11817
+ });
11818
+ }
11819
+ return false;
11820
+ });
11821
+ const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(renderSafeConfigs, opts);
11766
11822
  for (const duplicate of duplicates) {
11767
11823
  for (const target of configTargets(duplicate, opts, activeConfigs)) {
11768
11824
  skipped.push({
@@ -11914,6 +11970,8 @@ var init_apply = __esm(() => {
11914
11970
  init_machine();
11915
11971
  init_session_render();
11916
11972
  init_session_render_ownership();
11973
+ init_redact();
11974
+ init_template();
11917
11975
  init_transforms();
11918
11976
  });
11919
11977
 
@@ -12304,14 +12362,6 @@ function redactionPlaceholders(storedLine) {
12304
12362
  }
12305
12363
  return found;
12306
12364
  }
12307
- function redactFormatForTarget(targetPath, storedFormat) {
12308
- const p = targetPath.toLowerCase();
12309
- if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
12310
- return "shell";
12311
- if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
12312
- return "ini";
12313
- return storedFormat;
12314
- }
12315
12365
  function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
12316
12366
  return redactionPlaceholders(storedLine).some((p) => !diskLine.includes(p));
12317
12367
  }
@@ -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
@@ -5494,6 +5494,14 @@ function reasonFor(key, value) {
5494
5494
  function isReferenceValue(value) {
5495
5495
  return /^\{\{[A-Z][A-Z0-9_]*\}\}$/.test(value) || /^\$\{[A-Z][A-Z0-9_]*\}$/.test(value) || /^\$[A-Z][A-Z0-9_]*$/.test(value) || /^%[A-Z][A-Z0-9_]*%$/.test(value);
5496
5496
  }
5497
+ function redactFormatForTarget(targetPath, storedFormat) {
5498
+ const p = targetPath.toLowerCase();
5499
+ if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
5500
+ return "shell";
5501
+ if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
5502
+ return "ini";
5503
+ return storedFormat;
5504
+ }
5497
5505
  function redactContent(content, format) {
5498
5506
  switch (format) {
5499
5507
  case "shell":
@@ -9591,8 +9599,9 @@ function normalizeTargetPath(p) {
9591
9599
  }
9592
9600
  }
9593
9601
  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: [] };
9602
+ const variables = opts.vars ?? {};
9603
+ const renderedTarget = renderForApply(targetPath, variables);
9604
+ const rendered = renderForApply(content, variables);
9596
9605
  const renderedTargetPath = renderedTarget.content;
9597
9606
  const renderedContent = rendered.content;
9598
9607
  const targetAgent = meta.agent ?? config.agent;
@@ -9631,13 +9640,42 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
9631
9640
  ...meta
9632
9641
  };
9633
9642
  }
9634
- function renderForApply(content, variables, preview) {
9635
- if (preview)
9636
- return renderMachineAwareContentPreview(content, variables);
9637
- return {
9638
- content: renderMachineAwareContent(content, variables),
9639
- unresolved: []
9640
- };
9643
+ function renderForApply(content, variables) {
9644
+ return renderMachineAwareContentPreview(content, variables);
9645
+ }
9646
+ function wouldDestroyACredential(targetPath, renderedContent, format) {
9647
+ const secretTokens = parseTemplateVars(renderedContent).filter(isSecretVarName);
9648
+ if (secretTokens.length === 0)
9649
+ return [];
9650
+ let current;
9651
+ try {
9652
+ const path = expandPath(targetPath);
9653
+ if (!existsSync6(path))
9654
+ return [];
9655
+ current = readFileSync4(path, "utf-8");
9656
+ } catch {
9657
+ return secretTokens;
9658
+ }
9659
+ if (current === renderedContent)
9660
+ return [];
9661
+ if (scanSecrets(current, redactFormatForTarget(targetPath, format)).length > 0)
9662
+ return secretTokens;
9663
+ return secretTokens.filter((name) => countPlaceholder(current, name) < countPlaceholder(renderedContent, name));
9664
+ }
9665
+ function countPlaceholder(content, name) {
9666
+ return content.match(new RegExp(`\\{\\{${name}(?::[^}]*)?\\}\\}`, "g"))?.length ?? 0;
9667
+ }
9668
+ async function resolveApplyVariables(opts) {
9669
+ const machine = detectMachineContext();
9670
+ let base;
9671
+ try {
9672
+ const store = opts.store ?? resolveConfigStore();
9673
+ const profile = await store.resolveProfileForMachine(machine);
9674
+ base = resolveProfileVariables(profile, machine);
9675
+ } catch {
9676
+ base = machineContextToVariables(machine);
9677
+ }
9678
+ return { ...base, ...opts.vars ?? {} };
9641
9679
  }
9642
9680
  function isAntigravityRuleTarget(agent, targetPath) {
9643
9681
  return agent === "antigravity" && /\.(md|mdc|markdown)$/i.test(targetPath);
@@ -9722,7 +9760,8 @@ async function applyConfigs(configs, opts = {}) {
9722
9760
  return report.results;
9723
9761
  }
9724
9762
  async function applyConfigsWithReport(configs, opts = {}) {
9725
- const prepared = prepareConfigBatch(configs, opts);
9763
+ const effective = { ...opts, vars: await resolveApplyVariables(opts) };
9764
+ const prepared = prepareConfigBatch(configs, effective);
9726
9765
  if (prepared.failures.length > 0) {
9727
9766
  return {
9728
9767
  results: [],
@@ -9736,7 +9775,7 @@ async function applyConfigsWithReport(configs, opts = {}) {
9736
9775
  if (config.kind === "reference" || isRetiredOrUnsupportedConfigAgent(config.agent))
9737
9776
  continue;
9738
9777
  try {
9739
- results.push(await applyPreparedConfig(config, opts));
9778
+ results.push(await applyPreparedConfig(config, effective));
9740
9779
  } catch (error) {
9741
9780
  failures.push({
9742
9781
  config_id: config.id,
@@ -9776,7 +9815,27 @@ function prepareConfigBatch(configs, opts) {
9776
9815
  }
9777
9816
  return false;
9778
9817
  });
9779
- const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(activeConfigs, opts);
9818
+ const renderSafeConfigs = activeConfigs.filter((config) => {
9819
+ const behavior = canonicalConfigApplyBehavior(config, opts, activeConfigs);
9820
+ const targets = [
9821
+ ...behavior.primary ? [{ path: behavior.primary.path, content: behavior.primary.content }] : [],
9822
+ ...behavior.outputs.map((output) => ({ path: output.path, content: output.content }))
9823
+ ];
9824
+ const blocked = [...new Set(targets.flatMap((target) => wouldDestroyACredential(target.path, target.content, config.format)))].sort();
9825
+ if (blocked.length === 0)
9826
+ return true;
9827
+ for (const target of targets) {
9828
+ skipped.push({
9829
+ config_id: config.id,
9830
+ config_slug: config.slug,
9831
+ path: target.path,
9832
+ owner: "unresolved-secret-placeholder",
9833
+ reason: `no value for ${blocked.map((name) => `{{${name}}}`).join(", ")} and the file holds something else there; writing the placeholder would overwrite a live credential`
9834
+ });
9835
+ }
9836
+ return false;
9837
+ });
9838
+ const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(renderSafeConfigs, opts);
9780
9839
  for (const duplicate of duplicates) {
9781
9840
  for (const target of configTargets(duplicate, opts, activeConfigs)) {
9782
9841
  skipped.push({
@@ -11698,14 +11757,6 @@ function redactionPlaceholders(storedLine) {
11698
11757
  }
11699
11758
  return found;
11700
11759
  }
11701
- function redactFormatForTarget(targetPath, storedFormat) {
11702
- const p = targetPath.toLowerCase();
11703
- if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
11704
- return "shell";
11705
- if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
11706
- return "ini";
11707
- return storedFormat;
11708
- }
11709
11760
  function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
11710
11761
  return redactionPlaceholders(storedLine).some((p) => !diskLine.includes(p));
11711
11762
  }
@@ -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":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=apply-text-format-credential-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-text-format-credential-guard.test.d.ts","sourceRoot":"","sources":["../../src/lib/apply-text-format-credential-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;AA+QD,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"}
@@ -21,6 +21,30 @@ export interface RedactedVar {
21
21
  reason: string;
22
22
  }
23
23
  export type RedactFormat = "shell" | "json" | "toml" | "ini" | "markdown" | "text" | "yaml";
24
+ /**
25
+ * Pick the redaction dialect for the file actually being read, from its PATH.
26
+ *
27
+ * `ConfigFormat` — what a stored row declares — is
28
+ * `text | json | toml | yaml | markdown | ini` and has NO `shell` member, while
29
+ * `RedactFormat` does. `detectFormat` returns `text` for any extensionless path,
30
+ * so `~/.zshrc`, `~/.bashrc` and `~/.npmrc` are all stored as `text`, and `text`
31
+ * routes to `redactGeneric` — which matches token SHAPES only and never key
32
+ * names. A credential whose key is secret-class but whose value has no
33
+ * recognisable shape is therefore invisible on exactly the files most likely to
34
+ * hold one.
35
+ *
36
+ * Resolving the dialect from the path closes that. It is deliberately
37
+ * PATH-KEYED rather than a widening of `ConfigFormat`: `.md` still resolves to
38
+ * `markdown`, so a rules file documenting `NPM_TOKEN=...` in prose is not
39
+ * mistaken for a shell assignment and frozen from ever shipping an edit.
40
+ *
41
+ * Exported so every caller that must choose a dialect shares ONE definition.
42
+ * Two surfaces need it and they must not drift: `diff`, the last read before a
43
+ * value reaches a transcript, and the `apply` guard, the last check before a
44
+ * value is overwritten. A per-surface copy is how one of them silently keeps the
45
+ * gap (todos e4d9c22e).
46
+ */
47
+ export declare function redactFormatForTarget(targetPath: string, storedFormat: RedactFormat): RedactFormat;
24
48
  export declare function redactContent(content: string, format: RedactFormat): RedactResult;
25
49
  /** Detect secrets without modifying content. Returns list of findings. */
26
50
  export declare function scanSecrets(content: string, format: RedactFormat): RedactedVar[];
@@ -1 +1 @@
1
- {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/lib/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAgND,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5F,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CAQjF;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,EAAE,CAGhF;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/lib/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAgND,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5F;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,YAAY,CAKlG;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CAQjF;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,EAAE,CAGhF;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/lib/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAW/E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAQ/C,CAAC;AAwDF,eAAO,MAAM,aAAa,EAAE,WAAW,EAwDtC,CAAC;AAIF,eAAO,MAAM,oBAAoB;;cAC2B,cAAc;WAAsB,WAAW;YAAwB,YAAY;GAe9I,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiE/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGhF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmClF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA8JD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+B9F;AAGD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAU/D;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAgBzD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAQ3D;AAGD,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/lib/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAW/E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAQ/C,CAAC;AAwDF,eAAO,MAAM,aAAa,EAAE,WAAW,EAwDtC,CAAC;AAIF,eAAO,MAAM,oBAAoB;;cAC2B,cAAc;WAAsB,WAAW;YAAwB,YAAY;GAe9I,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiE/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGhF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmClF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAkJD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+B9F;AAGD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAU/D;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAgBzD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAQ3D;AAGD,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
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
  }
@@ -5169,6 +5166,7 @@ var init_zod = __esm(() => {
5169
5166
  var exports_redact = {};
5170
5167
  __export(exports_redact, {
5171
5168
  scanSecrets: () => scanSecrets,
5169
+ redactFormatForTarget: () => redactFormatForTarget,
5172
5170
  redactContent: () => redactContent,
5173
5171
  isSecretVarName: () => isSecretVarName,
5174
5172
  hasSecrets: () => hasSecrets
@@ -5323,6 +5321,14 @@ function reasonFor(key, value) {
5323
5321
  function isReferenceValue(value) {
5324
5322
  return /^\{\{[A-Z][A-Z0-9_]*\}\}$/.test(value) || /^\$\{[A-Z][A-Z0-9_]*\}$/.test(value) || /^\$[A-Z][A-Z0-9_]*$/.test(value) || /^%[A-Z][A-Z0-9_]*%$/.test(value);
5325
5323
  }
5324
+ function redactFormatForTarget(targetPath, storedFormat) {
5325
+ const p = targetPath.toLowerCase();
5326
+ if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
5327
+ return "shell";
5328
+ if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
5329
+ return "ini";
5330
+ return storedFormat;
5331
+ }
5326
5332
  function redactContent(content, format) {
5327
5333
  switch (format) {
5328
5334
  case "shell":
@@ -5894,8 +5900,9 @@ function normalizeTargetPath(p) {
5894
5900
  }
5895
5901
  }
5896
5902
  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: [] };
5903
+ const variables = opts.vars ?? {};
5904
+ const renderedTarget = renderForApply(targetPath, variables);
5905
+ const rendered = renderForApply(content, variables);
5899
5906
  const renderedTargetPath = renderedTarget.content;
5900
5907
  const renderedContent = rendered.content;
5901
5908
  const targetAgent = meta.agent ?? config.agent;
@@ -5934,13 +5941,42 @@ async function writeConfigResult(config, targetPath, content, opts, meta = {}) {
5934
5941
  ...meta
5935
5942
  };
5936
5943
  }
5937
- function renderForApply(content, variables, preview) {
5938
- if (preview)
5939
- return renderMachineAwareContentPreview(content, variables);
5940
- return {
5941
- content: renderMachineAwareContent(content, variables),
5942
- unresolved: []
5943
- };
5944
+ function renderForApply(content, variables) {
5945
+ return renderMachineAwareContentPreview(content, variables);
5946
+ }
5947
+ function wouldDestroyACredential(targetPath, renderedContent, format) {
5948
+ const secretTokens = parseTemplateVars(renderedContent).filter(isSecretVarName);
5949
+ if (secretTokens.length === 0)
5950
+ return [];
5951
+ let current;
5952
+ try {
5953
+ const path = expandPath(targetPath);
5954
+ if (!existsSync4(path))
5955
+ return [];
5956
+ current = readFileSync2(path, "utf-8");
5957
+ } catch {
5958
+ return secretTokens;
5959
+ }
5960
+ if (current === renderedContent)
5961
+ return [];
5962
+ if (scanSecrets(current, redactFormatForTarget(targetPath, format)).length > 0)
5963
+ return secretTokens;
5964
+ return secretTokens.filter((name) => countPlaceholder(current, name) < countPlaceholder(renderedContent, name));
5965
+ }
5966
+ function countPlaceholder(content, name) {
5967
+ return content.match(new RegExp(`\\{\\{${name}(?::[^}]*)?\\}\\}`, "g"))?.length ?? 0;
5968
+ }
5969
+ async function resolveApplyVariables(opts) {
5970
+ const machine = detectMachineContext();
5971
+ let base;
5972
+ try {
5973
+ const store = opts.store ?? resolveConfigStore();
5974
+ const profile = await store.resolveProfileForMachine(machine);
5975
+ base = resolveProfileVariables(profile, machine);
5976
+ } catch {
5977
+ base = machineContextToVariables(machine);
5978
+ }
5979
+ return { ...base, ...opts.vars ?? {} };
5944
5980
  }
5945
5981
  function isAntigravityRuleTarget(agent, targetPath) {
5946
5982
  return agent === "antigravity" && /\.(md|mdc|markdown)$/i.test(targetPath);
@@ -6025,7 +6061,8 @@ async function applyConfigs(configs, opts = {}) {
6025
6061
  return report.results;
6026
6062
  }
6027
6063
  async function applyConfigsWithReport(configs, opts = {}) {
6028
- const prepared = prepareConfigBatch(configs, opts);
6064
+ const effective = { ...opts, vars: await resolveApplyVariables(opts) };
6065
+ const prepared = prepareConfigBatch(configs, effective);
6029
6066
  if (prepared.failures.length > 0) {
6030
6067
  return {
6031
6068
  results: [],
@@ -6039,7 +6076,7 @@ async function applyConfigsWithReport(configs, opts = {}) {
6039
6076
  if (config.kind === "reference" || isRetiredOrUnsupportedConfigAgent(config.agent))
6040
6077
  continue;
6041
6078
  try {
6042
- results.push(await applyPreparedConfig(config, opts));
6079
+ results.push(await applyPreparedConfig(config, effective));
6043
6080
  } catch (error) {
6044
6081
  failures.push({
6045
6082
  config_id: config.id,
@@ -6079,7 +6116,27 @@ function prepareConfigBatch(configs, opts) {
6079
6116
  }
6080
6117
  return false;
6081
6118
  });
6082
- const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(activeConfigs, opts);
6119
+ const renderSafeConfigs = activeConfigs.filter((config) => {
6120
+ const behavior = canonicalConfigApplyBehavior(config, opts, activeConfigs);
6121
+ const targets = [
6122
+ ...behavior.primary ? [{ path: behavior.primary.path, content: behavior.primary.content }] : [],
6123
+ ...behavior.outputs.map((output) => ({ path: output.path, content: output.content }))
6124
+ ];
6125
+ const blocked = [...new Set(targets.flatMap((target) => wouldDestroyACredential(target.path, target.content, config.format)))].sort();
6126
+ if (blocked.length === 0)
6127
+ return true;
6128
+ for (const target of targets) {
6129
+ skipped.push({
6130
+ config_id: config.id,
6131
+ config_slug: config.slug,
6132
+ path: target.path,
6133
+ owner: "unresolved-secret-placeholder",
6134
+ reason: `no value for ${blocked.map((name) => `{{${name}}}`).join(", ")} and the file holds something else there; writing the placeholder would overwrite a live credential`
6135
+ });
6136
+ }
6137
+ return false;
6138
+ });
6139
+ const { configs: deduplicated, duplicates } = deduplicateEquivalentProfileConfigs(renderSafeConfigs, opts);
6083
6140
  for (const duplicate of duplicates) {
6084
6141
  for (const target of configTargets(duplicate, opts, activeConfigs)) {
6085
6142
  skipped.push({
@@ -6231,6 +6288,8 @@ var init_apply = __esm(() => {
6231
6288
  init_machine();
6232
6289
  init_session_render();
6233
6290
  init_session_render_ownership();
6291
+ init_redact();
6292
+ init_template();
6234
6293
  init_transforms();
6235
6294
  });
6236
6295
 
@@ -6531,14 +6590,6 @@ function redactionPlaceholders(storedLine) {
6531
6590
  }
6532
6591
  return found;
6533
6592
  }
6534
- function redactFormatForTarget(targetPath, storedFormat) {
6535
- const p = targetPath.toLowerCase();
6536
- if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
6537
- return "shell";
6538
- if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
6539
- return "ini";
6540
- return storedFormat;
6541
- }
6542
6593
  function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
6543
6594
  return redactionPlaceholders(storedLine).some((p) => !diskLine.includes(p));
6544
6595
  }
@@ -6858,7 +6909,7 @@ var init_sync_dir = __esm(() => {
6858
6909
  var require_package = __commonJS((exports, module) => {
6859
6910
  module.exports = {
6860
6911
  name: "@hasna/instructions",
6861
- version: "0.4.14",
6912
+ version: "0.4.16",
6862
6913
  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
6914
  type: "module",
6864
6915
  main: "dist/index.js",
@@ -6897,7 +6948,7 @@ var require_package = __commonJS((exports, module) => {
6897
6948
  "dev:mcp": "bun run src/mcp/index.ts",
6898
6949
  "dev:serve": "bun run src/server/index.ts",
6899
6950
  seed: "bun run scripts/seed.ts",
6900
- prepublishOnly: "bun run build",
6951
+ prepublishOnly: "bun run scripts/check-publish-hold.ts && bun run build",
6901
6952
  postinstall: "mkdir -p $HOME/.hasna/instructions $HOME/.hasna/instructions/backups 2>/dev/null || true"
6902
6953
  },
6903
6954
  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.16",
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": [