Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 42 additions & 33 deletions crates/forge_config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,70 +15,79 @@
#[serde(rename_all = "snake_case")]
#[setters(strip_option)]
pub struct ForgeConfig {
/// Configuration for the retry mechanism
/// Retry settings applied at the system level to all IO operations.
pub retry: Option<RetryConfig>,
/// The maximum number of lines returned for FSSearch
/// Maximum number of lines returned by a single file search operation.
pub max_search_lines: usize,
/// Maximum bytes allowed for search results
/// Maximum number of bytes returned by a single file search operation.
pub max_search_result_bytes: usize,
/// Maximum characters for fetch content
/// Maximum number of characters returned from a URL fetch.
pub max_fetch_chars: usize,
/// Maximum lines for shell output prefix
/// Maximum number of lines captured from the leading portion of shell
/// command output.
pub max_stdout_prefix_lines: usize,
/// Maximum lines for shell output suffix
/// Maximum number of lines captured from the trailing portion of shell
/// command output.
pub max_stdout_suffix_lines: usize,
/// Maximum characters per line for shell output
/// Maximum number of characters per line in shell command output.
pub max_stdout_line_chars: usize,
/// Maximum characters per line for file read operations
/// Maximum number of characters per line when reading a file.
pub max_line_chars: usize,
/// Maximum number of lines to read from a file
/// Maximum number of lines read from a file in a single operation.
pub max_read_lines: u64,
/// Maximum number of files that can be read in a single batch operation
/// Maximum number of files read in a single batch operation.
pub max_file_read_batch_size: usize,
/// HTTP configuration
/// HTTP client settings including proxy, TLS, and timeout configuration.
pub http: Option<HttpConfig>,
/// Maximum file size in bytes for operations
/// Maximum file size in bytes permitted for read operations.
pub max_file_size_bytes: u64,
/// Maximum image file size in bytes for binary read operations
/// Maximum image file size in bytes permitted for read operations.
pub max_image_size_bytes: u64,
/// Maximum execution time in seconds for a single tool call
/// Maximum time in seconds a single tool call may run before being
/// cancelled.
pub tool_timeout_secs: u64,
/// Whether to automatically open HTML dump files in the browser
/// Whether to automatically open HTML dump files in the browser after
/// creation.
pub auto_open_dump: bool,
/// Path where debug request files should be written
/// Directory where debug request files are written; disabled when absent.
pub debug_requests: Option<PathBuf>,
/// Custom history file path
/// Path to the conversation history file; defaults to the global history
/// location when absent.
pub custom_history_path: Option<PathBuf>,
/// Maximum number of conversations to show in list
/// Maximum number of conversations shown in the conversation list.
pub max_conversations: usize,
/// Maximum number of results to return from initial vector search
/// Maximum number of candidate results returned from the initial semantic
/// search vector query.
pub max_sem_search_results: usize,
/// Top-k parameter for relevance filtering during semantic search
/// Number of top results retained after re-ranking in semantic search.
pub sem_search_top_k: usize,
/// URL for the indexing server
/// Base URL of the Forge services API used for semantic search and
/// indexing.
#[dummy(expr = "\"https://example.com/api\".to_string()")]
pub services_url: String,
/// Maximum number of file extensions to include in the system prompt
/// Maximum number of file extensions included in the agent system prompt.
pub max_extensions: usize,
/// Format for automatically creating a dump when a task is completed
/// Format used when automatically creating a session dump after task
/// completion; disabled when absent.
pub auto_dump: Option<AutoDumpFormat>,
/// Maximum number of files read concurrently in parallel operations
/// Maximum number of files read concurrently during batch operations.
pub max_parallel_file_reads: usize,
/// TTL in seconds for the model API list cache
/// Time-to-live in seconds for the cached model API list.
pub model_cache_ttl_secs: u64,
/// Default model and provider configuration used when not overridden by
/// individual agents.
/// individual agents.
#[serde(default)]
pub session: Option<ModelConfig>,
/// Provider and model to use for commit message generation
/// Model and provider configuration used for commit message generation.
#[serde(default)]
pub commit: Option<ModelConfig>,
/// Provider and model to use for shell command suggestion generation
/// Model and provider configuration used for shell command suggestion
/// generation.
#[serde(default)]
pub suggest: Option<ModelConfig>,

// --- Workflow fields ---
/// Configuration for automatic forge updates
/// Configuration for automatic Forge updates.
#[serde(skip_serializing_if = "Option::is_none")]
pub updates: Option<Update>,

Expand Down Expand Up @@ -116,17 +125,17 @@
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compact: Option<Compact>,

/// Whether the application is running in restricted mode.
/// When true, tool execution requires explicit permission grants.
/// Whether restricted mode is active; when enabled, tool execution requires
/// explicit permission grants.
pub restricted: bool,

/// Whether tool use is supported in the current environment.
/// When false, tool calls are disabled regardless of agent configuration.
/// Whether tool use is supported in the current environment; when false,
/// all tool calls are disabled.
pub tool_supported: bool,
}

#[cfg(test)]
mod tests {

Check warning on line 138 in crates/forge_config/src/config.rs

View workflow job for this annotation

GitHub Actions / Lint Fix

items after a test module
use pretty_assertions::assert_eq;

use super::*;
Expand Down
8 changes: 7 additions & 1 deletion crates/forge_config/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ impl ConfigWriter {
/// Serializes and writes the configuration to `path`, creating all parent
/// directories recursively if they do not already exist.
///
/// The output includes a leading `$schema` key pointing to the Forge
/// configuration JSON schema, which enables editor validation and
/// auto-complete.
///
/// # Errors
///
/// Returns an error if the configuration cannot be serialized or the file
Expand All @@ -25,7 +29,9 @@ impl ConfigWriter {
std::fs::create_dir_all(parent)?;
}

let contents = toml_edit::ser::to_string_pretty(&self.config)?;
let config_toml = toml_edit::ser::to_string_pretty(&self.config)?;
let contents =
format!("\"$schema\" = \"https://forgecode.dev/schema.json\"\n\n{config_toml}");
Comment on lines +32 to +34
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $schema key is a JSON Schema convention that is not recognized by TOML parsers or editors. While the code successfully writes "$schema" = "https://forgecode.dev/schema.json" as a valid TOML key-value pair, this will not enable IDE validation, auto-complete, or inline documentation as described in the PR. Modern editors only recognize $schema for JSON files, not TOML files. The schema file forge.schema.json is a JSON Schema designed for JSON, but the config file being written is TOML format (using toml_edit::ser::to_string_pretty).

To fix this, either:

  1. Switch the config format from TOML to JSON, or
  2. Use a TOML-specific schema mechanism if one exists (though TOML has no standard schema validation like JSON Schema)
  3. Document that users need to manually configure their editor to associate the schema with TOML files (which most editors don't support)
Suggested change
let config_toml = toml_edit::ser::to_string_pretty(&self.config)?;
let contents =
format!("\"$schema\" = \"https://forgecode.dev/schema.json\"\n\n{config_toml}");
let config_json = serde_json::to_string_pretty(&self.config)?;
let mut schema_obj: serde_json::Value = serde_json::from_str(&config_json)?;
if let serde_json::Value::Object(ref mut map) = schema_obj {
map.insert(
"$schema".to_string(),
serde_json::Value::String("https://forgecode.dev/schema.json".to_string()),
);
}
let contents = serde_json::to_string_pretty(&schema_obj)?;

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.


std::fs::write(path, contents)?;

Expand Down
60 changes: 30 additions & 30 deletions forge.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "object",
"properties": {
"auto_dump": {
"description": "Format for automatically creating a dump when a task is completed",
"description": "Format used when automatically creating a session dump after task\ncompletion; disabled when absent.",
"anyOf": [
{
"$ref": "#/$defs/AutoDumpFormat"
Expand All @@ -16,11 +16,11 @@
]
},
"auto_open_dump": {
"description": "Whether to automatically open HTML dump files in the browser",
"description": "Whether to automatically open HTML dump files in the browser after\ncreation.",
"type": "boolean"
},
"commit": {
"description": "Provider and model to use for commit message generation",
"description": "Model and provider configuration used for commit message generation.",
"anyOf": [
{
"$ref": "#/$defs/ModelConfig"
Expand All @@ -43,21 +43,21 @@
]
},
"custom_history_path": {
"description": "Custom history file path",
"description": "Path to the conversation history file; defaults to the global history\nlocation when absent.",
"type": [
"string",
"null"
]
},
"debug_requests": {
"description": "Path where debug request files should be written",
"description": "Directory where debug request files are written; disabled when absent.",
"type": [
"string",
"null"
]
},
"http": {
"description": "HTTP configuration",
"description": "HTTP client settings including proxy, TLS, and timeout configuration.",
"anyOf": [
{
"$ref": "#/$defs/HttpConfig"
Expand All @@ -68,55 +68,55 @@
]
},
"max_conversations": {
"description": "Maximum number of conversations to show in list",
"description": "Maximum number of conversations shown in the conversation list.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_extensions": {
"description": "Maximum number of file extensions to include in the system prompt",
"description": "Maximum number of file extensions included in the agent system prompt.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_fetch_chars": {
"description": "Maximum characters for fetch content",
"description": "Maximum number of characters returned from a URL fetch.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_file_read_batch_size": {
"description": "Maximum number of files that can be read in a single batch operation",
"description": "Maximum number of files read in a single batch operation.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_file_size_bytes": {
"description": "Maximum file size in bytes for operations",
"description": "Maximum file size in bytes permitted for read operations.",
"type": "integer",
"format": "uint64",
"minimum": 0
},
"max_image_size_bytes": {
"description": "Maximum image file size in bytes for binary read operations",
"description": "Maximum image file size in bytes permitted for read operations.",
"type": "integer",
"format": "uint64",
"minimum": 0
},
"max_line_chars": {
"description": "Maximum characters per line for file read operations",
"description": "Maximum number of characters per line when reading a file.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_parallel_file_reads": {
"description": "Maximum number of files read concurrently in parallel operations",
"description": "Maximum number of files read concurrently during batch operations.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_read_lines": {
"description": "Maximum number of lines to read from a file",
"description": "Maximum number of lines read from a file in a single operation.",
"type": "integer",
"format": "uint64",
"minimum": 0
Expand All @@ -131,37 +131,37 @@
"minimum": 0
},
"max_search_lines": {
"description": "The maximum number of lines returned for FSSearch",
"description": "Maximum number of lines returned by a single file search operation.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_search_result_bytes": {
"description": "Maximum bytes allowed for search results",
"description": "Maximum number of bytes returned by a single file search operation.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_sem_search_results": {
"description": "Maximum number of results to return from initial vector search",
"description": "Maximum number of candidate results returned from the initial semantic\nsearch vector query.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_stdout_line_chars": {
"description": "Maximum characters per line for shell output",
"description": "Maximum number of characters per line in shell command output.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_stdout_prefix_lines": {
"description": "Maximum lines for shell output prefix",
"description": "Maximum number of lines captured from the leading portion of shell\ncommand output.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"max_stdout_suffix_lines": {
"description": "Maximum lines for shell output suffix",
"description": "Maximum number of lines captured from the trailing portion of shell\ncommand output.",
"type": "integer",
"format": "uint",
"minimum": 0
Expand All @@ -185,17 +185,17 @@
"minimum": 0
},
"model_cache_ttl_secs": {
"description": "TTL in seconds for the model API list cache",
"description": "Time-to-live in seconds for the cached model API list.",
"type": "integer",
"format": "uint64",
"minimum": 0
},
"restricted": {
"description": "Whether the application is running in restricted mode.\nWhen true, tool execution requires explicit permission grants.",
"description": "Whether restricted mode is active; when enabled, tool execution requires\nexplicit permission grants.",
"type": "boolean"
},
"retry": {
"description": "Configuration for the retry mechanism",
"description": "Retry settings applied at the system level to all IO operations.",
"anyOf": [
{
"$ref": "#/$defs/RetryConfig"
Expand All @@ -206,13 +206,13 @@
]
},
"sem_search_top_k": {
"description": "Top-k parameter for relevance filtering during semantic search",
"description": "Number of top results retained after re-ranking in semantic search.",
"type": "integer",
"format": "uint",
"minimum": 0
},
"services_url": {
"description": "URL for the indexing server",
"description": "Base URL of the Forge services API used for semantic search and\nindexing.",
"type": "string"
},
"session": {
Expand All @@ -228,7 +228,7 @@
"default": null
},
"suggest": {
"description": "Provider and model to use for shell command suggestion generation",
"description": "Model and provider configuration used for shell command suggestion\ngeneration.",
"anyOf": [
{
"$ref": "#/$defs/ModelConfig"
Expand All @@ -251,11 +251,11 @@
]
},
"tool_supported": {
"description": "Whether tool use is supported in the current environment.\nWhen false, tool calls are disabled regardless of agent configuration.",
"description": "Whether tool use is supported in the current environment; when false,\nall tool calls are disabled.",
"type": "boolean"
},
"tool_timeout_secs": {
"description": "Maximum execution time in seconds for a single tool call",
"description": "Maximum time in seconds a single tool call may run before being\ncancelled.",
"type": "integer",
"format": "uint64",
"minimum": 0
Expand All @@ -281,7 +281,7 @@
]
},
"updates": {
"description": "Configuration for automatic forge updates",
"description": "Configuration for automatic Forge updates.",
"anyOf": [
{
"$ref": "#/$defs/Update"
Expand Down
Loading