VOOZH about

URL: https://deepwiki.com/guanguans/ai-commit/4.4-configuration-system-implementation

⇱ Configuration System Implementation | guanguans/ai-commit | DeepWiki


Loading...
Menu

Configuration System Implementation

This page documents the internal implementation of ConfigManager — the class responsible for reading, merging, and persisting ai-commit's configuration data. It covers the class hierarchy, three-layer merge strategy, file I/O methods, serialization, and the exception thrown for unsupported file types.

For user-facing documentation on the config command (set, get, unset, reset, list, edit), see Configuration Management. For the full reference of configuration keys, see Configuration File Format and Precedence. For the ConfigManager public API surface, see Configuration Manager API.


Class Overview

ConfigManager is defined in app/ConfigManager.php and extends Illuminate\Config\Repository. It additionally implements \JsonSerializable, \Stringable, Arrayable (Laravel contract), and Jsonable (Laravel contract).

SymbolSource
ConfigManagerapp/ConfigManager.php
BASE_NAME'.ai-commit.json'
BASE_DIRNAME'.ai-commit'
JSON_OPTIONSJSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR

The class uses several Laravel/PHP traits:

TraitPurpose
ConditionableFluent when()/unless() chaining
Dumpabledump()/dd() debugging
ForwardsCallsProxy unknown calls to an inner object
MacroableRuntime extension via ::macro()
TappableFluent tap() chaining

Sources: app/ConfigManager.php39-50


Bootstrap and Registration

During application startup, ConfigManager::load() is invoked in the booting hook:

bootstrap/app.php40-42

load() calls make(), then registers the resulting instance under the ai-commit key of Laravel's config store via Config::set('ai-commit', $self). After this point, config('ai-commit') returns the ConfigManager instance directly.

Application startup sequence — ConfigManager bootstrap


Sources: bootstrap/app.php40-42 app/ConfigManager.php62-65


Three-Layer Configuration Merge

make() assembles configuration from up to three sources, applied in order using array_replace_recursive so that each subsequent layer overrides only the keys it defines.

LayerSourcePath
1 — Defaultsconfig/ai-commit.phpPHP file in application config dir
2 — Global~/.ai-commit/.ai-commit.json (Unix) or C:\Users\<user>\.ai-commit\.ai-commit.json (Windows)ConfigManager::globalPath()
3 — Local./.ai-commit.json (current working directory)ConfigManager::localPath()

Only layers whose files actually exist are included (array_filter(..., file_exists(...))).

Three-layer merge — make() and readFrom()


Sources: app/ConfigManager.php70-88 app/ConfigManager.php252-269


Static Factory Methods

MethodSignatureDescription
load()static load(): selfCreates instance and registers it in Laravel config as ai-commit
make()static make(?array $items = null): selfBuilds from three layers, or from provided array if non-null
makeFrom()static makeFrom(string ...$files): selfBuilds from an explicit list of file paths

makeFrom() is also used in tests to construct a config with only the defaults layer, without polluting the result with any real global or local config file.

Sources: app/ConfigManager.php62-88 tests/TestCase.php53-57


File Path Resolution

globalPath() and localPath() are static helpers for deriving canonical paths.

globalPath():
 Unix → join_paths(exec('cd ~; pwd'), '.ai-commit', '.ai-commit.json')
 Windows → join_paths('C:\\Users', get_current_user(), '.ai-commit', '.ai-commit.json')

localPath($path = '.ai-commit.json'):
 → join_paths(getcwd(), $path)
 (falls back to realpath('') if getcwd() returns false)

Sources: app/ConfigManager.php90-106


Reading Files — readFrom()

The private readFrom(string ...$files) method is the core I/O primitive. It iterates over the provided file paths and dispatches on file extension:

ExtensionRead mechanism
.phprequire $file (returns PHP array)
.jsonFile::json($file, JSON_THROW_ON_ERROR)
anything elsethrows UnsupportedConfigFileTypeException

All loaded arrays are merged via array_replace_recursive in a left-to-right order, so later files win on key conflicts.

readFrom() dispatch


Sources: app/ConfigManager.php252-269 app/Exceptions/UnsupportedConfigFileTypeException.php


Writing Files — putFile(), putGlobal(), putLocal()

MethodWrites to
putFile(string $file, int $options)Arbitrary path
putGlobal(int $options)globalPath()
putLocal(int $options)localPath()

putGlobal() and putLocal() delegate to putFile(). All three use JSON_OPTIONS as the default serialization options.

Before writing, putFile() applies a filtering step using toDotArray(): it removes dot-notation keys whose values are plain PHP objects that do not implement JsonSerializable, Arrayable, or Jsonable. There is also an explicit whitelist: keys matching the glob patterns generators.*.parameters.messages, generators.*.parameters.prompt, and generators.*.parameters.user are always stripped before writing (they are runtime-only values).

After filtering, File::ensureDirectoryExists() is called on the parent directory before writing the JSON output.

Sources: app/ConfigManager.php111-163


Merging Into Existing State — replace() and replaceFrom()

MethodDescription
replace(array $items)Deep-merges $items into $this->items using array_replace_recursive
replaceFrom(string $file)Reads a file via readFrom() then calls replace()

replaceFrom() is used by ConfigCommand::handle() to synchronize the in-memory ConfigManager with the file being operated on before performing set/unset/reset actions.

Sources: app/ConfigManager.php168-181 app/Commands/ConfigCommand.php61


Forgetting Keys — forget()


Delegates to Arr::forget($this->items, $keys), which supports dot-notation and array of keys. Returns $this for chaining.

Sources: app/ConfigManager.php188-193


Serialization

ConfigManager provides three representations of its state:

MethodReturn typeNotes
toArray()arrayRecursively resolves Arrayable values
toDotArray()arrayFlat dot-notation keys via Arr::dot()
toJson(mixed $options)stringCalls jsonSerialize() then json_encode()
jsonSerialize()arrayRecursively resolves JsonSerializable, Jsonable, Arrayable
__toString()stringDelegates to toJson()

Serialization dispatch in jsonSerialize()


Sources: app/ConfigManager.php202-247


UnsupportedConfigFileTypeException

Thrown by readFrom() when a file with an unsupported extension (not .php or .json) is passed to any of the factory methods or to replaceFrom(). The exception is constructed via a static make($file) factory method on the exception class.

Test coverage:

it('will throw UnsupportedConfigFileTypeException when read from config file', function (): void {
 ConfigManager::makeFrom(fixtures_path('ai-commit.yml'));
})->throws(UnsupportedConfigFileTypeException::class);

Sources: tests/Unit/ConfigManagerTest.php70-72 app/ConfigManager.php261


ConfigCommand Integration

ConfigCommand retrieves the singleton ConfigManager from the container via config('ai-commit') in its constructor. The handle() method selects which file to operate on (local, global, or --file), then calls replaceFrom() to load that file's state, and dispatches on the action argument:

ActionConfigManager calls
setset($key, $value), putFile($file)
gettoJson() or get($key)
unsetforget($key), putFile($file)
resetset($key, $defaults[$key]) or replace($defaults), putFile($file)
listtoDotArray()
editOpens file in editor process

ConfigCommand::handle() flow


Sources: app/Commands/ConfigCommand.php55-114


Summary of Key Interactions


Sources: app/ConfigManager.php39-270 app/Commands/ConfigCommand.php30-221