AtlatestRepositoryfjo

fjo / tree / src / fjopipelines.sgl

1;;; (fjo pipelines) - Woodpecker CI pipeline tools.
2;;;
3;;; MCP tools and CLI handlers for viewing Woodpecker CI pipeline
4;;; status with step details.
5
6(define-library (fjo pipelines)
7 (import (sigil core)
8 (sigil string)
9 (sigil json)
10 (sigil array)
11 (sigil mcp server)
12 (woodpecker pipelines)
13 (fjo config))
14 (export register-pipeline-tools!
15 tool-pipelines-list
16 tool-pipelines-get)
17 (begin
19 ;; ============================================================
20 ;; Helpers
21 ;; ============================================================
23 ;;; Look up a Woodpecker repo ID from owner/repo.
24 (define (lookup-repo-id base-url token owner repo)
25 (guard (e (else
26 (error (string-append "Failed to look up Woodpecker repo "
27 owner "/" repo ": "
28 (exception-message e)))))
29 (let ((result (woodpecker-repo-lookup base-url token
30 (string-append owner "/" repo))))
31 (dict-ref result id:))))
33 (define (truncate-message msg max-len)
34 (let ((first-line (let ((nl (string-find msg "\n")))
35 (if nl (substring msg 0 nl) msg))))
36 (if (> (string-length first-line) max-len)
37 (string-append (substring first-line 0 max-len) "...")
38 first-line)))
40 ;; ============================================================
41 ;; Tool Schemas
42 ;; ============================================================
44 (define pipelines-list-schema
45 '((type . "object")
46 (properties . ((owner . ((type . "string")
47 (description . "Repository owner (user or org)")))
48 (repo . ((type . "string")
49 (description . "Repository name")))))
50 (required . ("owner" "repo"))))
52 (define pipelines-get-schema
53 '((type . "object")
54 (properties . ((owner . ((type . "string")
55 (description . "Repository owner (user or org)")))
56 (repo . ((type . "string")
57 (description . "Repository name")))
58 (number . ((type . "integer")
59 (description . "Pipeline number")))))
60 (required . ("owner" "repo" "number"))))
62 ;; ============================================================
63 ;; Formatting
64 ;; ============================================================
66 (define (format-pipeline p)
67 (let ((number (dict-ref p number:))
68 (status (dict-ref p status:))
69 (branch (dict-ref p branch: ""))
70 (event (dict-ref p event: ""))
71 (message (dict-ref p message: ""))
72 (author (dict-ref p author: "")))
73 (string-append
74 "#" (number->string number)
75 " [" status "] "
76 (if (not (string=? event ""))
77 (string-append event " ")
78 "")
79 (if (not (string=? branch ""))
80 (string-append "on " branch " ")
81 "")
82 (if (not (string=? author ""))
83 (string-append "by " author " ")
84 "")
85 "- " (truncate-message message 60))))
87 (define (format-step s)
88 (let ((name (dict-ref s name:))
89 (state (dict-ref s state: "unknown"))
90 (exit-code (dict-ref s exit_code: 0)))
91 (string-append
92 " " name " [" state "]"
93 (if (not (= exit-code 0))
94 (string-append " (exit " (number->string exit-code) ")")
95 ""))))
97 (define (format-pipeline-detail p steps)
98 (let ((number (dict-ref p number:))
99 (status (dict-ref p status:))
100 (branch (dict-ref p branch: ""))
101 (event (dict-ref p event: ""))
102 (message (dict-ref p message: ""))
103 (author (dict-ref p author: ""))
104 (commit (dict-ref p commit: "")))
105 (string-append
106 "Pipeline #" (number->string number) "\n"
107 "Status: " status "\n"
108 "Event: " event "\n"
109 "Branch: " branch "\n"
110 "Commit: " (if (> (string-length commit) 8)
111 (substring commit 0 8)
112 commit) "\n"
113 "Author: " author "\n"
114 "Message: " (truncate-message message 80) "\n"
115 "\nSteps:\n"
116 (if (and steps (> (array-length steps) 0))
117 (string-join (map format-step (array->list steps)) "\n")
118 " (no steps)"))))
120 ;; ============================================================
121 ;; Tool Handlers
122 ;; ============================================================
124 (define (tool-pipelines-list args)
125 (let* ((owner (dict-ref args owner:))
126 (repo (dict-ref args repo:))
127 (base (fjo-woodpecker-url))
128 (token (fjo-woodpecker-token))
129 (repo-id (lookup-repo-id base token owner repo))
130 (pipelines (woodpecker-list-pipelines base token repo-id)))
131 (if (or (not pipelines) (= (array-length pipelines) 0))
132 (string-append "No pipelines found for " owner "/" repo ".")
133 (string-append
134 "Pipelines for " owner "/" repo ":\n\n"
135 (string-join (map format-pipeline (array->list pipelines)) "\n")))))
137 (define (tool-pipelines-get args)
138 (let* ((owner (dict-ref args owner:))
139 (repo (dict-ref args repo:))
140 (number (dict-ref args number:))
141 (base (fjo-woodpecker-url))
142 (token (fjo-woodpecker-token))
143 (repo-id (lookup-repo-id base token owner repo))
144 (pipeline (woodpecker-get-pipeline base token repo-id number))
145 (steps (woodpecker-list-steps base token repo-id number)))
146 (if (not pipeline)
147 (string-append "Pipeline #" (number->string number) " not found.")
148 (format-pipeline-detail pipeline steps))))
150 ;; ============================================================
151 ;; MCP Registration
152 ;; ============================================================
154 (define (register-pipeline-tools! server register)
155 (register server
156 "fjo/pipelines-list"
157 "List recent Woodpecker CI pipelines for a repository."
158 pipelines-list-schema
159 tool-pipelines-list)
161 (register server
162 "fjo/pipelines-get"
163 "Get details of a specific pipeline with step status."
164 pipelines-get-schema
165 tool-pipelines-get))
167 ))