Skip to content
45 changes: 45 additions & 0 deletions .zai-scheduled.yml.template
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,37 @@ tasks:
# URL to fetch content from
gist_url: https://gist.githubusercontent.com/AndreiDrang/1580ae796fe56074b600cee6352a5f14/raw

# --- AGENTS.md upgrade scoping (all optional) ---
#
# The bot always collects REAL repository context (file tree, existing
# AGENTS.md files, and key file contents) and feeds it to the model so it
# never hallucinates a project from the repo name alone.
#
# Limit which parts of the repo are analyzed for context (default: whole repo):
# context_paths:
# - "src"
# - "tests"
#
# Restrict where AGENTS.md may be written. Root AGENTS.md is ALWAYS allowed.
# When omitted, the model may update/create AGENTS.md anywhere justified.
# target_paths:
# - "src/lib"
# - "tests"
#
# Globs to ignore when collecting context (merged with built-in defaults
# like node_modules, dist, build, coverage, *.lock):
# exclude_paths:
# - "docs/generated/**"
#
# Prompt/context budgets:
# max_context_chars: 120000 # total chars of file contents sent to the model
# max_file_chars: 12000 # per-file content cap (large files head-truncated)
# max_files_to_fetch: 40 # hard cap on content fetches (rate-limit guard)
#
# Generation policy:
# allow_create_new: true # may propose new child AGENTS.md files (default true)
# update_existing_only: false # if true, only existing AGENTS.md files may change

# PR configuration
pr_title: "chore: update AGENTS.md files"
pr_body: |
Expand Down Expand Up @@ -114,6 +145,20 @@ tasks:
# Required config: branch (optional, defaults to main)
# Optional config: gist_url, pr_title, pr_body, commit_message
#
# AGENTS.md upgrade scoping (all optional):
# context_paths - limit which repo paths are analyzed for context
# target_paths - restrict where AGENTS.md files may be written
# exclude_paths - globs ignored when collecting context
# max_context_chars - total context char budget (default 120000)
# max_file_chars - per-file content cap (default 12000)
# max_files_to_fetch - hard cap on content fetches (default 40)
# allow_create_new - may propose new child AGENTS.md (default true)
# update_existing_only - only existing AGENTS.md files may change (default false)
#
# The bot ALWAYS collects real repository context before generating, and
# VALIDATES output against it: it rejects files that reference paths that do
# not exist in the repo, preventing hallucinated content from being committed.
#
# Example command for auto-discovery mode (should be in Gist):
# /init-agentsmd
# This command must return JSON like:
Expand Down
55 changes: 44 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,55 @@
# PROJECT KNOWLEDGE BASE

**Generated:** 2026-06-27T00:00:00Z
**Generated:** 2026-07-01T00:00:00Z
**Branch:** main
**Refresh:** reconciled line counts, added `scheduled` handler + `config/` + `code-scope.js`
**Refresh:** reconciled against `plans/*` (scheduled-tasks pipeline); verified scheduled event routing, config loader, handler symbols, manual `/zai update-agents` command, action.yml inputs, and line counts. Gaps flagged: no scheduled test coverage, no `docs/`, README not updated.

## OVERVIEW
JavaScript GitHub Action that performs PR auto-review and collaborator-gated `/zai` PR comment commands. Runtime executes bundled `dist/index.js`; maintained logic lives in `src/index.js` plus modular services in `src/lib/*`.
JavaScript GitHub Action with three event flows: (1) PR auto-review, (2) collaborator-gated `/zai` PR comment commands, and (3) cron-triggered scheduled tasks (`.zai-scheduled.yml`) that regenerate AGENTS.md files and open PRs. Runtime executes bundled `dist/index.js`; maintained logic lives in `src/index.js` plus modular services in `src/lib/*`.

## STRUCTURE
```text
zai-code-bot/
├── src/index.js # Runtime orchestration and event dispatch (~1095 lines)
├── src/lib/ # Commands/auth/context/comments/api/services
├── src/lib/events.js # Event-type detection incl. `schedule` (cron) routing
├── src/lib/commands.js # `/zai` parser + allowlist (incl. `update-agents`)
├── src/lib/auto-review.js # Large PR batching and synthesis
├── src/lib/changed-files.js # Paginated changed-files fetch (3000 file limit)
├── src/lib/pr-context.js # Shared PR context fetch (files, content at ref, refs)
├── src/lib/code-scope.js # Token-budget calculation for prompt sizing
├── src/lib/config/scheduled-config.js # Scheduled-task config loader (.zai-scheduled.yml)
├── src/lib/handlers/ # Command handlers (ask/review/explain/describe/impact/help/scheduled)
├── src/lib/repository-context.js # Real repo context collection (tree + AGENTS.md discovery + key files)
├── src/lib/agents-validation.js # Hallucination guard: validates generated AGENTS.md vs real repo
├── src/lib/handlers/ # Command + scheduled handlers (ask/review/explain/describe/impact/help/scheduled)
├── tests/ # Unit and integration coverage
├── dist/index.js # Generated ncc bundle executed by GitHub
├── dist/licenses.txt # Generated third-party licenses
├── action.yml # Action inputs and runtime entry
├── action.yml # Action inputs (incl. ZAI_SCHEDULED_*, ZAI_AGENTS_GIST_URL)
├── .zai-scheduled.yml # Scheduled-task config for THIS repo (AGENTS.md upkeep)
├── .zai-scheduled.yml.template # Consumer template for scheduled tasks
├── .github/workflows/ci.yml # Test/build/dist-drift/audit gates
├── .github/workflows/zai-agents-update.yml # Self-hosted scheduled AGENTS.md upkeep workflow
└── .github/workflows/code-review.yml # Consumer usage example
```

## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Route events and command execution | `src/index.js` | `run()`, pull_request path, issue_comment command path |
| Parse commands and enforce allowlist | `src/lib/commands.js` | `/zai` parser, command normalization, help fallback |
| Route events and command execution | `src/index.js` | `run()`, pull_request path, issue_comment command path, schedule path |
| Schedule event detection | `src/lib/events.js` | `getEventType` returns `schedule`; `shouldProcessEvent` always processes cron events |
| Parse commands and enforce allowlist | `src/lib/commands.js` | `/zai` parser, command normalization, help fallback; `update-agents` in allowlist |
| Authorization and fork policy | `src/lib/auth.js` | Collaborator checks and fork-safe behavior |
| Comment/reaction behavior | `src/lib/comments.js` | Marker-based upsert, threaded reply (`replyToId`), reactions |
| API retry/error handling | `src/lib/api.js`, `src/lib/logging.js` | Retry policy, categorized safe errors |
| Large PR batching and synthesis | `src/lib/auto-review.js` | Batch creation, context limit handling, synthesis prompt |
| Paginated changed-files fetch | `src/lib/changed-files.js` | Handles GitHub's 3000 file API limit |
| Shared PR context fetch | `src/lib/pr-context.js` | `fetchPrFiles`, `fetchFileAtRef`, `resolvePrRefs`; user-safe fallbacks, size limits |
| Scheduled-task config loading | `src/lib/config/scheduled-config.js` | `loadScheduledConfig`, `getTasksToRun`, `validateAndNormalizeConfig`, `validateAgentsConfig`, `getGistUrl`; scoping fields (`context_paths`/`target_paths`/`exclude_paths`/budgets) |
| Scheduled-task execution | `src/lib/handlers/scheduled.js` | `handleScheduledEvent`, `handleUpdateAgentsTask` (grounded flow: context→prompt→validate→PR), `SCHEDULED_HANDLERS`; see child `src/lib/handlers/AGENTS.md` |
| Repository context for AGENTS.md gen | `src/lib/repository-context.js` | `collectRepositoryContext` (git tree + existing AGENTS.md discovery + key files, budgeted), `renderRepositoryContext` |
| AGENTS.md output validation | `src/lib/agents-validation.js` | `validateGeneratedAgentFiles` — rejects non-AGENTS paths, out-of-scope writes, and hallucinated content referencing non-existent files |
| Manual `/zai update-agents` | `src/index.js` (`dispatchCommand`) | Reuses `handleUpdateAgentsTask` for ad-hoc AGENTS.md updates |
| Command-specific behavior | `src/lib/handlers/AGENTS.md` | Local guide for each handler module |
| Test strategy and fixtures | `tests/AGENTS.md` | Test map and suite conventions |
| Action runtime contract | `action.yml` | Node runtime + dist entrypoint |
Expand Down Expand Up @@ -64,9 +77,25 @@ zai-code-bot/
| `resolvePrRefs` | function | `src/lib/pr-context.js` | low | Resolves base/head refs for diff context |
| `MAX_PR_FILES_API_LIMIT` | constant | `src/lib/changed-files.js` | low | GitHub API ceiling (3000) |
| `calculateTokenBudget` | function | `src/lib/code-scope.js` | medium | Token/char budget sizing for prompts |
| `detectEventType` | function | `src/lib/events.js` | low | Event-type detection for routing |
| `getEventType` | function | `src/lib/events.js` | low | Event-type detection for routing (incl. `schedule`) |
| `shouldProcessEvent` | function | `src/lib/events.js` | low | Event filter; always-process gate for cron events |
| `loadScheduledConfig` | function | `src/lib/config/scheduled-config.js` | low | Parses `.zai-scheduled.yml` task config |
| `handleScheduledCommand` | function | `src/lib/handlers/scheduled.js` | medium | Scheduled-task execution (largest handler) |
| `validateAndNormalizeConfig` | function | `src/lib/config/scheduled-config.js` | low | Schema validation + default-merging for tasks |
| `getTasksToRun` | function | `src/lib/config/scheduled-config.js` | low | Filters tasks whose schedule matches the event |
| `getGistUrl` | function | `src/lib/config/scheduled-config.js` | low | Resolves gist URL priority: task > defaults > env |
| `handleScheduledEvent` | function | `src/lib/handlers/scheduled.js` | medium | Scheduled pipeline entry: load config, run matching tasks |
| `executeScheduledTask` | function | `src/lib/handlers/scheduled.js` | medium | Per-task executor; builds context, dispatches via registry |
| `handleUpdateAgentsTask` | function | `src/lib/handlers/scheduled.js` | medium | AGENTS.md regeneration: gist → collect repo context → grounded Z.ai prompt → validate → JSON diff → PR |
| `SCHEDULED_HANDLERS` | constant | `src/lib/handlers/scheduled.js` | low | Command→handler registry; `getScheduledHandler`/`registerScheduledHandler` extend it |
| `buildAgentsUpgradePrompt` | function | `src/lib/handlers/scheduled.js` | low | Grounded prompt: embeds real tree + existing AGENTS.md + key files; tells model it has NO live repo access |
| `parseFileUpdatesFromResponse` | function | `src/lib/handlers/scheduled.js` | low | Multi-format JSON extraction from Z.ai output |
| `callZaiApiWithRetry` | function | `src/lib/handlers/scheduled.js` | low | Z.ai HTTP client (native https) with retry for scheduled tasks |
| `fetchFromUrl` | function | `src/lib/handlers/scheduled.js` | low | HTTP GET for gist command text (30s timeout) |
| `createPR` | function | `src/lib/handlers/scheduled.js` | low | Branch + multi-file commit + PR open for scheduled changes |
| `collectRepositoryContext` | function | `src/lib/repository-context.js` | medium | git.getTree → existing AGENTS.md discovery + key-file contents, budgeted + glob-excluded |
| `renderRepositoryContext` | function | `src/lib/repository-context.js` | low | Renders collected context into a compact prompt block |
| `validateGeneratedAgentFiles` | function | `src/lib/agents-validation.js` | medium | Pre-PR guard: rejects non-AGENTS paths, out-of-scope/target writes, and hallucinated content |
| `validateAgentsConfig` | function | `src/lib/config/scheduled-config.js` | low | Validates scoping/budget config fields (`context_paths`, `target_paths`, etc.) |

## CONVENTIONS
- Edit maintained code in `src/`; do not hand-edit generated `dist/index.js`.
Expand All @@ -79,6 +108,8 @@ zai-code-bot/
- Bypassing authorization/fork checks for command handlers.
- Executing command logic for non-PR issue comments.
- Allowing unbounded context payloads into prompts.
- Asking an LLM to "scan the repo" without providing real repo context (causes hallucinated AGENTS.md — PR #15 bug).
- Committing AGENTS.md output that references files/languages not present in the repo.
- Editing `dist/` manually or shipping source changes without rebuilt artifacts.
- Treating `.github/workflows/code-review.yml` example as runtime logic.

Expand All @@ -99,6 +130,8 @@ After source changes: run `npm run build` and commit `dist/index.js` + `dist/lic
## NOTES
- CI (`.github/workflows/ci.yml`) enforces tests, build, dist drift, and security audit across Node 20 + 22.
- No linting/formatting configs (ESLint, Prettier) — rely on code review and CI gates.
- 7 command handlers: ask (521), review (218), explain (355), describe (129), impact (336), help (95), scheduled (1075 — largest handler, drives scheduled tasks via `.zai-scheduled.yml`).
- 7 command handlers + scheduled pipeline: ask (521), review (218), explain (355), describe (129), impact (336), help (95), scheduled (~1180 — largest handler, drives scheduled tasks via `.zai-scheduled.yml`). `update-agents` (manual `/zai` command) reuses `handleUpdateAgentsTask` from the scheduled module.
- AGENTS.md upgrade flow (the `update-agents` task) is grounded + validated: `handleUpdateAgentsTask` calls `collectRepositoryContext` (`src/lib/repository-context.js`) to gather the real file tree, auto-discover existing AGENTS.md files, and fetch key file contents; `buildAgentsUpgradePrompt` embeds that context and tells the model it has NO live repo access; `validateGeneratedAgentFiles` (`src/lib/agents-validation.js`) rejects hallucinated/non-AGENTS/out-of-scope output before any PR. Scoping knobs live in `.zai-scheduled.yml` task config: `context_paths`, `target_paths`, `exclude_paths`, `max_context_chars`, `max_file_chars`, `max_files_to_fetch`, `allow_create_new`, `update_existing_only`.
- Scheduled pipeline gaps (per `plans/*`): `docs/scheduled-tasks.md` not created; README has no scheduled section. (Scheduled unit/integration tests now exist: `tests/handlers/scheduled.test.js`, `tests/scheduled-config.test.js`, `tests/repository-context.test.js`, `tests/agents-validation.test.js`.)
- Test framework: Vitest v3 (not Jest). Command: `npm test` → `vitest run --coverage`.
- Large files: src/lib/handlers/scheduled.js (1075 lines), src/index.js (1095 lines), src/lib/handlers/ask.js (521 lines), src/lib/pr-context.js (433 lines).
- Large files: src/lib/handlers/scheduled.js (1081 lines), src/index.js (1095 lines), src/lib/handlers/ask.js (521 lines), src/lib/pr-context.js (433 lines).
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ GitHub Action for automatic PR reviews and context-rich `/zai` commands powered
## Features

- Automatic pull request review on `opened` and `synchronize`
- Interactive PR commands: `/zai ask`, `/zai review`, `/zai explain`, `/zai describe`, `/zai impact`, `/zai help`
- Interactive PR commands: `/zai ask`, `/zai review`, `/zai explain`, `/zai describe`, `/zai impact`, `/zai update-agents`, `/zai help`
- Scheduled tasks (cron): periodically regenerate `AGENTS.md` files and open PRs (see [Scheduled Tasks](docs/scheduled-tasks.md))
- Context-aware command prompts with full-file, diff, and thread context
- Inline review-comment support (`pull_request_review_comment`) with file/line anchors
- `/zai explain` auto-detects selected line range from review comments
Expand Down Expand Up @@ -66,6 +67,9 @@ jobs:
| `ZAI_AUTO_REVIEW_MAX_BATCH_CHARS` | No | `120000` | Approximate character budget per batched PR auto-review request |
| `ZAI_AUTO_REVIEW_MAX_FILES_PER_BATCH` | No | `40` | Maximum distinct files included in each batched PR auto-review request |
| `ZAI_AUTO_REVIEW_MAX_PATCH_CHARS` | No | `18000` | Maximum diff characters per file chunk before a large patch is split across review parts |
| `ZAI_SCHEDULED_ENABLED` | No | `true` | Master switch for the scheduled-tasks pipeline |
| `ZAI_SCHEDULED_CONFIG_PATH` | No | `.zai-scheduled.yml` | Path to the scheduled-tasks config file |
| `ZAI_AGENTS_GIST_URL` | No | - | Fallback Gist URL for the `update-agents` task (lowest priority) |

## Commands

Expand All @@ -78,6 +82,7 @@ Commands are processed from PR issue comments and PR review comments. Supported
| `/zai explain` | `/zai explain <lines>` | Explain selected lines (e.g., `/zai explain 10-25`) |
| `/zai describe` | `/zai describe` | Generate a PR description from commit messages |
| `/zai impact` | `/zai impact` | Analyze potential impact of changes |
| `/zai update-agents` | `/zai update-agents` | Regenerate `AGENTS.md` files on demand (same as the scheduled task) |
| `/zai help` | `/zai help` | Show command help |

**Note:** Only collaborators (and PR authors on their own fork PRs) can use these commands.
Expand All @@ -93,6 +98,37 @@ Commands are processed from PR issue comments and PR review comments. Supported
- Command execution is authorization-gated; fork PR authors can run commands on their own PR
- If GitHub's changed-files API limit is reached, the final review notes that coverage is incomplete beyond the platform ceiling

## Scheduled Tasks

In addition to PR review and `/zai` commands, Zai Code Bot can run tasks on a
schedule. The built-in `update-agents` task periodically regenerates your
`AGENTS.md` knowledge files and opens a pull request with the changes — a PR is
created only when at least one file actually changed.

### Minimal setup

1. Add a `.zai-scheduled.yml` to your repo root (copy `.zai-scheduled.yml.template`).
2. Put the generation command in a public Gist and expose its raw URL via the
`ZAI_AGENTS_GIST_URL` action input (or `gist_url` in the config).
3. Add a `schedule` (cron) workflow that runs the action. A ready-to-use
workflow snippet is included in `.zai-scheduled.yml.template`.

### `gist_url` priority

The Gist URL is resolved first-non-empty-wins:
`task.config.gist_url` > `defaults.gist_url` > `ZAI_AGENTS_GIST_URL`.

### Manual run

Trigger an AGENTS.md regeneration on any PR with:

```
/zai update-agents
```

For the full configuration reference, cron syntax, schedule-matching behavior,
troubleshooting, and examples, see **[docs/scheduled-tasks.md](docs/scheduled-tasks.md)**.

## Setup

1. Generate a Z.ai API key from your Z.ai account.
Expand Down
Loading
Loading