# APIs / Context Summary
**devai** injects the following modules/variables into the various script stages:
- In all scripts (`# Before All`, `# Data`, `# Output`, `# After All`)
- [utils](#utils) - A set of utility functions and submodules.
- [devai](#devai) - A module to control the devai flow (e.g., `return devai.skip("No need to perform this input")`)
- [CTX](#ctx) - A set of constants mostly related to the various paths used for this execution (e.g., `CTX.AGENT_FILE_PATH`)
- In the `# Before All` stage
- `inputs` - The list of inputs given to the run command (in solo mode, only one)
- When `-f "**/some/glob*.*"` is used, each input will be the matching `FileMeta` object.
- In the `# Data` stage
- `input` - The individual input given from the devai run
- In the case of `-f ...`, it will be the [FileMeta](#filemeta) structure for each file.
- In the `# Output` stage
- `data` - Whatever is returned by the `# Data` script.
- `ai_response` - The [AiResponse](#airesponse)
- In the `# After All` stage
- `inputs` - The inputs sent or modified by `# Before All`
- `outputs` - The outputs returned by the `# Output` stage
- Same order as `inputs` and `nil` when an item has been skipped or the output did not return anything.
## utils
The utils top module is comprised of the following submodules.
### utils.file
See [FileRecord](#filerecord), See [FileMeta](#filemeta), [MdSection](#mdsection) for return types.
```lua
-- Load file text content and return its FileRecord (See below), with `.content`
local file = utils.file.load("doc/some-file.md") -- FileRecord
-- Save file content (will create directories as needed)
utils.file.save("doc/some-file.md", "some new content") -- void (no return for now)
-- List files matching a glob pattern
local all_doc_files = utils.file.list("doc/**/*.md") -- {FileMeta, ...}
-- Get the first file reference matching a glob pattern
local first_doc_file = utils.file.first("doc/**/*.md") -- FileMeta | Nil
-- Ensure a file exists by creating it if not found
local file_meta = utils.file.ensure_exists("./some/file.md", "optional content")
-- FileMeta
-- Load markdown sections from a file
-- If the second argument is absent, then all sections will be returned (nested as items as well)
local sections = utils.file.load_md_sections("doc/readme.md", "# Summary")
-- {MdSection, ...}
```
### utils.path
```lua
-- Check if a path exists
local exists = path.exists("doc/some-file.md") -- bool
-- Check if a path is a file
local is_file = path.is_file("doc/some-file.md") -- bool
-- Check if a path is a directory
local is_dir = path.is_dir("doc/") -- bool
-- Get the parent directory of a path
local parent_dir = path.parent("doc/some-file.md") -- string
```
### utils.git
```lua
-- Restore a file to its last committed state
utils.git.restore("src/main.rs") -- void
```
### utils.web
See [WebResponse](#webresponse), [WebError](#weberror) for return types.
```lua
-- Fetch content from a URL
local content = utils.web.get("https://example.com") -- WebResponse / WebError
```
### utils.md
See [MdBlock](#mdblock)
```lua
-- Extract the markdown blocks (if no name, all blocks will be extracted)
local blocks = utils.md.extract_blocks("js") -- {MdBlock}
-- returns {} if no block found
-- If content starts with ```, it will remove the first and last ```, and return the content in between
-- Otherwise, it returns the original content
local content = utils.md.outer_block_content_or_raw(content) -- string
```
### utils.json
```lua
-- Parse a JSON string into a table
local obj = utils.json.parse('{"name": "John", "age": 30}') -- Object (lua table)
-- Stringify a table into a JSON string
local json_str = utils.json.stringify(obj) -- string
-- Stringify a table into a single-line JSON string
local json_line_str = utils.json.stringify_to_line(obj) -- string
```
### utils.rust
```lua
-- === utils.rust
-- Prune Rust code to keep only function declarations (removes function bodies)
local result = utils.rust.prune_to_declarations(code) -- string
```
## utils.html
```lua
-- Prune HTML content to remove some empty tags, comments, and such
local cleaned_html = utils.html.prune_to_content(html_content) -- string
```
## utils.text
```lua
local trimmed = utils.text.trim(content) -- string
local trimmed = utils.text.trim_start(content) -- string
local trimmed = utils.text.trim_end(content) -- string
-- Truncate content to a maximum length
-- - ellipsis - optional third argument
local truncated_content = utils.text.truncate(content, 100, "...") -- string
-- Ensure
-- - second argument of type `{prefix = string, suffix = string}` both optional
-- - if defined, it will add the prefix and suffix if they are not present
utils.text.ensure(content, {prefix = "./", suffix = ".md"}) -> string
-- Ensure content ends with a single newline
local normalized_content = utils.text.ensure_single_ending_newline(content)
-- string
-- Remove the first line from content
local content_without_first_line = utils.text.remove_first_line(content) -- string
-- Remove the last line from content
local content_without_last_line = utils.text.remove_last_line(content) -- string
-- (Advanced) Replace markers in content with new sections
-- - Markers for now are in between `<>` and `<>`
local updated_content = utils.text.replace_markers(content, new_sections) -- string
```
## utils.cmd
See [CmdResponse](#cmdresponse), [CmdError](#cmderror) for return types.
```lua
-- Execute a system command utils.cmd.exec(cmd_name, cmd_args)
local result = utils.cmd.exec("ls", {"-ll", "./**/*.md"}) -- CmdResponse / CmdError
```
## devai
devai also provides the `devai` module in the context of all scripts, which allows control over the devai flow.
```lua
-- Return a before all response structure
local before_all_response = devai.before_all_response({
before_all = "Some before all data",
inputs = {"one", "two", "three", 4, "five"}
})
-- Skip input cycle with an optional reason
-- This can be used in the `# Data`, `# Before All`, and `# Output` stages
local skip_response = devai.skip("File already contains the documentation")
```
## CTX
All Lua scripts get the `CTX` table in scope to get the path of the runtime and agent.
| Key | Value |
|----------------------|-----------------------------------------------------------|
| CTX.WORKSPACE_DIR | `/absolute/path/to/workspace_dir` |
| CTX.DEVAI_DIR | `./.devai` |
| CTX.AGENT_NAME | `command-ctx-reflect` |
| CTX.AGENT_FILE_PATH | `./.devai/custom/command-agent/command-ctx-reflect.devai` |
| CTX.AGENT_FILE_DIR | `./.devai/custom/command-agent` |
| CTX.AGENT_FILE_NAME | `command-ctx-reflect.devai` |
| CTX.AGENT_FILE_STEM | `command-ctx-reflect` |
- All paths are relative to `WORKSPACE_DIR`
- The `AGENT_NAME` is the name provided that resolves to the `AGENT_FILE_PATH`. You can use this name to do a `devai::run(CTX.AGENT_NAME, [])`
- These are available in `devai run ..` as well as `devai solo ...`
# Common Types
## FileMeta
The `FileMeta` data structure represents the information of a given file without its content.
This is what `-f "**/some/glob*.*"` would return for each of the inputs.
```lua
{
path = "doc/README.md",
name = "README.md",
stem = "README",
ext = "md",
}
```
## FileRecord
The `FileRecord` data structure represents the information of a given file plus its text content.
This is what is returned by `utils.file.load(path)`, for example.
```lua
{
path = "doc/README.md",
name = "README.md",
stem = "README",
ext = "md",
content = "the content of the file",
}
```
## MdSection
The `MdSection` is a markdown section with the following representation:
```lua
{
content = "_section_content", -- after the eventual heading
heading_content = "# Summary", -- can be "" if there is no heading (top section)
heading_name = "Summary", -- can be "" if there is no heading
heading_level = 1, -- Will be 0 when there is no heading
heading_raw = "# Summary\n", -- Will be "" when there is no heading. Simplifies reconstitution logic
}
```
## MdBlock
The `MdBlock` is a markdown section with the following representation:
```lua
{
content = "_block_content_", -- The content of the block
lang = "js", -- string | nil
}
```
## CmdResponse
The `CmdResponse` is returned by `utils.cmd.exec`
```lua
{
stdout = string, -- Standard output from the command
stderr = string, -- Standard error from the command
exit = number -- Exit code (0 for success)
}
```
## CmdError
When `utils.cmd.exec` fails, here is the type:
```lua
{
stdout = string | nil, -- Standard output if available
stderr = string | nil, -- Standard error if available
exit = number | nil, -- Exit code if available
error = string -- Error message from command execution
}
```
## WebResponse
The `WebResponse`
```lua
{
success = true,
status = number,
url = string,
content = string,
}
```
## WebError
In case of an error, the WebError is:
```lua
{
success = false,
status = number | nil,
url = string,
error = string,
}
```