@pushary/agent-hooks 0.55.0 → 0.57.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.
@@ -13,17 +13,18 @@ import {
13
13
  describeToolCall,
14
14
  fetchModeState,
15
15
  getPolicy,
16
+ repoKeyFor,
16
17
  reportEvent,
17
18
  resolvePolicy,
18
19
  waitForAnswer
19
- } from "../chunk-H6RGO7DN.js";
20
+ } from "../chunk-LEWSYLHL.js";
20
21
  import {
21
22
  getMachineId
22
23
  } from "../chunk-RN3NOEJF.js";
23
24
  import "../chunk-DWED7BS3.js";
24
25
  import {
25
26
  redactSecrets
26
- } from "../chunk-P5VOLS3Q.js";
27
+ } from "../chunk-SYAAGYLZ.js";
27
28
  import {
28
29
  getApiKey,
29
30
  getBaseUrl
@@ -528,8 +529,8 @@ var createRemoteApprover = (deps) => {
528
529
  const sessionId = deps.getSessionId?.();
529
530
  const modeState = await fetchModeState2(deps.apiKey, sessionId);
530
531
  if (modeState.kill) return deny("Stopped by user \u2014 this agent was halted from Pushary");
531
- const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion, "claude_code");
532
- const policy = resolvePolicy2(policyConfig, toolName, modeState.mode, input, deps.cwd);
532
+ const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion, "claude_code", true);
533
+ const policy = resolvePolicy2(policyConfig, toolName, modeState.mode, input, deps.cwd, repoKeyFor(deps.cwd));
533
534
  if (policy.timeoutSeconds === 0 && policy.timeoutAction === "approve") return allow(input);
534
535
  if (policy.timeoutSeconds === 0 && policy.timeoutAction === "deny") {
535
536
  return deny(`Denied by policy for ${policy.tool}`);
@@ -6,15 +6,15 @@ import {
6
6
  removeCodexHooks,
7
7
  removeGeminiSettings,
8
8
  removeInstructionBlock
9
- } from "../chunk-2VW2FEYC.js";
9
+ } from "../chunk-GSERIC6Q.js";
10
10
  import {
11
11
  execNpm
12
12
  } from "../chunk-J7JWI3KU.js";
13
13
  import {
14
14
  removeClaudeMcpServers,
15
15
  removePusharySettings
16
- } from "../chunk-PSZAXNUB.js";
17
- import "../chunk-P5VOLS3Q.js";
16
+ } from "../chunk-QYQO6RHJ.js";
17
+ import "../chunk-SYAAGYLZ.js";
18
18
 
19
19
  // bin/pushary-clean.ts
20
20
  import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
@@ -31,21 +31,21 @@ import {
31
31
  toCodexWire,
32
32
  toPolicyLookup,
33
33
  waitForAnswer
34
- } from "../chunk-H6RGO7DN.js";
34
+ } from "../chunk-LEWSYLHL.js";
35
35
  import {
36
36
  getMachineId
37
37
  } from "../chunk-RN3NOEJF.js";
38
38
  import {
39
39
  isGatingMoment,
40
40
  recordKeylessMoment
41
- } from "../chunk-UXIAIMSS.js";
41
+ } from "../chunk-5TECIV6T.js";
42
42
  import "../chunk-DWED7BS3.js";
43
43
  import {
44
44
  DECISION_LINE_MAX,
45
45
  effectiveWaitSeconds,
46
46
  hookWaitClamped,
47
47
  hookWaitDeadline
48
- } from "../chunk-P5VOLS3Q.js";
48
+ } from "../chunk-SYAAGYLZ.js";
49
49
  import {
50
50
  getApiKey
51
51
  } from "../chunk-NKXSILEW.js";
@@ -3,12 +3,12 @@ import {
3
3
  askUser,
4
4
  reportEvent,
5
5
  waitForAnswer
6
- } from "../chunk-H6RGO7DN.js";
6
+ } from "../chunk-LEWSYLHL.js";
7
7
  import {
8
8
  getMachineId
9
9
  } from "../chunk-RN3NOEJF.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-P5VOLS3Q.js";
11
+ import "../chunk-SYAAGYLZ.js";
12
12
  import {
13
13
  getApiKey
14
14
  } from "../chunk-NKXSILEW.js";
@@ -11,18 +11,18 @@ import {
11
11
  missingGeminiHookEvents,
12
12
  readCodexMcpAuth,
13
13
  untrustedCodexHookEvents
14
- } from "../chunk-2VW2FEYC.js";
14
+ } from "../chunk-GSERIC6Q.js";
15
15
  import {
16
16
  execNpm
17
17
  } from "../chunk-J7JWI3KU.js";
18
18
  import {
19
19
  readLedgerSummary
20
- } from "../chunk-UXIAIMSS.js";
20
+ } from "../chunk-5TECIV6T.js";
21
21
  import {
22
22
  callMcpTool,
23
23
  sendMcpRequest
24
24
  } from "../chunk-DWED7BS3.js";
25
- import "../chunk-P5VOLS3Q.js";
25
+ import "../chunk-SYAAGYLZ.js";
26
26
  import {
27
27
  getBaseUrl
28
28
  } from "../chunk-NKXSILEW.js";
@@ -23,21 +23,21 @@ import {
23
23
  savePendingQuestion,
24
24
  sendNotification,
25
25
  waitForAnswer
26
- } from "../chunk-H6RGO7DN.js";
26
+ } from "../chunk-LEWSYLHL.js";
27
27
  import {
28
28
  getMachineId
29
29
  } from "../chunk-RN3NOEJF.js";
30
30
  import {
31
31
  isGatingMoment,
32
32
  recordKeylessMoment
33
- } from "../chunk-UXIAIMSS.js";
33
+ } from "../chunk-5TECIV6T.js";
34
34
  import "../chunk-DWED7BS3.js";
35
35
  import {
36
36
  DECISION_LINE_MAX,
37
37
  effectiveWaitSeconds,
38
38
  hookWaitClamped,
39
39
  hookWaitDeadline
40
- } from "../chunk-P5VOLS3Q.js";
40
+ } from "../chunk-SYAAGYLZ.js";
41
41
  import {
42
42
  getApiKey
43
43
  } from "../chunk-NKXSILEW.js";
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-XKXF2G7T.js";
5
- import "../chunk-PSZAXNUB.js";
4
+ } from "../chunk-NPYANTRC.js";
5
+ import "../chunk-QYQO6RHJ.js";
6
6
  import "../chunk-YHG74UFF.js";
7
- import "../chunk-H6RGO7DN.js";
7
+ import "../chunk-LEWSYLHL.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-UXIAIMSS.js";
9
+ import "../chunk-5TECIV6T.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-P5VOLS3Q.js";
11
+ import "../chunk-SYAAGYLZ.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleNotification
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-notification-hook.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionDenied
4
- } from "../chunk-XKXF2G7T.js";
5
- import "../chunk-PSZAXNUB.js";
4
+ } from "../chunk-NPYANTRC.js";
5
+ import "../chunk-QYQO6RHJ.js";
6
6
  import "../chunk-YHG74UFF.js";
7
- import "../chunk-H6RGO7DN.js";
7
+ import "../chunk-LEWSYLHL.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-UXIAIMSS.js";
9
+ import "../chunk-5TECIV6T.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-P5VOLS3Q.js";
11
+ import "../chunk-SYAAGYLZ.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-permission-denied-hook.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionRequest
4
- } from "../chunk-XKXF2G7T.js";
5
- import "../chunk-PSZAXNUB.js";
4
+ } from "../chunk-NPYANTRC.js";
5
+ import "../chunk-QYQO6RHJ.js";
6
6
  import "../chunk-YHG74UFF.js";
7
- import "../chunk-H6RGO7DN.js";
7
+ import "../chunk-LEWSYLHL.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-UXIAIMSS.js";
9
+ import "../chunk-5TECIV6T.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-P5VOLS3Q.js";
11
+ import "../chunk-SYAAGYLZ.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-permission-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePostToolUse
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-post-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleUserPrompt
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-prompt-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionEnd
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-session-end-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionStart
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-session-start-hook.ts
@@ -13,7 +13,7 @@ import {
13
13
  renderAgentInstructions,
14
14
  renderProjectAgentInstructions,
15
15
  writeInstructionBlock
16
- } from "../chunk-2VW2FEYC.js";
16
+ } from "../chunk-GSERIC6Q.js";
17
17
  import {
18
18
  execNpm,
19
19
  npmErrorMessage,
@@ -25,15 +25,15 @@ import {
25
25
  addClaudeMcpServer,
26
26
  addPusharyHooks,
27
27
  addPusharyToolPermissions
28
- } from "../chunk-PSZAXNUB.js";
28
+ } from "../chunk-QYQO6RHJ.js";
29
29
  import {
30
30
  reportEvent
31
- } from "../chunk-H6RGO7DN.js";
31
+ } from "../chunk-LEWSYLHL.js";
32
32
  import "../chunk-RN3NOEJF.js";
33
33
  import "../chunk-DWED7BS3.js";
34
34
  import {
35
35
  isValidApiKey
36
- } from "../chunk-P5VOLS3Q.js";
36
+ } from "../chunk-SYAAGYLZ.js";
37
37
  import "../chunk-NKXSILEW.js";
38
38
 
39
39
  // bin/pushary-setup.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readLedgerSummary
4
- } from "../chunk-UXIAIMSS.js";
5
- import "../chunk-P5VOLS3Q.js";
4
+ } from "../chunk-5TECIV6T.js";
5
+ import "../chunk-SYAAGYLZ.js";
6
6
  import {
7
7
  getApiKey
8
8
  } from "../chunk-NKXSILEW.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-stop-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStopFailure
4
- } from "../chunk-H6RGO7DN.js";
4
+ } from "../chunk-LEWSYLHL.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-P5VOLS3Q.js";
7
+ import "../chunk-SYAAGYLZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-stopfailure-hook.ts
@@ -8,8 +8,8 @@ import {
8
8
  addClaudeMcpServer,
9
9
  addPusharyHooks,
10
10
  addPusharyToolPermissions
11
- } from "../chunk-PSZAXNUB.js";
12
- import "../chunk-P5VOLS3Q.js";
11
+ } from "../chunk-QYQO6RHJ.js";
12
+ import "../chunk-SYAAGYLZ.js";
13
13
  import {
14
14
  getApiKey
15
15
  } from "../chunk-NKXSILEW.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-P5VOLS3Q.js";
3
+ } from "./chunk-SYAAGYLZ.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_BUDGETS
3
- } from "./chunk-P5VOLS3Q.js";
3
+ } from "./chunk-SYAAGYLZ.js";
4
4
 
5
5
  // src/codex-config.ts
6
6
  import { createHash } from "crypto";
@@ -10,13 +10,16 @@ import {
10
10
  DECISION_LINE_MAX,
11
11
  classifyDecisionRisk,
12
12
  isApprovalMode,
13
+ isRepoKeyMatch,
13
14
  isSafeReadOnlyCommand,
15
+ localRepoKey,
14
16
  matchRankWeight,
15
17
  matchToolPatternAny,
18
+ normalizeRepoRemote,
16
19
  policyArgForms,
17
20
  redactSecrets,
18
21
  redactSecretsDeep
19
- } from "./chunk-P5VOLS3Q.js";
22
+ } from "./chunk-SYAAGYLZ.js";
20
23
  import {
21
24
  getApiKey,
22
25
  getBaseUrl
@@ -47,14 +50,19 @@ var isWaitForAnswerResponse = (data) => {
47
50
 
48
51
  // src/policy.ts
49
52
  var CACHE_TTL_MS = 60 * 1e3;
50
- var cacheFile = (apiKey, agent) => {
51
- const hash = createHash("sha256").update(agent ? `${apiKey}:${agent}` : apiKey).digest("hex").slice(0, 12);
53
+ var policyCacheFile = (apiKey, agent, repoAware = false) => {
54
+ const parts = [apiKey, agent, repoAware ? "repoAware" : void 0].filter(Boolean);
55
+ const hash = createHash("sha256").update(parts.join(":")).digest("hex").slice(0, 12);
52
56
  return join(tmpdir(), `pushary-policy-${hash}.json`);
53
57
  };
54
- var fetchPolicy = async (apiKey, agent) => {
58
+ var fetchPolicy = async (apiKey, agent, repoAware = false) => {
55
59
  return withRetry(async () => {
56
60
  const baseUrl = getBaseUrl();
57
- const url = agent ? `${baseUrl}/api/mcp/policy?agent=${encodeURIComponent(agent)}` : `${baseUrl}/api/mcp/policy`;
61
+ const params = new URLSearchParams();
62
+ if (agent) params.set("agent", agent);
63
+ if (repoAware) params.set("repoAware", "1");
64
+ const query = params.toString();
65
+ const url = query ? `${baseUrl}/api/mcp/policy?${query}` : `${baseUrl}/api/mcp/policy`;
58
66
  const response = await fetch(url, {
59
67
  headers: { "Authorization": `Bearer ${apiKey}` },
60
68
  signal: AbortSignal.timeout(1e4)
@@ -67,8 +75,8 @@ var fetchPolicy = async (apiKey, agent) => {
67
75
  return raw;
68
76
  }, { maxAttempts: 2 });
69
77
  };
70
- var getPolicy = async (apiKey, expectedVersion, agent) => {
71
- const path = cacheFile(apiKey, agent);
78
+ var getPolicy = async (apiKey, expectedVersion, agent, repoAware = false) => {
79
+ const path = policyCacheFile(apiKey, agent, repoAware);
72
80
  let staleCache = null;
73
81
  if (existsSync(path)) {
74
82
  try {
@@ -85,7 +93,7 @@ var getPolicy = async (apiKey, expectedVersion, agent) => {
85
93
  }
86
94
  }
87
95
  try {
88
- const policy = await fetchPolicy(apiKey, agent);
96
+ const policy = await fetchPolicy(apiKey, agent, repoAware);
89
97
  try {
90
98
  writeFileSync(path, JSON.stringify({ ...policy, _cachedAt: Date.now(), _policyVersion: expectedVersion ?? null }), "utf-8");
91
99
  } catch {
@@ -96,18 +104,23 @@ var getPolicy = async (apiKey, expectedVersion, agent) => {
96
104
  throw new Error("Failed to fetch policy and no cached policy available");
97
105
  }
98
106
  };
99
- var findBestMatch = (policies, toolName, args) => {
107
+ var findBestMatch = (policies, toolName, args, repoKey) => {
100
108
  let best;
101
109
  let bestWeight = 0;
110
+ let bestScoped = -1;
102
111
  let bestLength = -1;
103
112
  for (const candidate of policies) {
113
+ if (!isRepoKeyMatch(candidate.repoKey, repoKey)) continue;
104
114
  const rank = matchToolPatternAny(candidate.tool, toolName, args);
105
115
  if (rank === "none") continue;
106
116
  const weight = matchRankWeight(rank);
117
+ const scoped = candidate.repoKey ? 1 : 0;
107
118
  const length = rank === "prefix" ? candidate.tool.length : -1;
108
- if (weight > bestWeight || weight === bestWeight && length > bestLength) {
119
+ const better = weight > bestWeight || weight === bestWeight && scoped > bestScoped || weight === bestWeight && scoped === bestScoped && length > bestLength;
120
+ if (better) {
109
121
  best = { policy: candidate, rank };
110
122
  bestWeight = weight;
123
+ bestScoped = scoped;
111
124
  bestLength = length;
112
125
  }
113
126
  }
@@ -120,20 +133,20 @@ var autoApprove = (tool) => ({
120
133
  mode: "terminal_only",
121
134
  pushFirstSeconds: 0
122
135
  });
123
- var resolveAutoResolveOrigin = (config, toolName, toolInput, cwd) => {
136
+ var resolveAutoResolveOrigin = (config, toolName, toolInput, cwd, repoKey) => {
124
137
  const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
125
138
  const arg = args[0];
126
- const match = findBestMatch(config.policies, toolName, args);
139
+ const match = findBestMatch(config.policies, toolName, args, repoKey);
127
140
  const governedBySpecificRule = match?.rank === "exact" || match?.rank === "prefix";
128
141
  if (!governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg)) {
129
142
  return "safe_readonly";
130
143
  }
131
144
  return "policy_timeout";
132
145
  };
133
- var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd) => {
146
+ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey) => {
134
147
  const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
135
148
  const arg = args[0];
136
- const match = findBestMatch(config.policies, toolName, args);
149
+ const match = findBestMatch(config.policies, toolName, args, repoKey);
137
150
  let base = match?.policy ?? config.policies.find((p) => p.tool === "*") ?? {
138
151
  tool: toolName,
139
152
  timeoutSeconds: config.defaultTimeoutSeconds,
@@ -271,6 +284,16 @@ var deriveToolTarget = (toolName, toolInput) => {
271
284
  }
272
285
  return void 0;
273
286
  };
287
+ var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "deno", "rake", "make", "just", "task", "turbo"]);
288
+ var RUN_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "run-script", "exec", "task"]);
289
+ var deriveReceiptCommandHead = (command) => {
290
+ if (typeof command !== "string") return void 0;
291
+ const tokens = command.trim().split(/\s+/);
292
+ const [first, second] = tokens;
293
+ const keep = first && second && SCRIPT_RUNNERS.has(first.toLowerCase()) && RUN_SUBCOMMANDS.has(second.toLowerCase()) ? 3 : 2;
294
+ const head = tokens.slice(0, keep).join(" ");
295
+ return head ? head.slice(0, TOOL_TARGET_MAX_LENGTH) : void 0;
296
+ };
274
297
  var RECEIPT_TARGET_MAX_LENGTH = 256;
275
298
  var isToolResultError = (toolResult) => {
276
299
  try {
@@ -296,7 +319,7 @@ var deriveReceiptMeta = (toolName, toolInput, toolResult, cwd) => {
296
319
  };
297
320
  }
298
321
  if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor") {
299
- const head = deriveCommandHead(toolInput.command);
322
+ const head = deriveReceiptCommandHead(toolInput.command);
300
323
  if (!head) return void 0;
301
324
  return {
302
325
  kind: head === "git commit" ? "commit" : "bash",
@@ -641,17 +664,101 @@ var permissionTimeoutDecision = (timeoutAction) => {
641
664
  };
642
665
  var preToolUseTimeoutDecision = (timeoutAction, denyReason = "No response within timeout") => timeoutAction === "deny" ? codexDeny(denyReason) : codexPass();
643
666
 
667
+ // src/repo.ts
668
+ import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
669
+ import { basename, dirname, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "path";
670
+ var MAX_PARENT_WALK = 64;
671
+ var GIT_FILE_PREFIX = "gitdir:";
672
+ var findGitDir = (startDir) => {
673
+ let current = resolve2(startDir);
674
+ for (let depth = 0; depth < MAX_PARENT_WALK; depth += 1) {
675
+ const candidate = join4(current, ".git");
676
+ try {
677
+ if (existsSync3(candidate)) {
678
+ if (statSync3(candidate).isDirectory()) return candidate;
679
+ const pointer = readFileSync3(candidate, "utf-8").trim();
680
+ if (pointer.startsWith(GIT_FILE_PREFIX)) {
681
+ const target = pointer.slice(GIT_FILE_PREFIX.length).trim();
682
+ return isAbsolute2(target) ? target : resolve2(current, target);
683
+ }
684
+ }
685
+ } catch {
686
+ }
687
+ const parent = dirname(current);
688
+ if (parent === current) return void 0;
689
+ current = parent;
690
+ }
691
+ return void 0;
692
+ };
693
+ var configPathFor = (gitDir) => {
694
+ const index = gitDir.replace(/\\/g, "/").indexOf("/worktrees/");
695
+ return index === -1 ? join4(gitDir, "config") : join4(gitDir.slice(0, index), "config");
696
+ };
697
+ var ORIGIN_SECTION = /^\[remote "origin"\]/;
698
+ var SECTION_HEADER = /^\[/;
699
+ var URL_LINE = /^url\s*=\s*(.+)$/;
700
+ var readOriginRemote = (gitDir) => {
701
+ try {
702
+ const configPath = configPathFor(gitDir);
703
+ if (!existsSync3(configPath)) return void 0;
704
+ let inOrigin = false;
705
+ for (const rawLine of readFileSync3(configPath, "utf-8").split("\n")) {
706
+ const line = rawLine.trim();
707
+ if (SECTION_HEADER.test(line)) {
708
+ inOrigin = ORIGIN_SECTION.test(line);
709
+ continue;
710
+ }
711
+ if (!inOrigin) continue;
712
+ const url = URL_LINE.exec(line);
713
+ if (url) return url[1]?.trim();
714
+ }
715
+ } catch {
716
+ }
717
+ return void 0;
718
+ };
719
+ var deriveRepoKey = (cwd) => {
720
+ const dir = cwd && cwd.length > 0 ? cwd : process.cwd();
721
+ try {
722
+ const gitDir = findGitDir(dir);
723
+ if (gitDir) {
724
+ const remote = readOriginRemote(gitDir);
725
+ const normalized = remote ? normalizeRepoRemote(remote) : void 0;
726
+ if (normalized) return normalized;
727
+ const root = gitDir.endsWith(".git") ? dirname(gitDir) : dir;
728
+ return localRepoKey(basename(root));
729
+ }
730
+ return localRepoKey(basename(resolve2(dir)));
731
+ } catch {
732
+ return void 0;
733
+ }
734
+ };
735
+ var repoKeyCache = /* @__PURE__ */ new Map();
736
+ var REPO_KEY_ENV = "PUSHARY_REPO_KEY";
737
+ var repoKeyFor = (cwd) => {
738
+ const override = process.env[REPO_KEY_ENV];
739
+ if (typeof override === "string") {
740
+ const trimmed = override.trim();
741
+ if (trimmed.toLowerCase() === "off") return void 0;
742
+ if (trimmed.length > 0) return trimmed.toLowerCase();
743
+ }
744
+ const dir = cwd && cwd.length > 0 ? cwd : process.cwd();
745
+ if (repoKeyCache.has(dir)) return repoKeyCache.get(dir);
746
+ const derived = deriveRepoKey(dir);
747
+ repoKeyCache.set(dir, derived);
748
+ return derived;
749
+ };
750
+
644
751
  // src/throttle.ts
645
- import { join as join4 } from "path";
752
+ import { join as join5 } from "path";
646
753
  import { tmpdir as tmpdir4 } from "os";
647
754
  import { createHash as createHash2 } from "crypto";
648
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, statSync as statSync3, writeFileSync as writeFileSync4 } from "fs";
649
- var THROTTLE_DIR = join4(tmpdir4(), "pushary-throttle");
650
- var markerPath = (key) => join4(THROTTLE_DIR, createHash2("sha256").update(key).digest("hex").slice(0, 16));
755
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
756
+ var THROTTLE_DIR = join5(tmpdir4(), "pushary-throttle");
757
+ var markerPath = (key) => join5(THROTTLE_DIR, createHash2("sha256").update(key).digest("hex").slice(0, 16));
651
758
  var throttlePass = (key, windowMs) => {
652
759
  try {
653
760
  const path = markerPath(key);
654
- if (existsSync3(path) && Date.now() - statSync3(path).mtimeMs < windowMs) return false;
761
+ if (existsSync4(path) && Date.now() - statSync4(path).mtimeMs < windowMs) return false;
655
762
  mkdirSync3(THROTTLE_DIR, { recursive: true });
656
763
  writeFileSync4(path, "", "utf-8");
657
764
  return true;
@@ -661,15 +768,14 @@ var throttlePass = (key, windowMs) => {
661
768
  };
662
769
 
663
770
  // src/events.ts
664
- import { basename, join as join5 } from "path";
665
- import { createHash as createHash3 } from "crypto";
666
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, statSync as statSync4, writeFileSync as writeFileSync5 } from "fs";
771
+ import { basename as basename2, join as join6 } from "path";
772
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
667
773
  import { tmpdir as tmpdir5 } from "os";
668
- var INTENT_DIR = join5(tmpdir5(), "pushary-intent");
774
+ var INTENT_DIR = join6(tmpdir5(), "pushary-intent");
669
775
  var INTENT_MAX_BYTES = 2048;
670
776
  var INTENT_GRACE_MS = 10 * 60 * 1e3;
671
777
  var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
672
- var intentFile = (sessionId) => join5(INTENT_DIR, sanitizeSession(sessionId));
778
+ var intentFile = (sessionId) => join6(INTENT_DIR, sanitizeSession(sessionId));
673
779
  var saveLastPrompt = (sessionId, prompt) => {
674
780
  try {
675
781
  const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
@@ -682,8 +788,8 @@ var saveLastPrompt = (sessionId, prompt) => {
682
788
  var readLastPrompt = (sessionId) => {
683
789
  try {
684
790
  const path = intentFile(sessionId);
685
- if (isDefaultSession(sessionId) && statSync4(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
686
- const value = readFileSync3(path, "utf-8").trim();
791
+ if (isDefaultSession(sessionId) && statSync5(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
792
+ const value = readFileSync4(path, "utf-8").trim();
687
793
  return value.length > 0 ? value : void 0;
688
794
  } catch {
689
795
  return void 0;
@@ -739,10 +845,9 @@ var notifyLateAnswers = async (apiKey, late, agentName, sessionId) => {
739
845
  var CLAUDE_CODE_AGENT = { type: "claude_code", label: "Claude Code" };
740
846
  var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
741
847
  var readFreshCachedPolicy = (apiKey) => {
742
- const hash = createHash3("sha256").update(apiKey).digest("hex").slice(0, 12);
743
- const path = join5(tmpdir5(), `pushary-policy-${hash}.json`);
744
- if (!existsSync4(path)) return null;
745
- const cached = JSON.parse(readFileSync3(path, "utf-8"));
848
+ const path = policyCacheFile(apiKey);
849
+ if (!existsSync5(path)) return null;
850
+ const cached = JSON.parse(readFileSync4(path, "utf-8"));
746
851
  if (!isPolicyConfig(cached)) return null;
747
852
  if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
748
853
  return cached;
@@ -752,7 +857,7 @@ var deriveDecisionSource = (toolName, toolInput, liveMode, cwd) => {
752
857
  if (liveMode.kill) return "terminal";
753
858
  const policy = readFreshCachedPolicy(getApiKey());
754
859
  if (!policy) return void 0;
755
- const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd);
860
+ const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
756
861
  if (resolved.timeoutSeconds === 0 && resolved.timeoutAction === "approve") return "policy_auto";
757
862
  if (resolved.mode === "push_only" || resolved.mode === "push_first") return "human";
758
863
  return "terminal";
@@ -765,10 +870,10 @@ var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, cwd) => {
765
870
  if (liveMode.kill) return void 0;
766
871
  const policy = readFreshCachedPolicy(getApiKey());
767
872
  if (!policy) return void 0;
768
- const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd);
873
+ const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
769
874
  if (resolved.timeoutSeconds !== 0 || resolved.timeoutAction !== "approve") return void 0;
770
875
  return {
771
- decisionOrigin: resolveAutoResolveOrigin(policy, toolName, toolInput, cwd),
876
+ decisionOrigin: resolveAutoResolveOrigin(policy, toolName, toolInput, cwd, repoKeyFor(cwd)),
772
877
  toolName,
773
878
  toolTarget: deriveToolTarget(toolName, toolInput)
774
879
  };
@@ -809,6 +914,7 @@ var reportEvent = async (event, options = {}) => {
809
914
  body: JSON.stringify({
810
915
  ...event,
811
916
  machineId: event.machineId ?? getMachineId(),
917
+ repoKey: event.repoKey ?? repoKeyFor(),
812
918
  installMode: detectInstallMode(),
813
919
  // Link-back for a phone-spawned session: forward the spawn id the daemon
814
920
  // set in the environment so the server can map spawnId -> sessionId.
@@ -833,7 +939,7 @@ var reportEvent = async (event, options = {}) => {
833
939
  var NON_GATED_MIRROR_TOOLS = /* @__PURE__ */ new Set(["TodoWrite"]);
834
940
  var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
835
941
  try {
836
- const projectName = basename(input.cwd ?? process.cwd());
942
+ const projectName = basename2(input.cwd ?? process.cwd());
837
943
  const action = describeToolCall(input.tool_name, input.tool_input, "event");
838
944
  const lookup = toPolicyLookup(input.tool_name, input.tool_input);
839
945
  const toolResult = input.tool_response ?? input.tool_result;
@@ -888,7 +994,7 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
888
994
  var TASK_TITLE_MAX_LENGTH = 120;
889
995
  var handleUserPrompt = async (input, agent = CLAUDE_CODE_AGENT) => {
890
996
  try {
891
- const projectName = basename(input.cwd ?? process.cwd());
997
+ const projectName = basename2(input.cwd ?? process.cwd());
892
998
  const sessionKey = input.session_id || DEFAULT_SESSION;
893
999
  const titlesEnabled = process.env.PUSHARY_TASK_TITLES !== "off";
894
1000
  const taskTitle = titlesEnabled ? input.prompt?.replace(/\s+/g, " ").trim().slice(0, TASK_TITLE_MAX_LENGTH) || void 0 : void 0;
@@ -933,7 +1039,7 @@ var summarizeStop = (input) => {
933
1039
  };
934
1040
  var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
935
1041
  try {
936
- const projectName = basename(input.cwd ?? process.cwd());
1042
+ const projectName = basename2(input.cwd ?? process.cwd());
937
1043
  const sessionKey = input.session_id || DEFAULT_SESSION;
938
1044
  const late = await reconcilePendingQuestions(sessionKey);
939
1045
  const tasks = [
@@ -969,7 +1075,7 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
969
1075
  };
970
1076
  var handleSessionStart = async (input, agent = CLAUDE_CODE_AGENT) => {
971
1077
  try {
972
- const projectName = basename(input.cwd ?? process.cwd());
1078
+ const projectName = basename2(input.cwd ?? process.cwd());
973
1079
  const action = input.source === "resume" ? "Session resumed" : "Session started";
974
1080
  await reportEvent({
975
1081
  event: "session_start",
@@ -991,7 +1097,7 @@ var SESSION_END_ACTIONS = {
991
1097
  };
992
1098
  var handleSessionEnd = async (input, agent = CLAUDE_CODE_AGENT) => {
993
1099
  try {
994
- const projectName = basename(input.cwd ?? process.cwd());
1100
+ const projectName = basename2(input.cwd ?? process.cwd());
995
1101
  const action = typeof input.reason === "string" && SESSION_END_ACTIONS[input.reason] || "Session ended";
996
1102
  const sessionKey = input.session_id || DEFAULT_SESSION;
997
1103
  const late = await reconcilePendingQuestions(sessionKey);
@@ -1015,7 +1121,7 @@ var NOTIFICATION_PUSH_TYPES = /* @__PURE__ */ new Set(["idle_prompt", "agent_nee
1015
1121
  var NOTIFICATION_THROTTLE_MS = 60 * 1e3;
1016
1122
  var handleNotification = async (input, agent = CLAUDE_CODE_AGENT) => {
1017
1123
  try {
1018
- const projectName = basename(input.cwd ?? process.cwd());
1124
+ const projectName = basename2(input.cwd ?? process.cwd());
1019
1125
  const notifType = input.notification_type ?? input.type ?? "";
1020
1126
  const sessionKey = input.session_id || DEFAULT_SESSION;
1021
1127
  if (NOTIFICATION_PUSH_TYPES.has(notifType)) {
@@ -1085,7 +1191,7 @@ var handleStopFailure = async (input, agent = CLAUDE_CODE_AGENT) => {
1085
1191
  } catch {
1086
1192
  return;
1087
1193
  }
1088
- const projectName = basename(input.cwd ?? process.cwd());
1194
+ const projectName = basename2(input.cwd ?? process.cwd());
1089
1195
  const mode = await fetchModeState(apiKey, input.session_id).catch(() => null);
1090
1196
  if (mode?.kill || mode?.mode === "terminal_only") return;
1091
1197
  const reason = describeStopFailure(input.error);
@@ -1117,6 +1223,7 @@ export {
1117
1223
  deriveBlocker,
1118
1224
  DEFAULT_SESSION,
1119
1225
  savePendingQuestion,
1226
+ repoKeyFor,
1120
1227
  throttlePass,
1121
1228
  readLastUserPrompt,
1122
1229
  CODEX_AGENT,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PRETOOLUSE_HANDLED_TOOLS
3
- } from "./chunk-PSZAXNUB.js";
3
+ } from "./chunk-QYQO6RHJ.js";
4
4
  import {
5
5
  denyReasonFrom,
6
6
  isDeferAnswer
@@ -18,25 +18,26 @@ import {
18
18
  getPolicy,
19
19
  readLastPrompt,
20
20
  readLastUserPrompt,
21
+ repoKeyFor,
21
22
  resolvePolicy,
22
23
  savePendingQuestion,
23
24
  sendNotification,
24
25
  throttlePass,
25
26
  waitForAnswer
26
- } from "./chunk-H6RGO7DN.js";
27
+ } from "./chunk-LEWSYLHL.js";
27
28
  import {
28
29
  getMachineId
29
30
  } from "./chunk-RN3NOEJF.js";
30
31
  import {
31
32
  isGatingMoment,
32
33
  recordKeylessMoment
33
- } from "./chunk-UXIAIMSS.js";
34
+ } from "./chunk-5TECIV6T.js";
34
35
  import {
35
36
  buildDecisionEpisodeFeatures,
36
37
  effectiveWaitSeconds,
37
38
  hookWaitClamped,
38
39
  hookWaitDeadline
39
- } from "./chunk-P5VOLS3Q.js";
40
+ } from "./chunk-SYAAGYLZ.js";
40
41
  import {
41
42
  getApiKey,
42
43
  getBaseUrl
@@ -385,7 +386,7 @@ var handlePreToolUse = async (input) => {
385
386
  }
386
387
  try {
387
388
  const modeState = await fetchModeState(apiKey, input.session_id);
388
- const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code");
389
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code", true);
389
390
  if (modeState.kill) {
390
391
  return deny("Stopped by user, this agent was halted from Pushary");
391
392
  }
@@ -395,7 +396,7 @@ var handlePreToolUse = async (input) => {
395
396
  if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
396
397
  return void 0;
397
398
  }
398
- const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd);
399
+ const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd, repoKeyFor(input.cwd));
399
400
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") {
400
401
  return allow();
401
402
  }
@@ -440,9 +441,9 @@ var handlePermissionRequest = async (input) => {
440
441
  }
441
442
  try {
442
443
  const modeState = await fetchModeState(apiKey, input.session_id);
443
- const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code");
444
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code", true);
444
445
  if (modeState.kill) return permReqDeny("Stopped by user \u2014 this agent was halted from Pushary");
445
- const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd);
446
+ const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd, repoKeyFor(input.cwd));
446
447
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return permReqAllow();
447
448
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
448
449
  return permReqDeny(`Denied by policy for ${toolPolicy.tool}`);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_BUDGETS
3
- } from "./chunk-P5VOLS3Q.js";
3
+ } from "./chunk-SYAAGYLZ.js";
4
4
 
5
5
  // src/claude-config.ts
6
6
  import { join } from "path";
@@ -19,6 +19,45 @@ var hookWaitDeadline = (startMs, policyWaitSeconds, agent, nowMs) => {
19
19
  };
20
20
  var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + hookMaxWaitSeconds(agent) * 1e3 < nowMs + Math.max(policyWaitSeconds, 0) * 1e3;
21
21
  var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
22
+ var REPO_KEY_MAX_LENGTH = 200;
23
+ var isRepoKeyMatch = (ruleRepoKey, currentRepoKey) => {
24
+ if (!ruleRepoKey) return true;
25
+ return ruleRepoKey === currentRepoKey;
26
+ };
27
+ var SCP_LIKE_REMOTE = /^[A-Za-z0-9._-]+@([A-Za-z0-9._-]+):(.+)$/;
28
+ var URL_LIKE_REMOTE = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/(.+)$/;
29
+ var NUMERIC_PORT = /:\d+$/;
30
+ var stripCredentials = (host) => {
31
+ const at = host.lastIndexOf("@");
32
+ return at === -1 ? host : host.slice(at + 1);
33
+ };
34
+ var normalizeRepoRemote = (remoteUrl) => {
35
+ const raw = remoteUrl.trim();
36
+ if (raw.length === 0) return void 0;
37
+ let hostAndPath;
38
+ const scp = SCP_LIKE_REMOTE.exec(raw);
39
+ if (scp) {
40
+ hostAndPath = `${scp[1]}/${scp[2]}`;
41
+ } else {
42
+ const url = URL_LIKE_REMOTE.exec(raw);
43
+ if (!url) return void 0;
44
+ if (/^file:/i.test(raw)) return void 0;
45
+ hostAndPath = url[1];
46
+ }
47
+ const slash = hostAndPath.indexOf("/");
48
+ if (slash <= 0) return void 0;
49
+ const host = stripCredentials(hostAndPath.slice(0, slash)).replace(NUMERIC_PORT, "");
50
+ const path = hostAndPath.slice(slash + 1).split("/").filter((segment) => segment.length > 0).join("/");
51
+ if (host.length === 0 || path.length === 0) return void 0;
52
+ const key = `${host}/${path}`.replace(/\.git$/i, "").toLowerCase();
53
+ return key.length > REPO_KEY_MAX_LENGTH ? key.slice(0, REPO_KEY_MAX_LENGTH) : key;
54
+ };
55
+ var localRepoKey = (directoryName) => {
56
+ const name = directoryName.trim().replace(/[/\\]+$/, "").toLowerCase();
57
+ if (name.length === 0 || name === "." || name === "..") return void 0;
58
+ const key = `local/${name}`;
59
+ return key.length > REPO_KEY_MAX_LENGTH ? key.slice(0, REPO_KEY_MAX_LENGTH) : key;
60
+ };
22
61
  var MATCH_RANKS = ["none", "tool", "prefix", "exact"];
23
62
  var matchRankWeight = (rank) => MATCH_RANKS.indexOf(rank);
24
63
  var GLOB_MAX_WILDCARDS = 8;
@@ -692,6 +731,9 @@ export {
692
731
  hookWaitDeadline,
693
732
  hookWaitClamped,
694
733
  effectiveWaitSeconds,
734
+ isRepoKeyMatch,
735
+ normalizeRepoRemote,
736
+ localRepoKey,
695
737
  matchRankWeight,
696
738
  policyArgForms,
697
739
  matchToolPatternAny,
@@ -26,9 +26,9 @@ interface ReceiptMeta {
26
26
  ok: boolean;
27
27
  }
28
28
 
29
- declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string) => Promise<PolicyConfig>;
29
+ declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string, repoAware?: boolean) => Promise<PolicyConfig>;
30
30
  type AutoResolveOrigin = 'safe_readonly' | 'policy_timeout';
31
- declare const resolvePolicy: (config: PolicyConfig, toolName: string, modeOverride?: ApprovalMode | null, toolInput?: Record<string, unknown>, cwd?: string) => ToolPolicy;
31
+ declare const resolvePolicy: (config: PolicyConfig, toolName: string, modeOverride?: ApprovalMode | null, toolInput?: Record<string, unknown>, cwd?: string, repoKey?: string) => ToolPolicy;
32
32
  interface ModeState {
33
33
  readonly mode: ApprovalMode | null;
34
34
  readonly kill: boolean;
@@ -57,6 +57,7 @@ interface AgentEvent {
57
57
  agentName?: string;
58
58
  action?: string;
59
59
  machineId?: string;
60
+ repoKey?: string;
60
61
  sessionId?: string;
61
62
  error?: string;
62
63
  taskTitle?: string;
package/dist/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-XKXF2G7T.js";
4
- import "../chunk-PSZAXNUB.js";
3
+ } from "../chunk-NPYANTRC.js";
4
+ import "../chunk-QYQO6RHJ.js";
5
5
  import "../chunk-YHG74UFF.js";
6
6
  import {
7
7
  askUser,
@@ -15,11 +15,11 @@ import {
15
15
  reportEvent,
16
16
  resolvePolicy,
17
17
  waitForAnswer
18
- } from "../chunk-H6RGO7DN.js";
18
+ } from "../chunk-LEWSYLHL.js";
19
19
  import "../chunk-RN3NOEJF.js";
20
- import "../chunk-UXIAIMSS.js";
20
+ import "../chunk-5TECIV6T.js";
21
21
  import "../chunk-DWED7BS3.js";
22
- import "../chunk-P5VOLS3Q.js";
22
+ import "../chunk-SYAAGYLZ.js";
23
23
  import {
24
24
  getApiKey,
25
25
  getBaseUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.55.0",
3
+ "version": "0.57.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",
@@ -74,7 +74,7 @@
74
74
  "scripts": {
75
75
  "build": "node scripts/bundle-plugin.mjs && tsup",
76
76
  "dev": "tsup --watch",
77
- "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts && bun test src/wrapper/modeLoop.test.ts && bun test src/wrapper/stdinHandoff.test.ts && bun test src/wrapper/localLeg.test.ts && bun test src/crypto.test.ts && bun test src/crypto-vector.test.ts && bun test src/transcript-capture.test.ts && bun test src/transcript-sync.test.ts && bun test src/spawn-daemon.test.ts && bun test src/decision-episode.test.ts"
77
+ "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts && bun test src/wrapper/modeLoop.test.ts && bun test src/wrapper/stdinHandoff.test.ts && bun test src/wrapper/localLeg.test.ts && bun test src/crypto.test.ts && bun test src/crypto-vector.test.ts && bun test src/transcript-capture.test.ts && bun test src/transcript-sync.test.ts && bun test src/spawn-daemon.test.ts && bun test src/decision-episode.test.ts && bun test src/repo.test.ts"
78
78
  },
79
79
  "dependencies": {
80
80
  "@inquirer/prompts": "^8.4.2",