@windyroad/risk-scorer 0.1.1 → 0.1.2

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/bin/install.mjs CHANGED
@@ -20,6 +20,7 @@ Pipeline risk scoring, commit/push gates, and secret leak detection
20
20
  Options:
21
21
  --update Update this plugin and its skills
22
22
  --uninstall Remove this plugin
23
+ --scope Installation scope: project (default) or user
23
24
  --dry-run Show what would be done without executing
24
25
  --help, -h Show this help
25
26
  `);
@@ -38,5 +39,5 @@ if (flags.uninstall) {
38
39
  } else if (flags.update) {
39
40
  utils.updatePackage(PLUGIN);
40
41
  } else {
41
- utils.installPackage(PLUGIN, { deps: DEPS });
42
+ utils.installPackage(PLUGIN, { deps: DEPS, scope: flags.scope });
42
43
  }
@@ -47,7 +47,7 @@ if echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*npm run push:watch(\s|$)'; then
47
47
  fi
48
48
  PUSH_SCORE_FILE="${RDIR}/push"
49
49
  if [ ! -f "$PUSH_SCORE_FILE" ]; then
50
- risk_gate_deny "Push blocked: No push risk score found. Delegate to risk-scorer-pipeline (subagent_type: 'risk-scorer-pipeline') to assess cumulative pipeline risk."
50
+ risk_gate_deny "Push blocked: No push risk score found. Delegate to wr-risk-scorer:pipeline (subagent_type: 'wr-risk-scorer:pipeline') to assess cumulative pipeline risk."
51
51
  exit 0
52
52
  fi
53
53
  PUSH_NOW=$(date +%s)
@@ -65,7 +65,7 @@ if echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*npm run push:watch(\s|$)'; then
65
65
  fi
66
66
  PUSH_DENIED=$(python3 -c "print('yes' if float('${PUSH_SCORE}') >= 5 else 'no')" 2>/dev/null || echo "no")
67
67
  if [ "$PUSH_DENIED" = "yes" ]; then
68
- risk_gate_deny "Push blocked: Push risk score ${PUSH_SCORE}/25 (Medium or above). To proceed: (1) release first via \`npm run release:watch\`, (2) split the push, or (3) add risk-reducing measures. If risk-neutral or risk-reducing, delegate to risk-scorer-pipeline (subagent_type: 'risk-scorer-pipeline') — it will create a bypass marker."
68
+ risk_gate_deny "Push blocked: Push risk score ${PUSH_SCORE}/25 (Medium or above). To proceed: (1) release first via \`npm run release:watch\`, (2) split the push, or (3) add risk-reducing measures. If risk-neutral or risk-reducing, delegate to wr-risk-scorer:pipeline (subagent_type: 'wr-risk-scorer:pipeline') — it will create a bypass marker."
69
69
  exit 0
70
70
  fi
71
71
  fi
@@ -101,7 +101,7 @@ if echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*npm run release:watch(\s|$)'; the
101
101
  exit 0
102
102
  fi
103
103
  if ! check_risk_gate "$SESSION_ID" "release"; then
104
- risk_gate_deny "Release blocked: ${RISK_GATE_REASON}. To proceed: (1) split the release, (2) add risk-reducing measures, or (3) for a LIVE INCIDENT, delegate to risk-scorer-pipeline (subagent_type: 'risk-scorer-pipeline') with incident context for an incident bypass."
104
+ risk_gate_deny "Release blocked: ${RISK_GATE_REASON}. To proceed: (1) split the release, (2) add risk-reducing measures, or (3) for a LIVE INCIDENT, delegate to wr-risk-scorer:pipeline (subagent_type: 'wr-risk-scorer:pipeline') with incident context for an incident bypass."
105
105
  exit 0
106
106
  fi
107
107
  fi
@@ -21,7 +21,7 @@ check_risk_gate() {
21
21
 
22
22
  # 1. Score file must exist (fail-closed)
23
23
  if [ ! -f "$SCORE_FILE" ]; then
24
- RISK_GATE_REASON="No ${ACTION} risk score found. The risk-scorer agent must run first. It runs automatically on each prompt."
24
+ RISK_GATE_REASON="No ${ACTION} risk score found. Delegate to wr-risk-scorer:pipeline (subagent_type: 'wr-risk-scorer:pipeline') to assess cumulative pipeline risk."
25
25
  return 1
26
26
  fi
27
27
 
@@ -30,7 +30,7 @@ check_risk_gate() {
30
30
  local SCORE_TIME=$(_mtime "$SCORE_FILE")
31
31
  local AGE=$(( NOW - SCORE_TIME ))
32
32
  if [ "$AGE" -ge "$TTL_SECONDS" ]; then
33
- RISK_GATE_REASON="Risk score expired (${AGE}s old, TTL ${TTL_SECONDS}s). Stage all files with git add first, then submit a new prompt — the scorer runs automatically. Then call git commit in that response."
33
+ RISK_GATE_REASON="Risk score expired (${AGE}s old, TTL ${TTL_SECONDS}s). Delegate to wr-risk-scorer:pipeline (subagent_type: 'wr-risk-scorer:pipeline') to rescore."
34
34
  return 1
35
35
  fi
36
36
 
@@ -57,7 +57,7 @@ fi
57
57
 
58
58
  # Gate check: existence, TTL, drift, threshold
59
59
  if ! check_risk_gate "$SESSION_ID" "commit"; then
60
- risk_gate_deny "Commit blocked: ${RISK_GATE_REASON} To proceed: (1) stage files with git add, (2) delegate to risk-scorer-pipeline (subagent_type: 'risk-scorer-pipeline') to assess cumulative pipeline risk. If the commit is risk-neutral or risk-reducing, the scorer will create a bypass marker."
60
+ risk_gate_deny "Commit blocked: ${RISK_GATE_REASON} To proceed: (1) stage files with git add, (2) delegate to wr-risk-scorer:pipeline (subagent_type: 'wr-risk-scorer:pipeline') to assess cumulative pipeline risk. If the commit is risk-neutral or risk-reducing, the scorer will create a bypass marker."
61
61
  exit 0
62
62
  fi
63
63
 
@@ -4,7 +4,7 @@
4
4
  # risk-scorer agents. This is the ONLY place score files are written —
5
5
  # agents output structured markers, this hook writes the files.
6
6
  #
7
- # Handles: risk-scorer-pipeline, risk-scorer-plan, risk-scorer-wip, risk-scorer-policy
7
+ # Handles: wr-risk-scorer:pipeline, wr-risk-scorer:plan, wr-risk-scorer:wip, wr-risk-scorer:policy
8
8
  # Replaces: risk-policy-mark-reviewed.sh (which had fragile P001 backup parsing)
9
9
 
10
10
  set -euo pipefail
@@ -34,7 +34,7 @@ RDIR=$(_risk_dir "$SESSION_ID")
34
34
  # ---------------------------------------------------------------------------
35
35
  # Pipeline scorer: write commit/push/release scores + bypass markers
36
36
  # ---------------------------------------------------------------------------
37
- if echo "$SUBAGENT" | grep -qE 'risk-scorer-pipeline'; then
37
+ if echo "$SUBAGENT" | grep -qE 'risk-scorer.pipeline'; then
38
38
  # Parse RISK_SCORES: commit=N push=N release=N
39
39
  SCORES_LINE=$(echo "$AGENT_OUTPUT" | grep -E '^RISK_SCORES:' | tail -1) || true
40
40
  if [ -n "$SCORES_LINE" ]; then
@@ -79,7 +79,7 @@ fi
79
79
  # ---------------------------------------------------------------------------
80
80
  # Plan scorer: write plan-reviewed marker on PASS
81
81
  # ---------------------------------------------------------------------------
82
- if echo "$SUBAGENT" | grep -qE 'risk-scorer-plan'; then
82
+ if echo "$SUBAGENT" | grep -qE 'risk-scorer.plan'; then
83
83
  VERDICT_LINE=$(echo "$AGENT_OUTPUT" | grep -E '^RISK_VERDICT:' | tail -1) || true
84
84
  VERDICT=$(echo "$VERDICT_LINE" | sed 's/^RISK_VERDICT:[[:space:]]*//' | tr -d '[:space:]')
85
85
  case "$VERDICT" in
@@ -98,7 +98,7 @@ fi
98
98
  # ---------------------------------------------------------------------------
99
99
  # WIP scorer: write wip-reviewed marker (unblocks next edit)
100
100
  # ---------------------------------------------------------------------------
101
- if echo "$SUBAGENT" | grep -qE 'risk-scorer-wip'; then
101
+ if echo "$SUBAGENT" | grep -qE 'risk-scorer.wip'; then
102
102
  # WIP assessment was done — unblock next edit regardless of CONTINUE/PAUSE
103
103
  # (PAUSE is advisory guidance to the user, not a hard gate)
104
104
  touch "${RDIR}/wip-reviewed"
@@ -107,7 +107,7 @@ fi
107
107
  # ---------------------------------------------------------------------------
108
108
  # Policy scorer: write policy-reviewed marker on PASS
109
109
  # ---------------------------------------------------------------------------
110
- if echo "$SUBAGENT" | grep -qE 'risk-scorer-policy'; then
110
+ if echo "$SUBAGENT" | grep -qE 'risk-scorer.policy'; then
111
111
  VERDICT_LINE=$(echo "$AGENT_OUTPUT" | grep -E '^RISK_VERDICT:' | tail -1) || true
112
112
  VERDICT=$(echo "$VERDICT_LINE" | sed 's/^RISK_VERDICT:[[:space:]]*//' | tr -d '[:space:]')
113
113
  case "$VERDICT" in
@@ -24,7 +24,7 @@ cat <<'EOF'
24
24
  "hookSpecificOutput": {
25
25
  "hookEventName": "PreToolUse",
26
26
  "permissionDecision": "deny",
27
- "permissionDecisionReason": "BLOCKED: Risk-scorer must review the plan before exiting plan mode. Delegate to risk-scorer-plan (subagent_type: 'risk-scorer-plan') to review the plan file for risk, including projected release risk."
27
+ "permissionDecisionReason": "BLOCKED: Risk-scorer must review the plan before exiting plan mode. Delegate to wr-risk-scorer:plan (subagent_type: 'wr-risk-scorer:plan') to review the plan file for risk, including projected release risk."
28
28
  }
29
29
  }
30
30
  EOF
@@ -37,7 +37,7 @@ cat <<'EOF'
37
37
  "hookSpecificOutput": {
38
38
  "hookEventName": "PreToolUse",
39
39
  "permissionDecision": "deny",
40
- "permissionDecisionReason": "WIP risk assessment required. Delegate to risk-scorer-wip (subagent_type: 'risk-scorer-wip') to assess cumulative pipeline risk for changes so far."
40
+ "permissionDecisionReason": "WIP risk assessment required. Delegate to wr-risk-scorer:wip (subagent_type: 'wr-risk-scorer:wip') to assess cumulative pipeline risk for changes so far."
41
41
  }
42
42
  }
43
43
  EOF
@@ -46,15 +46,6 @@ export function checkPrerequisites() {
46
46
  );
47
47
  process.exit(1);
48
48
  }
49
-
50
- try {
51
- execSync("npx --version", { stdio: "pipe" });
52
- } catch {
53
- console.error(
54
- "Error: 'npx' not found. Install Node.js first:\n https://nodejs.org\n"
55
- );
56
- process.exit(1);
57
- }
58
49
  }
59
50
 
60
51
  export function addMarketplace() {
@@ -64,9 +55,9 @@ export function addMarketplace() {
64
55
  );
65
56
  }
66
57
 
67
- export function installPlugin(pluginName) {
58
+ export function installPlugin(pluginName, { scope = "project" } = {}) {
68
59
  return run(
69
- `claude plugin install ${pluginName}@${MARKETPLACE_NAME}`,
60
+ `claude plugin install ${pluginName}@${MARKETPLACE_NAME} --scope ${scope}`,
70
61
  pluginName
71
62
  );
72
63
  }
@@ -79,33 +70,14 @@ export function uninstallPlugin(pluginName) {
79
70
  return run(`claude plugin uninstall ${pluginName}`, `Removing ${pluginName}`);
80
71
  }
81
72
 
82
- export function installSkills() {
83
- return run(
84
- `npx -y skills add --yes --all ${MARKETPLACE_REPO}`,
85
- "Skills (via skills package)"
86
- );
87
- }
88
-
89
- export function updateSkills() {
90
- return run("npx -y skills update", "Skills update");
91
- }
92
-
93
- export function removeSkills() {
94
- return run(
95
- `npx -y skills remove --yes --all ${MARKETPLACE_REPO}`,
96
- "Removing skills"
97
- );
98
- }
99
-
100
73
  /**
101
- * Install a single package: marketplace add + plugin install + skills.
74
+ * Install a single package: marketplace add + plugin install.
102
75
  */
103
- export function installPackage(pluginName, { deps = [] } = {}) {
104
- console.log(`\nInstalling @windyroad/${pluginName.replace("wr-", "")}...\n`);
76
+ export function installPackage(pluginName, { deps = [], scope = "project" } = {}) {
77
+ console.log(`\nInstalling @windyroad/${pluginName.replace("wr-", "")} (${scope} scope)...\n`);
105
78
 
106
79
  addMarketplace();
107
- installPlugin(pluginName);
108
- installSkills();
80
+ installPlugin(pluginName, { scope });
109
81
 
110
82
  if (deps.length > 0) {
111
83
  console.log(`\nNote: This plugin works best with:`);
@@ -130,7 +102,6 @@ export function updatePackage(pluginName) {
130
102
  "Updating marketplace"
131
103
  );
132
104
  updatePlugin(pluginName);
133
- updateSkills();
134
105
 
135
106
  console.log("\nDone! Restart Claude Code to apply updates.\n");
136
107
  }
@@ -144,9 +115,6 @@ export function uninstallPackage(pluginName) {
144
115
  uninstallPlugin(pluginName);
145
116
 
146
117
  console.log("\nDone. Restart Claude Code to apply changes.\n");
147
- console.log("Note: Skills are shared across packages. Run");
148
- console.log(" npx @windyroad/agent-plugins --uninstall");
149
- console.log("to remove all skills.\n");
150
118
  }
151
119
 
152
120
  /**
@@ -154,10 +122,22 @@ export function uninstallPackage(pluginName) {
154
122
  */
155
123
  export function parseStandardArgs(argv) {
156
124
  const args = argv.slice(2);
157
- return {
125
+ const flags = {
158
126
  help: args.includes("--help") || args.includes("-h"),
159
127
  uninstall: args.includes("--uninstall"),
160
128
  update: args.includes("--update"),
161
129
  dryRun: args.includes("--dry-run"),
130
+ scope: "project",
162
131
  };
132
+ const scopeIdx = args.indexOf("--scope");
133
+ if (scopeIdx !== -1 && args[scopeIdx + 1]) {
134
+ const val = args[scopeIdx + 1];
135
+ if (["project", "user", "local"].includes(val)) {
136
+ flags.scope = val;
137
+ } else {
138
+ console.error("--scope requires: project, user, or local");
139
+ process.exit(1);
140
+ }
141
+ }
142
+ return flags;
163
143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/risk-scorer",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Pipeline risk scoring, commit/push gates, and secret leak detection",
5
5
  "bin": {
6
6
  "windyroad-risk-scorer": "./bin/install.mjs"