@sentry/junior-github 0.167.0 → 0.168.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -751,12 +751,14 @@ var inputSchema = z.object({
751
751
  }
752
752
  ).describe(
753
753
  "Optional destination directory under the sandbox root. Defaults to repos/{name}."
754
- ).optional()
754
+ ).optional(),
755
+ allowAdHoc: z.boolean().optional().describe(
756
+ "Set true to keep an intentional ad-hoc checkout when matching Workspaces exist. Prefer switchWorkspace; the checkout is already present after a successful switch."
757
+ )
755
758
  }).strict();
756
759
  var cloneSchema = z.object({
757
760
  path: z.string(),
758
- repo: z.string(),
759
- workspaces: z.array(z.string())
761
+ repo: z.string()
760
762
  });
761
763
  var outputSchema = pluginToolOutputSchema.extend({
762
764
  target: z.literal("cloneRepository"),
@@ -769,6 +771,17 @@ function parseRepo(value) {
769
771
  }
770
772
  return { owner: parts[0], name: parts[1] };
771
773
  }
774
+ function workspaceRequiredError(repoId, workspaces) {
775
+ const names = workspaces.map((name) => `"${name}"`).join(", ");
776
+ if (workspaces.length === 1) {
777
+ return new PluginToolInputError(
778
+ `Repository ${repoId} is part of Workspace ${names}. Call switchWorkspace with that name; the checkout is already present after the switch. Pass allowAdHoc=true only for an intentional ad-hoc checkout.`
779
+ );
780
+ }
781
+ return new PluginToolInputError(
782
+ `Repository ${repoId} is part of Workspaces ${names}. Call switchWorkspace with one of those names; the checkout is already present after the switch. Pass allowAdHoc=true only for an intentional ad-hoc checkout.`
783
+ );
784
+ }
772
785
  function defaultDirectory(repoName) {
773
786
  return `repos/${repoName}`;
774
787
  }
@@ -808,16 +821,33 @@ function createGitHubCloneRepositoryTool(ctx) {
808
821
  openWorldHint: true,
809
822
  readOnlyHint: true
810
823
  },
811
- description: "Clone a GitHub repository into the sandbox workspace. The destination must not already exist.",
824
+ description: "Clone a GitHub repository into the sandbox as an ad-hoc checkout. The destination must not already exist. When matching Workspaces exist this is a tool input error: call switchWorkspace instead, or pass allowAdHoc=true for an intentional ad-hoc checkout.",
812
825
  describeProposal(input) {
813
826
  const directory = typeof input.directory === "string" && input.directory.length > 0 ? input.directory : void 0;
814
- return directory ? `Shallow-clone ${input.repo} into the local sandbox at ${directory} for inspection (no GitHub mutation).` : `Shallow-clone ${input.repo} into the local sandbox for inspection (no GitHub mutation).`;
827
+ const adHoc = input.allowAdHoc === true ? " as an intentional ad-hoc checkout" : "";
828
+ return directory ? `Shallow-clone ${input.repo} into the local sandbox at ${directory}${adHoc} for inspection (no GitHub mutation).` : `Shallow-clone ${input.repo} into the local sandbox${adHoc} for inspection (no GitHub mutation).`;
815
829
  },
816
830
  executionMode: "sequential",
817
831
  inputSchema,
818
832
  outputSchema,
819
833
  async execute(input, options) {
820
834
  const repo = parseRepo(input.repo);
835
+ const repoId = `${repo.owner}/${repo.name}`;
836
+ let workspaces = [];
837
+ try {
838
+ workspaces = await ctx.workspaces.findByRepository({
839
+ provider: "github",
840
+ repo: repoId
841
+ });
842
+ } catch (error) {
843
+ ctx.log.error("github.clone.workspaces_lookup.failed", {
844
+ repo: repoId,
845
+ error: error instanceof Error ? error.message : String(error)
846
+ });
847
+ }
848
+ if (workspaces.length > 0 && input.allowAdHoc !== true) {
849
+ throw workspaceRequiredError(repoId, workspaces);
850
+ }
821
851
  const directory = input.directory ?? defaultDirectory(repo.name);
822
852
  const rootSegment = directory.split("/")[0] ?? directory;
823
853
  if (isReservedSandboxDirectory(rootSegment)) {
@@ -874,21 +904,7 @@ function createGitHubCloneRepositoryTool(ctx) {
874
904
  `GitHub repository clone failed: ${clone.stderr.trim() || `exit ${clone.exitCode}`}`
875
905
  );
876
906
  }
877
- const repoId = `${repo.owner}/${repo.name}`;
878
- let workspaces = [];
879
- try {
880
- workspaces = await ctx.workspaces.findByRepository({
881
- provider: "github",
882
- repo: repoId
883
- });
884
- } catch (error) {
885
- ctx.log.error("github.clone.workspaces_lookup.failed", {
886
- repo: repoId,
887
- error: error instanceof Error ? error.message : String(error)
888
- });
889
- }
890
- const data = { repo: repoId, path, workspaces };
891
- return { target: "cloneRepository", ...data };
907
+ return { target: "cloneRepository", repo: repoId, path };
892
908
  }
893
909
  });
894
910
  }
@@ -1,13 +1,13 @@
1
1
  import { type ToolRegistrationHookContext } from "@sentry/junior-plugin-api";
2
- /** Clone one GitHub repository into the sandbox workspace. */
2
+ /** Clone one GitHub repository into the sandbox as an ad-hoc checkout. */
3
3
  export declare function createGitHubCloneRepositoryTool(ctx: ToolRegistrationHookContext): import("@sentry/junior-plugin-api").PluginToolDefinition<{
4
4
  repo: string;
5
5
  directory?: string | undefined;
6
+ allowAdHoc?: boolean | undefined;
6
7
  }, {
7
8
  [x: string]: unknown;
8
9
  path: string;
9
10
  repo: string;
10
- workspaces: string[];
11
11
  target: "cloneRepository";
12
12
  truncated?: boolean | undefined;
13
13
  continuation?: {
@@ -18,7 +18,6 @@ export declare function createGitHubCloneRepositoryTool(ctx: ToolRegistrationHoo
18
18
  [x: string]: unknown;
19
19
  path: string;
20
20
  repo: string;
21
- workspaces: string[];
22
21
  target: "cloneRepository";
23
22
  truncated?: boolean | undefined;
24
23
  continuation?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-github",
3
- "version": "0.167.0",
3
+ "version": "0.168.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -31,7 +31,7 @@
31
31
  "@sinclair/typebox": "^0.34.49",
32
32
  "drizzle-orm": "^0.45.2",
33
33
  "zod": "^4.4.3",
34
- "@sentry/junior-plugin-api": "0.167.0"
34
+ "@sentry/junior-plugin-api": "0.168.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.9.1",
@@ -23,7 +23,7 @@ Use `git` and `gh` for repository work. Use `github_createPullRequest`, not `gh
23
23
  - Base conclusions on repository evidence. Do not claim a check ran unless it did.
24
24
  - For Junior-owned pull requests, push the branch before creating the PR. The runtime supplies repository-scoped GitHub App credentials for both; try the operations before requesting remediation and never ask for a user token.
25
25
  - Use `github_cloneRepository` instead of shelling out to `git clone` when a repository is not already available in the sandbox.
26
- - When a tool result shows a Workspace exists for the repo, prefer it via `switchWorkspace` over an ad-hoc checkout.
26
+ - If `github_cloneRepository` returns a tool input error about matching Workspaces, call `switchWorkspace`. The checkout is already present after a successful switch. Pass `allowAdHoc=true` only for an intentional ad-hoc checkout.
27
27
  - A tool-routing denial requires the named tool; only an upstream denial justifies permission remediation.
28
28
  - Stop for ambiguous targets, missing access, destructive operations, or unresolved upstream permission failures.
29
29
 
@@ -31,7 +31,7 @@ Use `git` and `gh` for repository work. Use `github_createPullRequest`, not `gh
31
31
 
32
32
  ### 1. Resolve and inspect
33
33
 
34
- Identify the repo, checkout, default/current branches, worktree state, repo instructions, package manager, and relevant checks. Prefer an existing checkout; otherwise clone shallowly.
34
+ Identify the repo, checkout, default/current branches, worktree state, repo instructions, package manager, and relevant checks. Prefer an existing checkout or matching Workspace; otherwise clone shallowly. If clone returns a Workspace tool input error, switch Workspace instead of cloning again, or pass `allowAdHoc=true`.
35
35
 
36
36
  A shallow clone is for fast inspection, not history rewriting. Before rebasing, merge-base analysis, blame/history work, or comparing against a base absent locally, fetch the needed refs and deepen incrementally. Use `--unshallow` only when bounded deepening is insufficient. Never use a force push to compensate for incomplete history.
37
37