| Field | Description | Required |
|---|---|---|
spec.type |
Agent type (claude-code, codex, gemini, opencode, or cursor) |
Yes |
spec.prompt |
Task prompt for the agent | Yes |
spec.credentials.type |
api-key or oauth |
Yes |
spec.credentials.secretRef.name |
Secret name with credentials | Yes |
spec.model |
Model override (e.g., claude-sonnet-4-20250514) |
No |
spec.image |
Custom agent image override (see Agent Image Interface) | No |
spec.workspaceRef.name |
Name of a Workspace resource to use | No |
spec.agentConfigRef.name |
Name of an AgentConfig resource to use | No |
spec.dependsOn |
Task names that must succeed before this Task starts (creates Waiting phase) |
No |
spec.branch |
Git branch to work on; only one Task with the same branch runs at a time (mutex) | No |
spec.ttlSecondsAfterFinished |
Auto-delete task after N seconds (0 for immediate) | No |
spec.podOverrides.resources |
CPU/memory requests and limits for the agent container | No |
spec.podOverrides.activeDeadlineSeconds |
Maximum duration in seconds before the agent pod is terminated | No |
spec.podOverrides.env |
Additional environment variables (built-in vars take precedence on conflict) | No |
spec.podOverrides.nodeSelector |
Node selection labels to constrain which nodes run agent pods | No |
When a Task has dependsOn, its prompt field supports Go text/template syntax for referencing upstream results. The template data has a single key .Deps containing a map keyed by dependency Task name:
| Variable | Type | Description |
|---|---|---|
{{index .Deps "<name>" "Results" "<key>"}} |
string | A specific key-value result from the dependency (e.g., branch, commit, pr) |
{{index .Deps "<name>" "Outputs"}} |
[]string | Raw output lines from the dependency |
{{index .Deps "<name>" "Name"}} |
string | The dependency Task name |
Example:
prompt: |
The scaffold task created code on branch {{index .Deps "scaffold" "Results" "branch"}}.
Open a PR for these changes.
dependsOn: [scaffold]If template rendering fails (e.g., missing key), the raw prompt string is used as-is.
| Field | Description | Required |
|---|---|---|
spec.repo |
Git repository URL to clone (HTTPS, git://, or SSH) | Yes |
spec.ref |
Branch, tag, or commit SHA to checkout (defaults to repo's default branch) | No |
spec.secretRef.name |
Secret containing credentials for git auth and gh CLI (see authentication methods below) |
No |
spec.remotes[].name |
Git remote name to add after cloning (must not be "origin") |
Yes (per remote) |
spec.remotes[].url |
Git remote URL | Yes (per remote) |
spec.files[].path |
Relative file path inside the repository (e.g., CLAUDE.md) |
Yes (per file) |
spec.files[].content |
File content to write | Yes (per file) |
The workspace secret referenced by spec.secretRef.name supports two authentication methods:
Personal Access Token (PAT):
The secret contains a single key:
| Key | Description |
|---|---|
GITHUB_TOKEN |
GitHub Personal Access Token for git auth and gh CLI |
kubectl create secret generic github-token \
--from-literal=GITHUB_TOKEN=<your-pat>GitHub App (recommended for production/org use):
The secret contains three keys, and the controller automatically exchanges them for a short-lived installation token before each task run:
| Key | Description |
|---|---|
appID |
GitHub App ID |
installationID |
GitHub App installation ID for the target organization |
privateKey |
PEM-encoded RSA private key (PKCS1 or PKCS8) |
kubectl create secret generic github-app-creds \
--from-literal=appID=12345 \
--from-literal=installationID=67890 \
--from-file=privateKey=my-app.private-key.pemGitHub Apps are preferred over PATs for production use because they offer fine-grained permissions, higher rate limits, no dependency on a specific user account, and automatically expiring tokens.
| Field | Description | Required |
|---|---|---|
spec.agentsMD |
Agent instructions (e.g. AGENTS.md, CLAUDE.md) written to ~/.claude/CLAUDE.md (additive with repo files) |
No |
spec.plugins[].name |
Plugin name (used as directory name and namespace) | Yes (per plugin) |
spec.plugins[].skills[].name |
Skill name (becomes skills/<name>/SKILL.md) |
Yes (per skill) |
spec.plugins[].skills[].content |
Skill content (markdown with frontmatter) | Yes (per skill) |
spec.plugins[].agents[].name |
Agent name (becomes agents/<name>.md) |
Yes (per agent) |
spec.plugins[].agents[].content |
Agent content (markdown with frontmatter) | Yes (per agent) |
spec.skills[].source |
skills.sh package in owner/repo format (e.g., vercel-labs/agent-skills) |
Yes (per skill) |
spec.skills[].skill |
Specific skill name from the package (installs all if omitted) | No |
spec.mcpServers[].name |
MCP server name (used as key in agent config) | Yes (per server) |
spec.mcpServers[].type |
Transport type: stdio, http, or sse |
Yes (per server) |
spec.mcpServers[].command |
Executable to run (stdio only) | No |
spec.mcpServers[].args |
Command-line arguments (stdio only) | No |
spec.mcpServers[].url |
Server endpoint (http/sse only) | No |
spec.mcpServers[].headers |
HTTP headers (http/sse only) | No |
spec.mcpServers[].env |
Environment variables for server process (stdio only) | No |
| Field | Description | Required |
|---|---|---|
spec.taskTemplate.workspaceRef.name |
Workspace resource (repo URL, auth, and clone target for spawned Tasks) | Yes (when using githubIssues or githubPullRequests) |
spec.when.githubIssues.repo |
Override repository to poll for issues (in owner/repo format or full URL); defaults to workspace repo URL |
No |
spec.when.githubIssues.labels |
Filter issues by labels | No |
spec.when.githubIssues.excludeLabels |
Exclude issues with these labels | No |
spec.when.githubIssues.state |
Filter by state: open, closed, all (default: open) |
No |
spec.when.githubIssues.types |
Filter by type: issues, pulls (default: issues) |
No |
spec.when.githubIssues.triggerComment |
Requires a matching command in the issue body or comments to include the issue. When combined with excludeComments, the latest matching command wins |
No |
spec.when.githubIssues.excludeComments |
Exclude issues whose most recent matching command is an exclude comment. When combined with triggerComment, the latest matching command wins |
No |
spec.when.githubIssues.assignee |
Filter by assignee username; use "*" for any assignee or "none" for unassigned |
No |
spec.when.githubIssues.author |
Filter by issue author username | No |
spec.when.githubIssues.priorityLabels |
Priority-order labels for task selection when maxConcurrency is set; index 0 is highest priority |
No |
spec.when.githubIssues.pollInterval |
Per-source poll interval override (e.g., "30s", "5m"); takes precedence over spec.pollInterval |
No |
spec.when.githubPullRequests.repo |
Override repository to poll for PRs (in owner/repo format or full URL); defaults to workspace repo URL |
No |
spec.when.githubPullRequests.labels |
Filter pull requests by labels | No |
spec.when.githubPullRequests.excludeLabels |
Exclude pull requests with these labels | No |
spec.when.githubPullRequests.state |
Filter by state: open, closed, all (default: open) |
No |
spec.when.githubPullRequests.reviewState |
Filter by aggregated review state: approved, changes_requested, any (default: any) |
No |
spec.when.githubPullRequests.triggerComment |
Requires a matching command in the PR body or comments to include the PR. When combined with excludeComments, the latest matching command wins |
No |
spec.when.githubPullRequests.excludeComments |
Exclude PRs whose most recent matching command is an exclude comment. When combined with triggerComment, the latest matching command wins |
No |
spec.when.githubPullRequests.author |
Filter by PR author username | No |
spec.when.githubPullRequests.draft |
Filter by draft state | No |
spec.when.githubPullRequests.priorityLabels |
Priority-order labels for task selection when maxConcurrency is set; index 0 is highest priority |
No |
spec.when.githubPullRequests.pollInterval |
Per-source poll interval override (e.g., "30s", "5m"); takes precedence over spec.pollInterval |
No |
spec.when.jira.pollInterval |
Per-source poll interval override (e.g., "30s", "5m"); takes precedence over spec.pollInterval |
No |
spec.when.cron.schedule |
Cron schedule expression (e.g., "0 * * * *") |
Yes (when using cron) |
spec.taskTemplate.type |
Agent type (claude-code, codex, gemini, opencode, or cursor) |
Yes |
spec.taskTemplate.credentials |
Credentials for the agent (same as Task) | Yes |
spec.taskTemplate.model |
Model override | No |
spec.taskTemplate.image |
Custom agent image override (see Agent Image Interface) | No |
spec.taskTemplate.agentConfigRef.name |
Name of an AgentConfig resource for spawned Tasks | No |
spec.taskTemplate.promptTemplate |
Go text/template for prompt (see template variables below) | No |
spec.taskTemplate.dependsOn |
Task names that spawned Tasks depend on | No |
spec.taskTemplate.branch |
Git branch template for spawned Tasks (supports Go template variables, e.g., kelos-task-{{.Number}}) |
No |
spec.taskTemplate.ttlSecondsAfterFinished |
Auto-delete spawned tasks after N seconds | No |
spec.taskTemplate.podOverrides |
Pod customization for spawned Tasks (resources, timeout, env, nodeSelector) | No |
spec.pollInterval |
How often to poll the source (default: 5m). Deprecated: use per-source pollInterval instead |
No |
spec.maxConcurrency |
Limit max concurrent running tasks (important for cost control) | No |
spec.maxTotalTasks |
Lifetime limit on total tasks created by this spawner | No |
spec.suspend |
Pause the spawner without deleting it; resume with spec.suspend: false (default: false) |
No |
The promptTemplate field uses Go text/template syntax. Available variables depend on the source type:
| Variable | Description | GitHub Issues | GitHub Pull Requests | Cron |
|---|---|---|---|---|
{{.ID}} |
Unique identifier | Issue/PR number as string (e.g., "42") |
Pull request number as string | Date-time string (e.g., "20260207-0900") |
{{.Number}} |
Issue or PR number | Issue/PR number (e.g., 42) |
Pull request number | 0 |
{{.Title}} |
Title of the work item | Issue/PR title | Pull request title | Trigger time (RFC3339) |
{{.Body}} |
Body text | Issue/PR body | Pull request body | Empty |
{{.URL}} |
URL to the source item | GitHub HTML URL | GitHub PR URL | Empty |
{{.Labels}} |
Comma-separated labels | Issue/PR labels | Pull request labels | Empty |
{{.Comments}} |
Concatenated comments | Issue/PR comments | PR conversation comments | Empty |
{{.Kind}} |
Type of work item | "Issue" or "PR" |
"PR" |
"Issue" |
{{.Branch}} |
Git branch to update | Empty | PR head branch (e.g., "kelos-task-42") |
Empty |
{{.ReviewState}} |
Aggregated review state | Empty | approved, changes_requested, or empty |
Empty |
{{.ReviewComments}} |
Formatted inline review comments | Empty | Inline PR review comments | Empty |
{{.Time}} |
Trigger time (RFC3339) | Empty | Empty | Cron tick time (e.g., "2026-02-07T09:00:00Z") |
{{.Schedule}} |
Cron schedule expression | Empty | Empty | Schedule string (e.g., "0 * * * *") |
| Field | Description |
|---|---|
status.phase |
Current phase: Pending, Waiting, Running, Succeeded, or Failed |
status.jobName |
Name of the Job created for this Task |
status.podName |
Name of the Pod running the Task |
status.startTime |
When the Task started running |
status.completionTime |
When the Task completed |
status.message |
Additional information about the current status |
status.outputs |
Automatically captured outputs: branch, commit, base-branch, pr, cost-usd, input-tokens, output-tokens |
status.results |
Parsed key-value map from outputs (e.g., results.branch, results.commit, results.pr, results.input-tokens) |
| Field | Description |
|---|---|
status.phase |
Current phase: Pending, Running, Suspended, or Failed |
status.deploymentName |
Name of the Deployment running the spawner (polling-based sources) |
status.cronJobName |
Name of the CronJob running the spawner (cron-based sources) |
status.totalDiscovered |
Total number of items discovered from the source |
status.totalTasksCreated |
Total number of Tasks created by this spawner |
status.activeTasks |
Number of currently active (non-terminal) Tasks |
status.lastDiscoveryTime |
Last time the source was polled |
status.message |
Additional information about the current status |
status.conditions |
Standard Kubernetes conditions for detailed status |
Kelos reads defaults from ~/.kelos/config.yaml (override with --config). CLI flags always take precedence over config file values.
# ~/.kelos/config.yaml
oauthToken: <your-oauth-token>
# or: apiKey: <your-api-key>
model: claude-sonnet-4-5-20250929
namespace: my-namespace| Field | Description |
|---|---|
oauthToken |
OAuth token — Kelos auto-creates the Kubernetes secret. Use none for an empty credential |
apiKey |
API key — Kelos auto-creates the Kubernetes secret. Use none for an empty credential (e.g., free-tier OpenCode models) |
secret |
(Advanced) Use a pre-created Kubernetes secret |
credentialType |
Credential type when using secret (api-key or oauth) |
Precedence: --secret flag > secret in config > oauthToken/apiKey in config.
The workspace field supports two forms:
Reference an existing Workspace resource by name:
workspace:
name: my-workspaceSpecify inline with a PAT — Kelos auto-creates the Workspace resource and secret:
workspace:
repo: https://github.com/your-org/repo.git
ref: main
token: <your-github-token> # optional, for private repos and gh CLISpecify inline with a GitHub App (recommended for production/org use):
workspace:
repo: https://github.com/your-org/repo.git
ref: main
githubApp:
appID: "12345"
installationID: "67890"
privateKeyPath: ~/.config/my-app.private-key.pem| Field | Description |
|---|---|
workspace.name |
Name of an existing Workspace resource |
workspace.repo |
Git repository URL — Kelos auto-creates a Workspace resource |
workspace.ref |
Git reference (branch, tag, or commit SHA) |
workspace.token |
GitHub PAT — Kelos auto-creates the secret and injects GITHUB_TOKEN |
workspace.githubApp.appID |
GitHub App ID |
workspace.githubApp.installationID |
GitHub App installation ID |
workspace.githubApp.privateKeyPath |
Path to PEM-encoded RSA private key file |
The token and githubApp fields are mutually exclusive. If both name and repo are set, name takes precedence. The --workspace CLI flag overrides all config values.
| Field | Description |
|---|---|
type |
Default agent type (claude-code, codex, gemini, opencode, or cursor) |
model |
Default model override |
namespace |
Default Kubernetes namespace |
agentConfig |
Default AgentConfig resource name |
The kelos CLI lets you manage the full lifecycle without writing YAML.
| Command | Description |
|---|---|
kelos install |
Install Kelos CRDs and controller into the cluster |
kelos uninstall |
Uninstall Kelos from the cluster |
kelos init |
Initialize ~/.kelos/config.yaml |
kelos version |
Print version information |
| Command | Description |
|---|---|
kelos run |
Create and run a new Task |
kelos create workspace |
Create a Workspace resource |
kelos create agentconfig |
Create an AgentConfig resource |
kelos get <resource> [name] |
List resources or view a specific resource (tasks, taskspawners, workspaces) |
kelos delete <resource> <name> |
Delete a resource |
kelos logs <task-name> [-f] |
View or stream logs from a task |
kelos suspend taskspawner <name> |
Pause a TaskSpawner (stops polling, running tasks continue) |
kelos resume taskspawner <name> |
Resume a paused TaskSpawner |
--version: Override the image tag used for controller and bundled agent images--image-pull-policy: SetimagePullPolicyon controller-managed images--disable-heartbeat: Do not install the telemetry heartbeat CronJob--spawner-resource-requests: Resource requests for spawner containers as comma-separatedname=valuepairs--spawner-resource-limits: Resource limits for spawner containers as comma-separatedname=valuepairs--token-refresher-resource-requests: Resource requests for token refresher sidecars as comma-separatedname=valuepairs, for examplecpu=100m,memory=128Mi--token-refresher-resource-limits: Resource limits for token refresher sidecars as comma-separatedname=valuepairs, for examplecpu=200m,memory=256Mi
--prompt, -p: Task prompt (required)--type, -t: Agent type (default:claude-code)--model: Model override--image: Custom agent image--name: Task name (auto-generated if omitted)--workspace: Workspace resource name--agent-config: AgentConfig resource name--depends-on: Task names this task depends on (repeatable)--branch: Git branch to work on--timeout: Maximum execution time (e.g.,30m,1h)--env: Additional env vars asNAME=VALUE(repeatable)--watch, -w: Watch task status after creation--secret: Pre-created secret name--credential-type: Credential type when using--secret(default:api-key)
--output, -o: Output format (yamlorjson)--detail, -d: Show detailed information for a specific resource--all-namespaces, -A: List resources across all namespaces
--config: Path to config file (default~/.kelos/config.yaml)--namespace, -n: Kubernetes namespace--kubeconfig: Path to kubeconfig file--dry-run: Print resources without creating them (supported byrun,create,install)--yes, -y: Skip confirmation prompts
Kelos collects anonymous, aggregate usage data to help improve the project. A kelos-telemetry CronJob runs daily at 06:00 UTC and reports the following:
| Data | Description |
|---|---|
| Installation ID | Random UUID, generated once per cluster |
| Kelos version | Installed controller version |
| Kubernetes version | Cluster K8s version |
| Task counts | Total tasks, breakdown by type and phase |
| Feature adoption | Number of TaskSpawners, AgentConfigs, Workspaces, and source types in use |
| Scale | Number of namespaces with Kelos resources |
| Usage totals | Aggregate cost (USD), input tokens, and output tokens |
No personal data, repository names, prompts, or source code is collected.
Install (or reinstall) with the --disable-heartbeat flag:
kelos install --disable-heartbeat