Experiments:

Notes from attempting to parse the generated code:

Dark-F# https://sharegpt.com/c/tTUgpiq

Before: code generated by ChatGPT

type RepoName = String
type PR = { title: String, body: String }
type PRList = List<PR>

let repoName = "example/repo"

let fetchMergedPRs = (repoName: RepoName) : PRList ->
  let lastMonth = Date.sub Date.now (Date.makeDuration ~days=30)
  let url = $"<https://api.github.com/repos/{repoName}/pulls?state=closed&sort=updated&direction=desc>"
  let response = HttpClient.request "get" url [("Accept", "application/vnd.github+json")] Bytes.empty
  let prs = Json.parse<PRList> (String.fromBytes response.body)
  prs
  |> List.filter (fun pr -> pr.merged_at > lastMonth)
  |> List.filter (fun pr -> String.contains "Changelog:" pr.body && !String.contains "no changelog" pr.body)

let concatenateTitlesAndDescriptions = (prs: PRList) : String ->
  prs
  |> List.map (fun pr -> $"{pr.title}\\n{pr.body}\\n\\n")
  |> String.concat

let prsWithChangelogs = fetchMergedPRs repoName
concatenateTitlesAndDescriptions prsWithChangelogs

type RepoName = String
type PR = { title: String, body: String }
type PRList = List<PR>

let repoName = "example/repo"

let fetchMergedPRs = (repoName: RepoName) : PRList ->
  let lastMonth = DateTime.subtract_v1 DateTime.now_v0 (Date.makeDuration ~days=30)
  let url = $"<https://api.github.com/repos/{repoName}/pulls?state=closed&sort=updated&direction=desc>"
  let response = HttpClient.request "get" url [("Accept", "application/vnd.github+json")] Bytes.empty
  let prs = Json.parse<PRList> (String.fromBytes response.body)
  List.filter_v2 prs (fun pr -> 
    pr.merged_at > lastMonth && 
    String.contains_v0 "Changelog:" pr.body && 
    !String.contains_v0 "no changelog" pr.body
  )

let concatenateTitlesAndDescriptions = (prs: PRList) : String ->
  List.map prs (fun pr -> 
    String.append_v1 (String.append_v1 pr.title "\\n") (String.append_v1 pr.body "\\n\\n")
  )
  |> String.concat

let prsWithChangelogs = fetchMergedPRs repoName
concatenateTitlesAndDescriptions prsWithChangelogs

Fixed code: what the code is supposed to look like

type RepoResponse = { url: String; title: String; body: String}

(
let repoName = "darkalng/dark"
let lastMonth = DateTime.subtractSeconds_v0 DateTime.now_v0 2,678,400
let url = "<https://api.github.com/repos/darklang/dark/pulls?state=closed&sort=updated&direction=desc>"
let headers = [ ( "Authorization", Secrets.GITHUB-TOKEN)]
let response= WASM.HttpClient.request "get" url headers Bytes.empty
let res = match response with
| Ok f -> f.body |> String.fromBytes |> Json.parse<List<RepoResponse>>
| Error _ -> Bytes.empty
res
let prs = match res with 
| Ok r -> r 
| Error _ -> Bytes.empty
List.map prs (fun pr -> 
String.append_v1 (String.append_v1 pr.title "\\n") (String.append_v1 pr.body "\\n\\n"))
)
|> String.join_v0 ""
)
)

Dark-JS: https://shareg.pt/hltixVZ

type GithubPR = {
  title: String
  body: String
}

let getPRsWithChangelog (repoName: String) : String {
  let since = DateTime.sub(Duration.days(30), DateTime.now())
  let baseUrl = "<https://api.github.com>"
  let token = Secrets.GITHUB_TOKEN

  let prs = HttpClient.request "get" (baseUrl + "/repos/" + repoName + "/pulls?state=closed&sort=updated&direction=desc&since=" + DateTime.toISOString(since)) [ ("Authorization", "token " + token) ] Bytes.empty
  let prsJson = String.fromBytes(prs.body)

  let prsWithChangelog = prsJson
    |> Json.parse<List<{ title: String, body: String, merged_at: String }>>() 
    |> List.filter (fun pr -> String.contains "Changelog:" pr.body && not (String.contains "no changelog" pr.body))

  let prsConcatenated = prsWithChangelog
    |> List.map (fun pr -> pr.title + "\\n\\n" + pr.body)
    |> String.join "\\n\\n"

  prsConcatenated
}

Dark-python: https://shareg.pt/aPiaAC4

type GithubPR = {
  title: String
  body: String
}

let getPRsWithChangelogs = (repoName: String) => {
  let url = "<https://api.github.com/repos/>" + repoName + "/pulls?state=closed&sort=updated&direction=desc&per_page=100"
  let response = HttpClient.request "get" url [("User-Agent", "Dark")]
  let allPRs = Json.parse<List<GithubPR>>(String.fromBytes response.body)
  let lastMonth = Date.subtract (Date.now()) { months: 1 }
  let prsWithChangelogs = List.filter (fun pr -> {
    let hasChangelog = String.contains pr.body "\\"Changelog:"
    let noChangelog = String.contains pr.body "no changelog"
    hasChangelog && not noChangelog
  }) allPRs
  let prDescriptions = List.map (fun pr -> pr.title ++ "\\n" ++ pr.body) prsWithChangelogs
  String.join prDescriptions "\\n"
}

getPRsWithChangelogs "OWNER/REPO_NAME"