@shield-agent/kya 0.34.0 → 0.35.3

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.
Files changed (181) hide show
  1. package/.cursor-plugin/plugin.json +2 -2
  2. package/.mcp.json +1 -1
  3. package/LIMITATIONS.md +2 -1
  4. package/README.md +37 -655
  5. package/amp/settings.example.json +1 -1
  6. package/assets/report-blocked.png +0 -0
  7. package/assets/report-certify.png +0 -0
  8. package/assets/report-overview.png +0 -0
  9. package/assets/screenshot-features.json +1 -0
  10. package/claude/claude_desktop_config.example.json +1 -1
  11. package/cline/cline_mcp_settings.example.json +1 -1
  12. package/copilot/mcp-config.example.json +1 -1
  13. package/dist/alerts/triage-setup.d.ts +4 -0
  14. package/dist/alerts/triage-setup.d.ts.map +1 -1
  15. package/dist/alerts/triage-setup.js +11 -1
  16. package/dist/alerts/triage-setup.js.map +1 -1
  17. package/dist/card-png.d.ts +3 -0
  18. package/dist/card-png.d.ts.map +1 -0
  19. package/dist/card-png.js +54 -0
  20. package/dist/card-png.js.map +1 -0
  21. package/dist/card.d.ts +62 -0
  22. package/dist/card.d.ts.map +1 -0
  23. package/dist/card.js +154 -0
  24. package/dist/card.js.map +1 -0
  25. package/dist/certify/evaluate.d.ts +11 -1
  26. package/dist/certify/evaluate.d.ts.map +1 -1
  27. package/dist/certify/evaluate.js +8 -1
  28. package/dist/certify/evaluate.js.map +1 -1
  29. package/dist/certify/render.d.ts.map +1 -1
  30. package/dist/certify/render.js +10 -1
  31. package/dist/certify/render.js.map +1 -1
  32. package/dist/cli.d.ts +2 -0
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +223 -52
  35. package/dist/cli.js.map +1 -1
  36. package/dist/commands/connect.d.ts.map +1 -1
  37. package/dist/commands/connect.js +4 -3
  38. package/dist/commands/connect.js.map +1 -1
  39. package/dist/commands/doctor.d.ts.map +1 -1
  40. package/dist/commands/doctor.js +12 -1
  41. package/dist/commands/doctor.js.map +1 -1
  42. package/dist/commands/hook.d.ts.map +1 -1
  43. package/dist/commands/hook.js +94 -8
  44. package/dist/commands/hook.js.map +1 -1
  45. package/dist/commands/init.d.ts +2 -0
  46. package/dist/commands/init.d.ts.map +1 -1
  47. package/dist/commands/init.js +2 -0
  48. package/dist/commands/init.js.map +1 -1
  49. package/dist/commands/receipt.d.ts +19 -0
  50. package/dist/commands/receipt.d.ts.map +1 -1
  51. package/dist/commands/receipt.js +21 -1
  52. package/dist/commands/receipt.js.map +1 -1
  53. package/dist/commands/serve-mcp.d.ts +2 -0
  54. package/dist/commands/serve-mcp.d.ts.map +1 -1
  55. package/dist/commands/serve-mcp.js +2 -0
  56. package/dist/commands/serve-mcp.js.map +1 -1
  57. package/dist/commands/start.d.ts +50 -2
  58. package/dist/commands/start.d.ts.map +1 -1
  59. package/dist/commands/start.js +233 -27
  60. package/dist/commands/start.js.map +1 -1
  61. package/dist/commands/team.d.ts +19 -2
  62. package/dist/commands/team.d.ts.map +1 -1
  63. package/dist/commands/team.js +93 -13
  64. package/dist/commands/team.js.map +1 -1
  65. package/dist/commands/uninstall.d.ts +20 -0
  66. package/dist/commands/uninstall.d.ts.map +1 -0
  67. package/dist/commands/uninstall.js +378 -0
  68. package/dist/commands/uninstall.js.map +1 -0
  69. package/dist/commands/wire-hooks.d.ts +16 -2
  70. package/dist/commands/wire-hooks.d.ts.map +1 -1
  71. package/dist/commands/wire-hooks.js +86 -21
  72. package/dist/commands/wire-hooks.js.map +1 -1
  73. package/dist/cta.d.ts +3 -1
  74. package/dist/cta.d.ts.map +1 -1
  75. package/dist/cta.js +11 -6
  76. package/dist/cta.js.map +1 -1
  77. package/dist/fs-atomic.d.ts.map +1 -1
  78. package/dist/fs-atomic.js +9 -2
  79. package/dist/fs-atomic.js.map +1 -1
  80. package/dist/guard-demo.d.ts +23 -0
  81. package/dist/guard-demo.d.ts.map +1 -0
  82. package/dist/guard-demo.js +34 -0
  83. package/dist/guard-demo.js.map +1 -0
  84. package/dist/guard-entry.d.ts +2 -0
  85. package/dist/guard-entry.d.ts.map +1 -0
  86. package/dist/guard-entry.js +50 -0
  87. package/dist/guard-entry.js.map +1 -0
  88. package/dist/guard-lite.d.ts +27 -0
  89. package/dist/guard-lite.d.ts.map +1 -0
  90. package/dist/guard-lite.js +274 -0
  91. package/dist/guard-lite.js.map +1 -0
  92. package/dist/guard-pack.d.ts +7 -2
  93. package/dist/guard-pack.d.ts.map +1 -1
  94. package/dist/guard-pack.js +202 -4
  95. package/dist/guard-pack.js.map +1 -1
  96. package/dist/hook-health.d.ts +7 -0
  97. package/dist/hook-health.d.ts.map +1 -0
  98. package/dist/hook-health.js +105 -0
  99. package/dist/hook-health.js.map +1 -0
  100. package/dist/manifest.d.ts +106 -0
  101. package/dist/manifest.d.ts.map +1 -0
  102. package/dist/manifest.js +454 -0
  103. package/dist/manifest.js.map +1 -0
  104. package/dist/mcp/http.d.ts +1 -0
  105. package/dist/mcp/http.d.ts.map +1 -1
  106. package/dist/mcp/http.js +1 -0
  107. package/dist/mcp/http.js.map +1 -1
  108. package/dist/mcp/protocol.d.ts +2 -0
  109. package/dist/mcp/protocol.d.ts.map +1 -1
  110. package/dist/mcp/protocol.js +5 -1
  111. package/dist/mcp/protocol.js.map +1 -1
  112. package/dist/mcp/stdio.d.ts +1 -0
  113. package/dist/mcp/stdio.d.ts.map +1 -1
  114. package/dist/mcp/stdio.js +1 -0
  115. package/dist/mcp/stdio.js.map +1 -1
  116. package/dist/never-list-input.d.ts +2 -0
  117. package/dist/never-list-input.d.ts.map +1 -1
  118. package/dist/never-list-input.js +30 -30
  119. package/dist/never-list-input.js.map +1 -1
  120. package/dist/offline-evaluate.d.ts.map +1 -1
  121. package/dist/offline-evaluate.js +2 -1
  122. package/dist/offline-evaluate.js.map +1 -1
  123. package/dist/parse-args.js +1 -1
  124. package/dist/parse-args.js.map +1 -1
  125. package/dist/receipt/changes.d.ts +2 -1
  126. package/dist/receipt/changes.d.ts.map +1 -1
  127. package/dist/receipt/changes.js +9 -0
  128. package/dist/receipt/changes.js.map +1 -1
  129. package/dist/receipt/dashboard.d.ts +14 -1
  130. package/dist/receipt/dashboard.d.ts.map +1 -1
  131. package/dist/receipt/dashboard.js +20 -2
  132. package/dist/receipt/dashboard.js.map +1 -1
  133. package/dist/receipt/live-server.d.ts +15 -0
  134. package/dist/receipt/live-server.d.ts.map +1 -1
  135. package/dist/receipt/live-server.js +71 -0
  136. package/dist/receipt/live-server.js.map +1 -1
  137. package/dist/receipt/receipt-css.d.ts.map +1 -1
  138. package/dist/receipt/receipt-css.js +45 -0
  139. package/dist/receipt/receipt-css.js.map +1 -1
  140. package/dist/receipt/render-receipt.d.ts +14 -0
  141. package/dist/receipt/render-receipt.d.ts.map +1 -1
  142. package/dist/receipt/render-receipt.js +451 -79
  143. package/dist/receipt/render-receipt.js.map +1 -1
  144. package/dist/receipt/search.d.ts.map +1 -1
  145. package/dist/receipt/search.js +2 -0
  146. package/dist/receipt/search.js.map +1 -1
  147. package/dist/receipt/semantic-setup.d.ts +4 -0
  148. package/dist/receipt/semantic-setup.d.ts.map +1 -1
  149. package/dist/receipt/semantic-setup.js +10 -1
  150. package/dist/receipt/semantic-setup.js.map +1 -1
  151. package/dist/receipt/share.d.ts +15 -0
  152. package/dist/receipt/share.d.ts.map +1 -1
  153. package/dist/receipt/share.js +34 -1
  154. package/dist/receipt/share.js.map +1 -1
  155. package/dist/telemetry.d.ts +17 -2
  156. package/dist/telemetry.d.ts.map +1 -1
  157. package/dist/telemetry.js +67 -7
  158. package/dist/telemetry.js.map +1 -1
  159. package/dist/trail.d.ts +39 -0
  160. package/dist/trail.d.ts.map +1 -1
  161. package/dist/trail.js +27 -0
  162. package/dist/trail.js.map +1 -1
  163. package/dist/write-journal.d.ts +39 -0
  164. package/dist/write-journal.d.ts.map +1 -0
  165. package/dist/write-journal.js +107 -0
  166. package/dist/write-journal.js.map +1 -0
  167. package/droid/README.md +1 -1
  168. package/droid/mcp.example.json +1 -1
  169. package/gemini/settings.example.json +1 -1
  170. package/grok/README.md +1 -1
  171. package/kilo/kilo.example.json +1 -1
  172. package/kimi/mcp.example.json +1 -1
  173. package/kiro/mcp.example.json +1 -1
  174. package/manifest.json +2 -2
  175. package/mastracode/mcp.example.json +1 -1
  176. package/mcp.json +1 -1
  177. package/openai/codex.config.example.toml +1 -1
  178. package/opencode/opencode.example.json +1 -1
  179. package/package.json +9 -3
  180. package/qwen/settings.example.json +1 -1
  181. package/server.json +3 -3
@@ -0,0 +1,27 @@
1
+ export interface GuardInput {
2
+ readonly stdinText: string;
3
+ readonly env: NodeJS.ProcessEnv;
4
+ readonly cwd: string;
5
+ /** Host id from `--host`: claude (default), codex, cursor, copilot or gemini. Sets the payload reading and the deny shape. */
6
+ readonly host?: string;
7
+ }
8
+ /** Hosts the guard speaks. Any other id gets the Claude-shaped deny and no ask. */
9
+ export declare const GUARD_HOSTS: readonly ["claude", "codex", "cursor", "copilot", "gemini"];
10
+ export interface GuardResult {
11
+ readonly exitCode: 0 | 2;
12
+ readonly stdout: string;
13
+ readonly stderr: string;
14
+ }
15
+ /** Evaluate one hook call. Synchronous; never throws. */
16
+ export declare function runGuard(input: GuardInput): GuardResult;
17
+ /** Text for /kya-guard:status. Reads the trail only. Never throws. */
18
+ export declare function guardStatus(env: NodeJS.ProcessEnv, now?: Date): string;
19
+ /**
20
+ * `--card [--id <id>]`: write the near-miss card (SVG only: this bundle starts no process, so it makes no PNG) and print the caption.
21
+ * Always exit 0: the slash command shows the text, and its `|| echo` fallback is for a missing node, not for "no card".
22
+ */
23
+ export declare function guardCard(env: NodeJS.ProcessEnv, id?: string): {
24
+ readonly exitCode: 0;
25
+ readonly text: string;
26
+ };
27
+ //# sourceMappingURL=guard-lite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-lite.d.ts","sourceRoot":"","sources":["../src/guard-lite.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8HAA8H;IAC9H,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,mFAAmF;AACnF,eAAO,MAAM,WAAW,6DAA8D,CAAC;AAGvF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AA4GD,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAiGvD;AAID,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAgClF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAY9G"}
@@ -0,0 +1,274 @@
1
+ /**
2
+ * The kya-guard plugin core (plugins/kya-guard). The lean sibling of `kya hook`: Claude Code PreToolUse in, verdict out,
3
+ * one row appended to ~/.kya/trail.jsonl. Bundled into plugins/kya-guard/dist/guard.mjs by scripts/build-guard.mjs.
4
+ *
5
+ * Same rules, same verdicts and same deny and ask text as src/commands/hook.ts, because it calls the same functions
6
+ * (evaluateOffline, guardPackMatch, neverListInputMatch). What it leaves out on purpose: the hosted plane, notify and
7
+ * OTLP, the update check, the IoC scan, usage ping, the dual-hook dedupe markers, the device salt and trail rotation.
8
+ * It imports no network module and no child process module (test/guard-bundle.test.ts enforces that on the bundle).
9
+ * Its only write while it guards is one append to the trail file (`--card`, started by the person, also writes one SVG). Any internal error lets the call through and leaves an `error` row.
10
+ */
11
+ import { appendFileSync, mkdirSync } from "node:fs";
12
+ import { dirname, isAbsolute } from "node:path";
13
+ import { basename } from "node:path";
14
+ import { eventIdOf, formatCard, pickNearMiss, recentNearMiss, ruleLabel, writeCardSvg } from "./card.js";
15
+ import { computeArgsHash } from "./hash.js";
16
+ import { deriveWireChangeFields } from "./diff-preview.js";
17
+ import { GUARD_DEFAULTS, GUARD_SHELL_RULES, guardContextFrom, guardHint, guardPackMatch, readGuardConfig } from "./guard-pack.js";
18
+ import { isNeverCode } from "./never-code.js";
19
+ import { NEVER_LIST_INPUT_RULES, neverListInputMatch } from "./never-list-input.js";
20
+ import { evaluateOffline, matchesNeverList } from "./offline-evaluate.js";
21
+ import { loadProjectPolicy } from "./project-policy.js";
22
+ import { findSampleTool } from "./sample-tools.js";
23
+ import { defaultSessionId, globalTrailPath, hostToProduct, readGlobalTrail } from "./trail.js";
24
+ /** Hosts the guard speaks. Any other id gets the Claude-shaped deny and no ask. */
25
+ export const GUARD_HOSTS = ["claude", "codex", "cursor", "copilot", "gemini"];
26
+ const hostId = (h) => (h ?? "claude").trim().toLowerCase() || "claude";
27
+ const ALLOW = { exitCode: 0, stdout: "", stderr: "" };
28
+ const GUARD_ASK = "GUARD_ASK";
29
+ const str = (v) => (typeof v === "string" && v ? v : undefined);
30
+ /**
31
+ * Hook envelopes of every host: Claude Code, Codex and Cursor (snake_case), Copilot CLI (camelCase, `toolArgs` can be a
32
+ * JSON string), Gemini CLI (`BeforeTool`, MCP tools named in `mcp_context`). Same reading as src/commands/hook.ts.
33
+ * Undefined when the text is not a JSON object with a tool name.
34
+ */
35
+ function parsePayload(text) {
36
+ let raw;
37
+ try {
38
+ raw = JSON.parse(text);
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
44
+ return undefined;
45
+ const p = raw;
46
+ const event = str(p.hook_event_name) ?? str(p.hookEventName);
47
+ const mcp = p.mcp_context && typeof p.mcp_context === "object" && !Array.isArray(p.mcp_context) ? p.mcp_context : undefined;
48
+ const geminiMcp = /^BeforeTool$/i.test(event ?? "") && str(mcp?.server_name) && str(mcp?.tool_name) ? `mcp__${str(mcp?.server_name)}__${str(mcp?.tool_name)}` : undefined;
49
+ const toolName = geminiMcp ?? str(p.tool_name) ?? str(p.toolName);
50
+ if (!toolName)
51
+ return undefined;
52
+ return { event, sessionId: str(p.session_id) ?? str(p.sessionId), cwd: str(p.cwd), toolName, toolInput: p.tool_input ?? p.toolInput ?? parseToolArgs(p.toolArgs) };
53
+ }
54
+ /** Copilot sends `toolArgs` as a JSON string. A string that does not parse stays a string, so the input rules fail open on it. */
55
+ function parseToolArgs(v) {
56
+ if (typeof v !== "string")
57
+ return v;
58
+ try {
59
+ return JSON.parse(v);
60
+ }
61
+ catch {
62
+ return v;
63
+ }
64
+ }
65
+ /** Each host reads its own deny body. Exit 2 blocks on all of them; the body carries the reason the agent sees. */
66
+ function deny(reason, host) {
67
+ const claude = { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason };
68
+ const body = host === "gemini" ? { decision: "deny", reason }
69
+ : host === "cursor" ? { permission: "deny", user_message: reason, agent_message: reason }
70
+ : host === "copilot" ? { permissionDecision: "deny", permissionDecisionReason: reason, hookSpecificOutput: claude }
71
+ : { hookSpecificOutput: claude };
72
+ return { exitCode: 2, stdout: `${JSON.stringify(body)}\n`, stderr: `${reason}\n` };
73
+ }
74
+ function ask(reason) {
75
+ const body = { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask", permissionDecisionReason: reason } };
76
+ return { exitCode: 0, stdout: `${JSON.stringify(body)}\n`, stderr: "" };
77
+ }
78
+ /** Same text as hook.ts denyReason. Never carries command text, a path or a matched secret. */
79
+ function denyReason(toolName, reasonCode, ruleId) {
80
+ if (reasonCode === "NEVER_EVENT" && ruleId) {
81
+ return `KYA denied ${toolName}: NEVER_EVENT (rule ${ruleId}; never-list rules cannot be overridden)`;
82
+ }
83
+ if ((isNeverCode(reasonCode) || reasonCode === GUARD_ASK) && reasonCode !== "NEVER_EVENT" && ruleId) {
84
+ const hint = guardHint(ruleId);
85
+ return `KYA denied ${toolName}: ${reasonCode} (${ruleId}).${hint ? ` ${hint}` : ""} Ask the user; see docs/guard-pack.md.`;
86
+ }
87
+ return `KYA denied ${toolName}: ${reasonCode}`;
88
+ }
89
+ function askReason(toolName, ruleId) {
90
+ const hint = ruleId ? guardHint(ruleId) : "";
91
+ return `KYA asks before ${toolName}: ${GUARD_ASK}${ruleId ? ` (${ruleId})` : ""}.${hint ? ` ${hint}` : ""} See docs/guard-pack.md.`;
92
+ }
93
+ /** The one write: append a JSON line to ~/.kya/trail.jsonl, same shape as appendTrail. No rotation, no salt file, no lock. */
94
+ function record(cwd, ev, env) {
95
+ const path = globalTrailPath(env);
96
+ mkdirSync(dirname(path), { recursive: true });
97
+ appendFileSync(path, `${JSON.stringify({ ...ev, project: ev.project ?? basename(cwd) })}\n`, "utf8");
98
+ }
99
+ /** Row for a call the guard did not judge. Returns the fail-open result; a failed write is said once on stderr. */
100
+ function failOpen(input, p, summary) {
101
+ const product = hostToProduct(hostId(input.host));
102
+ try {
103
+ record(p?.cwd ?? input.cwd, {
104
+ ts: new Date().toISOString(),
105
+ sessionId: p?.sessionId ?? defaultSessionId(input.env),
106
+ host: "ide",
107
+ product,
108
+ toolId: p?.toolName ?? "unknown",
109
+ verdict: "ALLOW",
110
+ outcome: "error",
111
+ reasonCode: "HOOK_ERROR",
112
+ mode: "offline",
113
+ summary,
114
+ }, input.env);
115
+ return ALLOW;
116
+ }
117
+ catch {
118
+ return { ...ALLOW, stderr: "KYA guard: error recording a failed hook call (the call was let through)\n" };
119
+ }
120
+ }
121
+ /** Evaluate one hook call. Synchronous; never throws. */
122
+ export function runGuard(input) {
123
+ const started = Date.now();
124
+ const host = hostId(input.host);
125
+ const product = hostToProduct(host);
126
+ let payload;
127
+ try {
128
+ payload = parsePayload(input.stdinText);
129
+ if (!payload)
130
+ return failOpen(input, undefined, "unreadable hook payload; the call was let through (fail-open)");
131
+ // The plugins declare PreToolUse (BeforeTool on Gemini) only; any other event carries no gate decision.
132
+ if (payload.event && !/^(PreToolUse|pre[_-]tool[_-]use|BeforeTool)$/i.test(payload.event))
133
+ return ALLOW;
134
+ const toolName = payload.toolName;
135
+ const cwd = payload.cwd && isAbsolute(payload.cwd) ? payload.cwd : input.cwd;
136
+ const guard = guardContextFrom(cwd, input.env);
137
+ // Same steps as runEvalTool offline: the sample tool decides irreversible and action class.
138
+ const sample = findSampleTool(toolName);
139
+ const args = payload.toolInput ?? {};
140
+ const argsHash = computeArgsHash(args);
141
+ const response = evaluateOffline({
142
+ toolId: toolName,
143
+ action: toolName,
144
+ argsHash,
145
+ irreversible: sample?.irreversible ?? false,
146
+ sessionRisk: "LOW",
147
+ approvalStatus: "NONE",
148
+ env: { host: "ide" },
149
+ ...(sample?.actionClass !== undefined ? { actionClass: sample.actionClass, dataClass: sample.dataClass } : {}),
150
+ }, "ide", args, loadProjectPolicy(cwd, () => undefined), guard);
151
+ // Only Claude Code has an ask this guard relies on: on every other host an ask rule is a deny (same as hook.ts askMode).
152
+ const isAsk = response.reasonCode === GUARD_ASK && host === "claude";
153
+ const askAsDeny = response.reasonCode === GUARD_ASK && !isAsk;
154
+ const outcome = isAsk ? "held" : response.verdict === "DENY" || askAsDeny ? "blocked" : "ran";
155
+ try {
156
+ // A secret-shaped write is denied because its text is a secret: no preview of that text reaches the trail.
157
+ const change = response.reasonCode === "NEVER_SECRET_WRITE" ? undefined : deriveWireChangeFields(toolName, args, input.env);
158
+ record(cwd, {
159
+ ts: new Date().toISOString(),
160
+ sessionId: payload.sessionId ?? defaultSessionId(input.env),
161
+ host: "ide",
162
+ product,
163
+ toolId: response.toolId ?? toolName,
164
+ verdict: response.verdict,
165
+ outcome,
166
+ reasonCode: response.reasonCode ?? "",
167
+ mode: "offline",
168
+ neverEvent: isNeverCode(response.reasonCode),
169
+ ...(response.ruleId ? { neverRule: response.ruleId } : {}),
170
+ argsHash: response.argsHash,
171
+ latencyMs: Date.now() - started,
172
+ ...(change ?? {}),
173
+ }, input.env);
174
+ }
175
+ catch {
176
+ /* the trail never decides the verdict */
177
+ }
178
+ if (response.verdict === "DENY" || askAsDeny)
179
+ return deny(denyReason(toolName, response.reasonCode, response.ruleId), host);
180
+ if (isAsk)
181
+ return ask(askReason(toolName, response.ruleId));
182
+ return ALLOW;
183
+ }
184
+ catch {
185
+ // Same stance as hook.ts: the never-list and the guard pack with its defaults still block when the evaluator broke.
186
+ try {
187
+ if (payload?.toolName) {
188
+ const cwd = payload.cwd && isAbsolute(payload.cwd) ? payload.cwd : input.cwd;
189
+ const inputHit = neverListInputMatch(payload.toolName, payload.toolInput, undefined, cwd);
190
+ const hit = inputHit ?? (matchesNeverList(payload.toolName) ? { rule: undefined } : undefined);
191
+ const guardHit = hit ? undefined : guardPackMatch(payload.toolName, payload.toolInput, guardContextFrom(cwd, input.env), GUARD_DEFAULTS);
192
+ if (hit || guardHit) {
193
+ const code = hit ? "NEVER_EVENT" : guardHit.code;
194
+ const rule = hit ? hit.rule : guardHit.rule;
195
+ try {
196
+ record(cwd, {
197
+ ts: new Date().toISOString(),
198
+ sessionId: payload.sessionId ?? defaultSessionId(input.env),
199
+ host: "ide",
200
+ product,
201
+ toolId: payload.toolName,
202
+ verdict: "DENY",
203
+ outcome: "blocked",
204
+ reasonCode: code,
205
+ mode: "offline",
206
+ neverEvent: isNeverCode(code),
207
+ ...(rule ? { neverRule: rule } : {}),
208
+ }, input.env);
209
+ }
210
+ catch { /* audit is best effort */ }
211
+ return deny(denyReason(payload.toolName, code, rule), host);
212
+ }
213
+ }
214
+ }
215
+ catch { /* fall through */ }
216
+ return failOpen(input, payload, "hook error; the call was let through (fail-open)");
217
+ }
218
+ }
219
+ const DAY_MS = 24 * 3600_000;
220
+ /** Text for /kya-guard:status. Reads the trail only. Never throws. */
221
+ export function guardStatus(env, now = new Date()) {
222
+ const ctx = guardContextFrom(process.cwd(), env);
223
+ const { problem } = readGuardConfig(ctx.home);
224
+ let rows = [];
225
+ let readable = true;
226
+ try {
227
+ rows = readGlobalTrail(env);
228
+ }
229
+ catch {
230
+ readable = false;
231
+ }
232
+ const since = now.getTime() - 7 * DAY_MS;
233
+ const week = rows.filter((e) => Date.parse(e.ts) >= since && e.outcome !== "error");
234
+ const stopped = week.filter((e) => e.outcome === "blocked");
235
+ const held = week.filter((e) => e.outcome === "held");
236
+ const last = [...stopped, ...held]
237
+ .sort((a, b) => Date.parse(b.ts) - Date.parse(a.ts))
238
+ .slice(0, 3);
239
+ const lines = [
240
+ "KYA Guard status",
241
+ `Rules loaded: ${NEVER_LIST_INPUT_RULES.length + GUARD_SHELL_RULES.length} (${NEVER_LIST_INPUT_RULES.length} never-list, ${GUARD_SHELL_RULES.length} guard shell), plus the secret-write and exfil checks.`,
242
+ readable ? `Calls seen in 7 days: ${week.length}` : "Calls seen in 7 days: unknown (the trail could not be read)",
243
+ `Calls blocked in 7 days: ${stopped.length} (${held.length} more held for you to answer)`,
244
+ last.length
245
+ ? `Last ${last.length} stopped: ${last.map((e) => { const r = e.neverRule ?? e.reasonCode; const l = ruleLabel(r); return `${l ? `${l} [${r}]` : r} (${e.ts.slice(0, 16).replace("T", " ")} UTC)`; }).join(", ")}`
246
+ : "Last stopped: none",
247
+ // The card is offered here, to the person. Never in the deny text the agent reads.
248
+ ...(recentNearMiss(week, now) ? ["Make a card to post about the last stop (an image with the rule, the host and the date, nothing private): /kya-guard:share"] : []),
249
+ ...(problem ? [`Config problem: ${problem}. The defaults are in force.`] : []),
250
+ "To allow one rule: edit ~/.kya/config.json yourself and add a guardAllow entry (rule id plus an exact path or command). The agent cannot edit that file.",
251
+ "Full report: npx @shield-agent/kya receipt --open",
252
+ ];
253
+ return `${lines.join("\n")}\n`;
254
+ }
255
+ /**
256
+ * `--card [--id <id>]`: write the near-miss card (SVG only: this bundle starts no process, so it makes no PNG) and print the caption.
257
+ * Always exit 0: the slash command shows the text, and its `|| echo` fallback is for a missing node, not for "no card".
258
+ */
259
+ export function guardCard(env, id) {
260
+ try {
261
+ const pick = pickNearMiss(readGlobalTrail(env), id);
262
+ if (!pick.ok)
263
+ return { exitCode: 0, text: `${pick.message}\n` };
264
+ const { svgPath, input } = writeCardSvg(env, pick.event);
265
+ return {
266
+ exitCode: 0,
267
+ text: formatCard({ svgPath, input, id: eventIdOf(pick.event), pngNote: "This plugin makes the SVG only. For a PNG too, run: npx @shield-agent/kya receipt --card" }),
268
+ };
269
+ }
270
+ catch {
271
+ return { exitCode: 0, text: "KYA Guard could not make the card (the trail or the cards folder could not be read or written). Nothing was sent anywhere.\n" };
272
+ }
273
+ }
274
+ //# sourceMappingURL=guard-lite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-lite.js","sourceRoot":"","sources":["../src/guard-lite.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- export type GuardCode = "NEVER_SHELL" | "NEVER_SECRET_READ" | "NEVER_SECRET_WRITE" | "NEVER_EXFIL";
1
+ /** GUARD_ASK is the second outcome: the human decides (see ASK_RULES). It is not a NEVER_ code, so it is not counted as a block. */
2
+ export type GuardCode = "NEVER_SHELL" | "NEVER_SECRET_READ" | "NEVER_SECRET_WRITE" | "NEVER_EXFIL" | "GUARD_ASK";
2
3
  export interface GuardHit {
3
4
  readonly rule: string;
4
5
  readonly code: GuardCode;
@@ -26,7 +27,9 @@ export interface GuardConfig {
26
27
  }
27
28
  export declare const GUARD_DEFAULTS: GuardConfig;
28
29
  /** Rule ids of the shell family, in match order. Listed in docs/guard-pack.md. */
29
- export declare const GUARD_SHELL_RULES: readonly ["rm-root-home", "terraform-destroy", "sql-drop", "git-force-push", "git-delete-default-branch", "git-hard-reset-clean", "docker-volumes", "k8s-delete-ns", "cloud-delete", "railway-volume-delete"];
30
+ export declare const GUARD_SHELL_RULES: readonly ["rm-root-home", "terraform-destroy", "sql-drop", "git-force-push", "git-delete-default-branch", "git-hard-reset-clean", "docker-volumes", "k8s-delete-ns", "cloud-delete", "railway-volume-delete", "sql-delete-all", "prisma-migrate-reset", "prisma-accept-data-loss", "supabase-reset-linked", "rm-project-wipe", "rm-git-dir", "mongo-drop-database", "redis-flush", "fly-destroy", "vercel-remove", "gh-repo-delete"];
31
+ /** Rules whose outcome is ask, not deny: routine on a local database or a scratch folder, catastrophic on a remote one. */
32
+ export declare const ASK_RULES: ReadonlySet<string>;
30
33
  /** Home for the guard: an absolute `HOME` from the given env, else the OS home. */
31
34
  export declare function guardContextFrom(cwd: string, env?: NodeJS.ProcessEnv): GuardContext;
32
35
  /**
@@ -55,4 +58,6 @@ export declare function matchesShellNeverList(command: string, ctx: {
55
58
  }): string | null;
56
59
  /** Actionable text for a deny, from the rule id alone (no command text, no matched secret). */
57
60
  export declare function guardHint(rule: string): string;
61
+ /** Human phrase for a guard-pack rule id (the `secret-read-*`, `secret-write-*` and `exfil-*` families included), or undefined. */
62
+ export declare function guardLabel(rule: string): string | undefined;
58
63
  //# sourceMappingURL=guard-pack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guard-pack.d.ts","sourceRoot":"","sources":["../src/guard-pack.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,aAAa,CAAC;AAEnG,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,qGAAqG;IACrG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,uBAAuB;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC;CAC5C;AAED,eAAO,MAAM,cAAc,EAAE,WAA4F,CAAC;AAE1H,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,+MAGpB,CAAC;AAMX,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAUhG;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAmCrH;AAwhBD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,EAClB,GAAG,EAAE,YAAY,EACjB,GAAG,GAAE,WAA4B,GAChC,QAAQ,GAAG,SAAS,CA0CtB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAM7H;AAgBD,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO9C"}
1
+ {"version":3,"file":"guard-pack.d.ts","sourceRoot":"","sources":["../src/guard-pack.ts"],"names":[],"mappings":"AAsBA,oIAAoI;AACpI,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,aAAa,GAAG,WAAW,CAAC;AAEjH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,qGAAqG;IACrG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,uBAAuB;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC;CAC5C;AAED,eAAO,MAAM,cAAc,EAAE,WAA4F,CAAC;AAE1H,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,saAMpB,CAAC;AAEX,2HAA2H;AAC3H,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,CAAqG,CAAC;AAMhJ,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAUhG;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAmCrH;AA8pBD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,EAClB,GAAG,EAAE,YAAY,EACjB,GAAG,GAAE,WAA4B,GAChC,QAAQ,GAAG,SAAS,CAiDtB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAM7H;AA4BD,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO9C;AA6BD,mIAAmI;AACnI,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO3D"}
@@ -21,7 +21,12 @@ export const GUARD_DEFAULTS = { shellNeverList: true, secretsGuard: true, exfilG
21
21
  export const GUARD_SHELL_RULES = [
22
22
  "rm-root-home", "terraform-destroy", "sql-drop", "git-force-push", "git-delete-default-branch", "git-hard-reset-clean",
23
23
  "docker-volumes", "k8s-delete-ns", "cloud-delete", "railway-volume-delete",
24
+ "sql-delete-all", "prisma-migrate-reset", "prisma-accept-data-loss", "supabase-reset-linked", "rm-project-wipe", "rm-git-dir",
25
+ "mongo-drop-database", "redis-flush", "fly-destroy", "vercel-remove", "gh-repo-delete",
26
+ // new-rule:ids (pnpm new-rule adds a rule id above this line)
24
27
  ];
28
+ /** Rules whose outcome is ask, not deny: routine on a local database or a scratch folder, catastrophic on a remote one. */
29
+ export const ASK_RULES = new Set(["sql-delete-all", "prisma-migrate-reset", "prisma-accept-data-loss", "rm-project-wipe"]);
25
30
  // ---------------------------------------------------------------------------------------------
26
31
  // Config
27
32
  // ---------------------------------------------------------------------------------------------
@@ -353,6 +358,128 @@ const railwayVolumeDelete = (sh) => (hasCommand(sh, (c) => HTTP_CLIENTS.has(c.na
353
358
  const { pos } = words(c.args);
354
359
  return pos[0] === "volume" && pos[1] === "delete";
355
360
  }) ? "" : NO;
361
+ /** Args after `tool`, run directly or through a package runner (`npx prisma ...`, `pnpm exec prisma@5 ...`). */
362
+ const RUNNERS = new Set(["npx", "pnpx", "bunx", "yarn", "pnpm", "npm", "bun"]);
363
+ function toolArgs(c, tool) {
364
+ if (c.name === tool)
365
+ return c.args;
366
+ if (!RUNNERS.has(c.name))
367
+ return NO;
368
+ const i = c.args.findIndex((a) => a === tool || a.startsWith(`${tool}@`));
369
+ return i >= 0 ? c.args.slice(i + 1) : NO;
370
+ }
371
+ /** DELETE FROM <table> with no WHERE before the end of the statement (`;` or the closing quote). */
372
+ const SQL_DELETE = /\bdelete\s+from\s+(?:only\s+)?(?:[\w.]+|\\?"[\w. ]+\\?"|`[^`]+`)([^;"'`]*)/gi;
373
+ const sqlDeleteAll = (sh) => {
374
+ if (!hasCommand(sh, (c) => DB_CLIENTS.has(c.name)))
375
+ return NO;
376
+ for (const m of sh.raw.matchAll(SQL_DELETE))
377
+ if (!/\bwhere\b/i.test(m[1]))
378
+ return "";
379
+ return NO;
380
+ };
381
+ const PRISMA_VALUE_OPTS = new Set(["--schema", "--config"]);
382
+ const prismaWords = (sh) => {
383
+ const out = [];
384
+ hasCommand(sh, (c) => {
385
+ const a = toolArgs(c, "prisma");
386
+ if (a)
387
+ out.push(words(a, PRISMA_VALUE_OPTS));
388
+ return false;
389
+ });
390
+ return out;
391
+ };
392
+ const prismaMigrateReset = (sh) => (prismaWords(sh).some((w) => w.pos[0] === "migrate" && w.pos[1] === "reset") ? "" : NO);
393
+ const prismaAcceptDataLoss = (sh) => (prismaWords(sh).some((w) => w.pos[0] === "db" && w.pos[1] === "push" && hasLong(w.flags, "--accept-data-loss")) ? "" : NO);
394
+ const SUPABASE_VALUE_OPTS = new Set(["--workdir", "--db-url", "--password", "-p", "--project-ref", "--version"]);
395
+ const supabaseResetLinked = (sh) => hasCommand(sh, (c) => {
396
+ const a = toolArgs(c, "supabase");
397
+ if (!a)
398
+ return false;
399
+ const { pos, flags } = words(a, SUPABASE_VALUE_OPTS);
400
+ return pos[0] === "db" && pos[1] === "reset" && (hasLong(flags, "--linked") || hasLong(flags, "--db-url"));
401
+ }) ? "" : NO;
402
+ /** `rm -r` targets that resolve to the project root (`.`, `./*`, `*`, its absolute path) or to its `.git`. A `cd` elsewhere in the command changes what a relative target means, so those are skipped. */
403
+ function rmProjectTargets(sh, ctx, want) {
404
+ const root = ctx.projectRoot ?? ctx.cwd;
405
+ const moved = hasCommand(sh, (c) => (c.name === "cd" || c.name === "pushd") && c.args.some((a) => a !== "." && a !== "./"));
406
+ const assigned = runtimeVars(sh.raw);
407
+ const hits = [];
408
+ hasCommand(sh, (c) => {
409
+ if (c.name !== "rm")
410
+ return false;
411
+ const { pos, flags } = words(c.args);
412
+ if (!isRecursiveFlags(flags) || (want === "root" && !isForceFlags(flags)))
413
+ return false;
414
+ for (const t of pos) {
415
+ if (t.includes("$(") || t.includes("`") || [...t.matchAll(/\$\{?([A-Za-z_][A-Za-z0-9_]*)/g)].some((m) => assigned.has(m[1])))
416
+ continue;
417
+ if (moved && !t.startsWith("/") && !t.startsWith("~") && !t.startsWith("$"))
418
+ continue;
419
+ const p = absPath(t, ctx).replace(/\/(\.\*|\*)$/, "");
420
+ if (want === "root" ? p === root : p === `${root}/.git`)
421
+ hits.push(p);
422
+ }
423
+ return false;
424
+ });
425
+ return hits;
426
+ }
427
+ const rmProjectWipe = (sh, ctx) => {
428
+ const t = rmProjectTargets(sh, ctx, "root");
429
+ return t.length ? t : NO;
430
+ };
431
+ const rmGitDir = (sh, ctx) => {
432
+ const t = rmProjectTargets(sh, ctx, "git");
433
+ return t.length ? t : NO;
434
+ };
435
+ const mongoDropDatabase = (sh) => (hasCommand(sh, named("mongosh", "mongo")) && /\bdropDatabase\s*\(/.test(sh.raw) ? "" : NO);
436
+ /** Every server a redis-cli call selects: all `-h` values and all `-u`/`--uri` URLs, in any order. An unreadable value counts as remote. */
437
+ const redisHosts = (args) => {
438
+ const out = [];
439
+ args.forEach((a, i) => {
440
+ if (a === "-h")
441
+ out.push(args[i + 1] ?? "");
442
+ else if (a === "-u" || a === "--uri")
443
+ out.push(/^unix:\/\//i.test(args[i + 1] ?? "") ? "localhost" : hostOf(args[i + 1] ?? "") ?? "");
444
+ });
445
+ return out;
446
+ };
447
+ // Deny unless every host-selecting option is loopback: `-h localhost -h prod` and `-u redis://127.0.0.1 -h prod` are remote.
448
+ const redisFlush = (sh) => hasCommand(sh, (c) => named("redis-cli", "valkey-cli")(c) && /\bflush(?:all|db)\b/i.test(sh.raw) && redisHosts(c.args).some((h) => !LOOPBACK.test(h))) ? "" : NO;
449
+ const FLY_VALUE_OPTS = new Set(["-a", "--app", "-c", "--config", "-t", "--access-token", "-o", "--org", "--region", "-r"]);
450
+ const flyDestroy = (sh) => hasCommand(sh, (c) => {
451
+ if (!named("fly", "flyctl")(c))
452
+ return false;
453
+ const { pos } = words(c.args, FLY_VALUE_OPTS);
454
+ const group = /^(volumes?|vol|apps?)$/.test(pos[0] ?? "");
455
+ return pos[0] === "destroy" || (group && /^(destroy|delete)$/.test(pos[1] ?? ""));
456
+ }) ? "" : NO;
457
+ const VERCEL_VALUE_OPTS = new Set(["-t", "--token", "-S", "--scope", "-A", "--local-config", "--global-config", "--cwd", "-d"]);
458
+ const vercelRemove = (sh) => hasCommand(sh, (c) => {
459
+ const a = toolArgs(c, "vercel") ?? toolArgs(c, "vc");
460
+ if (!a)
461
+ return false;
462
+ const { pos } = words(a, VERCEL_VALUE_OPTS);
463
+ const rm = (w) => w === "remove" || w === "rm";
464
+ return rm(pos[0]) || (pos[0] === "project" && rm(pos[1]));
465
+ }) ? "" : NO;
466
+ const ghDeleteMethod = (args) => args.some((a, i) => {
467
+ const v = a === "-X" || a === "--method" ? args[i + 1] : a.startsWith("--method=") ? a.slice(9) : /^-X./.test(a) ? a.slice(2).replace(/^=/, "") : undefined;
468
+ return v !== undefined && v.toUpperCase() === "DELETE";
469
+ });
470
+ /** `repos/...` path of a `gh api` endpoint: scheme, host, a GHES `api/v3/` prefix, leading slashes, query and fragment removed. */
471
+ const ghRepoPath = (endpoint) => /^repos\//i.test(endpoint.replace(/^https?:\/\/[^/]+/i, "").replace(/[?#].*$/, "").replace(/^\/+/, "").replace(/^api\/v3\//i, ""));
472
+ const GH_VALUE_OPTS = new Set(["-R", "--repo", "-H", "--header", "--hostname", "-X", "--method", "-f", "-F", "--field", "--raw-field", "--jq", "-q", "-t", "--template", "--input", "--cache", "-p", "--preview"]);
473
+ const ghRepoDelete = (sh) => hasCommand(sh, (c) => {
474
+ if (c.name !== "gh")
475
+ return false;
476
+ const { pos } = words(c.args, GH_VALUE_OPTS);
477
+ if (pos[0] === "repo")
478
+ return pos[1] === "delete";
479
+ // Every operand is checked, so a stray value cannot hide the endpoint.
480
+ return pos[0] === "api" && ghDeleteMethod(c.args) && pos.slice(1).some(ghRepoPath);
481
+ }) ? "" : NO;
482
+ // new-rule:matchers (pnpm new-rule adds a matcher stub above this line)
356
483
  const SHELL_MATCHERS = [
357
484
  ["rm-root-home", rmRoot],
358
485
  ["terraform-destroy", terraformDestroy],
@@ -364,6 +491,18 @@ const SHELL_MATCHERS = [
364
491
  ["k8s-delete-ns", k8sDeleteNs],
365
492
  ["cloud-delete", cloudDelete],
366
493
  ["railway-volume-delete", railwayVolumeDelete],
494
+ ["sql-delete-all", sqlDeleteAll],
495
+ ["prisma-migrate-reset", prismaMigrateReset],
496
+ ["prisma-accept-data-loss", prismaAcceptDataLoss],
497
+ ["supabase-reset-linked", supabaseResetLinked],
498
+ ["rm-project-wipe", rmProjectWipe],
499
+ ["rm-git-dir", rmGitDir],
500
+ ["mongo-drop-database", mongoDropDatabase],
501
+ ["redis-flush", redisFlush],
502
+ ["fly-destroy", flyDestroy],
503
+ ["vercel-remove", vercelRemove],
504
+ ["gh-repo-delete", ghRepoDelete],
505
+ // new-rule:entries (pnpm new-rule adds an entry above this line)
367
506
  ];
368
507
  // ---------------------------------------------------------------------------------------------
369
508
  // Secrets: reads, writes, exfiltration
@@ -632,8 +771,9 @@ function* shellHits(sh, ctx, cfg) {
632
771
  if (t === undefined)
633
772
  continue;
634
773
  const targets = typeof t === "string" ? [t] : t;
774
+ const code = ASK_RULES.has(rule) ? "GUARD_ASK" : "NEVER_SHELL";
635
775
  for (const target of targets)
636
- yield { rule, code: "NEVER_SHELL", ...(target ? { target } : {}) };
776
+ yield { rule, code, ...(target ? { target } : {}) };
637
777
  }
638
778
  }
639
779
  if (cfg.secretsGuard) {
@@ -659,12 +799,18 @@ export function guardPackMatch(toolId, toolInput, ctx, cfg = GUARD_DEFAULTS) {
659
799
  const f = fieldsOf(toolInput);
660
800
  if (!f)
661
801
  return NO;
802
+ // A deny anywhere in the call beats an ask, so `prisma migrate reset && rm -rf ~` is a deny.
803
+ let ask;
662
804
  if (isShellTool(toolId)) {
663
805
  for (const cmd of commandStrings(f)) {
664
806
  const sh = parseShell(cmd, ctx.home);
665
- for (const hit of shellHits(sh, ctx, cfg))
666
- if (!allowed(hit, cfg, ctx, cmd))
807
+ for (const hit of shellHits(sh, ctx, cfg)) {
808
+ if (allowed(hit, cfg, ctx, cmd))
809
+ continue;
810
+ if (hit.code !== "GUARD_ASK")
667
811
  return hit;
812
+ ask ??= hit;
813
+ }
668
814
  }
669
815
  }
670
816
  const path = typeof f.file_path === "string" ? f.file_path : typeof f.path === "string" ? f.path : undefined;
@@ -683,7 +829,7 @@ export function guardPackMatch(toolId, toolInput, ctx, cfg = GUARD_DEFAULTS) {
683
829
  }
684
830
  }
685
831
  if (!cfg.secretsGuard)
686
- return NO;
832
+ return ask;
687
833
  if (isWriteLike(toolId)) {
688
834
  for (const text of writtenStrings(f)) {
689
835
  const kind = secretShape(text);
@@ -705,6 +851,7 @@ export function guardPackMatch(toolId, toolInput, ctx, cfg = GUARD_DEFAULTS) {
705
851
  return hit;
706
852
  }
707
853
  }
854
+ return ask;
708
855
  }
709
856
  catch {
710
857
  // Fail open: a parse problem never adds a deny.
@@ -735,6 +882,18 @@ const HINTS = {
735
882
  "k8s-delete-ns": "Deleting a namespace or everything in all namespaces needs the user to run it.",
736
883
  "cloud-delete": "Cloud deletes are not reversible. Ask the user to run this.",
737
884
  "railway-volume-delete": "Volume deletes are not reversible. Ask the user to run this.",
885
+ "sql-delete-all": "DELETE without WHERE removes every row. Add a WHERE clause, or the user approves it.",
886
+ "prisma-migrate-reset": "prisma migrate reset drops the database. Check DATABASE_URL points at a local database, or the user approves it.",
887
+ "prisma-accept-data-loss": "--accept-data-loss can drop columns and tables. Run prisma db push without it, or the user approves it.",
888
+ "supabase-reset-linked": "supabase db reset on a linked project wipes the remote database. Reset the local stack only, without --linked or --db-url.",
889
+ "rm-project-wipe": "This removes the whole project folder. Delete the specific folder instead (dist, build, node_modules), or the user approves it.",
890
+ "rm-git-dir": "Removing .git deletes the project history. Ask the user.",
891
+ "mongo-drop-database": "dropDatabase deletes a whole database. Ask the user to run it, after a backup.",
892
+ "redis-flush": "FLUSHALL and FLUSHDB on a remote server delete every key. Ask the user to run it.",
893
+ "fly-destroy": "Destroying a Fly volume or app is not reversible. Use fly volumes list, or ask the user to run it.",
894
+ "vercel-remove": "Removing a Vercel project or deployment is not reversible. Ask the user to run it.",
895
+ "gh-repo-delete": "Deleting a GitHub repository is not reversible. Ask the user to run it.",
896
+ // new-rule:hints (pnpm new-rule adds a hint above this line)
738
897
  "guard-config-write": "Do not edit the KYA guard settings. Ask the user.",
739
898
  };
740
899
  /** Actionable text for a deny, from the rule id alone (no command text, no matched secret). */
@@ -750,4 +909,43 @@ export function guardHint(rule) {
750
909
  return "Do not send environment variables or credential files over the network.";
751
910
  return "";
752
911
  }
912
+ /** Human phrase per rule, for "My coding agent tried to <label>." (near-miss card, /kya-guard:status). Plain words, no command text. */
913
+ const LABELS = {
914
+ "rm-root-home": "wipe your home folder",
915
+ "terraform-destroy": "destroy your infrastructure",
916
+ "sql-drop": "drop or empty a database table",
917
+ "git-force-push": "force push over git history",
918
+ "git-delete-default-branch": "delete the main branch",
919
+ "git-hard-reset-clean": "throw away all uncommitted work",
920
+ "docker-volumes": "delete Docker volumes",
921
+ "k8s-delete-ns": "delete a Kubernetes namespace",
922
+ "cloud-delete": "delete cloud resources",
923
+ "railway-volume-delete": "delete a cloud volume",
924
+ "sql-delete-all": "delete every row in a table",
925
+ "prisma-migrate-reset": "reset your database",
926
+ "prisma-accept-data-loss": "push a schema change that can drop data",
927
+ "supabase-reset-linked": "wipe a linked Supabase database",
928
+ "rm-project-wipe": "wipe the project folder",
929
+ "rm-git-dir": "delete the git history of the project",
930
+ "mongo-drop-database": "drop a MongoDB database",
931
+ "redis-flush": "flush every key on a Redis server",
932
+ "fly-destroy": "destroy a Fly app or volume",
933
+ "vercel-remove": "remove a Vercel project",
934
+ "gh-repo-delete": "delete a GitHub repository",
935
+ // new-rule:labels (pnpm new-rule adds a label above this line)
936
+ "guard-config-write": "edit the KYA guard settings",
937
+ };
938
+ /** Human phrase for a guard-pack rule id (the `secret-read-*`, `secret-write-*` and `exfil-*` families included), or undefined. */
939
+ export function guardLabel(rule) {
940
+ const fixed = LABELS[rule];
941
+ if (fixed)
942
+ return fixed;
943
+ if (rule.startsWith("secret-read-"))
944
+ return "read a credentials file";
945
+ if (rule.startsWith("secret-write-"))
946
+ return "write a secret into a file";
947
+ if (rule.startsWith("exfil-"))
948
+ return "send secrets over the network";
949
+ return undefined;
950
+ }
753
951
  //# sourceMappingURL=guard-pack.js.map