refactor: susun semula struktur folder — Laravel source ke src/

This commit is contained in:
Saufi
2026-05-19 15:58:35 +08:00
parent f052251b94
commit bf53c71b45
10806 changed files with 1385379 additions and 121 deletions

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,78 @@
{
"name": "laravel/agent-detector",
"description": "Detect if code is running in an AI agent or automated development environment",
"keywords": [
"php",
"ai",
"agent",
"detection",
"cursor",
"claude",
"devin",
"automation"
],
"homepage": "https://github.com/laravel/agent-detector",
"license": "MIT",
"support": {
"issues": "https://github.com/laravel/agent-detector/issues",
"source": "https://github.com/laravel/agent-detector"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.2.0"
},
"require-dev": {
"laravel/pint": "^1.24.0",
"pestphp/pest": "^3.8.5|^4.1.0",
"pestphp/pest-plugin-type-coverage": "^3.0|^4.0.2",
"phpstan/phpstan": "^2.1.26",
"rector/rector": "^2.1.7",
"symfony/var-dumper": "^7.3.3"
},
"autoload": {
"psr-4": {
"Laravel\\AgentDetector\\": "src/"
},
"files": [
"src/functions.php"
]
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
},
"files": [
"tests/Overrides.php"
]
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"sort-packages": true,
"preferred-install": "dist",
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"scripts": {
"lint": "pint",
"refactor": "rector",
"test:type-coverage": "pest --type-coverage --exactly=100",
"test:lint": "pint --test",
"test:unit": "pest",
"test:types": "phpstan",
"test:refactor": "rector --dry-run",
"test": [
"@test:lint",
"@test:type-coverage",
"@test:unit",
"@test:types",
"@test:refactor"
]
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Laravel\AgentDetector;
class AgentDetector
{
public const AGENT_ENV_VARS = [
'CURSOR_AGENT' => KnownAgent::Cursor,
'GEMINI_CLI' => KnownAgent::Gemini,
'CODEX_SANDBOX' => KnownAgent::Codex,
'CODEX_CI' => KnownAgent::Codex,
'CODEX_THREAD_ID' => KnownAgent::Codex,
'AUGMENT_AGENT' => KnownAgent::AugmentCli,
'OPENCODE_CLIENT' => KnownAgent::Opencode,
'OPENCODE' => KnownAgent::Opencode,
'AMP_CURRENT_THREAD_ID' => KnownAgent::Amp,
'CLAUDECODE' => KnownAgent::Claude,
'CLAUDE_CODE' => KnownAgent::Claude,
'REPL_ID' => KnownAgent::Replit,
'COPILOT_MODEL' => KnownAgent::Copilot,
'COPILOT_ALLOW_ALL' => KnownAgent::Copilot,
'COPILOT_GITHUB_TOKEN' => KnownAgent::Copilot,
'COPILOT_CLI' => KnownAgent::Copilot,
'ANTIGRAVITY_AGENT' => KnownAgent::Antigravity,
'PI_CODING_AGENT' => KnownAgent::Pi,
'KIRO_AGENT_PATH' => KnownAgent::KiroCli,
];
public static function detect(): AgentResult
{
return self::fromAiAgentEnvVar()
?? self::fromKnownEnvVars()
?? self::fromFileSystem()
?? AgentResult::noAgent();
}
protected static function fromAiAgentEnvVar(): ?AgentResult
{
$aiAgent = getenv('AI_AGENT');
if ($aiAgent === false) {
return null;
}
$aiAgent = trim($aiAgent);
if ($aiAgent === '') {
return null;
}
return AgentResult::forAgent(match (true) {
in_array($aiAgent, ['github-copilot', 'github-copilot-cli']) => KnownAgent::Copilot,
str_starts_with($aiAgent, 'claude-code') => KnownAgent::Claude,
default => $aiAgent,
});
}
protected static function fromKnownEnvVars(): ?AgentResult
{
foreach (self::AGENT_ENV_VARS as $envVar => $agent) {
if (getenv($envVar) === false) {
continue;
}
return AgentResult::forAgent(match ($agent) {
KnownAgent::Claude => getenv('CLAUDE_CODE_IS_COWORK') !== false ? KnownAgent::Cowork : KnownAgent::Claude,
default => $agent,
});
}
return null;
}
protected static function fromFileSystem(): ?AgentResult
{
if (file_exists('/opt/.devin')) {
return AgentResult::forAgent(KnownAgent::Devin);
}
return null;
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Laravel\AgentDetector;
class AgentResult
{
public readonly bool $isAgent;
public function __construct(public readonly ?string $name = null)
{
$this->isAgent = $name !== null;
}
public static function forAgent(KnownAgent|string $name): self
{
return new self($name instanceof KnownAgent ? $name->value : $name);
}
public static function noAgent(): self
{
return new self();
}
public function knownAgent(): ?KnownAgent
{
return KnownAgent::tryFrom($this->name ?? '');
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Laravel\AgentDetector;
enum KnownAgent: string
{
case Cursor = 'cursor';
case Claude = 'claude';
case Cowork = 'cowork';
case Devin = 'devin';
case Replit = 'replit';
case Gemini = 'gemini';
case Codex = 'codex';
case V0 = 'v0';
case AugmentCli = 'augment-cli';
case Opencode = 'opencode';
case Amp = 'amp';
case Copilot = 'copilot';
case Antigravity = 'antigravity';
case Pi = 'pi';
case KiroCli = 'kiro-cli';
public function label(): string
{
return match ($this) {
self::AugmentCli => 'Augment CLI',
self::KiroCli => 'Kiro CLI',
self::V0 => 'v0',
default => ucfirst($this->value),
};
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Laravel\AgentDetector;
function detectAgent(): AgentResult
{
return AgentDetector::detect();
}