Add forgejo_milestone_create tool #20

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

Summary

Add a new src/milestones.ts module with a createMilestone() function and register a forgejo_milestone_create tool, so Forgejo milestones can be created directly from the extension (today only issues/PRs/labels are covered).

Background

The forgejo extension (extensions/forgejo/ in this repo) wraps the Forgejo REST API (/api/v1). It follows a layered architecture: src/api.ts (forgejoApiCall() HTTP wrapper returning Result<T, ApiError>), per-domain function files (src/issues.ts, src/pulls.ts), and src/index.ts (tool registration with TypeBox schemas, promptSnippet/promptGuidelines, shared targetOverrides).

Milestones are a distinct resource from issues/PRs in the Forgejo API, so this introduces a new src/milestones.ts file (mirroring the existing issues.ts/pulls.ts split) rather than folding milestone code into issues.ts. This is the first of several issues adding full milestone CRUD; it establishes the ForgejoMilestone interface and file that later issues (list/view/edit/close/reopen/delete) build on. It has no dependency on other issues in this batch.

Implementation Details

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

POST /repos/{owner}/{repo}/milestones (operationId issueCreateMilestone)

Request body (CreateMilestoneOption):

{
  title: string;          // required
  description?: string;
  due_on?: string;        // date-time, e.g. "2025-12-31T00:00:00Z"
  state?: "open" | "closed"; // server defaults to "open"
}

Response (201, Milestone):

{
  id: number;
  title: string;
  description?: string;
  due_on?: string;
  state: string;           // "open" | "closed"
  open_issues?: number;
  closed_issues?: number;
  created_at?: string;
  updated_at?: string;
  closed_at?: string;
}

Errors: 404 (repo not found).

Files to create/change:

  1. New src/milestones.ts:

    • Export ForgejoMilestone interface (fields above).
    • Export CreateMilestoneOptions interface: { title: string; description?: string; due_on?: string; state?: "open" | "closed" }.
    • Add a local milestonePath(target: ForgejoTarget, id?: number): string helper mirroring issuePath() in src/issues.ts (builds /repos/{owner}/{repo}/milestones or .../milestones/{id}).
    • Add createMilestone(target: ForgejoTarget, options: CreateMilestoneOptions): Promise<Result<ForgejoMilestone, ApiError>>POST via forgejoApiCall, body via omitUndefined(options).
  2. src/index.ts:

    • Import createMilestone, ForgejoMilestone from ./milestones.
    • Add a formatMilestone(milestone: ForgejoMilestone, action: string): { text: string; details: Record<string, unknown> } helper, mirroring formatIssue()/formatPullRequest() — include id, title, state, description, due_on, open/closed issue counts.
    • Register forgejo_milestone_create tool:
      • label: "Create Milestone"
      • promptSnippet: "Create a Forgejo milestone"
      • promptGuidelines: e.g. ["Use forgejo_list_milestones afterwards to confirm the new milestone's id for use in forgejo_issue_create/forgejo_pr_create."]
      • parameters: Type.Object({ ...targetOverrides, title: Type.String(...), description: Type.Optional(Type.String(...)), due_on: Type.Optional(Type.String({ description: "Due date (ISO 8601)" })), state: Type.Optional(Type.Union([Type.Literal("open"), Type.Literal("closed")])) })
      • execute: resolve target, call createMilestone, return toolSuccess/toolError using formatMilestone.
  3. tests/milestones.test.ts (new file): unit tests for createMilestone() mocking forgejoApiCall — verify URL (/repos/{owner}/{repo}/milestones), method POST, body payload (optional fields omitted when undefined), and success/error Result propagation.

  4. Tool-registration test file (tests/index.test.ts or tests/tools.test.ts): add coverage for forgejo_milestone_create — schema shape and execute() success/error paths.

Acceptance Criteria

  • src/milestones.ts exists with ForgejoMilestone, CreateMilestoneOptions, and createMilestone().
  • createMilestone() calls POST /repos/{owner}/{repo}/milestones with the correct body and returns Result<ForgejoMilestone, ApiError>.
  • forgejo_milestone_create tool is registered with title required, description/due_on/state optional, plus targetOverrides.
  • Successful creation returns the milestone's id, title, and state in the tool result text and details.
  • Unit tests cover createMilestone() 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 createMilestone() and forgejo_milestone_create pass; full suite green.
  2. Run npm run check — lint/typecheck passes.
  3. Manual smoke test: call forgejo_milestone_create with title: "v1.0", description: "First release"; verify a milestone appears in the Forgejo web UI with the returned id.
## Summary Add a new `src/milestones.ts` module with a `createMilestone()` function and register a `forgejo_milestone_create` tool, so Forgejo milestones can be created directly from the extension (today only issues/PRs/labels are covered). ## Background The `forgejo` extension (`extensions/forgejo/` in this repo) wraps the Forgejo REST API (`/api/v1`). It follows a layered architecture: `src/api.ts` (`forgejoApiCall()` HTTP wrapper returning `Result<T, ApiError>`), per-domain function files (`src/issues.ts`, `src/pulls.ts`), and `src/index.ts` (tool registration with TypeBox schemas, `promptSnippet`/`promptGuidelines`, shared `targetOverrides`). Milestones are a distinct resource from issues/PRs in the Forgejo API, so this introduces a new `src/milestones.ts` file (mirroring the existing `issues.ts`/`pulls.ts` split) rather than folding milestone code into `issues.ts`. This is the first of several issues adding full milestone CRUD; it establishes the `ForgejoMilestone` interface and file that later issues (list/view/edit/close/reopen/delete) build on. It has no dependency on other issues in this batch. ## Implementation Details **Confirmed Forgejo/Gitea API v1 contract** (Codeberg swagger, `v16.0.0-dev`): `POST /repos/{owner}/{repo}/milestones` (operationId `issueCreateMilestone`) Request body (`CreateMilestoneOption`): ```ts { title: string; // required description?: string; due_on?: string; // date-time, e.g. "2025-12-31T00:00:00Z" state?: "open" | "closed"; // server defaults to "open" } ``` Response (`201`, `Milestone`): ```ts { id: number; title: string; description?: string; due_on?: string; state: string; // "open" | "closed" open_issues?: number; closed_issues?: number; created_at?: string; updated_at?: string; closed_at?: string; } ``` Errors: `404` (repo not found). **Files to create/change:** 1. New `src/milestones.ts`: - Export `ForgejoMilestone` interface (fields above). - Export `CreateMilestoneOptions` interface: `{ title: string; description?: string; due_on?: string; state?: "open" | "closed" }`. - Add a local `milestonePath(target: ForgejoTarget, id?: number): string` helper mirroring `issuePath()` in `src/issues.ts` (builds `/repos/{owner}/{repo}/milestones` or `.../milestones/{id}`). - Add `createMilestone(target: ForgejoTarget, options: CreateMilestoneOptions): Promise<Result<ForgejoMilestone, ApiError>>` — `POST` via `forgejoApiCall`, body via `omitUndefined(options)`. 2. `src/index.ts`: - Import `createMilestone`, `ForgejoMilestone` from `./milestones`. - Add a `formatMilestone(milestone: ForgejoMilestone, action: string): { text: string; details: Record<string, unknown> }` helper, mirroring `formatIssue()`/`formatPullRequest()` — include id, title, state, description, due_on, open/closed issue counts. - Register `forgejo_milestone_create` tool: - `label`: "Create Milestone" - `promptSnippet`: "Create a Forgejo milestone" - `promptGuidelines`: e.g. `["Use forgejo_list_milestones afterwards to confirm the new milestone's id for use in forgejo_issue_create/forgejo_pr_create."]` - `parameters`: `Type.Object({ ...targetOverrides, title: Type.String(...), description: Type.Optional(Type.String(...)), due_on: Type.Optional(Type.String({ description: "Due date (ISO 8601)" })), state: Type.Optional(Type.Union([Type.Literal("open"), Type.Literal("closed")])) })` - `execute`: resolve target, call `createMilestone`, return `toolSuccess`/`toolError` using `formatMilestone`. 3. `tests/milestones.test.ts` (new file): unit tests for `createMilestone()` mocking `forgejoApiCall` — verify URL (`/repos/{owner}/{repo}/milestones`), method `POST`, body payload (optional fields omitted when undefined), and success/error `Result` propagation. 4. Tool-registration test file (`tests/index.test.ts` or `tests/tools.test.ts`): add coverage for `forgejo_milestone_create` — schema shape and execute() success/error paths. ## Acceptance Criteria - [ ] `src/milestones.ts` exists with `ForgejoMilestone`, `CreateMilestoneOptions`, and `createMilestone()`. - [ ] `createMilestone()` calls `POST /repos/{owner}/{repo}/milestones` with the correct body and returns `Result<ForgejoMilestone, ApiError>`. - [ ] `forgejo_milestone_create` tool is registered with `title` required, `description`/`due_on`/`state` optional, plus `targetOverrides`. - [ ] Successful creation returns the milestone's `id`, `title`, and `state` in the tool result text and `details`. - [ ] Unit tests cover `createMilestone()` 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 `createMilestone()` and `forgejo_milestone_create` pass; full suite green. 2. Run `npm run check` — lint/typecheck passes. 3. Manual smoke test: call `forgejo_milestone_create` with `title: "v1.0"`, `description: "First release"`; verify a milestone appears in the Forgejo web UI with the returned id.
david closed this issue 2026-08-18 07:54:10 +00:00
Author
Owner

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

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/34
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#20
No description provided.