@gotgenes/pi-permission-system 7.1.3 → 7.2.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +7 -3
  3. package/src/active-agent.ts +1 -1
  4. package/src/bash-arity.ts +1 -0
  5. package/src/config-modal.ts +2 -0
  6. package/src/forwarded-permissions/io.ts +4 -2
  7. package/src/forwarded-permissions/polling.ts +8 -7
  8. package/src/handlers/before-agent-start.ts +7 -6
  9. package/src/handlers/gates/bash-external-directory.ts +4 -4
  10. package/src/handlers/gates/bash-path-extractor.ts +4 -6
  11. package/src/handlers/gates/bash-path.ts +5 -5
  12. package/src/handlers/gates/descriptor.ts +6 -6
  13. package/src/handlers/gates/external-directory.ts +2 -2
  14. package/src/handlers/gates/helpers.ts +2 -2
  15. package/src/handlers/gates/path.ts +4 -4
  16. package/src/handlers/gates/runner.ts +7 -4
  17. package/src/handlers/gates/skill-read.ts +5 -5
  18. package/src/handlers/gates/tool.ts +5 -5
  19. package/src/handlers/lifecycle.ts +9 -8
  20. package/src/handlers/permission-gate-handler.ts +12 -7
  21. package/src/logging.ts +3 -0
  22. package/src/node-modules-discovery.ts +1 -1
  23. package/src/normalize.ts +1 -0
  24. package/src/permission-event-rpc.ts +2 -0
  25. package/src/permission-manager.ts +7 -6
  26. package/src/permission-merge.ts +4 -2
  27. package/src/permission-prompter.ts +3 -0
  28. package/src/permission-prompts.ts +1 -1
  29. package/src/policy-loader.ts +5 -5
  30. package/src/service.ts +1 -0
  31. package/src/skill-prompt-sanitizer.ts +3 -3
  32. package/src/tool-registry.ts +1 -1
  33. package/src/wildcard-matcher.ts +2 -2
  34. package/src/yolo-mode.ts +2 -1
  35. package/{tests → test}/active-agent.test.ts +1 -1
  36. package/{tests → test}/bash-arity.test.ts +4 -4
  37. package/{tests → test}/bash-external-directory.test.ts +3 -3
  38. package/{tests → test}/common.test.ts +1 -1
  39. package/{tests → test}/config-loader.test.ts +1 -1
  40. package/{tests → test}/config-modal.test.ts +9 -11
  41. package/{tests → test}/config-paths.test.ts +1 -1
  42. package/{tests → test}/config-reporter.test.ts +4 -4
  43. package/{tests → test}/denial-messages.test.ts +2 -2
  44. package/{tests → test}/expand-home.test.ts +1 -1
  45. package/{tests → test}/extension-config.test.ts +1 -1
  46. package/{tests → test}/extension-paths.test.ts +2 -2
  47. package/{tests → test}/forwarded-permissions/io.test.ts +2 -2
  48. package/{tests → test}/forwarding-manager.test.ts +1 -1
  49. package/{tests → test}/handlers/before-agent-start.test.ts +5 -5
  50. package/{tests → test}/handlers/external-directory-integration.test.ts +8 -8
  51. package/{tests → test}/handlers/external-directory-session-dedup.test.ts +6 -6
  52. package/{tests → test}/handlers/gates/bash-external-directory.test.ts +5 -8
  53. package/{tests → test}/handlers/gates/bash-path.test.ts +6 -9
  54. package/{tests → test}/handlers/gates/external-directory-messages.test.ts +1 -1
  55. package/{tests → test}/handlers/gates/external-directory.test.ts +4 -7
  56. package/{tests → test}/handlers/gates/helpers.test.ts +1 -1
  57. package/{tests → test}/handlers/gates/path.test.ts +6 -6
  58. package/{tests → test}/handlers/gates/runner.test.ts +5 -5
  59. package/{tests → test}/handlers/gates/skill-read.test.ts +12 -14
  60. package/{tests → test}/handlers/gates/tool.test.ts +4 -4
  61. package/{tests → test}/handlers/input-events.test.ts +7 -7
  62. package/{tests → test}/handlers/input.test.ts +5 -5
  63. package/{tests → test}/handlers/lifecycle.test.ts +2 -2
  64. package/{tests → test}/handlers/tool-call-events.test.ts +7 -7
  65. package/{tests → test}/handlers/tool-call.test.ts +5 -5
  66. package/{tests → test}/input-normalizer.test.ts +2 -2
  67. package/{tests → test}/mcp-targets.test.ts +1 -1
  68. package/{tests → test}/node-modules-discovery.test.ts +1 -1
  69. package/{tests → test}/normalize.test.ts +1 -1
  70. package/{tests → test}/path-utils.test.ts +1 -1
  71. package/{tests → test}/pattern-suggest.test.ts +1 -1
  72. package/{tests → test}/permission-dialog.test.ts +1 -1
  73. package/{tests → test}/permission-event-rpc.test.ts +4 -3
  74. package/{tests → test}/permission-events.test.ts +6 -4
  75. package/{tests → test}/permission-forwarding.test.ts +2 -1
  76. package/{tests → test}/permission-gate.test.ts +2 -2
  77. package/{tests → test}/permission-manager-unified.test.ts +9 -7
  78. package/{tests → test}/permission-merge.test.ts +1 -1
  79. package/{tests → test}/permission-prompter.test.ts +4 -4
  80. package/{tests → test}/permission-prompts.test.ts +4 -4
  81. package/{tests → test}/permission-session.test.ts +9 -9
  82. package/{tests → test}/permission-system.test.ts +21 -21
  83. package/{tests → test}/pi-infrastructure-read.test.ts +2 -2
  84. package/{tests → test}/policy-loader.test.ts +1 -1
  85. package/{tests → test}/rule.test.ts +2 -2
  86. package/{tests → test}/runtime.test.ts +4 -4
  87. package/{tests → test}/service.test.ts +4 -4
  88. package/{tests → test}/session-logger.test.ts +2 -2
  89. package/{tests → test}/session-rules.test.ts +2 -2
  90. package/{tests → test}/session-start.test.ts +4 -4
  91. package/{tests → test}/skill-prompt-sanitizer.test.ts +3 -3
  92. package/{tests → test}/subagent-context.test.ts +2 -2
  93. package/{tests → test}/synthesize.test.ts +3 -3
  94. package/{tests → test}/system-prompt-sanitizer.test.ts +1 -1
  95. package/{tests → test}/tool-input-preview.test.ts +3 -3
  96. package/{tests → test}/tool-registry.test.ts +1 -1
  97. package/{tests → test}/wildcard-matcher.test.ts +1 -1
  98. package/{tests → test}/yolo-mode.test.ts +2 -2
@@ -38,7 +38,7 @@ export function formatAskPrompt(
38
38
  const patternInfo = result.matchedPattern
39
39
  ? ` (matched '${result.matchedPattern}')`
40
40
  : "";
41
- return `${subject} requested bash command '${result.command || ""}'${patternInfo}. Allow this command?`;
41
+ return `${subject} requested bash command '${result.command ?? ""}'${patternInfo}. Allow this command?`;
42
42
  }
43
43
 
44
44
  if ((result.source === "mcp" || result.toolName === "mcp") && result.target) {
@@ -169,12 +169,12 @@ export class FilePolicyLoader implements PolicyLoader {
169
169
 
170
170
  constructor(options: PolicyLoaderOptions = {}) {
171
171
  this.globalConfigPath =
172
- options.globalConfigPath || defaultGlobalConfigPath();
173
- this.agentsDir = options.agentsDir || defaultAgentsDir();
174
- this.projectGlobalConfigPath = options.projectGlobalConfigPath || null;
175
- this.projectAgentsDir = options.projectAgentsDir || null;
172
+ options.globalConfigPath ?? defaultGlobalConfigPath();
173
+ this.agentsDir = options.agentsDir ?? defaultAgentsDir();
174
+ this.projectGlobalConfigPath = options.projectGlobalConfigPath ?? null;
175
+ this.projectAgentsDir = options.projectAgentsDir ?? null;
176
176
  this.globalMcpConfigPath =
177
- options.globalMcpConfigPath || defaultGlobalMcpConfigPath();
177
+ options.globalMcpConfigPath ?? defaultGlobalMcpConfigPath();
178
178
  this.configuredMcpServerNamesOverride = options.mcpServerNames
179
179
  ? [
180
180
  ...new Set(
package/src/service.ts CHANGED
@@ -71,5 +71,6 @@ export function getPermissionsService(): PermissionsService | undefined {
71
71
  * extension is torn down.
72
72
  */
73
73
  export function unpublishPermissionsService(): void {
74
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Symbol-keyed global property; Map.delete() is not applicable
74
75
  delete (globalThis as Record<symbol, unknown>)[SERVICE_KEY];
75
76
  }
@@ -70,9 +70,9 @@ function parseSkillEntries(sectionBody: string): ParsedSkillPromptEntry[] {
70
70
 
71
71
  for (const match of sectionBody.matchAll(skillBlockRegex)) {
72
72
  const block = match[1];
73
- const nameMatch = block.match(SKILL_NAME_REGEX);
74
- const descriptionMatch = block.match(SKILL_DESCRIPTION_REGEX);
75
- const locationMatch = block.match(SKILL_LOCATION_REGEX);
73
+ const nameMatch = SKILL_NAME_REGEX.exec(block);
74
+ const descriptionMatch = SKILL_DESCRIPTION_REGEX.exec(block);
75
+ const locationMatch = SKILL_LOCATION_REGEX.exec(block);
76
76
 
77
77
  if (!nameMatch || !descriptionMatch || !locationMatch) {
78
78
  continue;
@@ -35,7 +35,7 @@ function buildReverseAliases(
35
35
  const reverse = new Map<string, string[]>();
36
36
 
37
37
  for (const [alias, canonical] of Object.entries(aliases)) {
38
- const existing = reverse.get(canonical) || [];
38
+ const existing = reverse.get(canonical) ?? [];
39
39
  if (!existing.includes(alias)) {
40
40
  existing.push(alias);
41
41
  }
@@ -30,7 +30,7 @@ export function compileWildcardPattern<TState>(
30
30
  // space-and-arguments portion optional so that e.g. "git *" matches both
31
31
  // "git status" and bare "git". Mirrors OpenCode wildcard semantics.
32
32
  if (escaped.endsWith(" .*")) {
33
- escaped = escaped.slice(0, -3) + "( .*)?";
33
+ escaped = `${escaped.slice(0, -3)}( .*)?`;
34
34
  }
35
35
 
36
36
  return {
@@ -48,7 +48,7 @@ export function compileWildcardPatternEntries<TState>(
48
48
  );
49
49
  }
50
50
 
51
- function compileWildcardPatterns<TState>(
51
+ function _compileWildcardPatterns<TState>(
52
52
  patterns: Record<string, TState>,
53
53
  ): CompiledWildcardPattern<TState>[] {
54
54
  return compileWildcardPatternEntries(Object.entries(patterns));
package/src/yolo-mode.ts CHANGED
@@ -10,7 +10,8 @@ export interface AskPermissionResolutionOptions {
10
10
  export function isYoloModeEnabled(
11
11
  config: PermissionSystemExtensionConfig,
12
12
  ): boolean {
13
- return config.yoloMode === true;
13
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion -- typed as boolean but may be undefined at runtime (untyped callers); Boolean() guards against that
14
+ return Boolean(config.yoloMode);
14
15
  }
15
16
 
16
17
  export function shouldAutoApprovePermissionState(
@@ -5,7 +5,7 @@ import {
5
5
  getActiveAgentName,
6
6
  getActiveAgentNameFromSystemPrompt,
7
7
  normalizeAgentName,
8
- } from "../src/active-agent";
8
+ } from "#src/active-agent";
9
9
 
10
10
  afterEach(() => {
11
11
  vi.restoreAllMocks();
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { ARITY, prefix } from "../src/bash-arity";
2
+ import { ARITY, prefix } from "#src/bash-arity";
3
3
 
4
4
  describe("ARITY dictionary", () => {
5
5
  it("is exported as a plain object", () => {
@@ -7,7 +7,7 @@ describe("ARITY dictionary", () => {
7
7
  });
8
8
 
9
9
  it("maps 'git' to arity 2", () => {
10
- expect(ARITY["git"]).toBe(2);
10
+ expect(ARITY.git).toBe(2);
11
11
  });
12
12
 
13
13
  it("maps 'npm run' to arity 3", () => {
@@ -15,7 +15,7 @@ describe("ARITY dictionary", () => {
15
15
  });
16
16
 
17
17
  it("maps 'npm' to arity 2 (fallback when 'npm run' does not match)", () => {
18
- expect(ARITY["npm"]).toBe(2);
18
+ expect(ARITY.npm).toBe(2);
19
19
  });
20
20
 
21
21
  it("maps 'docker compose' to arity 3", () => {
@@ -23,7 +23,7 @@ describe("ARITY dictionary", () => {
23
23
  });
24
24
 
25
25
  it("maps 'docker' to arity 2 (fallback)", () => {
26
- expect(ARITY["docker"]).toBe(2);
26
+ expect(ARITY.docker).toBe(2);
27
27
  });
28
28
  });
29
29
 
@@ -9,12 +9,12 @@ vi.mock("node:os", () => {
9
9
  };
10
10
  });
11
11
 
12
- import { formatDenyReason } from "../src/denial-messages";
12
+ import { formatDenyReason } from "#src/denial-messages";
13
13
  import {
14
14
  extractExternalPathsFromBashCommand,
15
15
  extractTokensForPathRules,
16
- } from "../src/handlers/gates/bash-path-extractor";
17
- import { formatBashExternalDirectoryAskPrompt } from "../src/handlers/gates/external-directory-messages";
16
+ } from "#src/handlers/gates/bash-path-extractor";
17
+ import { formatBashExternalDirectoryAskPrompt } from "#src/handlers/gates/external-directory-messages";
18
18
 
19
19
  afterEach(() => {
20
20
  vi.restoreAllMocks();
@@ -6,7 +6,7 @@ import {
6
6
  isPermissionState,
7
7
  parseSimpleYamlMap,
8
8
  toRecord,
9
- } from "../src/common";
9
+ } from "#src/common";
10
10
 
11
11
  afterEach(() => {
12
12
  vi.restoreAllMocks();
@@ -7,7 +7,7 @@ import {
7
7
  loadAndMergeConfigs,
8
8
  loadUnifiedConfig,
9
9
  mergeUnifiedConfigs,
10
- } from "../src/config-loader";
10
+ } from "#src/config-loader";
11
11
 
12
12
  describe("loadUnifiedConfig", () => {
13
13
  let tempDir: string;
@@ -2,13 +2,13 @@ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { expect, test, vi } from "vitest";
5
- import { registerPermissionSystemCommand } from "../src/config-modal";
5
+ import { registerPermissionSystemCommand } from "#src/config-modal";
6
6
  import {
7
7
  DEFAULT_EXTENSION_CONFIG,
8
8
  normalizePermissionSystemConfig,
9
9
  type PermissionSystemExtensionConfig,
10
- } from "../src/extension-config";
11
- import type { Rule } from "../src/rule";
10
+ } from "#src/extension-config";
11
+ import type { Rule } from "#src/rule";
12
12
 
13
13
  vi.mock("@earendil-works/pi-coding-agent", () => ({
14
14
  getSettingsListTheme: () => ({}),
@@ -68,7 +68,7 @@ function createCommandContext(hasUI: boolean): {
68
68
  }
69
69
 
70
70
  function lastNotification(notifications: Notification[]): Notification {
71
- return notifications[notifications.length - 1] as Notification;
71
+ return notifications[notifications.length - 1];
72
72
  }
73
73
 
74
74
  test("permission-system command completions expose top-level config actions", () => {
@@ -101,7 +101,7 @@ test("permission-system command completions expose top-level config actions", ()
101
101
  definition = nextDefinition;
102
102
  },
103
103
  } as never,
104
- controller as never,
104
+ controller,
105
105
  );
106
106
 
107
107
  expect(definition!.getArgumentCompletions).toBeTypeOf("function");
@@ -172,13 +172,11 @@ test("permission-system command handlers manage config summary, persistence, and
172
172
  definition = nextDefinition;
173
173
  },
174
174
  } as never,
175
- controller as never,
175
+ controller,
176
176
  );
177
177
 
178
178
  expect(registeredName).toBe("permission-system");
179
- expect(definition!.description ?? "").toContain(
180
- "Configure pi-permission-system",
181
- );
179
+ expect(definition!.description).toContain("Configure pi-permission-system");
182
180
 
183
181
  const infoCtx = createCommandContext(true);
184
182
  await definition!.handler("show", infoCtx.ctx);
@@ -267,7 +265,7 @@ test("show output includes rule origins when getComposedRules is provided", asyn
267
265
  definition = nextDef;
268
266
  },
269
267
  } as never,
270
- controller as never,
268
+ controller,
271
269
  );
272
270
 
273
271
  const ctx = createCommandContext(true);
@@ -300,7 +298,7 @@ test("show output omits rule summary when getComposedRules is not provided", asy
300
298
  definition = nextDef;
301
299
  },
302
300
  } as never,
303
- controller as never,
301
+ controller,
304
302
  );
305
303
 
306
304
  const ctx = createCommandContext(true);
@@ -11,7 +11,7 @@ import {
11
11
  getLegacyProjectPolicyPath,
12
12
  getProjectConfigPath,
13
13
  REVIEW_LOG_FILENAME,
14
- } from "../src/config-paths";
14
+ } from "#src/config-paths";
15
15
 
16
16
  describe("config-paths", () => {
17
17
  const agentDir = "/home/user/.pi/agent";
@@ -8,10 +8,10 @@ import {
8
8
  import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { expect, test } from "vitest";
11
- import { buildResolvedConfigLogEntry } from "../src/config-reporter";
12
- import { createPermissionSystemLogger } from "../src/logging";
13
- import type { ResolvedPolicyPaths } from "../src/permission-manager";
14
- import { PermissionManager } from "../src/permission-manager";
11
+ import { buildResolvedConfigLogEntry } from "#src/config-reporter";
12
+ import { createPermissionSystemLogger } from "#src/logging";
13
+ import type { ResolvedPolicyPaths } from "#src/permission-manager";
14
+ import { PermissionManager } from "#src/permission-manager";
15
15
 
16
16
  test("buildResolvedConfigLogEntry includes policy paths and legacy detection flags", () => {
17
17
  const policyPaths: ResolvedPolicyPaths = {
@@ -6,8 +6,8 @@ import {
6
6
  formatDenyReason,
7
7
  formatUnavailableReason,
8
8
  formatUserDeniedReason,
9
- } from "../src/denial-messages";
10
- import type { PermissionCheckResult } from "../src/types";
9
+ } from "#src/denial-messages";
10
+ import type { PermissionCheckResult } from "#src/types";
11
11
 
12
12
  // ── Helpers ────────────────────────────────────────────────────────────────
13
13
 
@@ -8,7 +8,7 @@ vi.mock("node:os", () => ({
8
8
  default: { homedir: mockHomedir },
9
9
  }));
10
10
 
11
- import { expandHomePath } from "../src/expand-home";
11
+ import { expandHomePath } from "#src/expand-home";
12
12
 
13
13
  const FAKE_HOME = "/home/testuser";
14
14
 
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
3
3
  import {
4
4
  detectMisplacedPermissionKeys,
5
5
  normalizePermissionSystemConfig,
6
- } from "../src/extension-config";
6
+ } from "#src/extension-config";
7
7
 
8
8
  describe("detectMisplacedPermissionKeys", () => {
9
9
  it("returns an empty array for a record with only valid extension keys", () => {
@@ -9,8 +9,8 @@ vi.mock("../src/node-modules-discovery", () => ({
9
9
  discoverGlobalNodeModulesRoot: mockDiscoverGlobalNodeModulesRoot,
10
10
  }));
11
11
 
12
- import { getGlobalLogsDir } from "../src/config-paths";
13
- import { computeExtensionPaths } from "../src/extension-paths";
12
+ import { getGlobalLogsDir } from "#src/config-paths";
13
+ import { computeExtensionPaths } from "#src/extension-paths";
14
14
 
15
15
  describe("computeExtensionPaths", () => {
16
16
  beforeEach(() => {
@@ -1,12 +1,12 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import type { ForwardedPermissionLogger } from "../../src/forwarded-permissions/io";
3
+ import type { ForwardedPermissionLogger } from "#src/forwarded-permissions/io";
4
4
  import {
5
5
  formatUnknownErrorMessage,
6
6
  isErrnoCode,
7
7
  logPermissionForwardingError,
8
8
  logPermissionForwardingWarning,
9
- } from "../../src/forwarded-permissions/io";
9
+ } from "#src/forwarded-permissions/io";
10
10
 
11
11
  // ── helpers ────────────────────────────────────────────────────────────────
12
12
 
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
 
3
- import { ForwardingManager } from "../src/forwarding-manager";
3
+ import { ForwardingManager } from "#src/forwarding-manager";
4
4
 
5
5
  // ── Mocks ─────────────────────────────────────────────────────────────────
6
6
 
@@ -4,10 +4,10 @@ import { describe, expect, it, vi } from "vitest";
4
4
  import {
5
5
  AgentPrepHandler,
6
6
  shouldExposeTool,
7
- } from "../../src/handlers/before-agent-start";
8
- import type { PermissionSession } from "../../src/permission-session";
9
- import type { ToolRegistry } from "../../src/tool-registry";
10
- import type { PermissionState } from "../../src/types";
7
+ } from "#src/handlers/before-agent-start";
8
+ import type { PermissionSession } from "#src/permission-session";
9
+ import type { ToolRegistry } from "#src/tool-registry";
10
+ import type { PermissionState } from "#src/types";
11
11
 
12
12
  // ── SDK stubs ──────────────────────────────────────────────────────────────
13
13
  vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
@@ -52,7 +52,7 @@ function makeSession(
52
52
  activate: vi.fn(),
53
53
  refreshConfig: vi.fn(),
54
54
  resolveAgentName: vi.fn().mockReturnValue(null),
55
- getToolPermission: vi.fn().mockReturnValue("allow" as PermissionState),
55
+ getToolPermission: vi.fn().mockReturnValue("allow"),
56
56
  checkPermission: vi.fn().mockReturnValue({ state: "allow" }),
57
57
  shouldUpdateActiveTools: vi.fn().mockReturnValue(true),
58
58
  commitActiveToolsCacheKey: vi.fn(),
@@ -11,16 +11,16 @@
11
11
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
12
12
  import { describe, expect, it, vi } from "vitest";
13
13
 
14
- import { EXTENSION_TAG } from "../../src/denial-messages";
15
- import { formatExternalDirectoryAskPrompt } from "../../src/handlers/gates/external-directory-messages";
16
- import { PermissionGateHandler } from "../../src/handlers/permission-gate-handler";
14
+ import { EXTENSION_TAG } from "#src/denial-messages";
15
+ import { formatExternalDirectoryAskPrompt } from "#src/handlers/gates/external-directory-messages";
16
+ import { PermissionGateHandler } from "#src/handlers/permission-gate-handler";
17
17
  import {
18
18
  PERMISSIONS_DECISION_CHANNEL,
19
19
  type PermissionDecisionEvent,
20
- } from "../../src/permission-events";
21
- import type { PermissionSession } from "../../src/permission-session";
22
- import type { ToolRegistry } from "../../src/tool-registry";
23
- import type { PermissionCheckResult, PermissionState } from "../../src/types";
20
+ } from "#src/permission-events";
21
+ import type { PermissionSession } from "#src/permission-session";
22
+ import type { ToolRegistry } from "#src/tool-registry";
23
+ import type { PermissionCheckResult, PermissionState } from "#src/types";
24
24
 
25
25
  // ── SDK stubs ──────────────────────────────────────────────────────────────
26
26
  vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
@@ -110,7 +110,7 @@ function makeSession(
110
110
  activate: vi.fn(),
111
111
  resolveAgentName: vi.fn().mockReturnValue(null),
112
112
  checkPermission: makeCheckPermission("deny"),
113
- getToolPermission: vi.fn().mockReturnValue("allow" as PermissionState),
113
+ getToolPermission: vi.fn().mockReturnValue("allow"),
114
114
  getSessionRuleset: vi.fn().mockReturnValue([]),
115
115
  approveSessionRule: vi.fn(),
116
116
  getActiveSkillEntries: vi.fn().mockReturnValue([]),
@@ -11,12 +11,12 @@
11
11
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
12
12
  import { describe, expect, it, vi } from "vitest";
13
13
 
14
- import { PermissionGateHandler } from "../../src/handlers/permission-gate-handler";
15
- import type { PermissionSession } from "../../src/permission-session";
16
- import type { Rule } from "../../src/rule";
17
- import type { ToolRegistry } from "../../src/tool-registry";
18
- import type { PermissionCheckResult } from "../../src/types";
19
- import { wildcardMatch } from "../../src/wildcard-matcher";
14
+ import { PermissionGateHandler } from "#src/handlers/permission-gate-handler";
15
+ import type { PermissionSession } from "#src/permission-session";
16
+ import type { Rule } from "#src/rule";
17
+ import type { ToolRegistry } from "#src/tool-registry";
18
+ import type { PermissionCheckResult } from "#src/types";
19
+ import { wildcardMatch } from "#src/wildcard-matcher";
20
20
 
21
21
  // ── SDK stub ───────────────────────────────────────────────────────────────
22
22
  vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
@@ -1,15 +1,12 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
- import { describeBashExternalDirectoryGate } from "../../../src/handlers/gates/bash-external-directory";
2
+ import { describeBashExternalDirectoryGate } from "#src/handlers/gates/bash-external-directory";
3
3
  import type {
4
4
  GateBypass,
5
5
  GateDescriptor,
6
- } from "../../../src/handlers/gates/descriptor";
7
- import {
8
- isGateBypass,
9
- isGateDescriptor,
10
- } from "../../../src/handlers/gates/descriptor";
11
- import type { ToolCallContext } from "../../../src/handlers/gates/types";
12
- import type { PermissionCheckResult } from "../../../src/types";
6
+ } from "#src/handlers/gates/descriptor";
7
+ import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
8
+ import type { ToolCallContext } from "#src/handlers/gates/types";
9
+ import type { PermissionCheckResult } from "#src/types";
13
10
 
14
11
  // ── helpers ────────────────────────────────────────────────────────────────
15
12
 
@@ -9,18 +9,15 @@ vi.mock("node:os", () => {
9
9
  };
10
10
  });
11
11
 
12
- import { describeBashPathGate } from "../../../src/handlers/gates/bash-path";
12
+ import { describeBashPathGate } from "#src/handlers/gates/bash-path";
13
13
  import type {
14
14
  GateBypass,
15
15
  GateDescriptor,
16
- } from "../../../src/handlers/gates/descriptor";
17
- import {
18
- isGateBypass,
19
- isGateDescriptor,
20
- } from "../../../src/handlers/gates/descriptor";
21
- import type { ToolCallContext } from "../../../src/handlers/gates/types";
22
- import type { Rule } from "../../../src/rule";
23
- import type { PermissionCheckResult } from "../../../src/types";
16
+ } from "#src/handlers/gates/descriptor";
17
+ import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
18
+ import type { ToolCallContext } from "#src/handlers/gates/types";
19
+ import type { Rule } from "#src/rule";
20
+ import type { PermissionCheckResult } from "#src/types";
24
21
 
25
22
  afterEach(() => {
26
23
  vi.restoreAllMocks();
@@ -3,7 +3,7 @@ import { describe, expect, test } from "vitest";
3
3
  import {
4
4
  formatBashExternalDirectoryAskPrompt,
5
5
  formatExternalDirectoryAskPrompt,
6
- } from "../../../src/handlers/gates/external-directory-messages";
6
+ } from "#src/handlers/gates/external-directory-messages";
7
7
 
8
8
  // Denial message functions (formatExternalDirectoryDenyReason,
9
9
  // formatExternalDirectoryUserDeniedReason, formatExternalDirectoryHardStopHint,
@@ -3,13 +3,10 @@ import { describe, expect, it } from "vitest";
3
3
  import type {
4
4
  GateBypass,
5
5
  GateDescriptor,
6
- } from "../../../src/handlers/gates/descriptor";
7
- import {
8
- isGateBypass,
9
- isGateDescriptor,
10
- } from "../../../src/handlers/gates/descriptor";
11
- import { describeExternalDirectoryGate } from "../../../src/handlers/gates/external-directory";
12
- import type { ToolCallContext } from "../../../src/handlers/gates/types";
6
+ } from "#src/handlers/gates/descriptor";
7
+ import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
8
+ import { describeExternalDirectoryGate } from "#src/handlers/gates/external-directory";
9
+ import type { ToolCallContext } from "#src/handlers/gates/types";
13
10
 
14
11
  // ── helpers ───────────────────────────��────────────────────────────��───────
15
12
 
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
3
3
  import {
4
4
  deriveDecisionValue,
5
5
  deriveResolution,
6
- } from "../../../src/handlers/gates/helpers";
6
+ } from "#src/handlers/gates/helpers";
7
7
 
8
8
  describe("deriveDecisionValue", () => {
9
9
  it("returns command for bash", () => {
@@ -1,11 +1,11 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import type { GateDescriptor } from "../../../src/handlers/gates/descriptor";
4
- import { isGateDescriptor } from "../../../src/handlers/gates/descriptor";
5
- import { describePathGate } from "../../../src/handlers/gates/path";
6
- import type { ToolCallContext } from "../../../src/handlers/gates/types";
7
- import type { Rule } from "../../../src/rule";
8
- import type { PermissionCheckResult } from "../../../src/types";
3
+ import type { GateDescriptor } from "#src/handlers/gates/descriptor";
4
+ import { isGateDescriptor } from "#src/handlers/gates/descriptor";
5
+ import { describePathGate } from "#src/handlers/gates/path";
6
+ import type { ToolCallContext } from "#src/handlers/gates/types";
7
+ import type { Rule } from "#src/rule";
8
+ import type { PermissionCheckResult } from "#src/types";
9
9
 
10
10
  // ── helpers ────────────────────────────────────────────────────────────────
11
11
 
@@ -1,13 +1,13 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import type { DenialContext } from "../../../src/denial-messages";
4
- import { EXTENSION_TAG } from "../../../src/denial-messages";
3
+ import type { DenialContext } from "#src/denial-messages";
4
+ import { EXTENSION_TAG } from "#src/denial-messages";
5
5
  import type {
6
6
  GateDescriptor,
7
7
  GateRunnerDeps,
8
- } from "../../../src/handlers/gates/descriptor";
9
- import { runGateCheck } from "../../../src/handlers/gates/runner";
10
- import type { PermissionCheckResult } from "../../../src/types";
8
+ } from "#src/handlers/gates/descriptor";
9
+ import { runGateCheck } from "#src/handlers/gates/runner";
10
+ import type { PermissionCheckResult } from "#src/types";
11
11
 
12
12
  // ── helpers ────────────────────────────────────────────────────────────────
13
13
 
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import type { GateDescriptor } from "../../../src/handlers/gates/descriptor";
4
- import { describeSkillReadGate } from "../../../src/handlers/gates/skill-read";
5
- import type { ToolCallContext } from "../../../src/handlers/gates/types";
6
- import type { SkillPromptEntry } from "../../../src/skill-prompt-sanitizer";
3
+ import type { GateDescriptor } from "#src/handlers/gates/descriptor";
4
+ import { describeSkillReadGate } from "#src/handlers/gates/skill-read";
5
+ import type { ToolCallContext } from "#src/handlers/gates/types";
6
+ import type { SkillPromptEntry } from "#src/skill-prompt-sanitizer";
7
7
 
8
8
  // ── SDK stubs ──────────────────────────────────────────────────────────────
9
9
  vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
@@ -74,7 +74,7 @@ describe("describeSkillReadGate", () => {
74
74
  makeSkillEntry({ state: "ask" }),
75
75
  ]);
76
76
  expect(result).not.toBeNull();
77
- const desc = result as GateDescriptor;
77
+ const desc = result!;
78
78
  expect(desc.preResolved).toEqual({ state: "ask" });
79
79
  });
80
80
 
@@ -83,7 +83,7 @@ describe("describeSkillReadGate", () => {
83
83
  makeSkillEntry({ state: "allow" }),
84
84
  ]);
85
85
  expect(result).not.toBeNull();
86
- const desc = result as GateDescriptor;
86
+ const desc = result!;
87
87
  expect(desc.preResolved).toEqual({ state: "allow" });
88
88
  });
89
89
 
@@ -92,14 +92,14 @@ describe("describeSkillReadGate", () => {
92
92
  makeSkillEntry({ state: "deny" }),
93
93
  ]);
94
94
  expect(result).not.toBeNull();
95
- const desc = result as GateDescriptor;
95
+ const desc = result!;
96
96
  expect(desc.preResolved).toEqual({ state: "deny" });
97
97
  });
98
98
 
99
99
  it("decision surface is 'skill' and decision value is the skill name", () => {
100
100
  const result = describeSkillReadGate(makeTcc(), () => [
101
101
  makeSkillEntry({ name: "my-skill" }),
102
- ]) as GateDescriptor;
102
+ ])!;
103
103
  expect(result.decision.surface).toBe("skill");
104
104
  expect(result.decision.value).toBe("my-skill");
105
105
  });
@@ -107,7 +107,7 @@ describe("describeSkillReadGate", () => {
107
107
  it("denialContext contains the skill name and read path", () => {
108
108
  const result = describeSkillReadGate(makeTcc(), () => [
109
109
  makeSkillEntry({ name: "librarian" }),
110
- ]) as GateDescriptor;
110
+ ])!;
111
111
  expect(result.denialContext).toEqual({
112
112
  kind: "skill_read",
113
113
  skillName: "librarian",
@@ -120,7 +120,7 @@ describe("describeSkillReadGate", () => {
120
120
  const result = describeSkillReadGate(
121
121
  makeTcc({ agentName: "test-agent", toolCallId: "tc-42" }),
122
122
  () => [makeSkillEntry({ name: "my-skill" })],
123
- ) as GateDescriptor;
123
+ )!;
124
124
  expect(result.promptDetails).toMatchObject({
125
125
  source: "skill_read",
126
126
  agentName: "test-agent",
@@ -135,7 +135,7 @@ describe("describeSkillReadGate", () => {
135
135
  const result = describeSkillReadGate(
136
136
  makeTcc({ agentName: "agent-1" }),
137
137
  () => [makeSkillEntry({ name: "librarian" })],
138
- ) as GateDescriptor;
138
+ )!;
139
139
  expect(result.logContext).toMatchObject({
140
140
  source: "skill_read",
141
141
  skillName: "librarian",
@@ -144,9 +144,7 @@ describe("describeSkillReadGate", () => {
144
144
  });
145
145
 
146
146
  it("surface is 'skill' on the descriptor", () => {
147
- const result = describeSkillReadGate(makeTcc(), () => [
148
- makeSkillEntry(),
149
- ]) as GateDescriptor;
147
+ const result = describeSkillReadGate(makeTcc(), () => [makeSkillEntry()])!;
150
148
  expect(result.surface).toBe("skill");
151
149
  });
152
150
  });