mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
1346963e59
* feat: daily docs-sync bot workflow (Kilo CLI) Adds a scheduled workflow that keeps packages/kilo-docs in sync with PRs merged to Kilo-Org/cloud and Kilo-Org/kilocode: - watermark.mjs derives the processing window from the bot's own PR body marker (self-healing, no external state; 72h fallback, 14d cap) - collect.mjs queries merged PRs via the GitHub API and applies a deterministic pre-filter (bots, chores, docs-only PRs) - triage.mjs classifies PRs in chunks of 25 with kilo run; failed chunks degrade to unclassified instead of failing the run - edit.mjs updates docs in batches of 5 PRs with kilo run, bounded per batch; failures surface as skipped entries in the PR body - verify runs the kilo-docs build + test suite; one LLM fix pass on failure; still-red becomes a draft PR - upsert-pr.mjs maintains one rolling auto-docs PR (appends while open, fresh branch after merge), with a 15-file draft cap and a machine-readable processed-through watermark Also adds docs-sync.yml to the workflow allowlist in script/check-workflows.ts. * fix: correct kilo run invocation and auth - message positional must come before flags: --file is multi-value and consumes a trailing message as a file path (File not found) - authenticate via the existing KILO_API_KEY repo secret (the kilo provider reads it natively); drop the DOCS_SYNC_KILO_CONFIG config secret requirement - fix default model IDs: gateway provider id is kilo/, not kilocode/ - include stderr tail in triage/edit failure logs * fix: handle kilo run double-printed assistant output kilo run prints the assistant message twice (streaming render + final summary), so stdout can contain the same JSON array back-to-back. Parse the largest valid trailing array instead of slicing first-to-last bracket. Verified against real chunked triage output. * fix: reviewer-pass robustness fixes - edit.mjs: unambiguous summary file path in the batch prompt and a fallback read when the agent drops the docs-sync-out/ prefix, so real edits never report as skipped - prepare-branch.mjs: use the open auto-docs PR's actual head.ref instead of assuming docs/auto-sync - upsert-pr.mjs: compute the 15-file draft cap on the cumulative PR diff (origin/main...HEAD), not just the latest commit * fix: address Kilobot review findings Security: - sanitize HTML-comment sequences out of agent-generated PR body values so a crafted value cannot forge section markers or the watermark - draft any PR whose diff touches non-content files in packages/kilo-docs (outside pages/ and lib/nav/) — build-executable changes force human review before merge - on merge conflict, keep the conflicted rolling branch untouched (preserving human commits) and continue on a fresh dated branch that links the old PR Resilience: - retry GitHub API calls on network errors and 5xx, not just 403 rate limits - isolate per-PR collect failures instead of aborting the run - trust watermark markers only on bot-authored PRs and clamp future dates loudly - validate chunk triage entries belong to their chunk before the shared dedupe - use changed_files for files_total and skip docs-only classification on truncated (300+) file lists - pipe stderr in the edit pass so failure warnings carry the real CLI error * fix: address second Kilobot review round - escape pipe characters in changeRow actions (same as skippedRow) - sanitize agent-chosen file paths before they land in draftReasons and the PR body (residual marker-forgery path via filenames) - log expected fetch misses in prepare-branch instead of silent catches * feat: keep bot-authored PRs in the docs-sync digest Release and dependency bots ship user-facing changes (e.g. JetBrains release PRs from kilo-maintainer[bot]). The auto-docs label check and docs-only path filter remain as the loop guards.
77 lines
2.4 KiB
JavaScript
77 lines
2.4 KiB
JavaScript
// kilocode_change - new file
|
|
|
|
/**
|
|
* Extracts and validates the triage JSON array from raw LLM stdout.
|
|
* Usage: extract-json.mjs <raw-input-file> <output-file>
|
|
* Exit 0 on success, 1 on any failure. Also exports parseTriageEntries for
|
|
* the chunked triage runner.
|
|
*/
|
|
|
|
import fs from "node:fs"
|
|
import { pathToFileURL } from "node:url"
|
|
|
|
/** Returns validated triage entries, or null when extraction fails. */
|
|
export function parseTriageEntries(raw) {
|
|
// `kilo run` prints the assistant message twice (streaming render + final
|
|
// summary), so stdout can hold the same array back-to-back. Try each "["
|
|
// from the right and return the first slice that parses — i.e. the last
|
|
// (most recent) valid array in the output.
|
|
const end = raw.lastIndexOf("]")
|
|
if (end < 0) return null
|
|
|
|
const starts = []
|
|
for (let i = 0; i <= end; i++) {
|
|
if (raw[i] === "[") starts.push(i)
|
|
}
|
|
|
|
for (let s = starts.length - 1; s >= 0; s--) {
|
|
let parsed
|
|
try {
|
|
parsed = JSON.parse(raw.slice(starts[s], end + 1))
|
|
} catch {
|
|
continue
|
|
}
|
|
if (!Array.isArray(parsed)) continue
|
|
const entries = validate(parsed)
|
|
if (entries) return entries
|
|
}
|
|
return null
|
|
}
|
|
|
|
function validate(parsed) {
|
|
const entries = []
|
|
for (const e of parsed) {
|
|
const pr = Number(e?.pr)
|
|
const url = String(e?.url ?? "")
|
|
if (!Number.isInteger(pr) || !url.startsWith("http")) continue
|
|
entries.push({
|
|
pr,
|
|
url,
|
|
docs_worthy: e.docs_worthy === true,
|
|
reason: String(e.reason ?? ""),
|
|
target_sections: Array.isArray(e.target_sections) ? e.target_sections.map(String) : [],
|
|
priority: ["high", "medium", "low"].includes(e.priority) ? e.priority : "medium",
|
|
})
|
|
}
|
|
return entries.length > 0 ? entries : null
|
|
}
|
|
|
|
function main() {
|
|
const [, , inputPath, outputPath] = process.argv
|
|
if (!inputPath || !outputPath) {
|
|
console.error("usage: extract-json.mjs <raw-input-file> <output-file>")
|
|
process.exit(1)
|
|
}
|
|
const entries = parseTriageEntries(fs.readFileSync(inputPath, "utf8"))
|
|
if (!entries) {
|
|
console.error("no valid triage JSON array found in input")
|
|
process.exit(1)
|
|
}
|
|
fs.writeFileSync(outputPath, JSON.stringify(entries, null, 2))
|
|
console.log(`extracted ${entries.length} triage entries (${entries.filter((e) => e.docs_worthy).length} docs-worthy)`)
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
main()
|
|
}
|