@happyvertical/repos 0.74.8

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/errors.ts","../src/factory.ts","../src/github/rest.ts","../src/github/index.ts","../src/parsing.ts"],"sourcesContent":["/**\n * Repository error handling\n */\n\nexport enum RepositoryErrorCode {\n NOT_FOUND = 'NOT_FOUND',\n UNAUTHORIZED = 'UNAUTHORIZED',\n FORBIDDEN = 'FORBIDDEN',\n RATE_LIMITED = 'RATE_LIMITED',\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n NETWORK_ERROR = 'NETWORK_ERROR',\n UNKNOWN = 'UNKNOWN',\n}\n\nexport class RepositoryError extends Error {\n constructor(\n message: string,\n public code: RepositoryErrorCode,\n public statusCode?: number,\n public response?: unknown,\n ) {\n super(message);\n this.name = 'RepositoryError';\n Error.captureStackTrace(this, this.constructor);\n }\n\n static fromHTTPStatus(\n statusCode: number,\n message: string,\n response?: unknown,\n ): RepositoryError {\n let code: RepositoryErrorCode;\n\n switch (statusCode) {\n case 404:\n code = RepositoryErrorCode.NOT_FOUND;\n break;\n case 401:\n code = RepositoryErrorCode.UNAUTHORIZED;\n break;\n case 403:\n code = RepositoryErrorCode.FORBIDDEN;\n break;\n case 429:\n code = RepositoryErrorCode.RATE_LIMITED;\n break;\n case 422:\n code = RepositoryErrorCode.VALIDATION_ERROR;\n break;\n default:\n code = RepositoryErrorCode.UNKNOWN;\n }\n\n return new RepositoryError(message, code, statusCode, response);\n }\n\n static networkError(message: string, cause?: Error): RepositoryError {\n return new RepositoryError(\n message,\n RepositoryErrorCode.NETWORK_ERROR,\n undefined,\n cause,\n );\n }\n\n isRetryable(): boolean {\n return (\n this.code === RepositoryErrorCode.RATE_LIMITED ||\n this.code === RepositoryErrorCode.NETWORK_ERROR\n );\n }\n}\n","/**\n * Repository factory function\n *\n * Creates repository instances following the pattern from @happyvertical/files and @happyvertical/sql\n */\n\nimport { RepositoryError, RepositoryErrorCode } from './errors.js';\nimport type { IRepository, RepositoryConfig } from './types.js';\n\n/**\n * Check if value is already a repository instance\n */\nfunction isRepositoryInstance(value: unknown): value is IRepository {\n return (\n value !== null &&\n typeof value === 'object' &&\n 'getIssue' in value &&\n 'createIssue' in value &&\n 'addLabels' in value &&\n typeof (value as IRepository).getIssue === 'function'\n );\n}\n\n/**\n * Get a repository client instance\n *\n * This is the main entry point following the pattern from @happyvertical/files and @happyvertical/sql.\n * It can accept either a configuration object or an existing repository instance.\n *\n * @param options - Repository configuration or existing repository instance\n * @returns Promise resolving to repository interface\n *\n * @example\n * ```typescript\n * import { getRepository } from '@happyvertical/repos';\n *\n * // Create a GitHub repository client\n * const repo = await getRepository({\n * type: 'github',\n * owner: 'happyvertical',\n * repo: 'sdk',\n * token: process.env.GITHUB_TOKEN\n * });\n *\n * // Use the client\n * const issue = await repo.getIssue(352);\n * await repo.addLabels(352, ['type: feature', 'priority: high']);\n *\n * // Pass existing instance (returns it unchanged)\n * const sameRepo = await getRepository(repo);\n * ```\n */\nexport async function getRepository(\n options: RepositoryConfig | IRepository,\n): Promise<IRepository> {\n // If already a repository instance, return it\n if (isRepositoryInstance(options)) {\n return options;\n }\n\n // Validate configuration\n if (!options.type) {\n throw new RepositoryError(\n 'Repository type is required',\n RepositoryErrorCode.VALIDATION_ERROR,\n );\n }\n\n if (!options.owner || !options.repo) {\n throw new RepositoryError(\n 'Repository owner and repo are required',\n RepositoryErrorCode.VALIDATION_ERROR,\n );\n }\n\n if (!options.token) {\n throw new RepositoryError(\n 'Authentication token is required',\n RepositoryErrorCode.UNAUTHORIZED,\n );\n }\n\n // Create repository instance based on type\n switch (options.type) {\n case 'github': {\n const { GitHubRepository } = await import('./github/index.js');\n return new GitHubRepository(options);\n }\n\n case 'gitlab':\n throw new RepositoryError(\n 'GitLab support not yet implemented',\n RepositoryErrorCode.UNKNOWN,\n );\n\n case 'bitbucket':\n throw new RepositoryError(\n 'Bitbucket support not yet implemented',\n RepositoryErrorCode.UNKNOWN,\n );\n\n case 'azure':\n throw new RepositoryError(\n 'Azure DevOps support not yet implemented',\n RepositoryErrorCode.UNKNOWN,\n );\n\n default:\n throw new RepositoryError(\n `Unsupported repository type: ${options.type}`,\n RepositoryErrorCode.VALIDATION_ERROR,\n );\n }\n}\n","/**\n * GitHub REST API client\n */\n\nimport { RepositoryError } from '../errors.js';\n\nexport interface GitHubRestConfig {\n token: string;\n baseUrl?: string;\n}\n\n/**\n * GitHub REST API client\n */\nexport class GitHubRest {\n private token: string;\n private baseUrl: string;\n\n constructor(config: GitHubRestConfig) {\n this.token = config.token;\n this.baseUrl = config.baseUrl || 'https://api.github.com';\n }\n\n /**\n * Make a REST API request\n */\n async request(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<unknown> {\n const url = `${this.baseUrl}${path}`;\n const headers = {\n Authorization: `Bearer ${this.token}`,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n 'Content-Type': 'application/json',\n };\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw RepositoryError.fromHTTPStatus(\n response.status,\n `GitHub API error: ${response.statusText}\\n${error}`,\n error,\n );\n }\n\n return response.json();\n }\n\n /**\n * GET request\n */\n async get(path: string): Promise<unknown> {\n return this.request('GET', path);\n }\n\n /**\n * POST request\n */\n async post(path: string, body: unknown): Promise<unknown> {\n return this.request('POST', path, body);\n }\n\n /**\n * PATCH request\n */\n async patch(path: string, body: unknown): Promise<unknown> {\n return this.request('PATCH', path, body);\n }\n\n /**\n * PUT request\n */\n async put(path: string, body?: unknown): Promise<unknown> {\n return this.request('PUT', path, body);\n }\n\n /**\n * DELETE request\n */\n async delete(path: string): Promise<void> {\n await this.request('DELETE', path);\n }\n}\n","/**\n * GitHub repository implementation\n */\n\nimport { GraphQLClient, type IGraphQLClient } from '@happyvertical/graphql';\nimport type {\n Branch,\n Comment,\n CreateFromTemplateOptions,\n CreateIssueInput,\n CreatePRInput,\n IRepository,\n Issue,\n Label,\n MergeMethod,\n PullRequest,\n Repository,\n RepositoryConfig,\n SearchFilters,\n UpdateIssueInput,\n} from '../types.js';\nimport { GitHubRest } from './rest.js';\n\n/**\n * GitHub repository implementation\n */\nexport class GitHubRepository implements IRepository {\n private rest: GitHubRest;\n private graphql: IGraphQLClient;\n private owner: string;\n private repo: string;\n\n constructor(config: RepositoryConfig) {\n if (config.type !== 'github') {\n throw new Error('Invalid config type for GitHubRepository');\n }\n\n this.owner = config.owner;\n this.repo = config.repo;\n this.rest = new GitHubRest({\n token: config.token,\n baseUrl: config.baseUrl,\n });\n this.graphql = new GraphQLClient({\n endpoint: config.baseUrl\n ? `${config.baseUrl}/graphql`\n : 'https://api.github.com/graphql',\n token: config.token,\n });\n }\n\n // Repository Info\n async getRepository(): Promise<Repository> {\n const data = (await this.rest.get(`/repos/${this.owner}/${this.repo}`)) as {\n name: string;\n owner: { login: string };\n description: string;\n default_branch: string;\n html_url: string;\n private: boolean;\n };\n\n return {\n owner: data.owner.login,\n name: data.name,\n description: data.description,\n defaultBranch: data.default_branch,\n url: data.html_url,\n isPrivate: data.private,\n };\n }\n\n // Issues\n async getIssue(number: number): Promise<Issue> {\n const data = (await this.rest.get(\n `/repos/${this.owner}/${this.repo}/issues/${number}`,\n )) as {\n number: number;\n node_id: string;\n title: string;\n body: string | null;\n state: string;\n labels: Array<{ name: string; color: string; description: string }>;\n assignees: Array<{ login: string; id: number; type: string }>;\n user: { login: string; id: number; type: string };\n created_at: string;\n updated_at: string;\n closed_at: string | null;\n html_url: string;\n comments: number;\n };\n\n return {\n number: data.number,\n id: data.node_id,\n title: data.title,\n body: data.body || '',\n state: data.state === 'open' ? 'open' : 'closed',\n labels: data.labels.map((l) => ({\n name: l.name,\n color: l.color,\n description: l.description,\n })),\n assignees: data.assignees.map((a) => ({\n login: a.login,\n id: String(a.id),\n type: a.type === 'Bot' ? 'Bot' : 'User',\n })),\n author: {\n login: data.user.login,\n id: String(data.user.id),\n type: data.user.type === 'Bot' ? 'Bot' : 'User',\n },\n createdAt: new Date(data.created_at),\n updatedAt: new Date(data.updated_at),\n closedAt: data.closed_at ? new Date(data.closed_at) : undefined,\n url: data.html_url,\n commentsCount: data.comments,\n };\n }\n\n async createIssue(data: CreateIssueInput): Promise<Issue> {\n const result = (await this.rest.post(\n `/repos/${this.owner}/${this.repo}/issues`,\n data,\n )) as ReturnType<typeof this.getIssue> extends Promise<infer T> ? T : never;\n return this.getIssue(result.number);\n }\n\n async updateIssue(number: number, data: UpdateIssueInput): Promise<Issue> {\n await this.rest.patch(\n `/repos/${this.owner}/${this.repo}/issues/${number}`,\n data,\n );\n return this.getIssue(number);\n }\n\n async closeIssue(number: number): Promise<void> {\n await this.updateIssue(number, { state: 'closed' });\n }\n\n // Labels\n async addLabels(issueNumber: number, labels: string[]): Promise<void> {\n await this.rest.post(\n `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels`,\n { labels },\n );\n }\n\n async removeLabel(issueNumber: number, label: string): Promise<void> {\n const encodedLabel = encodeURIComponent(label);\n await this.rest.delete(\n `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels/${encodedLabel}`,\n );\n }\n\n async createLabel(label: Label): Promise<void> {\n await this.rest.post(`/repos/${this.owner}/${this.repo}/labels`, label);\n }\n\n async updateLabel(name: string, label: Label): Promise<void> {\n await this.rest.patch(\n `/repos/${this.owner}/${this.repo}/labels/${encodeURIComponent(name)}`,\n label,\n );\n }\n\n async listLabels(): Promise<Label[]> {\n const data = (await this.rest.get(\n `/repos/${this.owner}/${this.repo}/labels`,\n )) as Array<{ name: string; color: string; description: string }>;\n\n return data.map((l) => ({\n name: l.name,\n color: l.color,\n description: l.description,\n }));\n }\n\n // Comments\n async addComment(issueNumber: number, body: string): Promise<Comment> {\n const data = (await this.rest.post(\n `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments`,\n { body },\n )) as {\n id: number;\n body: string;\n user: { login: string; id: number; type: string };\n created_at: string;\n updated_at: string;\n html_url: string;\n };\n\n return {\n id: String(data.id),\n body: data.body,\n author: {\n login: data.user.login,\n id: String(data.user.id),\n type: data.user.type === 'Bot' ? 'Bot' : 'User',\n },\n createdAt: new Date(data.created_at),\n updatedAt: new Date(data.updated_at),\n url: data.html_url,\n };\n }\n\n async updateComment(commentId: string, body: string): Promise<Comment> {\n const data = (await this.rest.patch(\n `/repos/${this.owner}/${this.repo}/issues/comments/${commentId}`,\n { body },\n )) as ReturnType<typeof this.addComment> extends Promise<infer T>\n ? T\n : never;\n return data as Comment;\n }\n\n async deleteComment(commentId: string): Promise<void> {\n await this.rest.delete(\n `/repos/${this.owner}/${this.repo}/issues/comments/${commentId}`,\n );\n }\n\n async listComments(issueNumber: number): Promise<Comment[]> {\n const data = (await this.rest.get(\n `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments`,\n )) as Array<{\n id: number;\n body: string;\n user: { login: string; id: number; type: string };\n created_at: string;\n updated_at: string;\n html_url: string;\n }>;\n\n return data.map((c) => ({\n id: String(c.id),\n body: c.body,\n author: {\n login: c.user.login,\n id: String(c.user.id),\n type: c.user.type === 'Bot' ? 'Bot' : 'User',\n },\n createdAt: new Date(c.created_at),\n updatedAt: new Date(c.updated_at),\n url: c.html_url,\n }));\n }\n\n // Assignments\n async assignIssue(issueNumber: number, assignees: string[]): Promise<void> {\n await this.rest.post(\n `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/assignees`,\n { assignees },\n );\n }\n\n async unassignIssue(issueNumber: number, assignees: string[]): Promise<void> {\n await this.rest.delete(\n `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/assignees`,\n );\n }\n\n // Pull Requests\n async getPullRequest(number: number): Promise<PullRequest> {\n const issue = await this.getIssue(number);\n const data = (await this.rest.get(\n `/repos/${this.owner}/${this.repo}/pulls/${number}`,\n )) as {\n head: { ref: string };\n base: { ref: string };\n merged: boolean;\n merged_at: string | null;\n mergeable: boolean;\n draft: boolean;\n };\n\n return {\n ...issue,\n headRef: data.head.ref,\n baseRef: data.base.ref,\n merged: data.merged,\n mergedAt: data.merged_at ? new Date(data.merged_at) : undefined,\n mergeable: data.mergeable,\n draft: data.draft,\n };\n }\n\n async createPullRequest(data: CreatePRInput): Promise<PullRequest> {\n const result = (await this.rest.post(\n `/repos/${this.owner}/${this.repo}/pulls`,\n {\n title: data.title,\n body: data.body,\n head: data.headRef,\n base: data.baseRef,\n draft: data.draft,\n },\n )) as { number: number };\n return this.getPullRequest(result.number);\n }\n\n async mergePullRequest(number: number, method?: MergeMethod): Promise<void> {\n await this.rest.put(\n `/repos/${this.owner}/${this.repo}/pulls/${number}/merge`,\n {\n merge_method: method || 'merge',\n },\n );\n }\n\n // Search\n async searchIssues(query: string, filters?: SearchFilters): Promise<Issue[]> {\n let searchQuery = `${query} repo:${this.owner}/${this.repo}`;\n\n if (filters?.state) {\n searchQuery += ` state:${filters.state}`;\n }\n if (filters?.labels) {\n searchQuery += ` ${filters.labels.map((l) => `label:\"${l}\"`).join(' ')}`;\n }\n if (filters?.author) {\n searchQuery += ` author:${filters.author}`;\n }\n if (filters?.assignee) {\n searchQuery += ` assignee:${filters.assignee}`;\n }\n\n const params = new URLSearchParams({\n q: searchQuery,\n sort: filters?.sort || 'created',\n order: filters?.order || 'desc',\n per_page: String(filters?.limit || 30),\n });\n\n const data = (await this.rest.get(`/search/issues?${params}`)) as {\n items: Array<{\n number: number;\n }>;\n };\n\n return Promise.all(data.items.map((item) => this.getIssue(item.number)));\n }\n\n // Node ID resolution\n async getIssueNodeId(issueNumber: number): Promise<string> {\n const issue = await this.getIssue(issueNumber);\n return issue.id;\n }\n\n async getPRNodeId(prNumber: number): Promise<string> {\n const pr = await this.getPullRequest(prNumber);\n return pr.id;\n }\n\n // Branch Management\n async createBranch(name: string, fromRef: string): Promise<Branch> {\n // First get the SHA for the reference\n const refData = (await this.rest.get(\n `/repos/${this.owner}/${this.repo}/git/ref/heads/${fromRef}`,\n )) as { object: { sha: string } };\n\n // Create the new branch\n await this.rest.post(`/repos/${this.owner}/${this.repo}/git/refs`, {\n ref: `refs/heads/${name}`,\n sha: refData.object.sha,\n });\n\n return {\n name,\n sha: refData.object.sha,\n protected: false,\n };\n }\n\n async deleteBranch(name: string): Promise<void> {\n await this.rest.delete(\n `/repos/${this.owner}/${this.repo}/git/refs/heads/${name}`,\n );\n }\n\n async getBranch(name: string): Promise<Branch | null> {\n try {\n const data = (await this.rest.get(\n `/repos/${this.owner}/${this.repo}/branches/${encodeURIComponent(name)}`,\n )) as {\n name: string;\n commit: { sha: string };\n protected: boolean;\n };\n\n return {\n name: data.name,\n sha: data.commit.sha,\n protected: data.protected,\n };\n } catch (error) {\n // Return null if branch not found (404)\n if (\n error instanceof Error &&\n 'code' in error &&\n (error as { code: string }).code === 'NOT_FOUND'\n ) {\n return null;\n }\n throw error;\n }\n }\n\n // PR Draft/Review\n async markPRReady(prNumber: number): Promise<void> {\n const pr = await this.getPullRequest(prNumber);\n const mutation = `\n mutation($pullRequestId: ID!) {\n markPullRequestReadyForReview(input: {\n pullRequestId: $pullRequestId\n }) {\n pullRequest {\n id\n }\n }\n }\n `;\n await this.graphql.mutate(mutation, { pullRequestId: pr.id });\n }\n\n async convertPRToDraft(prNumber: number): Promise<void> {\n const pr = await this.getPullRequest(prNumber);\n const mutation = `\n mutation($pullRequestId: ID!) {\n convertPullRequestToDraft(input: {\n pullRequestId: $pullRequestId\n }) {\n pullRequest {\n id\n }\n }\n }\n `;\n await this.graphql.mutate(mutation, { pullRequestId: pr.id });\n }\n\n async requestReview(prNumber: number, reviewers: string[]): Promise<void> {\n await this.rest.post(\n `/repos/${this.owner}/${this.repo}/pulls/${prNumber}/requested_reviewers`,\n { reviewers },\n );\n }\n\n // Workflow\n async triggerWorkflow(\n workflowId: string,\n ref: string,\n inputs?: Record<string, string>,\n ): Promise<void> {\n await this.rest.post(\n `/repos/${this.owner}/${this.repo}/actions/workflows/${workflowId}/dispatches`,\n { ref, inputs: inputs || {} },\n );\n }\n\n // Linking\n async findPRsForIssue(issueNumber: number): Promise<PullRequest[]> {\n // Search for PRs that reference this issue with closing keywords\n const keywords = ['closes', 'fixes', 'resolves'];\n const searchTerms = keywords\n .map((k) => `${k} #${issueNumber}`)\n .join(' OR ');\n const query = `is:pr repo:${this.owner}/${this.repo} ${searchTerms}`;\n\n const data = (await this.rest.get(\n `/search/issues?q=${encodeURIComponent(query)}`,\n )) as {\n items: Array<{ number: number }>;\n };\n\n return Promise.all(\n data.items.map((item) => this.getPullRequest(item.number)),\n );\n }\n\n async findIssueForPR(prNumber: number): Promise<Issue | null> {\n const pr = await this.getPullRequest(prNumber);\n\n // Look for closing keywords in PR body\n const closingPattern = /(?:closes?|fixes?|resolves?)\\s+#(\\d+)/gi;\n const matches = [...pr.body.matchAll(closingPattern)];\n\n if (matches.length === 0) {\n return null;\n }\n\n // Return the first linked issue\n const issueNumber = Number.parseInt(matches[0][1], 10);\n try {\n return await this.getIssue(issueNumber);\n } catch {\n return null;\n }\n }\n\n // File Content\n async getFileContent(path: string, ref?: string): Promise<string | null> {\n try {\n const url = `/repos/${this.owner}/${this.repo}/contents/${path}${ref ? `?ref=${ref}` : ''}`;\n const data = (await this.rest.get(url)) as {\n type: string;\n content?: string;\n encoding?: string;\n };\n\n if (data.type !== 'file' || !data.content) {\n return null;\n }\n\n // GitHub returns base64-encoded content\n if (data.encoding === 'base64') {\n return Buffer.from(data.content, 'base64').toString('utf-8');\n }\n\n return data.content;\n } catch {\n // 404 means file doesn't exist\n return null;\n }\n }\n\n async listDirectoryFiles(path: string, ref?: string): Promise<string[]> {\n try {\n const url = `/repos/${this.owner}/${this.repo}/contents/${path}${ref ? `?ref=${ref}` : ''}`;\n const data = (await this.rest.get(url)) as Array<{\n name: string;\n type: string;\n }>;\n\n if (!Array.isArray(data)) {\n return [];\n }\n\n return data\n .filter((item) => item.type === 'file')\n .map((item) => item.name);\n } catch {\n // 404 means directory doesn't exist\n return [];\n }\n }\n\n // Repository Creation from Template\n\n /**\n * Create a new repository from this repository as a template.\n *\n * Uses the GitHub \"Generate\" API: POST /repos/{template_owner}/{template_repo}/generate\n * The current repository (this.owner/this.repo) is used as the template.\n */\n async createRepositoryFromTemplate(\n options: CreateFromTemplateOptions,\n ): Promise<Repository> {\n const data = (await this.rest.post(\n `/repos/${this.owner}/${this.repo}/generate`,\n {\n owner: options.owner,\n name: options.name,\n description: options.description || '',\n private: options.isPrivate ?? true,\n include_all_branches: options.includeAllBranches ?? false,\n },\n )) as {\n name: string;\n owner: { login: string };\n description: string;\n default_branch: string;\n html_url: string;\n private: boolean;\n };\n\n return {\n owner: data.owner.login,\n name: data.name,\n description: data.description || '',\n defaultBranch: data.default_branch,\n url: data.html_url,\n isPrivate: data.private,\n };\n }\n}\n","/**\n * Issue body parsing and rendering utilities\n *\n * Parses GitHub issue bodies created from YAML form templates.\n * GitHub renders form fields as markdown sections with ### headings.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport type { IRepository } from './types.js';\n\n/**\n * Template field definition - mirrors GitHub ISSUE_TEMPLATE YAML structure\n */\nexport interface TemplateField {\n type: 'textarea' | 'input' | 'dropdown' | 'checkboxes' | 'markdown';\n id?: string;\n attributes: {\n label: string;\n description?: string;\n placeholder?: string;\n value?: string;\n options?: string[];\n };\n validations?: {\n required?: boolean;\n };\n}\n\n/**\n * Issue template definition\n */\nexport interface IssueTemplate {\n name: string;\n description?: string;\n title?: string;\n labels?: string[];\n assignees?: string[];\n body: TemplateField[];\n}\n\n/**\n * Load and parse an issue template from a YAML file\n *\n * @param yamlPath - Path to the YAML template file\n * @returns Parsed issue template\n *\n * @example\n * ```typescript\n * const template = await loadIssueTemplate('.github/ISSUE_TEMPLATE/bug_report.yml');\n * ```\n */\nexport async function loadIssueTemplate(\n yamlPath: string,\n): Promise<IssueTemplate> {\n const content = await readFile(yamlPath, 'utf-8');\n return parseIssueTemplate(content);\n}\n\n/**\n * Parse an issue template from YAML content\n *\n * @param yamlContent - YAML content string\n * @returns Parsed issue template\n *\n * @example\n * ```typescript\n * const template = parseIssueTemplate(`\n * name: Bug Report\n * body:\n * - type: textarea\n * id: description\n * attributes:\n * label: Description\n * `);\n * ```\n */\nexport function parseIssueTemplate(yamlContent: string): IssueTemplate {\n const parsed = yaml.load(yamlContent) as IssueTemplate;\n\n if (!parsed.name) {\n throw new Error('Issue template must have a name');\n }\n\n if (!parsed.body || !Array.isArray(parsed.body)) {\n throw new Error('Issue template must have a body array');\n }\n\n return parsed;\n}\n\n/**\n * Parse an issue body into field values\n *\n * GitHub renders form fields as markdown sections:\n * ```\n * ### Label\n *\n * Content here\n *\n * ### Next Label\n * ```\n *\n * Without a template, returns a Record<string, string> keyed by label.\n * With a template, returns a Record<string, string> keyed by field ID.\n *\n * @param body - The issue body markdown\n * @param template - Optional template for mapping labels to field IDs\n * @returns Parsed fields as key-value pairs\n *\n * @example\n * ```typescript\n * // Without template - keyed by label\n * const fields = parseIssueBody(issue.body);\n * // { 'Description': '...', 'Steps to Reproduce': '...' }\n *\n * // With template - keyed by field ID\n * const template = await loadIssueTemplate('.github/ISSUE_TEMPLATE/bug_report.yml');\n * const fields = parseIssueBody(issue.body, template);\n * // { description: '...', reproduction: '...' }\n * ```\n */\nexport function parseIssueBody(\n body: string,\n template?: IssueTemplate,\n): Record<string, string> {\n const sections = parseSections(body);\n\n if (!template) {\n return sections;\n }\n\n // Build label-to-id mapping from template\n const labelToId = new Map<string, string>();\n for (const field of template.body) {\n if (field.id && field.attributes?.label) {\n labelToId.set(field.attributes.label, field.id);\n }\n }\n\n // Map labels to field IDs\n const result: Record<string, string> = {};\n for (const [label, content] of Object.entries(sections)) {\n const id = labelToId.get(label);\n if (id) {\n result[id] = content;\n } else {\n // Keep original label if no ID mapping found\n result[label] = content;\n }\n }\n\n return result;\n}\n\n/**\n * Parse markdown sections (### headers) into key-value pairs\n */\nfunction parseSections(body: string): Record<string, string> {\n const result: Record<string, string> = {};\n\n // Match ### headers and their content\n // Content is everything until the next ### or --- or end of string\n const sectionRegex =\n /^### (.+?)\\r?\\n\\r?\\n([\\s\\S]*?)(?=\\r?\\n### |\\r?\\n---|\\s*$)/gm;\n\n for (const match of body.matchAll(sectionRegex)) {\n const label = match[1].trim();\n const content = match[2].trim();\n result[label] = content;\n }\n\n return result;\n}\n\n/**\n * Render fields into an issue body markdown string\n *\n * Without a template, uses field keys as labels.\n * With a template, uses field IDs to look up labels and renders in template order.\n *\n * @param fields - Field values as key-value pairs\n * @param template - Optional template for field ordering and labels\n * @returns Markdown string for issue body\n *\n * @example\n * ```typescript\n * // Without template\n * const body = renderIssueBody({\n * 'Description': 'Bug description here',\n * 'Steps to Reproduce': '1. Do X\\n2. See error'\n * });\n *\n * // With template\n * const template = await loadIssueTemplate('.github/ISSUE_TEMPLATE/bug_report.yml');\n * const body = renderIssueBody({\n * description: 'Bug description here',\n * reproduction: '1. Do X\\n2. See error'\n * }, template);\n * ```\n */\nexport function renderIssueBody(\n fields: Record<string, string>,\n template?: IssueTemplate,\n): string {\n const sections: string[] = [];\n\n if (!template) {\n // Without template, render in field order\n for (const [label, content] of Object.entries(fields)) {\n sections.push(`### ${label}\\n\\n${content}`);\n }\n } else {\n // With template, render in template order using field IDs\n const idToContent = new Map(Object.entries(fields));\n\n for (const field of template.body) {\n // Skip markdown-only fields (no id)\n if (!field.id || field.type === 'markdown') {\n continue;\n }\n\n const label = field.attributes?.label || field.id;\n const content = idToContent.get(field.id);\n\n if (content !== undefined) {\n sections.push(`### ${label}\\n\\n${content}`);\n }\n }\n }\n\n return sections.join('\\n\\n');\n}\n\n/**\n * Get a specific field value from an issue body\n *\n * @param body - The issue body markdown\n * @param fieldIdOrLabel - Field ID (with template) or label (without template)\n * @param template - Optional template for field ID lookup\n * @returns Field value or undefined if not found\n */\nexport function getIssueField(\n body: string,\n fieldIdOrLabel: string,\n template?: IssueTemplate,\n): string | undefined {\n const fields = parseIssueBody(body, template);\n return fields[fieldIdOrLabel];\n}\n\n/**\n * Update a specific field in an issue body\n *\n * @param body - The issue body markdown\n * @param fieldIdOrLabel - Field ID (with template) or label (without template)\n * @param value - New value for the field\n * @param template - Optional template for field ID lookup\n * @returns Updated issue body markdown\n */\nexport function updateIssueField(\n body: string,\n fieldIdOrLabel: string,\n value: string,\n template?: IssueTemplate,\n): string {\n const fields = parseIssueBody(body, template);\n fields[fieldIdOrLabel] = value;\n return renderIssueBody(fields, template);\n}\n\n/**\n * Fetch issue templates from a repository via GitHub API\n *\n * @param repo - Repository client implementing IRepository\n * @returns Array of parsed issue templates\n *\n * @example\n * ```typescript\n * const repo = await getRepository({ type: 'github', owner: 'org', repo: 'name', token });\n * const templates = await fetchIssueTemplates(repo);\n * // [{ name: 'Bug Report', labels: ['bug'], body: [...] }, ...]\n * ```\n */\nexport async function fetchIssueTemplates(\n repo: IRepository,\n): Promise<IssueTemplate[]> {\n const templatePath = '.github/ISSUE_TEMPLATE';\n const files = await repo.listDirectoryFiles(templatePath);\n const templates: IssueTemplate[] = [];\n\n for (const file of files) {\n if (file.endsWith('.yml') || file.endsWith('.yaml')) {\n const content = await repo.getFileContent(`${templatePath}/${file}`);\n if (content) {\n try {\n templates.push(parseIssueTemplate(content));\n } catch (e) {\n console.warn(`[repos] Failed to parse template ${file}:`, e);\n }\n }\n }\n }\n\n return templates;\n}\n\n/**\n * Detect which template an issue was created from based on labels\n *\n * Matches issue labels against template labels to find the best match.\n * Returns the template with the most matching labels.\n *\n * @param labels - Issue labels\n * @param templates - Available templates\n * @returns Matching template or undefined if no match\n *\n * @example\n * ```typescript\n * const templates = await fetchIssueTemplates(repo);\n * const template = detectTemplateFromLabels(['bug', 'critical'], templates);\n * if (template) {\n * const fields = parseIssueBody(issue.body, template);\n * }\n * ```\n */\nexport function detectTemplateFromLabels(\n labels: string[],\n templates: IssueTemplate[],\n): IssueTemplate | undefined {\n const labelSet = new Set(labels.map((l) => l.toLowerCase()));\n\n let bestMatch: IssueTemplate | undefined;\n let bestScore = 0;\n\n for (const template of templates) {\n if (!template.labels || template.labels.length === 0) continue;\n\n const matchCount = template.labels.filter((l) =>\n labelSet.has(l.toLowerCase()),\n ).length;\n\n // Require at least one matching label\n if (matchCount > 0 && matchCount > bestScore) {\n bestScore = matchCount;\n bestMatch = template;\n }\n }\n\n return bestMatch;\n}\n"],"names":["RepositoryErrorCode","GitHubRepository"],"mappings":"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"}
package/metadata.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@happyvertical/repos",
3
+ "path": "packages/repos",
4
+ "position": {
5
+ "index": 22,
6
+ "count": 30
7
+ },
8
+ "description": "Standardized repository interface for GitHub, GitLab, Bitbucket, and Azure DevOps",
9
+ "provides": [
10
+ "Standardized repository interface for GitHub, GitLab, Bitbucket, and Azure DevOps"
11
+ ],
12
+ "implements": [],
13
+ "requires": {
14
+ "workspace": [
15
+ "@happyvertical/graphql"
16
+ ],
17
+ "externalHappyVertical": [],
18
+ "external": [
19
+ "js-yaml"
20
+ ]
21
+ },
22
+ "dependents": [
23
+ "@happyvertical/github-actions",
24
+ "@happyvertical/projects"
25
+ ],
26
+ "stability": {
27
+ "level": "stable",
28
+ "reason": "Primary package surface is described as implemented and production-oriented."
29
+ },
30
+ "keywords": [
31
+ "repos"
32
+ ]
33
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@happyvertical/repos",
3
+ "version": "0.74.8",
4
+ "description": "Standardized repository interface for GitHub, GitLab, Bitbucket, and Azure DevOps",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "bin": {
15
+ "have-repos-context": "./dist/cli/claude-context.js"
16
+ },
17
+ "dependencies": {
18
+ "js-yaml": "^4.1.1",
19
+ "@happyvertical/graphql": "0.74.8"
20
+ },
21
+ "devDependencies": {
22
+ "@types/js-yaml": "^4.0.9",
23
+ "@types/node": "25.0.10",
24
+ "typescript": "^5.9.3",
25
+ "vite": "7.3.2",
26
+ "vite-plugin-dts": "4.5.4",
27
+ "vitest": "4.1.5"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE",
33
+ "AGENT.md",
34
+ "metadata.json"
35
+ ],
36
+ "publishConfig": {
37
+ "registry": "https://registry.npmjs.org",
38
+ "access": "public"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/happyvertical/sdk.git",
43
+ "directory": "packages/repos"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/happyvertical/sdk/issues"
47
+ },
48
+ "homepage": "https://github.com/happyvertical/sdk/tree/main/packages/repos#readme",
49
+ "license": "MIT",
50
+ "scripts": {
51
+ "test": "npx vitest run --passWithNoTests",
52
+ "test:watch": "npx vitest",
53
+ "build": "vite build",
54
+ "build:watch": "vite build --watch",
55
+ "clean": "rm -rf dist",
56
+ "dev": "npm run build:watch & npm run test:watch"
57
+ }
58
+ }