Act
Overview
An Act is a reusable task definition for Ghostwriter. It is stored in a small TOML file and tells Ghostwriter how to run a predefined AI-assisted workflow.
An act usually contains:
- a
descriptionfor humans, instructionsthat define the AI assistant's role,- an
inputstemplate that becomes the prompt, - optional Ghostwriter settings such as
gw.scanDir,gw.interactive,gw.nonRecursive, orgw.threads.
Acts make repeated work easier. Instead of rewriting the same long prompt each time, you can run a named act and let Ghostwriter load the saved template for you.
Acts are loaded by ActProcessor. This class reads the TOML file, merges
inherited values, applies act settings to the processor, prepares episode
execution, and then runs the prompt against the project.
What an Act Does
At a high level, an act helps Ghostwriter do four things:
- Load a named workflow from a TOML file.
- Prepare the final prompt by combining the act template with the user request.
- Apply runtime settings such as scan rules or interactive mode.
- Execute one or more episodes in the configured order.
The normal command format is:
gw --act <name> [your request text]
You can also point directly to a TOML file:
gw --act C:\path\to\my-act.toml [your request text]
When an absolute TOML path is used as the act name, Ghostwriter uses that file only. In that case, lookup through bundled classpath resources is not used.
How Act Loading Works
ActProcessor.setAct(String) is the main entry point for act loading.
In simple terms, it does this:
- If no act name is provided, Ghostwriter uses
help. - It separates the act name from the optional user prompt.
- It detects optional episode selection syntax such as
help#2ortask#1,3!. - It calls
ActProcessor.loadAct(...)to load and merge act data. - It applies the user prompt into the
inputstemplate. - It applies act properties to the processor.
- It runs the selected episode flow.
Act File Structure
A typical act file can look like this:
description = '''
Short explanation of what this act does.
'''
instructions = '''
You are an expert assistant.
'''
inputs = '''
# Task
%s
'''
prompt = "Default prompt text"
# basedOn = "task"
[gw]
scanDir = "glob:."
interactive = true
Common top-level properties:
| Property | Meaning |
|---|---|
description |
Human-readable explanation of the act. |
instructions |
System-style instructions for the AI assistant. |
inputs |
Prompt template. It may be a string or a list of episode strings. |
prompt |
Default user prompt if no prompt is supplied on the command line. |
basedOn |
Parent act name used for inheritance. |
gw.* |
Ghostwriter runtime configuration values. |
| other keys | Additional configuration values passed into the configurator. |
Common gw.* properties used by the built-in acts include:
| Property | Meaning |
|---|---|
gw.scanDir |
What files or folders Ghostwriter should scan. |
gw.excludes |
Excluded paths. |
gw.nonRecursive |
Prevents recursion into child projects or modules. |
gw.threads |
Number of worker threads. |
gw.interactive |
Enables interactive chat-style act processing. |
gw.model |
Overrides the configured AI model. |
gw.acts |
Location of user-defined act files. |
Interactive and Non-Interactive Mode
Acts can run in two different styles.
Non-interactive mode
In non-interactive mode, the act is executed once using the resolved prompt and then finishes.
Use this mode when:
- you already know exactly what you want,
- the act should run as part of automation,
- you want a simple one-shot task.
Example:
gw --act commit
Interactive mode
If gw.interactive = true, Ghostwriter keeps the act open as a conversation.
This is useful when you want to refine the task step by step or when you do not
know the full request before starting.
While the act is running interactively, two special prompt commands are used:
| Constant | Value | Meaning |
|---|---|---|
AIFileProcessor.EXIT_SPECIAL_PROMPT_COMMAND |
. |
End the current act session. |
AIFileProcessor.CONTINUE_SPECIAL_PROMPT_COMMAND |
> |
Continue act processing without entering a new prompt. |
Practical usage:
- type
.on its own line to stop the act, - type
>on its own line to continue to the next processing step or episode.
This is especially helpful for multi-step acts and guided workflows.
Default Prompt with prompt
An act can define a default user prompt with the prompt property. This value
is used when the user runs the act without providing extra request text.
Example:
description = "Simple project summary"
instructions = "You are a project analyst."
prompt = "Summarize this project for a new developer."
inputs = '''
# Task
%s
'''
Behavior:
gw --act describe "focus on the API layer"usesfocus on the API layer.gw --act describeusesSummarize this project for a new developer.
This is handled during setAct(...), where the processor falls back to the
current default prompt when the command does not include one.
Episodes
An act can contain one prompt or multiple prompts.
When inputs is a single string, the act has one main prompt.
When inputs is a TOML array of strings, each array item becomes a separate
episode.
Example:
inputs = [
'''
# Analyze
Review the code and list the most important issues.
%s
''',
'''
# Fix
Apply the approved changes.
'''
]
In this case, Ghostwriter runs a sequence of episodes rather than a single step.
The Episodes helper manages the order and provides episode information to the
processor.
Episode names
If an episode starts with a first-level heading such as # Analyze, that label
is used as the episode display name.
Episode selection syntax
You can select specific episodes by adding #... after the act name.
Examples:
| Command | Meaning |
|---|---|
gw --act my-act |
Run all episodes in normal order. |
gw --act my-act#2 |
Start from episode 2. |
gw --act my-act#1,3 |
Request episodes 1 and 3 first, then continue in normal order. |
gw --act my-act#2! |
Run only episode 2 and stop normal order afterwards. |
Episode IDs are 1-based.
Episode flow control
The implementation also supports moving through episodes programmatically. During processing, Ghostwriter can:
- repeat the current episode,
- move to another episode by ID,
- move to another episode by name,
- continue with regular order,
- stop regular order when
!is used.
This behavior is coordinated by the episode logic used by ActProcessor and the
related exception-based flow control classes such as MoveToEpisodeException.
Inheritance and Value Merging
Acts can inherit from other acts with the basedOn property.
Example:
basedOn = "task"
This allows one act to reuse another act and override only the parts that need to change.
How inheritance is processed
ActProcessor.loadAct(...) loads act data from two possible sources:
- the user-defined acts directory configured by
gw.acts, - the built-in classpath resources under
/acts/.
If both exist for the same act name, Ghostwriter loads both and applies their values into one merged act definition.
The code then checks basedOn.
If a parent act is defined, that parent is loaded first, recursively, and the
child values are applied afterward.
Override order
The effective order is:
- parent built-in values,
- parent custom values,
- child built-in values,
- child custom values.
This means later values can override or extend earlier ones.
How string values are merged
When both parent and child define the same string property, Ghostwriter uses the
existing value as a template and replaces %s with the newer value.
Example:
Parent:
instructions = '''
You are helping with %s.
'''
Child:
instructions = "documentation tasks"
Merged result:
instructions = '''
You are helping with documentation tasks.
'''
How list values are merged
If inputs or another property is a list, items are merged position by
position.
- matching child items replace
%sinside matching parent items, - extra child items are appended,
- a parent string can also be merged into a child list.
This is what allows a base act to define a shared prompt skeleton and a child act to turn it into several episodes.
Inherited configurator values
After act loading, applyActData(...) also checks the current configurator.
If a property already exists there, Ghostwriter may inject that value into the
act string through %s replacement.
This means an act can inherit not only from another act, but also from runtime configuration that already exists in the processor.
Simple inheritance example
Parent act:
instructions = '''
You are working on the %s project.
'''
inputs = '''
# Task
%s
'''
Child act:
basedOn = "base"
instructions = "Ghostwriter"
inputs = '''
Create a summary.
%s
'''
Merged result:
instructions = '''
You are working on the Ghostwriter project.
'''
inputs = '''
# Task
Create a summary.
%s
'''
At runtime, the remaining %s in inputs is replaced with the actual user
prompt.
Placeholder Variables in ${...} Format
Act files may contain placeholders such as ${sonar.host} or ${sonar.token}.
These placeholders must stay exactly as written.
They are intended for dynamic substitution by functional tools at runtime. They can be resolved from sources such as:
- environment variables,
- Java system properties,
- action properties,
- configurator properties,
- other runtime configuration sources.
Important rules:
${...}placeholders are not for the LLM to resolve,- they must not be edited, expanded, renamed, or removed,
- they should be passed through unchanged until runtime substitution happens.
Example from the built-in sonar-fix act:
${sonar.host}/api/issues/search?componentKeys=...
In this example, ${sonar.host} is kept unchanged in the act definition and is
resolved later by the application environment.
Using an Act Step by Step
A simple real-world workflow looks like this:
-
Start with help
gw --act helpThis opens the built-in help act and explains available act behavior.
-
Run a task-oriented act
gw --act code-doc "add missing API documentation"Ghostwriter loads
code-doc.toml, injects the request intoinputs, and processes matching files. -
Use interactive mode if needed
For acts such as
help,task, orcommit, you can continue the session, refine the request, type>to continue, or type.to finish. -
Create a custom act
Put a file such as
my-act.tomlinto the directory configured bygw.acts. A simple example is:basedOn = "task" description = "My custom project workflow." prompt = "Analyze the current folder and suggest improvements." -
Run the custom act
gw --act my-act -
Use a direct TOML file path for one-off usage
gw --act C:\temp\custom.toml "review this module"
Built-in Acts
Ghostwriter includes several built-in act files in src/main/resources/acts.
code-doc
Purpose: Adds or updates documentation comments in source files.
When to use it: Use this act when you want Ghostwriter to generate or improve Javadoc, docstrings, XML comments, or equivalent documentation comments without changing program logic.
Notable behavior: It focuses on documentation quality, language-appropriate comment style, and outputting only updated file content.
commit
Purpose: Reviews local version-control changes and helps commit them in logical groups.
When to use it: Use this act when you want Ghostwriter to inspect changed files, group them by change type, generate commit messages, and perform the required VCS commands automatically.
Notable behavior: It is interactive, scans the current project, and includes
an ft.command.denylist section that can inherit values using %s.
grype-fix
Purpose: Fixes dependency vulnerabilities reported by Grype.
When to use it: Use this act when you want to update vulnerable dependencies, rebuild the project, and document security-related dependency changes.
Notable behavior: It is aimed at Maven projects, explains how to generate an SBOM with Syft, how to run Grype, and how to document fixed vulnerabilities.
help
Purpose: Explains Ghostwriter acts and helps users inspect or understand act definitions.
When to use it: Use this act when you need guidance about available acts, act structure, inheritance, or act usage.
Notable behavior: It runs in interactive mode, keeps scanning local to the current directory, and is designed as a user-facing help assistant.
sonar-fix
Purpose: Reviews SonarQube issues and applies code fixes based on security, quality, and maintainability rules.
When to use it: Use this act when you want Ghostwriter to fetch SonarQube issues, fix them in code, add or update tests, validate the build, and record changed files.
Notable behavior: It uses runtime placeholders such as ${sonar.host},
${sonar.token}, ${sonar.qualities}, and ${sonar.severity}. These values
must remain unchanged in the TOML file and are resolved at runtime.
task
Purpose: Provides a minimal general-purpose act template.
When to use it: Use this act when you want a simple prompt-driven workflow or when you want to build your own custom act on top of a lightweight base.
Notable behavior: It enables interactive mode and contains only a minimal prompt structure.
unit-tests
Purpose: Improves project test coverage by generating or updating unit tests.
When to use it: Use this act when you want Ghostwriter to build the project, measure JaCoCo coverage, identify uncovered code, and create high-quality unit tests.
Notable behavior: It targets test-related scanning, aims for strong coverage, and allows limited production refactoring when required for better testability.
Key Methods and Project Role
The Act feature is a core way to use Ghostwriter. It turns repeatable AI tasks into named, version-controlled definitions that can be shared and reused.
Important implementation points include:
ActProcessor.setAct(String)parses the act command and applies prompt and episode selection.ActProcessor.loadAct(...)loads acts from custom and built-in sources and resolvesbasedOninheritance.ActProcessor.setActData(...)and related merge helpers combine TOML values.ActProcessor.applyActData(...)transfers resolved values into runtime processor settings.Episodesmanages episode naming, ordering, selection, repetition, and jumps.
Together, these parts let Ghostwriter run anything from a simple one-step prompt to a guided multi-step workflow.
Summary
The Act feature gives Ghostwriter a structured and reusable way to perform work. It is useful because it combines:
- reusable prompt templates,
- optional inheritance,
- configurable runtime settings,
- interactive and non-interactive execution,
- multi-step episode workflows,
- support for runtime placeholders in
${...}format, - built-in and user-defined act definitions.
For new users, the easiest place to start is:
gw --act help
From there, you can explore built-in acts, run them directly, or create your own custom act files for repeatable project workflows.

