claude-warden 2.7.0 → 2.8.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/config/warden.default.yaml +20 -0
- package/dist/cli.cjs +24 -0
- package/dist/codex-export.cjs +24 -0
- package/dist/copilot.cjs +24 -0
- package/dist/index.cjs +40 -0
- package/hooks/hooks.json +11 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "warden",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Smart command safety filter for Claude Code — parses shell pipelines and evaluates per-command safety rules to auto-approve safe commands and block dangerous ones",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "banyudu"
|
|
@@ -19,6 +19,26 @@ askOnSubshell: true
|
|
|
19
19
|
notifyOnAsk: true
|
|
20
20
|
notifyOnDeny: true
|
|
21
21
|
|
|
22
|
+
# Guidance injected into every Claude Code session via the SessionStart hook.
|
|
23
|
+
# Claude sees this as a system message, so it can shape tool choice *before*
|
|
24
|
+
# warden needs to ask or deny. Keep it short — it competes for context.
|
|
25
|
+
#
|
|
26
|
+
# - Omit the key to use the built-in default (prefer jq, save temp scripts to
|
|
27
|
+
# tempScriptDir below, read deny reasons, mention /warden:allow and /warden:yolo).
|
|
28
|
+
# - Set to a string to override with your own guidance.
|
|
29
|
+
# - Set to `false` to disable injection entirely.
|
|
30
|
+
#
|
|
31
|
+
# sessionGuidance: |
|
|
32
|
+
# Warden is active. Prefer allow-listed tools (e.g. jq for JSON). For
|
|
33
|
+
# multi-line logic, save a temp script under /tmp/ instead of using
|
|
34
|
+
# inline `bash -c` / `node -e`.
|
|
35
|
+
|
|
36
|
+
# Directory the built-in guidance tells Claude to save throwaway multi-line
|
|
37
|
+
# scripts to. Defaults to `/tmp` so scripts don't pollute the repo. Point at
|
|
38
|
+
# another location (e.g. `.warden-scratch`) if you want them tracked per-project.
|
|
39
|
+
# Only used when `sessionGuidance` is unset.
|
|
40
|
+
# tempScriptDir: /tmp
|
|
41
|
+
|
|
22
42
|
# Additional commands to always allow (checked after alwaysDeny within this scope)
|
|
23
43
|
# alwaysAllow:
|
|
24
44
|
# - terraform
|
package/dist/cli.cjs
CHANGED
|
@@ -18933,6 +18933,18 @@ var DEFAULT_SKILL_RULES = {
|
|
|
18933
18933
|
rules: []
|
|
18934
18934
|
}]
|
|
18935
18935
|
};
|
|
18936
|
+
var DEFAULT_TEMP_SCRIPT_DIR = "/tmp";
|
|
18937
|
+
function buildDefaultSessionGuidance(tempScriptDir) {
|
|
18938
|
+
return [
|
|
18939
|
+
"Claude Warden is active. It filters Bash commands against safety rules and may ask or deny.",
|
|
18940
|
+
"",
|
|
18941
|
+
"- For JSON in shell pipelines, prefer `jq` (auto-allowed) over `python3 -c` / `node -e`.",
|
|
18942
|
+
`- For multi-line logic, save a temp script under \`${tempScriptDir}/\` (e.g. \`${tempScriptDir}/warden-task.sh\`) or add a \`package.json\` script rather than inline \`bash -c\` / \`node -e\`. Avoid polluting the repo with throwaway scripts.`,
|
|
18943
|
+
"- When Warden denies or asks, read the reason \u2014 it often names the preferred alternative.",
|
|
18944
|
+
"- To permanently allow a specific command, run `/warden:allow <cmd>`. To temporarily bypass filtering, `/warden:yolo`."
|
|
18945
|
+
].join("\n");
|
|
18946
|
+
}
|
|
18947
|
+
var DEFAULT_SESSION_GUIDANCE = buildDefaultSessionGuidance(DEFAULT_TEMP_SCRIPT_DIR);
|
|
18936
18948
|
var DEFAULT_CONFIG = {
|
|
18937
18949
|
defaultDecision: "ask",
|
|
18938
18950
|
askOnSubshell: true,
|
|
@@ -19809,6 +19821,18 @@ function mergeNonLayerFields(config, raw) {
|
|
|
19809
19821
|
if (typeof raw.notifyOnDeny === "boolean") {
|
|
19810
19822
|
config.notifyOnDeny = raw.notifyOnDeny;
|
|
19811
19823
|
}
|
|
19824
|
+
if (typeof raw.sessionGuidance === "string" || raw.sessionGuidance === false) {
|
|
19825
|
+
config.sessionGuidance = raw.sessionGuidance;
|
|
19826
|
+
} else if (raw.sessionGuidance !== void 0) {
|
|
19827
|
+
warn(`[warden] Warning: invalid sessionGuidance (expected string or false), ignoring
|
|
19828
|
+
`);
|
|
19829
|
+
}
|
|
19830
|
+
if (typeof raw.tempScriptDir === "string" && raw.tempScriptDir.length > 0) {
|
|
19831
|
+
config.tempScriptDir = raw.tempScriptDir;
|
|
19832
|
+
} else if (raw.tempScriptDir !== void 0) {
|
|
19833
|
+
warn(`[warden] Warning: invalid tempScriptDir (expected non-empty string), ignoring
|
|
19834
|
+
`);
|
|
19835
|
+
}
|
|
19812
19836
|
if (raw.skills && typeof raw.skills === "object") {
|
|
19813
19837
|
const skills = raw.skills;
|
|
19814
19838
|
if (typeof skills.defaultDecision === "string") {
|
package/dist/codex-export.cjs
CHANGED
|
@@ -18937,6 +18937,18 @@ var DEFAULT_SKILL_RULES = {
|
|
|
18937
18937
|
rules: []
|
|
18938
18938
|
}]
|
|
18939
18939
|
};
|
|
18940
|
+
var DEFAULT_TEMP_SCRIPT_DIR = "/tmp";
|
|
18941
|
+
function buildDefaultSessionGuidance(tempScriptDir) {
|
|
18942
|
+
return [
|
|
18943
|
+
"Claude Warden is active. It filters Bash commands against safety rules and may ask or deny.",
|
|
18944
|
+
"",
|
|
18945
|
+
"- For JSON in shell pipelines, prefer `jq` (auto-allowed) over `python3 -c` / `node -e`.",
|
|
18946
|
+
`- For multi-line logic, save a temp script under \`${tempScriptDir}/\` (e.g. \`${tempScriptDir}/warden-task.sh\`) or add a \`package.json\` script rather than inline \`bash -c\` / \`node -e\`. Avoid polluting the repo with throwaway scripts.`,
|
|
18947
|
+
"- When Warden denies or asks, read the reason \u2014 it often names the preferred alternative.",
|
|
18948
|
+
"- To permanently allow a specific command, run `/warden:allow <cmd>`. To temporarily bypass filtering, `/warden:yolo`."
|
|
18949
|
+
].join("\n");
|
|
18950
|
+
}
|
|
18951
|
+
var DEFAULT_SESSION_GUIDANCE = buildDefaultSessionGuidance(DEFAULT_TEMP_SCRIPT_DIR);
|
|
18940
18952
|
var DEFAULT_CONFIG = {
|
|
18941
18953
|
defaultDecision: "ask",
|
|
18942
18954
|
askOnSubshell: true,
|
|
@@ -19813,6 +19825,18 @@ function mergeNonLayerFields(config, raw) {
|
|
|
19813
19825
|
if (typeof raw.notifyOnDeny === "boolean") {
|
|
19814
19826
|
config.notifyOnDeny = raw.notifyOnDeny;
|
|
19815
19827
|
}
|
|
19828
|
+
if (typeof raw.sessionGuidance === "string" || raw.sessionGuidance === false) {
|
|
19829
|
+
config.sessionGuidance = raw.sessionGuidance;
|
|
19830
|
+
} else if (raw.sessionGuidance !== void 0) {
|
|
19831
|
+
warn(`[warden] Warning: invalid sessionGuidance (expected string or false), ignoring
|
|
19832
|
+
`);
|
|
19833
|
+
}
|
|
19834
|
+
if (typeof raw.tempScriptDir === "string" && raw.tempScriptDir.length > 0) {
|
|
19835
|
+
config.tempScriptDir = raw.tempScriptDir;
|
|
19836
|
+
} else if (raw.tempScriptDir !== void 0) {
|
|
19837
|
+
warn(`[warden] Warning: invalid tempScriptDir (expected non-empty string), ignoring
|
|
19838
|
+
`);
|
|
19839
|
+
}
|
|
19816
19840
|
if (raw.skills && typeof raw.skills === "object") {
|
|
19817
19841
|
const skills = raw.skills;
|
|
19818
19842
|
if (typeof skills.defaultDecision === "string") {
|
package/dist/copilot.cjs
CHANGED
|
@@ -18933,6 +18933,18 @@ var DEFAULT_SKILL_RULES = {
|
|
|
18933
18933
|
rules: []
|
|
18934
18934
|
}]
|
|
18935
18935
|
};
|
|
18936
|
+
var DEFAULT_TEMP_SCRIPT_DIR = "/tmp";
|
|
18937
|
+
function buildDefaultSessionGuidance(tempScriptDir) {
|
|
18938
|
+
return [
|
|
18939
|
+
"Claude Warden is active. It filters Bash commands against safety rules and may ask or deny.",
|
|
18940
|
+
"",
|
|
18941
|
+
"- For JSON in shell pipelines, prefer `jq` (auto-allowed) over `python3 -c` / `node -e`.",
|
|
18942
|
+
`- For multi-line logic, save a temp script under \`${tempScriptDir}/\` (e.g. \`${tempScriptDir}/warden-task.sh\`) or add a \`package.json\` script rather than inline \`bash -c\` / \`node -e\`. Avoid polluting the repo with throwaway scripts.`,
|
|
18943
|
+
"- When Warden denies or asks, read the reason \u2014 it often names the preferred alternative.",
|
|
18944
|
+
"- To permanently allow a specific command, run `/warden:allow <cmd>`. To temporarily bypass filtering, `/warden:yolo`."
|
|
18945
|
+
].join("\n");
|
|
18946
|
+
}
|
|
18947
|
+
var DEFAULT_SESSION_GUIDANCE = buildDefaultSessionGuidance(DEFAULT_TEMP_SCRIPT_DIR);
|
|
18936
18948
|
var DEFAULT_CONFIG = {
|
|
18937
18949
|
defaultDecision: "ask",
|
|
18938
18950
|
askOnSubshell: true,
|
|
@@ -19806,6 +19818,18 @@ function mergeNonLayerFields(config, raw) {
|
|
|
19806
19818
|
if (typeof raw.notifyOnDeny === "boolean") {
|
|
19807
19819
|
config.notifyOnDeny = raw.notifyOnDeny;
|
|
19808
19820
|
}
|
|
19821
|
+
if (typeof raw.sessionGuidance === "string" || raw.sessionGuidance === false) {
|
|
19822
|
+
config.sessionGuidance = raw.sessionGuidance;
|
|
19823
|
+
} else if (raw.sessionGuidance !== void 0) {
|
|
19824
|
+
warn(`[warden] Warning: invalid sessionGuidance (expected string or false), ignoring
|
|
19825
|
+
`);
|
|
19826
|
+
}
|
|
19827
|
+
if (typeof raw.tempScriptDir === "string" && raw.tempScriptDir.length > 0) {
|
|
19828
|
+
config.tempScriptDir = raw.tempScriptDir;
|
|
19829
|
+
} else if (raw.tempScriptDir !== void 0) {
|
|
19830
|
+
warn(`[warden] Warning: invalid tempScriptDir (expected non-empty string), ignoring
|
|
19831
|
+
`);
|
|
19832
|
+
}
|
|
19809
19833
|
if (raw.skills && typeof raw.skills === "object") {
|
|
19810
19834
|
const skills = raw.skills;
|
|
19811
19835
|
if (typeof skills.defaultDecision === "string") {
|
package/dist/index.cjs
CHANGED
|
@@ -18933,6 +18933,18 @@ var DEFAULT_SKILL_RULES = {
|
|
|
18933
18933
|
rules: []
|
|
18934
18934
|
}]
|
|
18935
18935
|
};
|
|
18936
|
+
var DEFAULT_TEMP_SCRIPT_DIR = "/tmp";
|
|
18937
|
+
function buildDefaultSessionGuidance(tempScriptDir) {
|
|
18938
|
+
return [
|
|
18939
|
+
"Claude Warden is active. It filters Bash commands against safety rules and may ask or deny.",
|
|
18940
|
+
"",
|
|
18941
|
+
"- For JSON in shell pipelines, prefer `jq` (auto-allowed) over `python3 -c` / `node -e`.",
|
|
18942
|
+
`- For multi-line logic, save a temp script under \`${tempScriptDir}/\` (e.g. \`${tempScriptDir}/warden-task.sh\`) or add a \`package.json\` script rather than inline \`bash -c\` / \`node -e\`. Avoid polluting the repo with throwaway scripts.`,
|
|
18943
|
+
"- When Warden denies or asks, read the reason \u2014 it often names the preferred alternative.",
|
|
18944
|
+
"- To permanently allow a specific command, run `/warden:allow <cmd>`. To temporarily bypass filtering, `/warden:yolo`."
|
|
18945
|
+
].join("\n");
|
|
18946
|
+
}
|
|
18947
|
+
var DEFAULT_SESSION_GUIDANCE = buildDefaultSessionGuidance(DEFAULT_TEMP_SCRIPT_DIR);
|
|
18936
18948
|
var DEFAULT_CONFIG = {
|
|
18937
18949
|
defaultDecision: "ask",
|
|
18938
18950
|
askOnSubshell: true,
|
|
@@ -19806,6 +19818,18 @@ function mergeNonLayerFields(config, raw) {
|
|
|
19806
19818
|
if (typeof raw.notifyOnDeny === "boolean") {
|
|
19807
19819
|
config.notifyOnDeny = raw.notifyOnDeny;
|
|
19808
19820
|
}
|
|
19821
|
+
if (typeof raw.sessionGuidance === "string" || raw.sessionGuidance === false) {
|
|
19822
|
+
config.sessionGuidance = raw.sessionGuidance;
|
|
19823
|
+
} else if (raw.sessionGuidance !== void 0) {
|
|
19824
|
+
warn(`[warden] Warning: invalid sessionGuidance (expected string or false), ignoring
|
|
19825
|
+
`);
|
|
19826
|
+
}
|
|
19827
|
+
if (typeof raw.tempScriptDir === "string" && raw.tempScriptDir.length > 0) {
|
|
19828
|
+
config.tempScriptDir = raw.tempScriptDir;
|
|
19829
|
+
} else if (raw.tempScriptDir !== void 0) {
|
|
19830
|
+
warn(`[warden] Warning: invalid tempScriptDir (expected non-empty string), ignoring
|
|
19831
|
+
`);
|
|
19832
|
+
}
|
|
19809
19833
|
if (raw.skills && typeof raw.skills === "object") {
|
|
19810
19834
|
const skills = raw.skills;
|
|
19811
19835
|
if (typeof skills.defaultDecision === "string") {
|
|
@@ -20990,6 +21014,18 @@ function emitDecision(decision, reason, stderrMessage) {
|
|
|
20990
21014
|
}
|
|
20991
21015
|
process.exit(0);
|
|
20992
21016
|
}
|
|
21017
|
+
function handleSessionStart(config) {
|
|
21018
|
+
if (config.sessionGuidance === false) process.exit(0);
|
|
21019
|
+
const text = config.sessionGuidance ?? buildDefaultSessionGuidance(config.tempScriptDir ?? DEFAULT_TEMP_SCRIPT_DIR);
|
|
21020
|
+
const output = {
|
|
21021
|
+
hookSpecificOutput: {
|
|
21022
|
+
hookEventName: "SessionStart",
|
|
21023
|
+
additionalContext: text
|
|
21024
|
+
}
|
|
21025
|
+
};
|
|
21026
|
+
process.stdout.write(JSON.stringify(output));
|
|
21027
|
+
process.exit(0);
|
|
21028
|
+
}
|
|
20993
21029
|
function handleYoloMode(sessionId, result) {
|
|
20994
21030
|
const yoloState = getYoloState(sessionId);
|
|
20995
21031
|
if (!yoloState) return;
|
|
@@ -21011,6 +21047,10 @@ async function main() {
|
|
|
21011
21047
|
} catch {
|
|
21012
21048
|
process.exit(0);
|
|
21013
21049
|
}
|
|
21050
|
+
if (input.hook_event_name === "SessionStart") {
|
|
21051
|
+
const config2 = loadConfig(input.cwd);
|
|
21052
|
+
handleSessionStart(config2);
|
|
21053
|
+
}
|
|
21014
21054
|
if (input.tool_name !== "Bash" && input.tool_name !== "Skill") {
|
|
21015
21055
|
process.exit(0);
|
|
21016
21056
|
}
|
package/hooks/hooks.json
CHANGED