adachsoft / composer-tool
AI tool-call adapter for running Composer operations via adachsoft/composer-lib.
Installs: 7
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/adachsoft/composer-tool
Requires
Requires (Dev)
- adachsoft/php-code-style: ^0.2.1
- friendsofphp/php-cs-fixer: ^3.89
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.4
README
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 and
modification.
Requirements
- PHP ^8.3
adachsoft/ai-tool-calladachsoft/composer-libadachsoft/command-executor-libadachsoft/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
// Example: install with defaults
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'install',
],
));
$data = $result->result->all();
// $data contains (minimal, AI-friendly payload):
// - operation (string)
// - status (string enum from composer-lib, e.g. SUCCESS|FAILURE)
// - stdout (string)
// - stderr (string)
// Example: update specific packages in dry-run mode
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'update',
'packages' => ['vendor/package-a', 'vendor/package-b'],
'with_all_dependencies' => true,
'dry_run' => true,
],
));
// Example: require packages with constraints as dev dependencies
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'require',
'package_constraints' => [
'vendor/package-a' => '^1.0',
'vendor/package-b' => '~2.3',
],
'dev' => true,
],
));
// Example: show package information with dependency tree
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'show',
'package' => 'vendor/package',
'tree' => true,
],
));
// Example: list licenses for all installed packages
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'licenses',
],
));
// Example: find why a package is installed
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'depends',
'package' => 'vendor/package',
'tree' => true,
],
));
// Example: check which packages prevent installing a given version
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'prohibits',
'package' => 'vendor/package',
'constraint' => '^1.0',
],
));
// Example: run a Composer script with additional arguments
$result = $facade->callTool(new ToolCallRequestDto(
toolName: 'composer',
parameters: [
'operation' => 'run_script',
'script' => 'my-script',
'args' => ['--flag', 'value'],
],
));
// Example: clear Composer cache
$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):
installno_dev(bool, default: false) skip dev dependenciesprefer_dist(bool, default: true)optimize_autoloader(bool, default: true)dry_run(bool, default: false)
updatepackages(string[] optional) list of package names to updatewith_all_dependencies(bool, default: false)no_dev(bool, default: false)dry_run(bool, default: false)
requirepackage_constraints(map<string,string>, required) package => constraintdev(bool, default: false) require as dev dependencydry_run(bool, default: false)
removepackages(string[] required) list of package names to removedry_run(bool, default: false)
dump_autoloadoptimize(bool, default: true)classmap_authoritative(bool, default: false)
validateno_check_publish(bool, default: true)
outdateddirect(bool, default: true) only directly required packages
diagnose- no extra parameters
showpackage(string, optional) package name to show (all if omitted)tree(bool, default: false) show dependency tree
licenses- no extra parameters
dependspackage(string, required) package name to inspecttree(bool, default: false) show dependency tree
prohibitspackage(string, required) package nameconstraint(string, optional) version constraint
run_scriptscript(string, required) Composer script name from composer.jsonargs(string[] optional, default: []) additional arguments
clear_cache- no extra parameters
composer.json tools (for AI agents)
In addition to the composer CLI-like tool, this library provides three
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.jsondepending on theoperationparameter:readwriteread_and_write
- Generic tool that can read and/or write
composer_json_read- Convenience wrapper for
composer_json_fileinreadmode. - Always returns a read-only view of the current
composer.jsonfile.
- Convenience wrapper for
composer_json_write- Convenience wrapper for
composer_json_fileinwritemode. - Only performs write operations on
composer.json.
- Convenience wrapper for
Each of these tools expects a working_directory configuration passed
via its corresponding factory:
ComposerJsonReadWriteToolFactoryComposerJsonReadToolFactoryComposerJsonWriteToolFactory
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;
$facade = AiToolCallFacadeBuilder::new()
->withSpiFactories([
new ComposerJsonReadToolFactory(),
new ComposerJsonWriteToolFactory(),
new ComposerJsonReadWriteToolFactory(),
])
->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__,
]),
])
->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.jsoncontents 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."
- Full
data(object / map)- Structured representation of
composer.jsonas 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."
- Structured representation of
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();
// payload contains, among others:
// - operation: "read"
// - path: absolute path to composer.json
// - data: decoded JSON as a map
// - raw: raw JSON string content
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
- allowed values:
config_patch_mode(string, default:PERMANENT)- allowed values:
PERMANENT,TEMPORARY,SKIP
- allowed values:
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
operationfield:- message:
Composer tool call is missing required parameter "operation".
- message:
- 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.
- message:
- Unsupported
operationvalue:- message:
Unsupported Composer operation "foo". Allowed operations: install, update, require, remove, dump_autoload, validate, outdated, diagnose, show, licenses, depends, prohibits, run_script, clear_cache.
- message:
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:
InvalidComposerJsonExceptionNonInteractiveViolationException
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