Add forgejo_label_create tool #18

Closed
opened 2026-08-18 02:29:24 +00:00 by david · 1 comment
Owner

Summary

Add a createLabel() function and a forgejo_label_create tool to the forgejo pi extension, so repository labels can be created directly (today the extension can only list/add/remove labels by id, not create new ones).

Background

The forgejo extension (extensions/forgejo/ in this repo) is a TypeScript pi extension that wraps the Forgejo REST API (/api/v1) for issue/PR/label operations. It follows a layered architecture:

  • src/api.tsforgejoApiCall(), a generic authenticated HTTP wrapper returning Result<T, ApiError> (see src/errors.ts for the Result/success/failure pattern).
  • src/issues.ts — one function per issue/label operation (e.g. listLabels(), addLabels(), removeLabels()), each calling forgejoApiCall().
  • src/index.ts — registers one pi tool per operation using TypeBox schemas (Type.Object({...})), with promptSnippet + promptGuidelines metadata, and a shared targetOverrides object (host?/owner?/repo?) merged into every tool's params.

Labels currently support list (forgejo_list_labels), add-to-issue (forgejo_issue_add_labels), and remove-from-issue (forgejo_issue_remove_labels) — but not repository-level label creation. This issue adds that missing piece.

This is the first of a related batch of issues also adding full milestone CRUD tools; it has no dependency on the others and can be implemented first.

Implementation Details

Confirmed Forgejo/Gitea API v1 contract (from the live Codeberg swagger spec, v16.0.0-dev):

POST /repos/{owner}/{repo}/labels (operationId issueCreateLabel)

Request body (CreateLabelOption):

{
  name: string;          // required
  color: string;         // required, e.g. "#00aabb"
  description?: string;
  exclusive?: boolean;
  is_archived?: boolean;
}

Response (201, Label):

{
  id: number;
  name: string;
  color: string;
  description?: string;
  exclusive?: boolean;
  is_archived?: boolean;
  url?: string;
}

Errors: 404 (repo not found), 422 (validation failure, e.g. bad color format).

Files to change:

  1. src/issues.ts:

    • Extend the existing ForgejoLabel interface with exclusive?: boolean, is_archived?: boolean, url?: string.
    • Add a CreateLabelOptions interface: { name: string; color: string; description?: string; exclusive?: boolean; is_archived?: boolean }.
    • Add createLabel(target: ForgejoTarget, options: CreateLabelOptions): Promise<Result<ForgejoLabel, ApiError>>POST to /repos/{owner}/{repo}/labels via forgejoApiCall, body built with omitUndefined(options) (same pattern as createIssue).
  2. src/index.ts:

    • Import createLabel from ./issues.
    • Register a new tool forgejo_label_create:
      • label: "Create Label"
      • description: "Create a Forgejo repository label."
      • promptSnippet: "Create a Forgejo label"
      • promptGuidelines: something like ["Use forgejo_list_labels afterwards to confirm the new label's id."]
      • parameters: Type.Object({ ...targetOverrides, name: Type.String(...), color: Type.String({ description: "Hex color, e.g. #00aabb" }), description: Type.Optional(Type.String(...)), exclusive: Type.Optional(Type.Boolean(...)), is_archived: Type.Optional(Type.Boolean(...)) })
      • execute: resolve target via resolveTarget(params), call createLabel, return a toolSuccess with the label's id/name/color in both text and details (mirroring how other create tools format output), or toolError on failure.
  3. tests/issues.test.ts: add unit tests for createLabel() mocking forgejoApiCall — verify URL, method (POST), body payload (including that undefined optional fields are omitted), and success/error Result propagation.

  4. tests/index.test.ts (or tests/tools.test.ts, whichever currently covers tool registration/execution): add tests for the forgejo_label_create tool — schema shape, and that execute() calls createLabel with the resolved target and returns success/error tool results correctly.

Acceptance Criteria

  • createLabel() exists in src/issues.ts, calls POST /repos/{owner}/{repo}/labels with the correct body, and returns Result<ForgejoLabel, ApiError>.
  • forgejo_label_create tool is registered in src/index.ts with name/color required and description/exclusive/is_archived optional, plus targetOverrides.
  • Successful creation returns the label's id, name, and color in the tool result text and details.
  • API errors (404, 422, etc.) are surfaced via the existing toolError/describeError machinery, matching the style of other create tools.
  • Unit tests cover createLabel() (URL, method, body, success, error) and the tool registration/execution path.
  • npm test and npm run check pass with no regressions.

Test Plan

  1. Run npm test — new tests for createLabel() and forgejo_label_create pass; full suite remains green.
  2. Run npm run check — lint/typecheck passes.
  3. Manual smoke test (requires FORGEJO_TOKEN and a real repo): call forgejo_label_create with name: "triage", color: "#ff0000", then run forgejo_list_labels and confirm the new label appears with a real id.
## Summary Add a `createLabel()` function and a `forgejo_label_create` tool to the `forgejo` pi extension, so repository labels can be created directly (today the extension can only list/add/remove labels by id, not create new ones). ## Background The `forgejo` extension (`extensions/forgejo/` in this repo) is a TypeScript pi extension that wraps the Forgejo REST API (`/api/v1`) for issue/PR/label operations. It follows a layered architecture: - `src/api.ts` — `forgejoApiCall()`, a generic authenticated HTTP wrapper returning `Result<T, ApiError>` (see `src/errors.ts` for the `Result`/`success`/`failure` pattern). - `src/issues.ts` — one function per issue/label operation (e.g. `listLabels()`, `addLabels()`, `removeLabels()`), each calling `forgejoApiCall()`. - `src/index.ts` — registers one pi tool per operation using TypeBox schemas (`Type.Object({...})`), with `promptSnippet` + `promptGuidelines` metadata, and a shared `targetOverrides` object (`host?`/`owner?`/`repo?`) merged into every tool's params. Labels currently support list (`forgejo_list_labels`), add-to-issue (`forgejo_issue_add_labels`), and remove-from-issue (`forgejo_issue_remove_labels`) — but not repository-level label creation. This issue adds that missing piece. This is the first of a related batch of issues also adding full milestone CRUD tools; it has no dependency on the others and can be implemented first. ## Implementation Details **Confirmed Forgejo/Gitea API v1 contract** (from the live Codeberg swagger spec, `v16.0.0-dev`): `POST /repos/{owner}/{repo}/labels` (operationId `issueCreateLabel`) Request body (`CreateLabelOption`): ```ts { name: string; // required color: string; // required, e.g. "#00aabb" description?: string; exclusive?: boolean; is_archived?: boolean; } ``` Response (`201`, `Label`): ```ts { id: number; name: string; color: string; description?: string; exclusive?: boolean; is_archived?: boolean; url?: string; } ``` Errors: `404` (repo not found), `422` (validation failure, e.g. bad color format). **Files to change:** 1. `src/issues.ts`: - Extend the existing `ForgejoLabel` interface with `exclusive?: boolean`, `is_archived?: boolean`, `url?: string`. - Add a `CreateLabelOptions` interface: `{ name: string; color: string; description?: string; exclusive?: boolean; is_archived?: boolean }`. - Add `createLabel(target: ForgejoTarget, options: CreateLabelOptions): Promise<Result<ForgejoLabel, ApiError>>` — `POST` to `/repos/{owner}/{repo}/labels` via `forgejoApiCall`, body built with `omitUndefined(options)` (same pattern as `createIssue`). 2. `src/index.ts`: - Import `createLabel` from `./issues`. - Register a new tool `forgejo_label_create`: - `label`: "Create Label" - `description`: "Create a Forgejo repository label." - `promptSnippet`: "Create a Forgejo label" - `promptGuidelines`: something like `["Use forgejo_list_labels afterwards to confirm the new label's id."]` - `parameters`: `Type.Object({ ...targetOverrides, name: Type.String(...), color: Type.String({ description: "Hex color, e.g. #00aabb" }), description: Type.Optional(Type.String(...)), exclusive: Type.Optional(Type.Boolean(...)), is_archived: Type.Optional(Type.Boolean(...)) })` - `execute`: resolve target via `resolveTarget(params)`, call `createLabel`, return a `toolSuccess` with the label's `id`/`name`/`color` in both text and `details` (mirroring how other create tools format output), or `toolError` on failure. 3. `tests/issues.test.ts`: add unit tests for `createLabel()` mocking `forgejoApiCall` — verify URL, method (`POST`), body payload (including that `undefined` optional fields are omitted), and success/error `Result` propagation. 4. `tests/index.test.ts` (or `tests/tools.test.ts`, whichever currently covers tool registration/execution): add tests for the `forgejo_label_create` tool — schema shape, and that `execute()` calls `createLabel` with the resolved target and returns success/error tool results correctly. ## Acceptance Criteria - [ ] `createLabel()` exists in `src/issues.ts`, calls `POST /repos/{owner}/{repo}/labels` with the correct body, and returns `Result<ForgejoLabel, ApiError>`. - [ ] `forgejo_label_create` tool is registered in `src/index.ts` with `name`/`color` required and `description`/`exclusive`/`is_archived` optional, plus `targetOverrides`. - [ ] Successful creation returns the label's `id`, `name`, and `color` in the tool result text and `details`. - [ ] API errors (404, 422, etc.) are surfaced via the existing `toolError`/`describeError` machinery, matching the style of other create tools. - [ ] Unit tests cover `createLabel()` (URL, method, body, success, error) and the tool registration/execution path. - [ ] `npm test` and `npm run check` pass with no regressions. ## Test Plan 1. Run `npm test` — new tests for `createLabel()` and `forgejo_label_create` pass; full suite remains green. 2. Run `npm run check` — lint/typecheck passes. 3. Manual smoke test (requires `FORGEJO_TOKEN` and a real repo): call `forgejo_label_create` with `name: "triage"`, `color: "#ff0000"`, then run `forgejo_list_labels` and confirm the new label appears with a real `id`.
david closed this issue 2026-08-18 07:46:29 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #33

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/33
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills#18
No description provided.