464 lines · 15.9 KB · TypeScript
RawDownload
import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import { NonNegativeInt, PositiveInt, ThreadId, TrimmedNonEmptyString } from "./baseSchemas.ts";import { SourceControlProviderError, SourceControlProviderInfo } from "./sourceControl.ts";import { VcsDriverKind } from "./vcs.ts"; const TrimmedNonEmptyStringSchema = TrimmedNonEmptyString;const GIT_LIST_BRANCHES_MAX_LIMIT = 200; // Domain Types export const GitStackedAction = Schema.Literals([  "commit",  "push",  "create_pr",  "commit_push",  "commit_push_pr",]);export type GitStackedAction = typeof GitStackedAction.Type;export const GitActionProgressPhase = Schema.Literals(["branch", "commit", "push", "pr"]);export type GitActionProgressPhase = typeof GitActionProgressPhase.Type;export const GitActionProgressKind = Schema.Literals([  "action_started",  "phase_started",  "hook_started",  "hook_output",  "hook_finished",  "action_finished",  "action_failed",]);export type GitActionProgressKind = typeof GitActionProgressKind.Type;export const GitActionProgressStream = Schema.Literals(["stdout", "stderr"]);export type GitActionProgressStream = typeof GitActionProgressStream.Type;const GitCommitStepStatus = Schema.Literals([  "created",  "skipped_no_changes",  "skipped_not_requested",]);const GitPushStepStatus = Schema.Literals([  "pushed",  "skipped_not_requested",  "skipped_up_to_date",]);const GitBranchStepStatus = Schema.Literals(["created", "skipped_not_requested"]);const GitPrStepStatus = Schema.Literals(["created", "opened_existing", "skipped_not_requested"]);const VcsStatusChangeRequestState = Schema.Literals(["open", "closed", "merged"]);const GitPullRequestReference = TrimmedNonEmptyStringSchema;const GitPullRequestState = Schema.Literals(["open", "closed", "merged"]);const GitPreparePullRequestThreadMode = Schema.Literals(["local", "worktree"]);export const GitRunStackedActionToastRunAction = Schema.Struct({  kind: GitStackedAction,});export type GitRunStackedActionToastRunAction = typeof GitRunStackedActionToastRunAction.Type;const GitRunStackedActionToastCta = Schema.Union([  Schema.Struct({    kind: Schema.Literal("none"),  }),  Schema.Struct({    kind: Schema.Literal("open_pr"),    label: TrimmedNonEmptyStringSchema,    url: Schema.String,  }),  Schema.Struct({    kind: Schema.Literal("run_action"),    label: TrimmedNonEmptyStringSchema,    action: GitRunStackedActionToastRunAction,  }),]);export type GitRunStackedActionToastCta = typeof GitRunStackedActionToastCta.Type;const GitRunStackedActionToast = Schema.Struct({  title: TrimmedNonEmptyStringSchema,  description: Schema.optional(TrimmedNonEmptyStringSchema),  cta: GitRunStackedActionToastCta,});export type GitRunStackedActionToast = typeof GitRunStackedActionToast.Type; export const VcsRef = Schema.Struct({  name: TrimmedNonEmptyStringSchema,  isRemote: Schema.optional(Schema.Boolean),  remoteName: Schema.optional(TrimmedNonEmptyStringSchema),  current: Schema.Boolean,  isDefault: Schema.Boolean,  worktreePath: TrimmedNonEmptyStringSchema.pipe(Schema.NullOr),});export type VcsRef = typeof VcsRef.Type; const VcsWorktree = Schema.Struct({  path: TrimmedNonEmptyStringSchema,  refName: TrimmedNonEmptyStringSchema,});const GitResolvedPullRequest = Schema.Struct({  number: PositiveInt,  title: TrimmedNonEmptyStringSchema,  url: Schema.String,  baseBranch: TrimmedNonEmptyStringSchema,  headBranch: TrimmedNonEmptyStringSchema,  state: GitPullRequestState,});export type GitResolvedPullRequest = typeof GitResolvedPullRequest.Type; // RPC Inputs export const VcsStatusInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,});export type VcsStatusInput = typeof VcsStatusInput.Type; export const VcsPullInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,});export type VcsPullInput = typeof VcsPullInput.Type; export const GitRunStackedActionInput = Schema.Struct({  actionId: TrimmedNonEmptyStringSchema,  cwd: TrimmedNonEmptyStringSchema,  action: GitStackedAction,  commitMessage: Schema.optional(TrimmedNonEmptyStringSchema.check(Schema.isMaxLength(10_000))),  featureBranch: Schema.optional(Schema.Boolean),  filePaths: Schema.optional(    Schema.Array(TrimmedNonEmptyStringSchema).check(Schema.isMinLength(1)),  ),  /** The thread the action runs beside; a pull request it creates is linked to it. */  threadId: Schema.optional(ThreadId),});export type GitRunStackedActionInput = typeof GitRunStackedActionInput.Type; export const VcsListRefsInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  query: Schema.optional(TrimmedNonEmptyStringSchema.check(Schema.isMaxLength(256))),  cursor: Schema.optional(NonNegativeInt),  includeMatchingRemoteRefs: Schema.optional(Schema.Boolean),  refKind: Schema.optional(Schema.Literals(["all", "local", "remote"])),  refresh: Schema.optional(Schema.Boolean),  limit: Schema.optional(    PositiveInt.check(Schema.isLessThanOrEqualTo(GIT_LIST_BRANCHES_MAX_LIMIT)),  ),});export type VcsListRefsInput = typeof VcsListRefsInput.Type; export const VcsCreateWorktreeInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  refName: TrimmedNonEmptyStringSchema,  newRefName: Schema.optional(TrimmedNonEmptyStringSchema),  baseRefName: Schema.optional(TrimmedNonEmptyStringSchema),  path: Schema.NullOr(TrimmedNonEmptyStringSchema),});export type VcsCreateWorktreeInput = typeof VcsCreateWorktreeInput.Type; export const GitPullRequestRefInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  reference: GitPullRequestReference,});export type GitPullRequestRefInput = typeof GitPullRequestRefInput.Type; export const GitPreparePullRequestThreadInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  reference: GitPullRequestReference,  mode: GitPreparePullRequestThreadMode,  threadId: Schema.optional(ThreadId),});export type GitPreparePullRequestThreadInput = typeof GitPreparePullRequestThreadInput.Type; export const VcsRemoveWorktreeInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  path: TrimmedNonEmptyStringSchema,  force: Schema.optional(Schema.Boolean),});export type VcsRemoveWorktreeInput = typeof VcsRemoveWorktreeInput.Type; export const VcsCreateRefInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  refName: TrimmedNonEmptyStringSchema,  switchRef: Schema.optional(Schema.Boolean),});export type VcsCreateRefInput = typeof VcsCreateRefInput.Type; export const VcsCreateRefResult = Schema.Struct({  refName: TrimmedNonEmptyStringSchema,});export type VcsCreateRefResult = typeof VcsCreateRefResult.Type; export const VcsSwitchRefInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  refName: TrimmedNonEmptyStringSchema,});export type VcsSwitchRefInput = typeof VcsSwitchRefInput.Type; export const VcsInitInput = Schema.Struct({  cwd: TrimmedNonEmptyStringSchema,  kind: Schema.optional(VcsDriverKind),});export type VcsInitInput = typeof VcsInitInput.Type; // RPC Results const VcsStatusChangeRequest = Schema.Struct({  number: PositiveInt,  title: TrimmedNonEmptyStringSchema,  url: Schema.String,  baseRef: TrimmedNonEmptyStringSchema,  headRef: TrimmedNonEmptyStringSchema,  state: VcsStatusChangeRequestState,  /** Optional for compatibility with older servers and providers. */  isDraft: Schema.optional(Schema.Boolean),  /**   * Last provider-side activity (ISO), including comments and metadata edits.   * This is not the time a change request closed or merged. Optional for old   * servers and providers whose lookups do not report it.   */  updatedAt: Schema.optional(Schema.NullOr(Schema.String)),}); const VcsStatusLocalShape = {  isRepo: Schema.Boolean,  sourceControlProvider: Schema.optional(SourceControlProviderInfo),  hasPrimaryRemote: Schema.Boolean,  isDefaultRef: Schema.Boolean,  refName: Schema.NullOr(TrimmedNonEmptyStringSchema),  hasWorkingTreeChanges: Schema.Boolean,  workingTree: Schema.Struct({    files: Schema.Array(      Schema.Struct({        path: TrimmedNonEmptyStringSchema,        insertions: NonNegativeInt,        deletions: NonNegativeInt,      }),    ),    insertions: NonNegativeInt,    deletions: NonNegativeInt,  }),}; const VcsStatusRemoteShape = {  hasUpstream: Schema.Boolean,  aheadCount: NonNegativeInt,  behindCount: NonNegativeInt,  aheadOfDefaultCount: Schema.optional(NonNegativeInt),  pr: Schema.NullOr(VcsStatusChangeRequest),}; export const VcsStatusLocalResult = Schema.Struct(VcsStatusLocalShape);export type VcsStatusLocalResult = typeof VcsStatusLocalResult.Type; export const VcsStatusRemoteResult = Schema.Struct(VcsStatusRemoteShape);export type VcsStatusRemoteResult = typeof VcsStatusRemoteResult.Type; export const VcsStatusResult = Schema.Struct({  ...VcsStatusLocalShape,  ...VcsStatusRemoteShape,});export type VcsStatusResult = typeof VcsStatusResult.Type; export const VcsStatusStreamEvent = Schema.Union([  Schema.TaggedStruct("snapshot", {    local: VcsStatusLocalResult,    remote: Schema.NullOr(VcsStatusRemoteResult),  }),  Schema.TaggedStruct("localUpdated", {    local: VcsStatusLocalResult,  }),  Schema.TaggedStruct("remoteUpdated", {    remote: Schema.NullOr(VcsStatusRemoteResult),  }),]);export type VcsStatusStreamEvent = typeof VcsStatusStreamEvent.Type; export const VcsListRefsResult = Schema.Struct({  refs: Schema.Array(VcsRef),  isRepo: Schema.Boolean,  hasPrimaryRemote: Schema.Boolean,  nextCursor: NonNegativeInt.pipe(Schema.NullOr),  totalCount: NonNegativeInt,});export type VcsListRefsResult = typeof VcsListRefsResult.Type; export const VcsCreateWorktreeResult = Schema.Struct({  worktree: VcsWorktree,});export type VcsCreateWorktreeResult = typeof VcsCreateWorktreeResult.Type; export const GitResolvePullRequestResult = Schema.Struct({  pullRequest: GitResolvedPullRequest,});export type GitResolvePullRequestResult = typeof GitResolvePullRequestResult.Type; export const GitPreparePullRequestThreadResult = Schema.Struct({  pullRequest: GitResolvedPullRequest,  branch: TrimmedNonEmptyStringSchema,  worktreePath: TrimmedNonEmptyStringSchema.pipe(Schema.NullOr),  /**   * False when the checkout could not be brought to the pull request head — a reused worktree   * holding local commits or uncommitted changes keeps its own state, so the code being handed   * over is older than the pull request.   */  isOnPullRequestHead: Schema.Boolean.pipe(Schema.withDecodingDefaultKey(Effect.succeed(true))),});export type GitPreparePullRequestThreadResult = typeof GitPreparePullRequestThreadResult.Type; export const VcsSwitchRefResult = Schema.Struct({  refName: Schema.NullOr(TrimmedNonEmptyStringSchema),});export type VcsSwitchRefResult = typeof VcsSwitchRefResult.Type; export const GitRunStackedActionResult = Schema.Struct({  action: GitStackedAction,  branch: Schema.Struct({    status: GitBranchStepStatus,    name: Schema.optional(TrimmedNonEmptyStringSchema),  }),  commit: Schema.Struct({    status: GitCommitStepStatus,    commitSha: Schema.optional(TrimmedNonEmptyStringSchema),    subject: Schema.optional(TrimmedNonEmptyStringSchema),  }),  push: Schema.Struct({    status: GitPushStepStatus,    branch: Schema.optional(TrimmedNonEmptyStringSchema),    upstreamBranch: Schema.optional(TrimmedNonEmptyStringSchema),    setUpstream: Schema.optional(Schema.Boolean),  }),  pr: Schema.Struct({    status: GitPrStepStatus,    url: Schema.optional(Schema.String),    number: Schema.optional(PositiveInt),    baseBranch: Schema.optional(TrimmedNonEmptyStringSchema),    headBranch: Schema.optional(TrimmedNonEmptyStringSchema),    title: Schema.optional(TrimmedNonEmptyStringSchema),  }),  toast: GitRunStackedActionToast,});export type GitRunStackedActionResult = typeof GitRunStackedActionResult.Type; export const VcsPullResult = Schema.Struct({  status: Schema.Literals(["pulled", "skipped_up_to_date"]),  refName: TrimmedNonEmptyStringSchema,  upstreamRef: TrimmedNonEmptyStringSchema.pipe(Schema.NullOr),});export type VcsPullResult = typeof VcsPullResult.Type; // RPC / domain errorsexport class GitCommandError extends Schema.TaggedError<GitCommandError>()("GitCommandError", {  operation: Schema.String,  command: Schema.String,  cwd: Schema.String,  argumentCount: Schema.optional(Schema.Number),  exitCode: Schema.optional(Schema.Number),  stdoutLength: Schema.optional(Schema.Number),  stderrLength: Schema.optional(Schema.Number),  outputLength: Schema.optional(Schema.Number),  detail: Schema.String,  cause: Schema.optional(Schema.Defect()),}) {  override get message(): string {    return `Git command failed in ${this.operation} (${this.cwd}): ${this.detail}`;  }} export class TextGenerationError extends Schema.TaggedError<TextGenerationError>()(  "TextGenerationError",  {    operation: Schema.String,    detail: Schema.String,    cause: Schema.optional(Schema.Defect()),  },) {  override get message(): string {    return `Text generation failed in ${this.operation}: ${this.detail}`;  }} export class GitManagerError extends Schema.TaggedError<GitManagerError>()("GitManagerError", {  operation: Schema.String,  cwd: Schema.String,  detail: Schema.String,  cause: Schema.optional(Schema.Defect()),}) {  override get message(): string {    return `Git manager failed in ${this.operation}: ${this.detail}`;  }} export class GitPullRequestMaterializationError extends Schema.TaggedError<GitPullRequestMaterializationError>()(  "GitPullRequestMaterializationError",  {    cwd: TrimmedNonEmptyStringSchema,    pullRequestNumber: PositiveInt,    headRepository: Schema.NullOr(TrimmedNonEmptyStringSchema),    headBranch: TrimmedNonEmptyStringSchema,    localBranch: TrimmedNonEmptyStringSchema,    cause: Schema.Defect(),  },) {  override get message(): string {    return `Failed to materialize pull request #${this.pullRequestNumber} branch ${this.headBranch} as ${this.localBranch}.`;  }} export const GitManagerServiceError = Schema.Union([  GitManagerError,  GitPullRequestMaterializationError,  GitCommandError,  SourceControlProviderError,  TextGenerationError,]);export type GitManagerServiceError = typeof GitManagerServiceError.Type; const GitActionProgressBase = Schema.Struct({  actionId: TrimmedNonEmptyStringSchema,  cwd: TrimmedNonEmptyStringSchema,  action: GitStackedAction,}); const GitActionStartedEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("action_started"),  phases: Schema.Array(GitActionProgressPhase),});const GitActionPhaseStartedEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("phase_started"),  phase: GitActionProgressPhase,  label: TrimmedNonEmptyStringSchema,});const GitActionHookStartedEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("hook_started"),  hookName: TrimmedNonEmptyStringSchema,});const GitActionHookOutputEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("hook_output"),  hookName: Schema.NullOr(TrimmedNonEmptyStringSchema),  stream: GitActionProgressStream,  text: TrimmedNonEmptyStringSchema,});const GitActionHookFinishedEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("hook_finished"),  hookName: TrimmedNonEmptyStringSchema,  exitCode: Schema.NullOr(Schema.Int),  durationMs: Schema.NullOr(NonNegativeInt),});const GitActionFinishedEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("action_finished"),  result: GitRunStackedActionResult,});const GitActionFailedEvent = Schema.Struct({  ...GitActionProgressBase.fields,  kind: Schema.Literal("action_failed"),  phase: Schema.NullOr(GitActionProgressPhase),  message: TrimmedNonEmptyStringSchema,}); export const GitActionProgressEvent = Schema.Union([  GitActionStartedEvent,  GitActionPhaseStartedEvent,  GitActionHookStartedEvent,  GitActionHookOutputEvent,  GitActionHookFinishedEvent,  GitActionFinishedEvent,  GitActionFailedEvent,]);export type GitActionProgressEvent = typeof GitActionProgressEvent.Type;