Add forgejo_milestone_edit tool #23

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

Summary

Add an editMilestone() function to src/milestones.ts and register a forgejo_milestone_edit tool for partial updates (title/description/due date/state) — mirroring forgejo_issue_edit.

Background

Depends on: #20 (Add forgejo_milestone_create tool) (needs the ForgejoMilestone interface, milestonePath() helper, and src/milestones.ts scaffolding).

The existing editIssue() (src/issues.ts) is the reference pattern: a PATCH request with an omitUndefined()-filtered body, so only explicitly-provided fields are changed. This issue is also a prerequisite for the close/reopen tools (a later issue), which will be implemented as thin wrappers calling editMilestone(target, id, { state: ... }) — exactly how closeIssue()/reopenIssue() wrap editIssue() today.

Implementation Details

Confirmed Forgejo/Gitea API v1 contract:

PATCH /repos/{owner}/{repo}/milestones/{id} (operationId issueEditMilestone)

Request body (EditMilestoneOption, all fields optional — partial update):

{
  title?: string;
  description?: string;
  due_on?: string;   // date-time
  state?: string;    // "open" | "closed" by convention
}

Response (200): ForgejoMilestone. Errors: 404 if not found.

Files to change:

  1. src/milestones.ts:

    • Add EditMilestoneOptions interface: { title?: string; description?: string; due_on?: string; state?: "open" | "closed" }.
    • Add editMilestone(target: ForgejoTarget, id: number, options: EditMilestoneOptions): Promise<Result<ForgejoMilestone, ApiError>>PATCH via forgejoApiCall, body via omitUndefined(options), using milestonePath(target, id).
  2. src/index.ts:

    • Import editMilestone from ./milestones.
    • Register forgejo_milestone_edit tool:
      • label: "Edit Milestone"
      • promptSnippet: "Edit a Forgejo milestone"
      • promptGuidelines: e.g. ["Only fields you pass are changed; omit fields you want to keep."] (matching forgejo_issue_edit's guideline).
      • parameters: Type.Object({ ...targetOverrides, id: Type.Integer({ minimum: 1, description: "Milestone id" }), title: Type.Optional(Type.String(...)), description: Type.Optional(Type.String(...)), due_on: Type.Optional(Type.String(...)), state: Type.Optional(Type.Union([Type.Literal("open"), Type.Literal("closed")])) })
      • execute: resolve target, call editMilestone, return via formatMilestone(result.value, "Updated milestone").
  3. tests/milestones.test.ts: add unit tests for editMilestone() — verify URL, PATCH method, that only provided fields appear in the body (omitUndefined behavior), and success/error propagation.

  4. Tool-registration tests: add coverage for forgejo_milestone_edit — schema shape, partial-update behavior, execute() success/error paths.

Acceptance Criteria

  • editMilestone() exists in src/milestones.ts, calls PATCH /repos/{owner}/{repo}/milestones/{id} with only the provided fields in the body, and returns Result<ForgejoMilestone, ApiError>.
  • forgejo_milestone_edit tool is registered with a required id and all other fields (title, description, due_on, state) optional, plus targetOverrides.
  • Omitted fields are not sent in the request body (verified by a unit test asserting the exact body shape for a partial update).
  • Unit tests cover editMilestone() (full and partial updates) 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 editMilestone() and forgejo_milestone_edit pass; full suite green.
  2. Run npm run check — lint/typecheck passes.
  3. Manual smoke test: create a milestone, call forgejo_milestone_edit id=<id> description="Updated" and confirm only the description changes (title/due_on/state untouched) via forgejo_milestone_view.
## Summary Add an `editMilestone()` function to `src/milestones.ts` and register a `forgejo_milestone_edit` tool for partial updates (title/description/due date/state) — mirroring `forgejo_issue_edit`. ## Background **Depends on:** #20 (Add forgejo_milestone_create tool) (needs the `ForgejoMilestone` interface, `milestonePath()` helper, and `src/milestones.ts` scaffolding). The existing `editIssue()` (`src/issues.ts`) is the reference pattern: a `PATCH` request with an `omitUndefined()`-filtered body, so only explicitly-provided fields are changed. This issue is also a **prerequisite** for the close/reopen tools (a later issue), which will be implemented as thin wrappers calling `editMilestone(target, id, { state: ... })` — exactly how `closeIssue()`/`reopenIssue()` wrap `editIssue()` today. ## Implementation Details **Confirmed Forgejo/Gitea API v1 contract:** `PATCH /repos/{owner}/{repo}/milestones/{id}` (operationId `issueEditMilestone`) Request body (`EditMilestoneOption`, all fields optional — partial update): ```ts { title?: string; description?: string; due_on?: string; // date-time state?: string; // "open" | "closed" by convention } ``` Response (`200`): `ForgejoMilestone`. Errors: `404` if not found. **Files to change:** 1. `src/milestones.ts`: - Add `EditMilestoneOptions` interface: `{ title?: string; description?: string; due_on?: string; state?: "open" | "closed" }`. - Add `editMilestone(target: ForgejoTarget, id: number, options: EditMilestoneOptions): Promise<Result<ForgejoMilestone, ApiError>>` — `PATCH` via `forgejoApiCall`, body via `omitUndefined(options)`, using `milestonePath(target, id)`. 2. `src/index.ts`: - Import `editMilestone` from `./milestones`. - Register `forgejo_milestone_edit` tool: - `label`: "Edit Milestone" - `promptSnippet`: "Edit a Forgejo milestone" - `promptGuidelines`: e.g. `["Only fields you pass are changed; omit fields you want to keep."]` (matching `forgejo_issue_edit`'s guideline). - `parameters`: `Type.Object({ ...targetOverrides, id: Type.Integer({ minimum: 1, description: "Milestone id" }), title: Type.Optional(Type.String(...)), description: Type.Optional(Type.String(...)), due_on: Type.Optional(Type.String(...)), state: Type.Optional(Type.Union([Type.Literal("open"), Type.Literal("closed")])) })` - `execute`: resolve target, call `editMilestone`, return via `formatMilestone(result.value, "Updated milestone")`. 3. `tests/milestones.test.ts`: add unit tests for `editMilestone()` — verify URL, `PATCH` method, that only provided fields appear in the body (omitUndefined behavior), and success/error propagation. 4. Tool-registration tests: add coverage for `forgejo_milestone_edit` — schema shape, partial-update behavior, execute() success/error paths. ## Acceptance Criteria - [ ] `editMilestone()` exists in `src/milestones.ts`, calls `PATCH /repos/{owner}/{repo}/milestones/{id}` with only the provided fields in the body, and returns `Result<ForgejoMilestone, ApiError>`. - [ ] `forgejo_milestone_edit` tool is registered with a required `id` and all other fields (`title`, `description`, `due_on`, `state`) optional, plus `targetOverrides`. - [ ] Omitted fields are not sent in the request body (verified by a unit test asserting the exact body shape for a partial update). - [ ] Unit tests cover `editMilestone()` (full and partial updates) 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 `editMilestone()` and `forgejo_milestone_edit` pass; full suite green. 2. Run `npm run check` — lint/typecheck passes. 3. Manual smoke test: create a milestone, call `forgejo_milestone_edit id=<id> description="Updated"` and confirm only the description changes (title/due_on/state untouched) via `forgejo_milestone_view`.
david closed this issue 2026-08-18 08:18:18 +00:00
Author
Owner

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

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