AdachSoft Composer Tool
AI tool-call adapter for running Composer operations via adachsoft/composer-lib,
implemented as a SPI tool for adachsoft/ai-tool-call.
This library exposes a primary AI tool named composer for Composer CLI
operations, and additional tools for direct composer.json inspection,
modification, and validation/fixing.
Requirements
- PHP ^8.3
adachsoft/ai-tool-call
adachsoft/composer-lib
adachsoft/command-executor-lib
adachsoft/normalized-safe-path
Installation
Install via Composer:
composer require adachsoft/composer-tool
Quick Start
Registering the main composer tool in AiToolCall
use AdachSoft\AiToolCall\PublicApi\Builder\AiToolCallFacadeBuilder;
use AdachSoft\AiToolCall\SPI\Collection\ConfigMap;
use AdachSoft\AiToolCall\PublicApi\Dto\ToolCallRequestDto;
use AdachSoft\ComposerTool\Tool\ComposerToolFactory;
$facade = AiToolCallFacadeBuilder::new()
->withSpiFactories([
new ComposerToolFactory(),
])
->withToolConfigs([
'composer' => new ConfigMap([
'working_directory' => __DIR__,
// optional configuration, see below
'hard_timeout_seconds' => 60,
'activity_timeout_seconds' => 30,
'non_interactive_enforced' => true,
'output_capture_mode' => 'FULL', // FULL|STDOUT_ONLY|STDERR_ONLY|DISCARD
'config_patch_mode' => 'PERMANENT', // PERMANENT|TEMPORARY|SKIP
'backup_enabled' => true,
]),
])
->build();
Calling the composer tool
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'install',
],
));
$data = $result->result->all();
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'update',
'packages' => ['vendor/package-a', 'vendor/package-b'],
'with_all_dependencies' => true,
'dry_run' => true,
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'require',
'package_constraints' => [
'vendor/package-a' => '^1.0',
'vendor/package-b' => '~2.3',
],
'dev' => true,
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'show',
'package' => 'vendor/package',
'tree' => true,
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'licenses',
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'depends',
'package' => 'vendor/package',
'tree' => true,
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'prohibits',
'package' => 'vendor/package',
'constraint' => '^1.0',
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'run_script',
'script' => 'my-script',
'args' => ['--flag', 'value'],
],
));
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'clear_cache',
],
));
Available operations and parameters (composer tool)
The composer tool exposes the following operations (aligned with the
adachsoft/composer-lib 0.3.x public API; see
AdachSoft\ComposerTool\Tool\ComposerTool::getDefinition() for
machine-readable schema):
Common optional parameter:
-
package_path (string, optional) - relative path (from the configured working_directory) to a monorepo package directory whose composer.json and working directory should be used for the operation, e.g. "packages/some-package". When omitted or an empty string, the base working_directory (root project) is used. Invalid or unsafe paths (e.g. traversing outside the working_directory) cause the tool call to fail with an InvalidToolCallException.
-
install
no_dev (bool, default: false) - skip dev dependencies
prefer_dist (bool, default: true)
optimize_autoloader (bool, default: true)
dry_run (bool, default: false)
-
update
packages (string[] optional) - list of package names to update
with_all_dependencies (bool, default: false)
no_dev (bool, default: false)
dry_run (bool, default: false)
-
require
package_constraints (map<string,string>, required) - package => constraint
dev (bool, default: false) - require as dev dependency
dry_run (bool, default: false)
-
remove
packages (string[] required) - list of package names to remove
dry_run (bool, default: false)
-
dump_autoload
optimize (bool, default: true)
classmap_authoritative (bool, default: false)
-
validate
no_check_publish (bool, default: true)
-
outdated
direct (bool, default: true) - only directly required packages
-
diagnose
-
show
package (string, optional) - package name to show (all if omitted)
tree (bool, default: false) - show dependency tree
-
licenses
-
depends
package (string, required) - package name to inspect
tree (bool, default: false) - show dependency tree
-
prohibits
package (string, required) - package name
constraint (string, optional) - version constraint
-
run_script
script (string, required) - Composer script name from composer.json
args (string[] optional, default: []) - additional arguments
-
clear_cache
composer.json tools (for AI agents)
In addition to the composer CLI-like tool, this library provides four
SPI tools dedicated to working with composer.json in a given working
directory. These tools are intended to be called by AI agents, not by
humans directly.
Tools overview
-
composer_json_file
- Generic tool that can read and/or write
composer.json depending on
the operation parameter:
read
write
read_and_write
- Accepts an optional
package_path parameter to target a specific package directory inside a monorepo relative to the configured working_directory. When omitted or empty, the root composer.json in working_directory is used.
-
composer_json_read
- Convenience wrapper for
composer_json_file in read mode.
- Always returns a read-only view of the current
composer.json file.
- Accepts an optional
package_path parameter to read composer.json from a specific monorepo package directory relative to working_directory. When omitted or empty, the root composer.json is read.
-
composer_json_write
- Convenience wrapper for
composer_json_file in write mode.
- Only performs write operations on
composer.json.
- Accepts an optional
package_path parameter to write composer.json for a specific monorepo package directory relative to working_directory. When omitted or empty, the root composer.json is written.
-
composer_json_fix
- Tool for validating and fixing
composer.json using adachsoft/composer-json-fixer.
- Supports operations:
validate and fix.
- Accepts an optional
package_path parameter to validate/fix composer.json for a specific monorepo package directory relative to working_directory. When omitted or empty, the root composer.json is targeted.
Each of these tools expects a working_directory configuration passed
via its corresponding factory:
ComposerJsonReadWriteToolFactory
ComposerJsonReadToolFactory
ComposerJsonWriteToolFactory
ComposerJsonFixToolFactory
Registering composer.json tools in AiToolCall
use AdachSoft\AiToolCall\PublicApi\Builder\AiToolCallFacadeBuilder;
use AdachSoft\AiToolCall\SPI\Collection\ConfigMap;
use AdachSoft\ComposerTool\Tool\ComposerJsonReadToolFactory;
use AdachSoft\ComposerTool\Tool\ComposerJsonWriteToolFactory;
use AdachSoft\ComposerTool\Tool\ComposerJsonReadWriteToolFactory;
use AdachSoft\ComposerTool\Tool\ComposerJsonFixToolFactory;
$facade = AiToolCallFacadeBuilder::new()
->withSpiFactories([
new ComposerJsonReadToolFactory(),
new ComposerJsonWriteToolFactory(),
new ComposerJsonReadWriteToolFactory(),
new ComposerJsonFixToolFactory(),
])
->withToolConfigs([
'composer_json_read' => new ConfigMap([
'working_directory' => __DIR__,
]),
'composer_json_write' => new ConfigMap([
'working_directory' => __DIR__,
]),
'composer_json_file' => new ConfigMap([
'working_directory' => __DIR__,
]),
'composer_json_fix' => new ConfigMap([
'working_directory' => __DIR__,
]),
])
->build();
Parameters for write operations: json vs data
For tools that write composer.json (composer_json_write and
composer_json_file in write / read_and_write modes), the desired
file content can be provided in two alternative forms:
-
json (string)
- Full
composer.json contents as a raw JSON string.
- Semantics for the agent: "I already know the exact textual content of
the file. Take this JSON string, validate it, and write it."
-
data (object / map)
- Structured representation of
composer.json as a JSON object
(associative array in PHP).
- Semantics for the agent: "I have the semantic structure of the file.
Take this object, encode it to JSON using project conventions, and
write it."
At least one of json or data is required for write operations.
If neither is provided, the call is rejected with an
InvalidToolCallException.
The detailed rationale for this dual input model is described in
docs/ADR-001-composer-json-write-parameters.md.
Example: read current composer.json
use AdachSoft\AiToolCall\PublicApi\Dto\ToolCallRequestDto;
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer_json_read',
parameters: [],
));
$payload = $result->result->all();
Example: validate composer.json
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer_json_fix',
parameters: [
'operation' => 'validate',
],
));
$payload = $result->result->all();
Example: fix composer.json issues
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer_json_fix',
parameters: [
'operation' => 'fix',
],
));
$payload = $result->result->all();
Example: overwrite composer.json with a JSON string
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer_json_write',
parameters: [
'json' => "{\n \"name\": \"vendor/package\"\n}\n",
'backup' => true,
'pretty' => true,
],
));
Example: update composer.json using structured data
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer_json_write',
parameters: [
'data' => [
'name' => 'vendor/package',
'description' => 'Updated by AI agent',
],
'backup' => true,
'pretty' => true,
],
));
Configuration options (factory)
ComposerToolFactory reads configuration from ConfigMap with the following keys:
working_directory (string, required, non-empty)
hard_timeout_seconds (int, default: 60)
activity_timeout_seconds (int, default: 30)
non_interactive_enforced (bool, default: true)
output_capture_mode (string, default: FULL)
- allowed values:
FULL, STDOUT_ONLY, STDERR_ONLY, DISCARD
config_patch_mode (string, default: PERMANENT)
- allowed values:
PERMANENT, TEMPORARY, SKIP
backup_enabled (bool, default: true)
Invalid configuration results in ToolConfigurationException being thrown
by the SPI layer (ComposerToolFactory).
Error handling
The tool is designed so that AI agents receive clear, structured feedback
about what went wrong.
Invalid tool calls (schema / parameters)
The following cases result in AdachSoft\AiToolCall\SPI\Exception\InvalidToolCallException:
- Missing required parameters, for example calling the tool without
the
operation field:
- message:
Composer tool call is missing required parameter "operation".
- Parameters with wrong types, for example passing an integer where a string
is expected:
- message:
Composer tool parameter "operation" must be a string, got int.
- Unsupported
operation value:
- message:
Unsupported Composer operation "foo". Allowed operations: install, update, require, remove, dump_autoload, validate, outdated, diagnose, show, licenses, depends, prohibits, run_script, clear_cache.
These messages are intended to be directly consumable by an AI agent and are
also propagated to any higher-level wrapper (for example as
"Tool call failed: <message>").
Composer-level validation failures
Certain domain-specific failures from adachsoft/composer-lib are reported
as regular tool results instead of SPI exceptions:
InvalidComposerJsonException
NonInteractiveViolationException
In these cases the tool call succeeds from the SPI perspective, but the
result payload indicates failure:
{
"operation": "install",
"status": "FAILURE",
"stdout": "",
"stderr": "Interactive mode is not allowed here."
}
This makes it easy for an AI agent to understand what Composer reported
and decide how to proceed (e.g. adjust arguments and retry).
Unexpected runtime failures
Any other unexpected failure during Composer execution (bugs, environment
issues, unhandled exceptions thrown by ComposerClientInterface or its
collaborators) is wrapped in
AdachSoft\AiToolCall\SPI\Exception\ToolExecutionException.
The exception message always includes the Composer operation name and the
original error message, for example:
Composer operation "install" failed: <original-message>
Higher-level tooling (such as the AI integration layer) can then format
this as a single error string (e.g. "Tool call failed: Composer operation \"install\" failed: ..."),
while still retaining the original exception as the previous cause.
Versioning
This package follows Semantic Versioning and is versioned exclusively
through Git tags. The composer.json file intentionally does not contain
an explicit version field.
License
MIT