Skip to content

Commit 7b4b686

Browse files
committed
feat: Add Context7 docs source integration
- Add DocsSource class for retrieving content from Context7 - Implement DocsSourceFetcher for API communication - Update JSON schema for validation
1 parent 9d06d01 commit 7b4b686

File tree

6 files changed

+365
-1
lines changed

6 files changed

+365
-1
lines changed

json-schema.json

+57-1
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@
685685
"git_diff",
686686
"tree",
687687
"mcp",
688-
"composer"
688+
"composer",
689+
"docs"
689690
],
690691
"description": "Type of content source"
691692
},
@@ -812,6 +813,18 @@
812813
"then": {
813814
"$ref": "#/definitions/treeSource"
814815
}
816+
},
817+
{
818+
"if": {
819+
"properties": {
820+
"type": {
821+
"const": "docs"
822+
}
823+
}
824+
},
825+
"then": {
826+
"$ref": "#/definitions/docsSource"
827+
}
815828
}
816829
]
817830
},
@@ -1345,6 +1358,49 @@
13451358
],
13461359
"description": "Configuration for rendering diffs"
13471360
},
1361+
"docsSource": {
1362+
"type": "object",
1363+
"description": "Source for documentation content",
1364+
"required": [
1365+
"type",
1366+
"library",
1367+
"topic"
1368+
],
1369+
"properties": {
1370+
"type": {
1371+
"type": "string",
1372+
"enum": [
1373+
"docs"
1374+
],
1375+
"description": "Source type - docs"
1376+
},
1377+
"library": {
1378+
"type": "string",
1379+
"description": "Library identifier (e.g., \"laravel/docs\")"
1380+
},
1381+
"topic": {
1382+
"type": "string",
1383+
"description": "Topic to search for within the library"
1384+
},
1385+
"tokens": {
1386+
"type": "integer",
1387+
"default": 500,
1388+
"description": "Maximum token count to retrieve"
1389+
},
1390+
"description": {
1391+
"type": "string",
1392+
"description": "Human-readable description of the source"
1393+
},
1394+
"tags": {
1395+
"type": "array",
1396+
"items": {
1397+
"type": "string"
1398+
},
1399+
"description": "List of tags for this source"
1400+
}
1401+
},
1402+
"additionalProperties": false
1403+
},
13481404
"treeSource": {
13491405
"description": "Generates a hierarchical visualization of directory structures",
13501406
"properties": {

src/Application/Kernel.php

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Butschster\ContextGenerator\Modifier\PhpSignature\PhpSignatureModifierBootloader;
2424
use Butschster\ContextGenerator\Modifier\Sanitizer\SanitizerModifierBootloader;
2525
use Butschster\ContextGenerator\Source\Composer\ComposerSourceBootloader;
26+
use Butschster\ContextGenerator\Source\Docs\DocsSourceBootloader;
2627
use Butschster\ContextGenerator\Source\File\FileSourceBootloader;
2728
use Butschster\ContextGenerator\Source\GitDiff\GitDiffSourceBootloader;
2829
use Butschster\ContextGenerator\Source\Github\GithubSourceBootloader;
@@ -73,6 +74,7 @@ protected function defineBootloaders(): array
7374
GitDiffSourceBootloader::class,
7475
TreeSourceBootloader::class,
7576
McpSourceBootloader::class,
77+
DocsSourceBootloader::class,
7678

7779
// Modifiers
7880
PhpContentFilterBootloader::class,

src/Source/Docs/DocsSource.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\Source\Docs;
6+
7+
use Butschster\ContextGenerator\Source\BaseSource;
8+
9+
/**
10+
* Source for retrieving documentation from Context7 service
11+
*/
12+
final class DocsSource extends BaseSource
13+
{
14+
/**
15+
* @param string $library Library identifier (e.g., "laravel/docs")
16+
* @param string $topic Topic to search for within the library
17+
* @param string $description Human-readable description
18+
* @param int $tokens Maximum token count to retrieve
19+
* @param array<non-empty-string> $tags
20+
*/
21+
public function __construct(
22+
public readonly string $library,
23+
public readonly string $topic,
24+
string $description = '',
25+
public readonly int $tokens = 2000,
26+
array $tags = [],
27+
) {
28+
parent::__construct(description: $description, tags: $tags);
29+
}
30+
31+
#[\Override]
32+
public function jsonSerialize(): array
33+
{
34+
return \array_filter([
35+
'type' => 'docs',
36+
...parent::jsonSerialize(),
37+
'library' => $this->library,
38+
'topic' => $this->topic,
39+
'tokens' => $this->tokens,
40+
], static fn($value) => $value !== null && $value !== '' && $value !== []);
41+
}
42+
}
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\Source\Docs;
6+
7+
use Butschster\ContextGenerator\Application\Bootloader\HttpClientBootloader;
8+
use Butschster\ContextGenerator\Application\Bootloader\SourceFetcherBootloader;
9+
use Butschster\ContextGenerator\Application\Logger\HasPrefixLoggerInterface;
10+
use Butschster\ContextGenerator\Lib\Content\ContentBuilderFactory;
11+
use Butschster\ContextGenerator\Lib\HttpClient\HttpClientInterface;
12+
use Butschster\ContextGenerator\Lib\Variable\VariableResolver;
13+
use Butschster\ContextGenerator\Source\Registry\SourceRegistryInterface;
14+
use Spiral\Boot\Bootloader\Bootloader;
15+
use Spiral\Core\FactoryInterface;
16+
17+
final class DocsSourceBootloader extends Bootloader
18+
{
19+
#[\Override]
20+
public function defineDependencies(): array
21+
{
22+
return [HttpClientBootloader::class];
23+
}
24+
25+
#[\Override]
26+
public function defineSingletons(): array
27+
{
28+
return [
29+
DocsSourceFetcher::class => static fn(
30+
FactoryInterface $factory,
31+
ContentBuilderFactory $builderFactory,
32+
HttpClientInterface $httpClient,
33+
VariableResolver $variables,
34+
HasPrefixLoggerInterface $logger,
35+
): DocsSourceFetcher => $factory->make(DocsSourceFetcher::class, [
36+
'defaultHeaders' => [
37+
'User-Agent' => 'CTX Bot',
38+
'Accept' => 'text/plain',
39+
'Accept-Language' => 'en-US,en;q=0.9',
40+
],
41+
]),
42+
];
43+
}
44+
45+
public function init(
46+
SourceFetcherBootloader $registry,
47+
SourceRegistryInterface $sourceRegistry,
48+
DocsSourceFactory $factory,
49+
): void {
50+
$registry->register(DocsSourceFetcher::class);
51+
$sourceRegistry->register($factory);
52+
}
53+
}

src/Source/Docs/DocsSourceFactory.php

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\Source\Docs;
6+
7+
use Butschster\ContextGenerator\Source\Registry\AbstractSourceFactory;
8+
9+
/**
10+
* Factory for creating DocsSource instances
11+
*/
12+
final readonly class DocsSourceFactory extends AbstractSourceFactory
13+
{
14+
#[\Override]
15+
public function getType(): string
16+
{
17+
return 'docs';
18+
}
19+
20+
#[\Override]
21+
public function create(array $config): DocsSource
22+
{
23+
$this->logger?->debug('Creating Docs source', [
24+
'path' => $this->dirs->getRootPath(),
25+
'config' => $config,
26+
]);
27+
28+
if (!isset($config['library']) || !\is_string($config['library'])) {
29+
throw new \RuntimeException('Docs source must have a "library" string property');
30+
}
31+
32+
if (!isset($config['topic']) || !\is_string($config['topic'])) {
33+
throw new \RuntimeException('Docs source must have a "topic" string property');
34+
}
35+
36+
$tokens = 2000;
37+
if (isset($config['tokens']) && (\is_int($config['tokens']) || \is_string($config['tokens']))) {
38+
$tokens = (int) $config['tokens'];
39+
}
40+
41+
return new DocsSource(
42+
library: $config['library'],
43+
topic: $config['topic'],
44+
description: $config['description'] ?? '',
45+
tokens: $tokens,
46+
tags: $config['tags'] ?? [],
47+
);
48+
}
49+
}

0 commit comments

Comments
 (0)