Scripting with Cue

Cue’s evaluation model prevents you from changing values and interacting with the outside world, where values can change between runs. This is done to enforce consistency. To work with the external world, Cue has a tooling layer where you can access the execute commands, read and write files, or make network calls. (the list of “tools” can be found here)

To use this system, there are a couple of differences from regular cue:

  • scripting files have a _tool.cue suffix
  • commands are run with cue cmd <name>

You will also be able to use your “pure” Cue from these files, but not the other way around (as this would break the rules).

  • Cue explicitly prevents computation
  • But we need to often combine these configuarations and tasks in ops
  • Cue scripting / workflow

Example Script

This example is taken from the cue help cmd text with some minor changes to the comments.

Tool files have a common format:

  • command: <name>: { ... } are the commands you can call with cue cmd.
  • Each command has a number of tasks <task name>: <task type> & { ... }. The task types come from the tool/... builtins.
  • Task types have different fields, some are inputs and others are outputs
  • Tasks will run concurrently by default. They will be ordered into a DAG if there are references to other tasks.

ex_tool.cue (run with cue cmd prompter)

package foo

import (

// moved to the data.cue file to show how we can reference "pure" Cue files
// city: "Amsterdam"

// A command named "prompter"
command: prompter: {

	// save transcript to this file
	var: file: *"out.txt" | string @tag(file) // you can use "-t flag=filename.txt" to change the output file, see "cue help injection" for more details

	// prompt the user for some input
	ask: cli.Ask & {
		prompt:   "What is your name?"
		response: string

	// run an external command, starts after ask
	echo: exec.Run & {
		// note the reference to ask and city here
		cmd:    ["echo", "Hello", ask.response + "!", "Have you been to", city + "?"]
		stdout: string // capture stdout, don't print to the terminal

	// append to a file, starts after echo
	append: file.Append & {
		filename: var.file
		contents: echo.stdout // becuase we reference the echo task

	// also starts after echo, and concurrently with append
	print: cli.Print & {
		text: echo.stdout // write the output to the terminal since we captured it previously
Last modified December 19, 2020: reorg a number of dirs (11a63be)

2021 Hofstadter, Inc