guardrails-ref 1.0.9 → 1.1.1

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 CHANGED
@@ -4,7 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node: >=18](https://img.shields.io/badge/node-%3E%3D18-green.svg)](https://nodejs.org)
6
6
 
7
- CLI for [Agent Guardrails](https://github.com/9atar6/agent-guardrails) — init, add, remove, setup, validate, check, upgrade, and list GUARDRAIL.md files.
7
+ CLI for [Agent Guardrails](https://github.com/9atar6/agent-guardrails) — init, add, remove, setup, validate, check, upgrade, list, and why GUARDRAIL.md files.
8
8
 
9
9
  ## Why?
10
10
 
@@ -16,7 +16,7 @@ AI coding agents (Cursor, Claude Code, etc.) don't remember across sessions. Gua
16
16
  npx guardrails-ref init
17
17
  ```
18
18
 
19
- Creates `.agents/guardrails/`, adds the `no-plaintext-secrets` example, and configures Cursor and Claude Code to read your guardrails. No global install needed.
19
+ Creates `.agents/guardrails/`, adds the `no-plaintext-secrets` example, and configures Cursor, Claude Code, and VS Code Copilot to read your guardrails. No global install needed.
20
20
 
21
21
  > **Note:** IDEs don't yet recognize guardrails natively. The `setup` command adds a rule so the AI reads them. Once IDEs add support, this won't be needed.
22
22
 
@@ -24,19 +24,26 @@ Creates `.agents/guardrails/`, adds the `no-plaintext-secrets` example, and conf
24
24
 
25
25
  | Command | Description |
26
26
  |---------|-------------|
27
- | `npx guardrails-ref init [path]` | Create `.agents/guardrails/`, add no-plaintext-secrets, configure Cursor and Claude Code |
27
+ | `npx guardrails-ref init [path]` | Create `.agents/guardrails/`, add no-plaintext-secrets, configure Cursor, Claude Code, and VS Code Copilot |
28
+ | `npx guardrails-ref init --minimal [path]` | Create `.agents/guardrails/` only (no example, no setup) |
28
29
  | `npx guardrails-ref add <name> [name2 ...] [path]` | Add example guardrail(s) — pass multiple names to add several at once |
29
30
  | `npx guardrails-ref remove <name> [path]` | Remove a guardrail (name required) |
30
- | `npx guardrails-ref setup [path]` | Add the guardrail rule to Cursor rules and Claude instructions (use `--remove` to undo) |
31
+ | `npx guardrails-ref setup [path]` | Add the guardrail rule to Cursor, Claude Code, and VS Code Copilot |
32
+ | `npx guardrails-ref setup --remove [path]` | Remove the guardrail rule from IDE configs |
33
+ | `npx guardrails-ref setup --ide <name> [path]` | Target IDE: `cursor`, `claude`, `copilot`, or `auto` (only configured IDEs) |
34
+ | `npx guardrails-ref setup --dry-run [path]` | Show what would be added/removed without writing files |
35
+ | `npx guardrails-ref setup --check [path]` | Show which IDEs are configured and whether they have the rule |
31
36
  | `npx guardrails-ref validate [path]` | Validate GUARDRAIL.md files (use `--json` for JSON, `--strict` to fail on warnings) |
32
37
  | `npx guardrails-ref check [path]` | Validate with minimal output (CI-friendly, use `--strict` to fail on warnings) |
33
38
  | `npx guardrails-ref upgrade [path]` | Update installed guardrails to latest templates (use `--dry-run` to preview, `--diff` to show changes) |
34
39
  | `npx guardrails-ref list [path]` | List discovered guardrails (use `--json` for JSON output) |
40
+ | `npx guardrails-ref why <name>` | Show guardrail template content (e.g. `why no-destructive-commands`) |
35
41
 
36
42
  ## Supported IDEs
37
43
 
38
44
  - **Cursor** — via `.cursor/rules/` or `.cursorrules`
39
45
  - **Claude Code** — via `.claude/instructions.md`
46
+ - **VS Code Copilot** — via `.github/copilot-instructions.md`
40
47
 
41
48
  ## CI/CD
42
49
 
@@ -54,12 +61,15 @@ Or with full output or JSON:
54
61
  run: npx guardrails-ref validate . --json
55
62
  ```
56
63
 
64
+ **Note:** `list` exits with code 1 when no guardrails are found, so it can be used in scripts to check for guardrail presence.
65
+
57
66
  ## Examples
58
67
 
59
68
  ```bash
60
69
  npx guardrails-ref init
61
70
  npx guardrails-ref add no-destructive-commands no-hardcoded-urls
62
71
  npx guardrails-ref add no-new-deps-without-approval
72
+ npx guardrails-ref why no-destructive-commands
63
73
  npx guardrails-ref validate .
64
74
  npx guardrails-ref list .
65
75
  ```
@@ -69,6 +79,9 @@ npx guardrails-ref list .
69
79
  | Name | What it prevents |
70
80
  |------|------------------|
71
81
  | `no-plaintext-secrets` | Logging or committing credentials |
82
+ | `no-placeholder-credentials` | Fake or placeholder API keys instead of asking for real values |
83
+ | `no-silent-error-handling` | Catching errors without surfacing them to the user |
84
+ | `require-access-control` | Exposing sensitive data or admin actions without role checks |
72
85
  | `database-migrations` | Direct schema changes instead of migrations |
73
86
  | `no-destructive-commands` | rm -rf, DROP TABLE, TRUNCATE without approval |
74
87
  | `no-new-deps-without-approval` | New packages without approval |
@@ -77,15 +90,20 @@ npx guardrails-ref list .
77
90
  | `rate-limiting` | Runaway tool calls and API loops |
78
91
  | `no-console-in-production` | console.log in production code |
79
92
  | `require-tests` | Merging code without tests |
93
+ | `prefer-existing-code` | Reimplementing when existing code or helpers exist |
80
94
  | `no-inline-styles` | Inline `style=` in HTML/JSX |
81
95
  | `no-raw-sql` | Raw SQL without parameterization |
82
96
  | `no-magic-numbers` | Unexplained numeric literals |
97
+ | `no-modifying-git-history` | git push --force, destructive rebase without approval |
98
+ | `no-deprecated-apis` | Suggesting deprecated or obsolete APIs |
99
+ | `no-unsafe-env-assumptions` | Assuming env vars exist without validation |
100
+ | `no-hardcoded-user-facing-strings` | Hardcoded labels, messages, errors in UI |
83
101
 
84
- Use `npx guardrails-ref add --list` to see all available guardrails.
102
+ Use `npx guardrails-ref add --list` to see all available guardrails. Use `npx guardrails-ref why <name>` to show a guardrail's full content (from templates).
85
103
 
86
104
  ## Troubleshooting
87
105
 
88
- - **"Unknown guardrail"** — Run `npx guardrails-ref list .` to see available names
106
+ - **"Unknown guardrail"** — Run `npx guardrails-ref add --list` to see available guardrail names
89
107
  - **Setup not working** — Try `npx guardrails-ref setup --remove` then `npx guardrails-ref setup` again
90
108
 
91
109
  ## Links
package/dist/cli.js CHANGED
@@ -5,9 +5,10 @@ import { fileURLToPath } from "node:url";
5
5
  import { program } from "commander";
6
6
  import chalk from "chalk";
7
7
  import { validatePath, listGuardrails } from "./validate.js";
8
- import { runSetup, runSetupRemove } from "./setup.js";
8
+ import { runSetup, runSetupRemove, runSetupCheck } from "./setup.js";
9
9
  import { runInit } from "./init.js";
10
10
  import { runAdd } from "./add.js";
11
+ import { runWhy } from "./why.js";
11
12
  import { runRemove } from "./remove.js";
12
13
  import { runUpgrade } from "./upgrade.js";
13
14
  import { TEMPLATE_NAMES } from "./templates.js";
@@ -92,31 +93,33 @@ program
92
93
  .description("Validate GUARDRAIL.md files in a directory or a single file")
93
94
  .option("-j, --json", "Output as JSON")
94
95
  .option("-s, --strict", "Fail on warnings (CI mode)")
95
- .action((path = ".", cmd) => {
96
- const opts = cmd?.opts?.() ?? {};
97
- runValidate(path, opts);
96
+ .action(function (path) {
97
+ const opts = this.opts();
98
+ runValidate(path ?? ".", opts);
98
99
  });
99
100
  program
100
101
  .command("check [path]")
101
102
  .description("Validate guardrails with minimal output (CI-friendly alias)")
102
103
  .option("-s, --strict", "Fail on warnings")
103
- .action((path = ".", cmd) => {
104
- const opts = cmd?.opts?.() ?? {};
105
- runValidate(path, { ...opts, minimal: true });
104
+ .action(function (path) {
105
+ const opts = this.opts();
106
+ runValidate(path ?? ".", { ...opts, minimal: true });
106
107
  });
107
108
  program
108
109
  .command("init [path]")
109
110
  .description("Create .agents/guardrails/, add no-plaintext-secrets, and run setup (one command to get started)")
110
- .action((path = ".") => {
111
- runInit(path);
111
+ .option("-m, --minimal", "Create .agents/guardrails/ only, no example and no setup")
112
+ .action(function (path) {
113
+ const opts = this.opts();
114
+ runInit(path ?? ".", opts.minimal);
112
115
  });
113
116
  program
114
117
  .command("add [names...]")
115
118
  .description("Add example guardrail(s) by name — pass multiple to add several at once")
116
119
  .option("-l, --list", "List available guardrails to add")
117
120
  .option("-p, --path <path>", "Target directory", ".")
118
- .action((names = [], cmd) => {
119
- const opts = cmd?.opts?.() ?? {};
121
+ .action(function (names = []) {
122
+ const opts = this.opts();
120
123
  if (opts.list) {
121
124
  console.log("Available guardrails:");
122
125
  for (const n of TEMPLATE_NAMES) {
@@ -162,8 +165,8 @@ program
162
165
  .description("Update installed guardrails to latest template versions")
163
166
  .option("-n, --dry-run", "Show what would be updated without writing")
164
167
  .option("-d, --diff", "Show diff for each updated guardrail")
165
- .action((path, cmd) => {
166
- const opts = cmd?.opts?.() ?? {};
168
+ .action(function (path) {
169
+ const opts = this.opts();
167
170
  runUpgrade(path ?? ".", opts.dryRun, opts.diff);
168
171
  });
169
172
  program
@@ -175,27 +178,59 @@ program
175
178
  });
176
179
  program
177
180
  .command("setup [path]")
178
- .description("Add the guardrail one-liner to Cursor rules and Claude instructions (required until IDEs support guardrails natively)")
179
- .option("-r, --remove", "Remove the guardrail rule from Cursor and Claude config")
180
- .action((path, cmd) => {
181
+ .description("Add the guardrail rule to Cursor, Claude Code, and VS Code Copilot (required until IDEs support guardrails natively)")
182
+ .option("-r, --remove", "Remove the guardrail rule from IDE configs")
183
+ .option("-i, --ide <name>", "Target IDE: cursor, claude, copilot, or auto (only configured IDEs)")
184
+ .option("-n, --dry-run", "Show what would be added/removed without writing files")
185
+ .option("-c, --check", "Show which IDEs are configured and whether they have the rule")
186
+ .action(function (path) {
181
187
  const p = path ?? ".";
182
- const opts = cmd?.opts?.() ?? {};
183
- const result = opts.remove ? runSetupRemove(p) : runSetup(p);
188
+ const opts = this.opts();
189
+ if (opts.check) {
190
+ const check = runSetupCheck(p);
191
+ const fmt = (name, r) => {
192
+ const status = !r.configured ? "not configured" : r.hasRule ? "has rule" : "no rule";
193
+ const color = !r.configured ? chalk.gray : r.hasRule ? chalk.green : chalk.yellow;
194
+ console.log(` ${name.padEnd(12)} ${color(status)}`);
195
+ };
196
+ console.log("IDE setup status:");
197
+ fmt("Cursor", check.cursor);
198
+ fmt("Claude Code", check.claude);
199
+ fmt("VS Code Copilot", check.copilot);
200
+ return;
201
+ }
202
+ const ide = opts.ide;
203
+ if (ide && !["cursor", "claude", "copilot", "auto"].includes(ide)) {
204
+ console.error(chalk.red("Invalid --ide. Use: cursor, claude, copilot, or auto"));
205
+ process.exit(1);
206
+ }
207
+ const result = opts.remove
208
+ ? runSetupRemove(p, ide, opts.dryRun)
209
+ : runSetup(p, ide, opts.dryRun);
184
210
  console.log(result.message);
185
211
  });
212
+ program
213
+ .command("why <name>")
214
+ .description("Show guardrail content (e.g. npx guardrails-ref why no-destructive-commands)")
215
+ .action((name) => {
216
+ const ok = runWhy(name);
217
+ process.exit(ok ? 0 : 1);
218
+ });
186
219
  program
187
220
  .command("list [path]")
188
221
  .description("List discovered guardrails")
189
222
  .option("-j, --json", "Output as JSON")
190
- .action((path = ".", cmd) => {
191
- const opts = cmd?.opts?.() ?? {};
192
- const guardrails = listGuardrails(path);
223
+ .action(function (path) {
224
+ const opts = this.opts();
225
+ const guardrails = listGuardrails(path ?? ".");
193
226
  if (opts.json) {
194
227
  console.log(JSON.stringify({ guardrails, total: guardrails.length }, null, 2));
228
+ process.exit(guardrails.length === 0 ? 1 : 0);
195
229
  return;
196
230
  }
197
231
  if (guardrails.length === 0) {
198
232
  console.log(chalk.yellow("No guardrails found"));
233
+ process.exit(1);
199
234
  return;
200
235
  }
201
236
  for (const g of guardrails) {
package/dist/init.d.ts CHANGED
@@ -3,4 +3,4 @@ export interface InitResult {
3
3
  exampleCreated: boolean;
4
4
  setupDone: string;
5
5
  }
6
- export declare function runInit(projectPath?: string): InitResult;
6
+ export declare function runInit(projectPath?: string, minimal?: boolean): InitResult;
package/dist/init.js CHANGED
@@ -3,7 +3,7 @@ import { resolve } from "path";
3
3
  import chalk from "chalk";
4
4
  import { runSetup } from "./setup.js";
5
5
  import { TEMPLATES } from "./templates.js";
6
- export function runInit(projectPath = ".") {
6
+ export function runInit(projectPath = ".", minimal = false) {
7
7
  const root = resolve(projectPath);
8
8
  const guardrailsDir = resolve(root, ".agents", "guardrails");
9
9
  const exampleDir = resolve(guardrailsDir, "no-plaintext-secrets");
@@ -13,6 +13,13 @@ export function runInit(projectPath = ".") {
13
13
  mkdirSync(guardrailsDir, { recursive: true });
14
14
  console.log(chalk.green("✓") + " Created .agents/guardrails/");
15
15
  }
16
+ if (minimal) {
17
+ return {
18
+ guardrailsDir,
19
+ exampleCreated: false,
20
+ setupDone: "",
21
+ };
22
+ }
16
23
  if (!existsSync(exampleFile)) {
17
24
  mkdirSync(exampleDir, { recursive: true });
18
25
  writeFileSync(exampleFile, TEMPLATES["no-plaintext-secrets"]);
package/dist/parse.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import matter from "gray-matter";
2
2
  import { readFileSync } from "fs";
3
- import { resolve } from "path";
3
+ import { resolve, dirname, basename } from "path";
4
4
  const NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
5
5
  const MAX_NAME_LENGTH = 64;
6
6
  const MAX_DESCRIPTION_LENGTH = 1024;
@@ -75,13 +75,12 @@ export function parseGuardrailFile(filePath) {
75
75
  warnings.push("triggers must be a list of strings");
76
76
  }
77
77
  }
78
- // Check directory name matches (only when file is inside a directory)
79
- const pathParts = filePath.split(/[/\\]/);
80
- if (pathParts.length > 1) {
81
- const dirName = pathParts.slice(-2)[0];
82
- if (dirName && dirName !== "." && typeof name === "string" && name !== dirName) {
83
- warnings.push(`name "${name}" does not match parent directory "${dirName}"`);
84
- }
78
+ // Check directory name matches (only when file is GUARDRAIL.md inside a directory, not GUARDRAILS.md at root)
79
+ const parentDir = dirname(filePath);
80
+ const dirName = basename(parentDir);
81
+ const fileName = basename(filePath);
82
+ if (fileName === "GUARDRAIL.md" && dirName && dirName !== "." && typeof name === "string" && name !== dirName) {
83
+ warnings.push(`name "${name}" does not match parent directory "${dirName}"`);
85
84
  }
86
85
  if (errors.length > 0) {
87
86
  return {
package/dist/setup.d.ts CHANGED
@@ -1,7 +1,27 @@
1
+ export type IdeName = "cursor" | "claude" | "copilot";
1
2
  export interface SetupResult {
2
3
  cursor: boolean;
3
4
  claude: boolean;
5
+ copilot: boolean;
4
6
  message: string;
5
7
  }
6
- export declare function runSetup(projectPath?: string): SetupResult;
7
- export declare function runSetupRemove(projectPath?: string): SetupResult;
8
+ export interface SetupCheckResult {
9
+ cursor: {
10
+ configured: boolean;
11
+ hasRule: boolean;
12
+ };
13
+ claude: {
14
+ configured: boolean;
15
+ hasRule: boolean;
16
+ };
17
+ copilot: {
18
+ configured: boolean;
19
+ hasRule: boolean;
20
+ };
21
+ }
22
+ /**
23
+ * Check which IDEs are configured and whether they have the guardrail rule.
24
+ */
25
+ export declare function runSetupCheck(projectPath?: string): SetupCheckResult;
26
+ export declare function runSetup(projectPath?: string, ideFilter?: IdeName | "all" | "auto", dryRun?: boolean): SetupResult;
27
+ export declare function runSetupRemove(projectPath?: string, ideFilter?: IdeName | "all" | "auto", dryRun?: boolean): SetupResult;
package/dist/setup.js CHANGED
@@ -3,6 +3,10 @@ import { resolve } from "path";
3
3
  import chalk from "chalk";
4
4
  const GUARDRAIL_RULE = "You MUST read and follow all constraints in .agents/guardrails/. Never violate a guardrail without explicit human approval.";
5
5
  const GUARDRAIL_RULE_MARKER = "You MUST read and follow all constraints in .agents/guardrails";
6
+ /**
7
+ * Removes the guardrail rule from file content.
8
+ * When the resulting content is empty, the caller may delete the file (e.g. .cursorrules, .claude/instructions.md).
9
+ */
6
10
  function removeRuleFromContent(content) {
7
11
  const lines = content.split("\n").filter((line) => !line.includes(GUARDRAIL_RULE_MARKER));
8
12
  return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
@@ -10,11 +14,7 @@ function removeRuleFromContent(content) {
10
14
  function hasRule(content) {
11
15
  return content.includes(GUARDRAIL_RULE_MARKER);
12
16
  }
13
- export function runSetup(projectPath = ".") {
14
- const root = resolve(projectPath);
15
- let cursorDone = false;
16
- let claudeDone = false;
17
- // Cursor: .cursor/rules/agent-guardrails.md (preferred) or .cursorrules (legacy)
17
+ function setupCursor(root) {
18
18
  const cursorRulesDir = resolve(root, ".cursor", "rules");
19
19
  const cursorRuleFile = resolve(cursorRulesDir, "agent-guardrails.md");
20
20
  const cursorRuleContent = `---
@@ -29,27 +29,27 @@ ${GUARDRAIL_RULE}
29
29
  const existing = readFileSync(cursorRuleFile, "utf-8");
30
30
  if (!hasRule(existing)) {
31
31
  writeFileSync(cursorRuleFile, cursorRuleContent);
32
- cursorDone = true;
32
+ return true;
33
33
  }
34
34
  }
35
35
  else if (existsSync(cursorrulesPath)) {
36
- // Legacy: append to .cursorrules
37
36
  const existing = readFileSync(cursorrulesPath, "utf-8");
38
37
  if (!hasRule(existing)) {
39
38
  const appended = existing.trimEnd() + "\n\n" + GUARDRAIL_RULE + "\n";
40
39
  writeFileSync(cursorrulesPath, appended);
41
- cursorDone = true;
40
+ return true;
42
41
  }
43
42
  }
44
43
  else {
45
- // Create .cursor/rules/agent-guardrails.md
46
44
  if (!existsSync(cursorRulesDir)) {
47
45
  mkdirSync(cursorRulesDir, { recursive: true });
48
46
  }
49
47
  writeFileSync(cursorRuleFile, cursorRuleContent);
50
- cursorDone = true;
48
+ return true;
51
49
  }
52
- // Claude Code: .claude/instructions.md
50
+ return false;
51
+ }
52
+ function setupClaude(root) {
53
53
  const claudeDir = resolve(root, ".claude");
54
54
  const claudeInstructions = resolve(claudeDir, "instructions.md");
55
55
  if (existsSync(claudeInstructions)) {
@@ -57,7 +57,7 @@ ${GUARDRAIL_RULE}
57
57
  if (!hasRule(existing)) {
58
58
  const appended = existing.trimEnd() + "\n\n" + GUARDRAIL_RULE + "\n";
59
59
  writeFileSync(claudeInstructions, appended);
60
- claudeDone = true;
60
+ return true;
61
61
  }
62
62
  }
63
63
  else {
@@ -65,35 +65,39 @@ ${GUARDRAIL_RULE}
65
65
  mkdirSync(claudeDir, { recursive: true });
66
66
  }
67
67
  writeFileSync(claudeInstructions, GUARDRAIL_RULE + "\n");
68
- claudeDone = true;
69
- }
70
- const messages = [];
71
- if (cursorDone) {
72
- messages.push(chalk.green("✓") + " Added guardrail rule for Cursor");
68
+ return true;
73
69
  }
74
- if (claudeDone) {
75
- messages.push(chalk.green("✓") + " Added guardrail rule for Claude Code");
70
+ return false;
71
+ }
72
+ function setupCopilot(root) {
73
+ const githubDir = resolve(root, ".github");
74
+ const copilotFile = resolve(githubDir, "copilot-instructions.md");
75
+ const ruleBlock = `\n\n${GUARDRAIL_RULE}\n`;
76
+ if (existsSync(copilotFile)) {
77
+ const existing = readFileSync(copilotFile, "utf-8");
78
+ if (!hasRule(existing)) {
79
+ const appended = existing.trimEnd() + ruleBlock;
80
+ writeFileSync(copilotFile, appended);
81
+ return true;
82
+ }
76
83
  }
77
- if (messages.length === 0) {
78
- messages.push(chalk.yellow("Guardrail rule already present in all config files."));
84
+ else {
85
+ if (!existsSync(githubDir)) {
86
+ mkdirSync(githubDir, { recursive: true });
87
+ }
88
+ writeFileSync(copilotFile, GUARDRAIL_RULE + "\n");
89
+ return true;
79
90
  }
80
- return {
81
- cursor: cursorDone,
82
- claude: claudeDone,
83
- message: messages.join("\n"),
84
- };
91
+ return false;
85
92
  }
86
- export function runSetupRemove(projectPath = ".") {
87
- const root = resolve(projectPath);
88
- let cursorDone = false;
89
- let claudeDone = false;
93
+ function removeCursor(root) {
90
94
  const cursorRulesDir = resolve(root, ".cursor", "rules");
91
95
  const cursorRuleFile = resolve(cursorRulesDir, "agent-guardrails.md");
92
96
  const cursorrulesPath = resolve(root, ".cursorrules");
93
- const claudeInstructions = resolve(root, ".claude", "instructions.md");
97
+ let done = false;
94
98
  if (existsSync(cursorRuleFile)) {
95
99
  rmSync(cursorRuleFile);
96
- cursorDone = true;
100
+ done = true;
97
101
  }
98
102
  if (existsSync(cursorrulesPath)) {
99
103
  const existing = readFileSync(cursorrulesPath, "utf-8");
@@ -105,9 +109,13 @@ export function runSetupRemove(projectPath = ".") {
105
109
  else {
106
110
  rmSync(cursorrulesPath);
107
111
  }
108
- cursorDone = true;
112
+ done = true;
109
113
  }
110
114
  }
115
+ return done;
116
+ }
117
+ function removeClaude(root) {
118
+ const claudeInstructions = resolve(root, ".claude", "instructions.md");
111
119
  if (existsSync(claudeInstructions)) {
112
120
  const existing = readFileSync(claudeInstructions, "utf-8");
113
121
  if (hasRule(existing)) {
@@ -118,22 +126,219 @@ export function runSetupRemove(projectPath = ".") {
118
126
  else {
119
127
  rmSync(claudeInstructions);
120
128
  }
121
- claudeDone = true;
129
+ return true;
130
+ }
131
+ }
132
+ return false;
133
+ }
134
+ function removeCopilot(root) {
135
+ const copilotFile = resolve(root, ".github", "copilot-instructions.md");
136
+ if (existsSync(copilotFile)) {
137
+ const existing = readFileSync(copilotFile, "utf-8");
138
+ if (hasRule(existing)) {
139
+ const cleaned = removeRuleFromContent(existing);
140
+ if (cleaned) {
141
+ writeFileSync(copilotFile, cleaned + "\n");
142
+ }
143
+ else {
144
+ rmSync(copilotFile);
145
+ }
146
+ return true;
147
+ }
148
+ }
149
+ return false;
150
+ }
151
+ function checkCursor(root) {
152
+ const cursorRuleFile = resolve(root, ".cursor", "rules", "agent-guardrails.md");
153
+ const cursorrulesPath = resolve(root, ".cursorrules");
154
+ if (existsSync(cursorRuleFile)) {
155
+ const content = readFileSync(cursorRuleFile, "utf-8");
156
+ return { configured: true, hasRule: hasRule(content) };
157
+ }
158
+ if (existsSync(cursorrulesPath)) {
159
+ const content = readFileSync(cursorrulesPath, "utf-8");
160
+ return { configured: true, hasRule: hasRule(content) };
161
+ }
162
+ return { configured: false, hasRule: false };
163
+ }
164
+ function checkClaude(root) {
165
+ const claudeInstructions = resolve(root, ".claude", "instructions.md");
166
+ if (existsSync(claudeInstructions)) {
167
+ const content = readFileSync(claudeInstructions, "utf-8");
168
+ return { configured: true, hasRule: hasRule(content) };
169
+ }
170
+ return { configured: false, hasRule: false };
171
+ }
172
+ function checkCopilot(root) {
173
+ const copilotFile = resolve(root, ".github", "copilot-instructions.md");
174
+ if (existsSync(copilotFile)) {
175
+ const content = readFileSync(copilotFile, "utf-8");
176
+ return { configured: true, hasRule: hasRule(content) };
177
+ }
178
+ return { configured: false, hasRule: false };
179
+ }
180
+ /**
181
+ * Check which IDEs are configured and whether they have the guardrail rule.
182
+ */
183
+ export function runSetupCheck(projectPath = ".") {
184
+ const root = resolve(projectPath);
185
+ return {
186
+ cursor: checkCursor(root),
187
+ claude: checkClaude(root),
188
+ copilot: checkCopilot(root),
189
+ };
190
+ }
191
+ function wouldSetupCursor(root) {
192
+ const cursorRuleFile = resolve(root, ".cursor", "rules", "agent-guardrails.md");
193
+ const cursorrulesPath = resolve(root, ".cursorrules");
194
+ if (existsSync(cursorRuleFile))
195
+ return !hasRule(readFileSync(cursorRuleFile, "utf-8"));
196
+ if (existsSync(cursorrulesPath))
197
+ return !hasRule(readFileSync(cursorrulesPath, "utf-8"));
198
+ return true;
199
+ }
200
+ function wouldSetupClaude(root) {
201
+ const claudeInstructions = resolve(root, ".claude", "instructions.md");
202
+ if (existsSync(claudeInstructions))
203
+ return !hasRule(readFileSync(claudeInstructions, "utf-8"));
204
+ return true;
205
+ }
206
+ function wouldSetupCopilot(root) {
207
+ const copilotFile = resolve(root, ".github", "copilot-instructions.md");
208
+ if (existsSync(copilotFile))
209
+ return !hasRule(readFileSync(copilotFile, "utf-8"));
210
+ return true;
211
+ }
212
+ function wouldRemoveCursor(root) {
213
+ const cursorRuleFile = resolve(root, ".cursor", "rules", "agent-guardrails.md");
214
+ const cursorrulesPath = resolve(root, ".cursorrules");
215
+ if (existsSync(cursorRuleFile))
216
+ return true;
217
+ if (existsSync(cursorrulesPath))
218
+ return hasRule(readFileSync(cursorrulesPath, "utf-8"));
219
+ return false;
220
+ }
221
+ function wouldRemoveClaude(root) {
222
+ const claudeInstructions = resolve(root, ".claude", "instructions.md");
223
+ return existsSync(claudeInstructions) && hasRule(readFileSync(claudeInstructions, "utf-8"));
224
+ }
225
+ function wouldRemoveCopilot(root) {
226
+ const copilotFile = resolve(root, ".github", "copilot-instructions.md");
227
+ return existsSync(copilotFile) && hasRule(readFileSync(copilotFile, "utf-8"));
228
+ }
229
+ export function runSetup(projectPath = ".", ideFilter, dryRun = false) {
230
+ const root = resolve(projectPath);
231
+ let ides;
232
+ if (ideFilter === "auto") {
233
+ const check = runSetupCheck(root);
234
+ ides = ["cursor", "claude", "copilot"].filter((ide) => check[ide].configured);
235
+ }
236
+ else if (ideFilter && ideFilter !== "all") {
237
+ ides = [ideFilter];
238
+ }
239
+ else {
240
+ ides = ["cursor", "claude", "copilot"];
241
+ }
242
+ let cursorDone = false;
243
+ let claudeDone = false;
244
+ let copilotDone = false;
245
+ if (dryRun) {
246
+ if (ides.includes("cursor"))
247
+ cursorDone = wouldSetupCursor(root);
248
+ if (ides.includes("claude"))
249
+ claudeDone = wouldSetupClaude(root);
250
+ if (ides.includes("copilot"))
251
+ copilotDone = wouldSetupCopilot(root);
252
+ const messages = [];
253
+ if (cursorDone)
254
+ messages.push(chalk.gray("[dry-run] Would add guardrail rule for Cursor"));
255
+ if (claudeDone)
256
+ messages.push(chalk.gray("[dry-run] Would add guardrail rule for Claude Code"));
257
+ if (copilotDone)
258
+ messages.push(chalk.gray("[dry-run] Would add guardrail rule for VS Code Copilot"));
259
+ if (messages.length === 0) {
260
+ messages.push(chalk.yellow("Guardrail rule already present in all target IDEs. Nothing to do."));
122
261
  }
262
+ return { cursor: cursorDone, claude: claudeDone, copilot: copilotDone, message: messages.join("\n") };
123
263
  }
264
+ if (ides.includes("cursor"))
265
+ cursorDone = setupCursor(root);
266
+ if (ides.includes("claude"))
267
+ claudeDone = setupClaude(root);
268
+ if (ides.includes("copilot"))
269
+ copilotDone = setupCopilot(root);
124
270
  const messages = [];
125
- if (cursorDone) {
126
- messages.push(chalk.green("✓") + " Removed guardrail rule from Cursor");
271
+ if (cursorDone)
272
+ messages.push(chalk.green("✓") + " Added guardrail rule for Cursor");
273
+ if (claudeDone)
274
+ messages.push(chalk.green("✓") + " Added guardrail rule for Claude Code");
275
+ if (copilotDone)
276
+ messages.push(chalk.green("✓") + " Added guardrail rule for VS Code Copilot");
277
+ if (messages.length === 0) {
278
+ messages.push(chalk.yellow("Guardrail rule already present in all configured IDEs."));
127
279
  }
128
- if (claudeDone) {
129
- messages.push(chalk.green("✓") + " Removed guardrail rule from Claude Code");
280
+ return {
281
+ cursor: cursorDone,
282
+ claude: claudeDone,
283
+ copilot: copilotDone,
284
+ message: messages.join("\n"),
285
+ };
286
+ }
287
+ export function runSetupRemove(projectPath = ".", ideFilter, dryRun = false) {
288
+ const root = resolve(projectPath);
289
+ let ides;
290
+ if (ideFilter === "auto") {
291
+ const check = runSetupCheck(root);
292
+ ides = ["cursor", "claude", "copilot"].filter((ide) => check[ide].hasRule);
293
+ }
294
+ else if (ideFilter && ideFilter !== "all") {
295
+ ides = [ideFilter];
296
+ }
297
+ else {
298
+ ides = ["cursor", "claude", "copilot"];
130
299
  }
300
+ let cursorDone = false;
301
+ let claudeDone = false;
302
+ let copilotDone = false;
303
+ if (dryRun) {
304
+ if (ides.includes("cursor"))
305
+ cursorDone = wouldRemoveCursor(root);
306
+ if (ides.includes("claude"))
307
+ claudeDone = wouldRemoveClaude(root);
308
+ if (ides.includes("copilot"))
309
+ copilotDone = wouldRemoveCopilot(root);
310
+ const messages = [];
311
+ if (cursorDone)
312
+ messages.push(chalk.gray("[dry-run] Would remove guardrail rule from Cursor"));
313
+ if (claudeDone)
314
+ messages.push(chalk.gray("[dry-run] Would remove guardrail rule from Claude Code"));
315
+ if (copilotDone)
316
+ messages.push(chalk.gray("[dry-run] Would remove guardrail rule from VS Code Copilot"));
317
+ if (messages.length === 0) {
318
+ messages.push(chalk.yellow("Guardrail rule not found in any config files. Nothing to do."));
319
+ }
320
+ return { cursor: cursorDone, claude: claudeDone, copilot: copilotDone, message: messages.join("\n") };
321
+ }
322
+ if (ides.includes("cursor"))
323
+ cursorDone = removeCursor(root);
324
+ if (ides.includes("claude"))
325
+ claudeDone = removeClaude(root);
326
+ if (ides.includes("copilot"))
327
+ copilotDone = removeCopilot(root);
328
+ const messages = [];
329
+ if (cursorDone)
330
+ messages.push(chalk.green("✓") + " Removed guardrail rule from Cursor");
331
+ if (claudeDone)
332
+ messages.push(chalk.green("✓") + " Removed guardrail rule from Claude Code");
333
+ if (copilotDone)
334
+ messages.push(chalk.green("✓") + " Removed guardrail rule from VS Code Copilot");
131
335
  if (messages.length === 0) {
132
336
  messages.push(chalk.yellow("Guardrail rule not found in any config files."));
133
337
  }
134
338
  return {
135
339
  cursor: cursorDone,
136
340
  claude: claudeDone,
341
+ copilot: copilotDone,
137
342
  message: messages.join("\n"),
138
343
  };
139
344
  }
package/dist/why.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runWhy(name: string): boolean;
package/dist/why.js ADDED
@@ -0,0 +1,13 @@
1
+ import chalk from "chalk";
2
+ import { TEMPLATES, TEMPLATE_NAMES } from "./templates.js";
3
+ export function runWhy(name) {
4
+ const normalized = name.toLowerCase().replace(/\s+/g, "-");
5
+ const content = TEMPLATES[normalized];
6
+ if (!content) {
7
+ console.error(chalk.red("Unknown guardrail:") + " " + name);
8
+ console.error(chalk.gray("Available: " + TEMPLATE_NAMES.join(", ")));
9
+ return false;
10
+ }
11
+ console.log(content);
12
+ return true;
13
+ }
@@ -1,10 +1,13 @@
1
1
  # Example Guardrails
2
2
 
3
- Reference guardrails you can add with `npx guardrails-ref add <name>`.
3
+ Reference guardrails you can add with `npx guardrails-ref add <name>`. Use `npx guardrails-ref why <name>` to show a guardrail's full content.
4
4
 
5
5
  | Name | What it prevents |
6
6
  |------|------------------|
7
7
  | `no-plaintext-secrets` | Logging or committing API keys, passwords, tokens |
8
+ | `no-placeholder-credentials` | Fake or placeholder API keys instead of asking for real values |
9
+ | `no-silent-error-handling` | Catching errors without surfacing them to the user |
10
+ | `require-access-control` | Exposing sensitive data or admin actions without role checks |
8
11
  | `database-migrations` | Direct schema changes instead of migrations |
9
12
  | `no-destructive-commands` | `rm -rf`, `DROP TABLE`, `TRUNCATE` without approval |
10
13
  | `no-new-deps-without-approval` | New packages without human confirmation |
@@ -13,8 +16,13 @@ Reference guardrails you can add with `npx guardrails-ref add <name>`.
13
16
  | `rate-limiting` | Runaway tool calls and API loops |
14
17
  | `no-console-in-production` | `console.log` in production code |
15
18
  | `require-tests` | Merging code without tests |
19
+ | `prefer-existing-code` | Reimplementing when existing code or helpers exist |
16
20
  | `no-inline-styles` | Inline `style=` in HTML/JSX |
17
21
  | `no-raw-sql` | Raw SQL without parameterization |
18
22
  | `no-magic-numbers` | Unexplained numeric literals |
23
+ | `no-modifying-git-history` | `git push --force`, destructive rebase without approval |
24
+ | `no-deprecated-apis` | Suggesting deprecated or obsolete APIs |
25
+ | `no-unsafe-env-assumptions` | Assuming env vars exist without validation |
26
+ | `no-hardcoded-user-facing-strings` | Hardcoded labels, messages, errors in UI |
19
27
 
20
28
  Each example lives in its own directory with a `GUARDRAIL.md` file. See `pre-commit/README.md` for pre-commit, Husky, or npm script setup. See the [specification](../spec/specification.md) for the format.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: no-deprecated-apis
3
+ description: Never suggest deprecated or obsolete APIs. Check package docs and current best practices before recommending patterns. Apply when adding dependencies, using libraries, or implementing features.
4
+ scope: project
5
+ severity: warning
6
+ triggers:
7
+ - "Adding dependencies"
8
+ - "Using library"
9
+ - "API usage"
10
+ - "Implementing feature"
11
+ - "Package documentation"
12
+ - "Deprecated"
13
+ license: MIT
14
+ metadata:
15
+ author: agent-guardrails
16
+ version: "1.0"
17
+ ---
18
+
19
+ # No Deprecated APIs
20
+
21
+ ## Trigger
22
+ Adding dependencies, using libraries, implementing features, or recommending APIs or patterns.
23
+
24
+ ## Instruction
25
+ - Never suggest deprecated or obsolete APIs; check the package's current documentation first
26
+ - When a pattern might be deprecated: verify against the latest docs before recommending
27
+ - Prefer current, maintained approaches over legacy patterns (e.g. fetch over XMLHttpRequest, modern Node APIs over legacy callbacks)
28
+ - If the project uses a specific version: ensure recommendations match that version's API
29
+ - When unsure about deprecation status: note the uncertainty and suggest verifying in docs
30
+
31
+ ## Reason
32
+ Agents often suggest outdated patterns from training data. Deprecated APIs may be removed in future versions and can cause technical debt. Current docs are the source of truth.
33
+
34
+ ## Provenance
35
+ Manual addition, agent-guardrails reference.
@@ -30,3 +30,6 @@ Adding API calls, integrating external services, or referencing URLs (base URLs,
30
30
 
31
31
  ## Reason
32
32
  Hardcoded URLs break across environments (dev/staging/prod), leak internal endpoints, and block easy configuration. Environment-based config is standard practice.
33
+
34
+ ## Provenance
35
+ Manual addition, agent-guardrails reference.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: no-hardcoded-user-facing-strings
3
+ description: Never hardcode user-facing text (labels, messages, errors) in components or views. Use i18n keys, translation files, or shared constants. Apply when adding UI text, error messages, or form labels.
4
+ scope: project
5
+ severity: warning
6
+ triggers:
7
+ - "Adding UI text"
8
+ - "Error messages"
9
+ - "Form labels"
10
+ - "User-facing strings"
11
+ - "Button text"
12
+ - "Placeholders"
13
+ license: MIT
14
+ metadata:
15
+ author: agent-guardrails
16
+ version: "1.0"
17
+ ---
18
+
19
+ # No Hardcoded User-Facing Strings
20
+
21
+ ## Trigger
22
+ Adding user-facing text: labels, buttons, error messages, placeholders, tooltips, or any string displayed to the user in the UI.
23
+
24
+ ## Instruction
25
+ - Never hardcode user-facing strings directly in components or views
26
+ - Use i18n/translation keys (e.g. t('errors.network'), i18n.t('submit')) when the project has i18n
27
+ - If no i18n: use shared constants or a strings module (e.g. STRINGS.ERROR_NETWORK) for reuse and consistency
28
+ - For error messages: centralize in a shared location so they can be updated and translated
29
+ - Exception: very small projects or prototypes may use inline strings; prefer externalizing as the project grows
30
+
31
+ ## Reason
32
+ Hardcoded strings scatter copy across the codebase, block localization, and make consistent messaging harder. Centralized strings enable i18n and easier content updates.
33
+
34
+ ## Provenance
35
+ Manual addition, agent-guardrails reference.
@@ -24,7 +24,9 @@ Adding styles to HTML, JSX, or component markup (e.g. React, Vue, Svelte).
24
24
  - Never add inline `style={{ ... }}` or `style="..."` attributes
25
25
  - Use CSS classes, CSS modules, Tailwind, or design system tokens instead
26
26
  - For dynamic values: use CSS variables or data attributes with stylesheets
27
- - Inline styles bypass design systems, hurt maintainability, and complicate theming
28
27
 
29
28
  ## Reason
30
- Inline styles scatter styling logic, prevent reuse, and make design consistency harder. Centralized styles (CSS, design tokens) enable theming and maintainability.
29
+ Inline styles scatter styling logic, prevent reuse, and make design consistency harder. They bypass design systems, hurt maintainability, and complicate theming. Centralized styles (CSS, design tokens) enable theming and maintainability.
30
+
31
+ ## Provenance
32
+ Manual addition, agent-guardrails reference.
@@ -29,3 +29,6 @@ Adding numeric literals for timeouts, limits, buffer sizes, retry counts, thresh
29
29
 
30
30
  ## Reason
31
31
  Magic numbers make code hard to understand and change. Named constants document intent and centralize configuration.
32
+
33
+ ## Provenance
34
+ Manual addition, agent-guardrails reference.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: no-modifying-git-history
3
+ description: Never run git push --force, destructive rebase, or history-rewriting commands without explicit human approval. Apply when suggesting git commands that modify shared history.
4
+ scope: project
5
+ severity: critical
6
+ triggers:
7
+ - "git push"
8
+ - "git rebase"
9
+ - "Force push"
10
+ - "Rewriting history"
11
+ - "git reset"
12
+ - "Amending commits"
13
+ license: MIT
14
+ metadata:
15
+ author: agent-guardrails
16
+ version: "1.0"
17
+ ---
18
+
19
+ # No Modifying Git History
20
+
21
+ ## Trigger
22
+ Suggesting or running git commands that modify history: force push, rebase that rewrites commits, reset, or amending pushed commits.
23
+
24
+ ## Instruction
25
+ - Never run `git push --force`, `git push -f`, or `git push --force-with-lease` without explicit human approval
26
+ - Never run `git rebase` on branches that may have been pushed or shared without approval
27
+ - Never run `git reset --hard` to undo commits that others may have pulled
28
+ - When the user asks to "fix" history or "undo" pushed commits: explain the impact and wait for confirmation
29
+ - For shared branches (main, develop): always warn that force push can overwrite others' work
30
+ - Prefer `git revert` over history-rewriting when undoing commits on shared branches
31
+
32
+ ## Reason
33
+ Agents have suggested force push and rebase that overwrote shared history, causing lost work and broken clones. History-rewriting on shared branches requires explicit human confirmation.
34
+
35
+ ## Provenance
36
+ Manual addition, common failure mode in autonomous coding agents.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: no-placeholder-credentials
3
+ description: Never use fake or placeholder API keys, env vars, or credentials. Ask for real values instead of inventing them. Apply when configuring APIs, integrations, or environment variables.
4
+ scope: project
5
+ severity: critical
6
+ triggers:
7
+ - "API integration"
8
+ - "Environment variables"
9
+ - "API key"
10
+ - "Configuration"
11
+ - "Credentials"
12
+ - "Placeholder"
13
+ - ".env"
14
+ license: MIT
15
+ metadata:
16
+ author: agent-guardrails
17
+ version: "1.0"
18
+ ---
19
+
20
+ # No Placeholder Credentials
21
+
22
+ ## Trigger
23
+ Configuring API integrations, environment variables, credentials, or any code that requires API keys, tokens, or secrets.
24
+
25
+ ## Instruction
26
+ - Never use fake, placeholder, or invented credentials (e.g. `sk_test_placeholder`, `your-api-key-here`, `xxx`)
27
+ - Never assume env vars exist without validation; ask the user to provide or verify values
28
+ - When a real value is required: stop and ask the user to provide it, or add it to .env
29
+ - Document required env vars in .env.example with placeholders for format only (e.g. `STRIPE_KEY=sk_test_...`), not for actual use
30
+ - For local development: use real test keys when available (e.g. Stripe test mode, sandbox APIs), or explicitly ask the user
31
+
32
+ ## Reason
33
+ Agents hallucinate placeholder values that appear to work but fail in production. Users assume the agent configured correctly when it did not. Always ask for real values when they are required.
34
+
35
+ ## Provenance
36
+ Manual addition, common failure mode in autonomous coding agents (DAPLab).
@@ -30,3 +30,6 @@ Writing database queries, SQL statements, or any code that executes SQL against
30
30
 
31
31
  ## Reason
32
32
  Raw SQL with string concatenation leads to SQL injection. Parameterized queries prevent injection and are required for security.
33
+
34
+ ## Provenance
35
+ Manual addition, SQL injection prevention.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: no-silent-error-handling
3
+ description: Never catch errors without surfacing them to the user. Avoid hiding failures in console or generic messages. Apply when adding try/catch, error handling, or API error responses.
4
+ scope: project
5
+ severity: critical
6
+ triggers:
7
+ - "Error handling"
8
+ - "try/catch"
9
+ - "Catching exceptions"
10
+ - "API errors"
11
+ - "Failure handling"
12
+ - "User feedback"
13
+ license: MIT
14
+ metadata:
15
+ author: agent-guardrails
16
+ version: "1.0"
17
+ ---
18
+
19
+ # No Silent Error Handling
20
+
21
+ ## Trigger
22
+ Adding error handling, try/catch blocks, API error responses, or any code that catches or handles failures.
23
+
24
+ ## Instruction
25
+ - Never catch errors without surfacing them to the user or logging them appropriately
26
+ - Avoid empty catch blocks or catch blocks that only log to console without user-visible feedback
27
+ - For user-facing flows: show clear error messages, not generic "Something went wrong" or "Generating…"
28
+ - For APIs: return meaningful error responses (status codes, error payloads) instead of swallowing failures
29
+ - When retrying: surface the failure if retries are exhausted
30
+ - Prefer rethrowing or propagating errors when you cannot handle them meaningfully
31
+
32
+ ## Reason
33
+ AI agents often suppress errors to "keep going," which hides real failures from users. Silent failures lead to incorrect assumptions, lost data, and debugging nightmares. Users must know when something failed.
34
+
35
+ ## Provenance
36
+ Manual addition, common failure mode in autonomous coding agents (DAPLab).
@@ -32,3 +32,6 @@ Suggesting or running shell commands that require elevated privileges (sudo, su,
32
32
 
33
33
  ## Reason
34
34
  Agents have run sudo commands that overwrote system configs or installed conflicting packages. Elevated privileges require explicit human confirmation.
35
+
36
+ ## Provenance
37
+ Manual addition, common failure mode in autonomous coding agents.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: no-unsafe-env-assumptions
3
+ description: Never assume environment variables exist without validation. Validate required env vars at startup and fail fast with clear errors. Apply when using process.env, os.environ, or config that depends on env.
4
+ scope: project
5
+ severity: critical
6
+ triggers:
7
+ - "Environment variables"
8
+ - "process.env"
9
+ - "os.environ"
10
+ - "Configuration"
11
+ - "Startup"
12
+ - "Config loading"
13
+ license: MIT
14
+ metadata:
15
+ author: agent-guardrails
16
+ version: "1.0"
17
+ ---
18
+
19
+ # No Unsafe Env Assumptions
20
+
21
+ ## Trigger
22
+ Using environment variables, loading configuration from env, or any code that reads `process.env`, `os.environ`, or similar at runtime.
23
+
24
+ ## Instruction
25
+ - Never assume required environment variables exist; validate at startup or first use
26
+ - For required vars: fail fast with a clear error message (e.g. "Missing required env: API_KEY. Add it to .env or set it before starting.")
27
+ - Use a config validation step (e.g. check all required vars before the app starts) instead of failing later with cryptic "undefined" errors
28
+ - Document required env vars in .env.example or README
29
+ - For optional vars: provide sensible defaults or handle absence explicitly
30
+
31
+ ## Reason
32
+ Code that assumes env vars exist fails at runtime with unclear errors (e.g. "Cannot read property of undefined"). Validating at startup surfaces configuration problems immediately with actionable messages.
33
+
34
+ ## Provenance
35
+ Manual addition, complements no-placeholder-credentials (don't invent values; validate when using).
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: prefer-existing-code
3
+ description: Prefer existing code and shared helpers over reimplementing. Search the codebase before adding new logic. Apply when adding features, refactoring, or implementing functionality.
4
+ scope: project
5
+ severity: warning
6
+ triggers:
7
+ - "Adding features"
8
+ - "Implementing"
9
+ - "Refactoring"
10
+ - "New function"
11
+ - "Helper"
12
+ - "Utility"
13
+ - "Duplicate"
14
+ license: MIT
15
+ metadata:
16
+ author: agent-guardrails
17
+ version: "1.0"
18
+ ---
19
+
20
+ # Prefer Existing Code
21
+
22
+ ## Trigger
23
+ Adding features, implementing functionality, refactoring, or writing new helpers and utilities.
24
+
25
+ ## Instruction
26
+ - Before implementing: search the codebase for existing functions, utilities, or patterns that already do what you need
27
+ - Prefer reusing existing code over copy-pasting or reimplementing
28
+ - When adding shared logic: extract to a common module or helper instead of duplicating
29
+ - If similar code exists elsewhere: refactor to use a shared implementation
30
+ - When the user asks for something that may already exist: check first and suggest the existing solution if applicable
31
+
32
+ ## Reason
33
+ Agents often reimplement logic that already exists, leading to duplication, inconsistency, and maintenance burden. Reusing existing code reduces bugs and keeps the codebase coherent.
34
+
35
+ ## Provenance
36
+ Manual addition, common failure mode in autonomous coding agents (DAPLab).
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: require-access-control
3
+ description: When handling sensitive data or admin actions, enforce role or permission checks. Never assume the current user has access. Apply when adding auth, admin features, or user-specific data.
4
+ scope: project
5
+ severity: critical
6
+ triggers:
7
+ - "Admin features"
8
+ - "User data"
9
+ - "Sensitive data"
10
+ - "Authorization"
11
+ - "Permissions"
12
+ - "Role check"
13
+ - "Private data"
14
+ license: MIT
15
+ metadata:
16
+ author: agent-guardrails
17
+ version: "1.0"
18
+ ---
19
+
20
+ # Require Access Control
21
+
22
+ ## Trigger
23
+ Adding admin features, handling user-specific or sensitive data, implementing authorization, or any code that differentiates between user roles or permissions.
24
+
25
+ ## Instruction
26
+ - Never assume the current user has access to sensitive data or admin actions
27
+ - Add explicit role or permission checks before exposing or modifying sensitive data
28
+ - For admin-only actions: verify the user has admin role or equivalent permission
29
+ - For user-specific data: verify the requester is the owner or has explicit access
30
+ - When adding new endpoints or UI: consider who should access them and add checks accordingly
31
+ - Avoid hardcoding "admin" or "user" assumptions; use the project's auth/permission system
32
+
33
+ ## Reason
34
+ Agents sometimes add features without access checks, exposing admin actions or private data to regular users. Access control must be explicit and enforced.
35
+
36
+ ## Provenance
37
+ Manual addition, security best practice.
@@ -29,3 +29,6 @@ Adding features, fixing bugs, modifying production code paths, or refactoring ex
29
29
 
30
30
  ## Reason
31
31
  Untested code leads to regressions and makes refactoring risky. Tests document expected behavior and catch breakage before production.
32
+
33
+ ## Provenance
34
+ Manual addition, agent-guardrails reference.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "guardrails-ref",
3
- "version": "1.0.9",
4
- "description": "Validate and manage Agent Guardrails (GUARDRAIL.md) — init, add, remove, setup, validate, check, upgrade, list",
3
+ "version": "1.1.1",
4
+ "description": "Validate and manage Agent Guardrails (GUARDRAIL.md) — init, add, remove, setup, validate, check, upgrade, list, why",
5
5
  "type": "module",
6
6
  "main": "dist/validate.js",
7
7
  "bin": {
@@ -21,7 +21,9 @@
21
21
  "guardrails",
22
22
  "ai",
23
23
  "cursor",
24
- "claude"
24
+ "claude",
25
+ "copilot",
26
+ "vscode"
25
27
  ],
26
28
  "license": "MIT",
27
29
  "repository": {