agent-context 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +6 -1
- data/bake/agent/context.rb +18 -43
- data/context/getting-started.md +48 -12
- data/context/index.yaml +5 -3
- data/context/usage.md +73 -0
- data/design.md +78 -0
- data/lib/agent/context/definition.rb +58 -0
- data/lib/agent/context/document.rb +88 -0
- data/lib/agent/context/errors.rb +20 -0
- data/lib/agent/context/exclusion.rb +90 -0
- data/lib/agent/context/installer.rb +76 -210
- data/lib/agent/context/package.rb +116 -0
- data/lib/agent/context/paths.rb +12 -0
- data/lib/agent/context/skills/definition.rb +146 -0
- data/lib/agent/context/skills/ownership.rb +69 -0
- data/lib/agent/context/skills.rb +15 -0
- data/lib/agent/context/version.rb +1 -1
- data/lib/agent/context.rb +3 -2
- data/license.md +2 -2
- data/post.md +5 -3
- data/readme.md +71 -32
- data/releases.md +9 -0
- data/specification.md +97 -18
- data.tar.gz.sig +4 -3
- metadata +53 -31
- metadata.gz.sig +0 -0
- data/lib/agent/context/index.rb +0 -325
data/specification.md
CHANGED
|
@@ -15,7 +15,7 @@ AI agents and automated tools working with software projects often lack access t
|
|
|
15
15
|
- **Standardization**: Define a consistent structure for contextual information across different programming languages and ecosystems.
|
|
16
16
|
- **Discoverability**: Enable automated tools to programmatically find and access contextual information.
|
|
17
17
|
- **Separation of Concerns**: Clearly separate provider context from consumer context.
|
|
18
|
-
- **Extensibility**:
|
|
18
|
+
- **Extensibility**: Preserve additional provider-authored metadata alongside the fields defined by this specification.
|
|
19
19
|
|
|
20
20
|
## 2. Core Concepts
|
|
21
21
|
|
|
@@ -32,9 +32,10 @@ A **Context Provider** is any software package, library, or module that includes
|
|
|
32
32
|
|
|
33
33
|
A **Context Consumer** is any project or tool that utilizes contextual information from its dependencies. Context consumers:
|
|
34
34
|
|
|
35
|
-
- Install context from their dependencies into a `.context/` directory.
|
|
35
|
+
- Install context from their dependencies into a `.agents/context/` directory.
|
|
36
|
+
- May install skill-marked context documents into a `.agents/skills/` directory.
|
|
36
37
|
- Use tools to discover and access available context.
|
|
37
|
-
-
|
|
38
|
+
- Read and apply guidance relevant to the task.
|
|
38
39
|
|
|
39
40
|
### 2.3 Context Files
|
|
40
41
|
|
|
@@ -47,6 +48,10 @@ A **Context Consumer** is any project or tool that utilizes contextual informati
|
|
|
47
48
|
- Security considerations.
|
|
48
49
|
- Troubleshooting guides.
|
|
49
50
|
|
|
51
|
+
A **Definition** is one installable unit loaded from a provider: an ordinary file, or a skill document together with its companion resources. A parsed **Document** represents the Markdown source and front matter of a definition. Files such as images and examples remain opaque.
|
|
52
|
+
|
|
53
|
+
A definition's **path** is its filename relative to the provider's `context/` directory, including any subdirectories and extension. Its **source path** locates the authored file in the installed package. A skill's **installed name** is its authored or derived `name`, used for its destination directory. These identifiers serve different purposes.
|
|
54
|
+
|
|
50
55
|
### 2.4 Context Installation
|
|
51
56
|
|
|
52
57
|
**Context Installation** is the process of copying context files from dependencies into a consumer's local context directory, making them available for use by tools and AI agents.
|
|
@@ -63,6 +68,7 @@ Context providers MUST include a `context/` directory in their package root. Thi
|
|
|
63
68
|
- **Versioning**: Must be versioned alongside the package code.
|
|
64
69
|
|
|
65
70
|
Example structure:
|
|
71
|
+
|
|
66
72
|
```
|
|
67
73
|
package-root/
|
|
68
74
|
├── context/
|
|
@@ -75,20 +81,23 @@ package-root/
|
|
|
75
81
|
└── package.json
|
|
76
82
|
```
|
|
77
83
|
|
|
78
|
-
### 3.2 Consumer Directory: `.context/`
|
|
84
|
+
### 3.2 Consumer Directory: `.agents/context/`
|
|
79
85
|
|
|
80
|
-
Context consumers SHOULD create a `.context/` directory in their project root to store installed context. This directory:
|
|
86
|
+
Context consumers SHOULD create a `.agents/context/` directory in their project root to store installed context. This directory:
|
|
81
87
|
|
|
82
88
|
- **Location**: Must be at the project root (typically where package manifests are located).
|
|
83
89
|
- **Purpose**: Contains context files copied from dependencies.
|
|
84
|
-
- **Organization**: Must organize context by
|
|
85
|
-
- **Exclusion**: SHOULD be excluded from version control
|
|
90
|
+
- **Organization**: Must organize context by resolved provider in subdirectories named by a package selector.
|
|
91
|
+
- **Exclusion**: The generated `.agents/context/` directory SHOULD be excluded from version control. A Git integration SHOULD add local exclusions to `.git/info/exclude` so projects do not need to commit rules for generated files. Other project-owned files may live directly under `.agents/`.
|
|
86
92
|
- **Transient Nature**: Should contain only reproducible content that can be regenerated from installed packages and MUST NOT contain unique or modified files.
|
|
87
93
|
|
|
94
|
+
A package selector identifies one resolved provider. It is normally the package name. Consumers that support multiple resolved versions MUST distinguish them, for example with `package-a@1.0.0` and `package-a@2.0.0`. Tools SHOULD display accepted selectors when listing providers and use those selectors consistently in commands and installed directory names.
|
|
95
|
+
|
|
88
96
|
Example structure:
|
|
97
|
+
|
|
89
98
|
```
|
|
90
99
|
project-root/
|
|
91
|
-
├── .context/
|
|
100
|
+
├── .agents/context/
|
|
92
101
|
│ ├── package-a/
|
|
93
102
|
│ │ ├── getting-started.md
|
|
94
103
|
│ │ └── configuration.md
|
|
@@ -100,9 +109,9 @@ project-root/
|
|
|
100
109
|
|
|
101
110
|
### 3.3 Directory Separation Rationale
|
|
102
111
|
|
|
103
|
-
The separation between `context/` and `.context/` serves several purposes:
|
|
112
|
+
The separation between `context/` and `.agents/context/` serves several purposes:
|
|
104
113
|
|
|
105
|
-
- **Ownership**: `context/` is controlled by the project itself, `.context/` contains external dependencies.
|
|
114
|
+
- **Ownership**: `context/` is controlled by the project itself, `.agents/context/` contains external dependencies.
|
|
106
115
|
- **Isolation**: Prevents conflicts between different packages' context files.
|
|
107
116
|
- **Discoverability**: Makes it easy to find context for specific packages.
|
|
108
117
|
- **Maintenance**: Allows independent management of provided vs. consumed context.
|
|
@@ -135,6 +144,7 @@ Context files SHOULD follow these naming conventions:
|
|
|
135
144
|
- Group related files in subdirectories when appropriate.
|
|
136
145
|
|
|
137
146
|
Common file names:
|
|
147
|
+
|
|
138
148
|
- `getting-started.md`
|
|
139
149
|
- `configuration.md`
|
|
140
150
|
- `troubleshooting.md`
|
|
@@ -142,6 +152,29 @@ Common file names:
|
|
|
142
152
|
- `security.md`
|
|
143
153
|
- `migration-guide.md`
|
|
144
154
|
|
|
155
|
+
### 4.4 Skill Documents
|
|
156
|
+
|
|
157
|
+
A context provider may distribute an Agent Skill as a Markdown document directly inside `context/`. The document MUST use YAML front matter with `metadata.type: skill` and a non-empty top-level `description`:
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
---
|
|
161
|
+
name: provider-workflow
|
|
162
|
+
description: Run the provider workflow when preparing a project.
|
|
163
|
+
metadata:
|
|
164
|
+
type: skill
|
|
165
|
+
---
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Within a context document's YAML `metadata` mapping, the string `skill` declares a skill. Consumers MUST treat all other `metadata.type` values, including absent, null, and unfamiliar values, as ordinary context. This allows providers to introduce new types while existing consumers continue installing their documents. Consumers MUST interpret only mappings as context metadata; other values of `metadata` are unrelated authored data and MUST be retained as ordinary context.
|
|
169
|
+
|
|
170
|
+
An authored top-level `name` MUST be preserved and used as the installed directory name. When `name` is absent, consumers MUST derive it from the lowercase provider package name (normalizing underscores to hyphens), a hyphen, and the source filename without its `.md` extension. Installed names MUST use lowercase ASCII letters, digits, and single hyphens, contain 1–64 characters, and begin and end with a letter or digit. Descriptions MUST contain 1–1,024 characters after trimming whitespace for validation; consumers MUST retain the authored description value.
|
|
171
|
+
|
|
172
|
+
Consumers MUST install the document as `.agents/skills/<name>/SKILL.md`. A companion directory named after the source filename without its extension MAY provide resources, independently of the installed name. Its files MUST be copied into the installed skill directory. Consumers MUST retain additional top-level fields and nested metadata, except for the resolved installation provenance defined in section 7. Skill documents and their resources belong exclusively to the installed skill.
|
|
173
|
+
|
|
174
|
+
Consumers MUST preserve the original Markdown body bytes following the front matter. Front matter formatting MAY be normalized during serialization.
|
|
175
|
+
|
|
176
|
+
Dependency-installed skill directories SHOULD be excluded from version control, while project-owned skills in `.agents/skills/` SHOULD remain trackable. Git integrations SHOULD add exact dependency-installed skill paths to `.git/info/exclude` rather than ignore the entire `.agents/skills/` directory. They SHOULD mark generated entries with comments and preserve user-authored rules outside that marked section.
|
|
177
|
+
|
|
145
178
|
## 5. Discovery and Installation
|
|
146
179
|
|
|
147
180
|
### 5.1 Discovery Process
|
|
@@ -163,14 +196,60 @@ The discovery process SHOULD integrate with the target language's package manage
|
|
|
163
196
|
Context installation MUST follow these principles:
|
|
164
197
|
|
|
165
198
|
1. **Copy Strategy**: Context files SHOULD be copied rather than symlinked.
|
|
166
|
-
2. **Namespace Isolation**: Each
|
|
167
|
-
3. **Preserve Structure**: The internal structure of
|
|
199
|
+
2. **Namespace Isolation**: Each resolved provider's ordinary context MUST be installed in its own subdirectory, named by its package selector.
|
|
200
|
+
3. **Preserve Structure**: The internal structure of ordinary context files MUST be preserved.
|
|
201
|
+
4. **Skill Separation**: Skill documents and their resources MUST be installed as skills only, not duplicated as ordinary context.
|
|
168
202
|
|
|
169
|
-
|
|
203
|
+
Consumers SHOULD validate document data as part of loading or parsing it and use that validated representation when applying changes. Installation MAY use direct file writes and copies. Consumers MUST report failures; completed changes and partially written output MAY remain after an error. After resolving the reported failure, consumers SHOULD support refreshing generated output by rerunning installation.
|
|
170
204
|
|
|
205
|
+
### 5.3 Installation Steps
|
|
206
|
+
|
|
207
|
+
For each selected provider:
|
|
208
|
+
|
|
209
|
+
1. Classify its context documents and identify companion skill resource directories, keeping their contents opaque.
|
|
210
|
+
2. If skills are supported, install each declared skill once as `.agents/skills/<name>/SKILL.md` and copy its companion resources into that skill directory.
|
|
211
|
+
3. Copy the remaining ordinary files into `.agents/context/<selector>/`, preserving their relative paths.
|
|
212
|
+
|
|
213
|
+
Skill documents and their resources are excluded from ordinary context copying even when the consumer does not support skill installation.
|
|
214
|
+
|
|
215
|
+
## 6. Discovering Installed Context
|
|
216
|
+
|
|
217
|
+
Consumers SHOULD provide commands to list providers, list their definitions, and show a selected definition. Agents discover installed guidance by enumerating Markdown files under `.agents/context/<selector>/` and reading the files relevant to their task. A packaged usage skill SHOULD explain this workflow and how to install missing guidance.
|
|
218
|
+
|
|
219
|
+
Providers SHOULD use descriptive filenames, keep guides focused on one topic, and link related documents. Ordinary documents need no front matter. Unrelated ordinary-document metadata MUST be tolerated. Skill resource files are opaque assets and MUST NOT be parsed as context documents.
|
|
220
|
+
|
|
221
|
+
Provider lookup identifies dependencies with a `context/` directory, including empty directories. An empty provider has no definitions; listing commands MAY omit it from the available guidance list. Refreshing an empty provider still reconciles its installed output.
|
|
222
|
+
|
|
223
|
+
Showing a definition MUST return its authored content. Lookup MUST prefer an exact provider-relative path, then that path with `.md` appended when no extension was supplied. Consumers MAY also accept installed skill names as a fallback after source path lookup. Definition paths retain their filename case and extension. Generated skill instructions add installation provenance when installed.
|
|
224
|
+
|
|
225
|
+
The installed directory layout is the discovery interface.
|
|
226
|
+
|
|
227
|
+
## 7. Skill Installation Ownership
|
|
228
|
+
|
|
229
|
+
Consumers MUST record resolved installation provenance inside `SKILL.md` front matter:
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
---
|
|
233
|
+
name: provider-workflow
|
|
234
|
+
description: Run the provider workflow when preparing a project.
|
|
235
|
+
metadata:
|
|
236
|
+
type: skill
|
|
237
|
+
ecosystem: cargo
|
|
238
|
+
package: provider
|
|
239
|
+
version: "1.0.0"
|
|
240
|
+
---
|
|
171
241
|
```
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
242
|
+
|
|
243
|
+
The installer supplies `metadata.ecosystem`, `metadata.package`, and `metadata.version` from its resolved provider, overriding source values for these fields. Ruby gems use `gem`; Cargo crates use `cargo`. These three fields MUST be non-empty strings, including a quoted version when YAML would otherwise decode it as a number. Additional metadata MUST be retained.
|
|
244
|
+
|
|
245
|
+
Consumers MUST discover ownership by scanning direct child directories of `.agents/skills/` and parsing their `SKILL.md` files. The skills root and managed skill directories MUST be regular directories. Symbolic links and non-directory children are skipped. Instruction files MUST be regular files. Managed directory names MUST satisfy section 4.4.
|
|
246
|
+
|
|
247
|
+
`metadata.type: skill` together with an explicit `metadata.ecosystem` identifies installation provenance. Consumers MUST validate the complete provenance tuple after recognizing that discriminator, and report malformed or unreadable metadata. Other directories are project-owned; an authored `metadata.version` alone is ordinary skill metadata.
|
|
248
|
+
|
|
249
|
+
A full refresh reconciles the installer's ecosystem; a package refresh reconciles that ecosystem and package. Full reconciliation includes providers removed from the dependency graph and providers that become empty. Consumers MUST retain skills belonging to other ecosystems and packages outside the requested scope, and MUST reject conflicting destinations.
|
|
250
|
+
|
|
251
|
+
Installers SHOULD write `SKILL.md` before copying companion resources, establishing ownership for subsequent refreshes. If writing the instructions fails, installers SHOULD remove that incomplete destination. Resource-copy failures leave completed instructions in place. The incremental failure behavior in section 5.2 applies to skills and Git exclusion updates.
|
|
252
|
+
|
|
253
|
+
The top-level resource name `SKILL.md` is reserved case-insensitively for generated instructions. Consumers MUST reject a resource file or directory with that name. Nested files with that name and other resource names are copied as opaque assets.
|
|
254
|
+
|
|
255
|
+
Git integrations SHOULD maintain one marked exclusion block per consuming project, covering generated context and exact dependency-owned skill directories from all ecosystems. Exclusion paths MUST be relative to the Git repository root. Nested projects SHOULD identify their block by appending the repository-relative project path, with a trailing slash, to the `# BEGIN bake-agent-context` and `# END bake-agent-context` markers. They MUST retain user-authored rules and blocks belonging to other projects. Installed instructions and resources are excluded with their containing skill directory.
|
data.tar.gz.sig
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
�
|
|
3
|
-
|
|
1
|
+
g���
|
|
2
|
+
p�˺Xc�%m�~�8�
|
|
3
|
+
��(��p��`�B���ƫ���N�����:���,fdd�sD]�iL�r�S�'��1eT���<��LB�Ww�����Z�+��A�A7�##k4�7�x*���\�� ����k�&�x����`:��P��6����!Q�#^!
|
|
4
|
+
�5i6��"{���_r�v"����BN�"X���%��H�΅�S�*���Z���uG��-{����� Ń�����NrV����ف>Ʀ�=����=�ʾJtr+��MםV=��2q�,��"���?�t��ݖ�X��>�R\o�I����|(��a���*ےN�45S��O͋sX��./$�N�
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agent-context
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -10,32 +10,28 @@ bindir: bin
|
|
|
10
10
|
cert_chain:
|
|
11
11
|
- |
|
|
12
12
|
-----BEGIN CERTIFICATE-----
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
|
36
|
-
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
|
37
|
-
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
|
38
|
-
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
|
13
|
+
MIIEFDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMREwDwYDVQQKDAhTb2Nr
|
|
14
|
+
ZXRyeTEbMBkGA1UEAwwSU29ja2V0cnkgUnVieSBHZW1zMB4XDTI2MDkyMTA3NTgx
|
|
15
|
+
NFoXDTI3MDkyMTA3NTgxNFowMDERMA8GA1UECgwIU29ja2V0cnkxGzAZBgNVBAMM
|
|
16
|
+
ElNvY2tldHJ5IFJ1YnkgR2VtczCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoC
|
|
17
|
+
ggGBAM/QgjVgDzDo/xJEQoFvAzcVFP7msnswkhPJB2UDsbxXCZmers5jV512TM5s
|
|
18
|
+
NwLHfzJiC4DcI5ax9ZYKM9Q+dS21YYagNqjtg3YPyDqR6phoibEA0VoMuInUQW68
|
|
19
|
+
i5OkCJzYKGD2pYYVZnuFNqyM2ECUvXh/fBmvPoHbncAHhWPaCBH8mQJ8sRNc6+RV
|
|
20
|
+
SoZZu1Yo/aW1zQ+SpZwad7s1ZmigfDNHKhJ22KwZHk+/5Zw1QXVfcajjswV0Mm2P
|
|
21
|
+
irgjKN3fyNVkwlly63EBlR4VydT+g9QtJtqh7ee0ThhI+v4wYovf4dbLNcEBTHu1
|
|
22
|
+
x1pGbXGx/2fBAST5eVwaXIx1V8VU22AJcBk1H2o5/1F0HfbD2HQjmFOA112KUwBW
|
|
23
|
+
9TtspZD3g6rCQFX53XvL9h6j2y1ukl7s5AdEzOe/x6r1MO8tSDtgcYoF89wgF/88
|
|
24
|
+
Q/Eski0FckQ+znfESZcFcpzs2sPDoYW6SNVX4tZcNty85Y6WBHfHKio14x63PeyC
|
|
25
|
+
CG1gMwIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU
|
|
26
|
+
SuMc0x24Xshtaa8SQVkBr2Z5WzIwDQYJKoZIhvcNAQELBQADggGBABI62ey5lDWm
|
|
27
|
+
j8jsTQaNBMBb7LbKS3XkBwyL1UNGEwlicp399rJDeuNgVSHqBCZ/nE4yleNjgW9Y
|
|
28
|
+
N6DKdxXCvXmFUoHvUINeAvThHxmlEGhSfXl1x55xHEig0rEP4jgdjnYdQI4fqeOM
|
|
29
|
+
zIDN0F3ruArke4Y62SQgWVN7vspdAA41hBIRl1vsvs0KWG8DIVQ8cmR+TG6zjOmK
|
|
30
|
+
iUiUZGNPnStV1O1xW83c+Ba4Am0krP6/forxDPhRvTehqkVYvMdPfYcICNb5IM58
|
|
31
|
+
5vaYvuj5AtfZuZWN0F/0KtSphhNC17rh6h8QmuYdMJ1clvlrkiEaO1UjPVwjgtxC
|
|
32
|
+
x/ARh5X6q0/YTXl4r7EfSJEeqW7qhgu9EUj3mGIPHYdX0Dqih/NgK20F3GvQtTfB
|
|
33
|
+
7sse0duKBHXONYVwKAsceUVvqtCfyF608bCilCkbhw3QaInRj+BkRQKJ5Tuj2PZR
|
|
34
|
+
76a/hwaV2wWn5RtAFBaUQzQdo/xTqJ7IkwaFjOZe/QyvyoRjCK9Rdg==
|
|
39
35
|
-----END CERTIFICATE-----
|
|
40
36
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
41
37
|
dependencies:
|
|
@@ -53,6 +49,20 @@ dependencies:
|
|
|
53
49
|
- - ">="
|
|
54
50
|
- !ruby/object:Gem::Version
|
|
55
51
|
version: '0.23'
|
|
52
|
+
- !ruby/object:Gem::Dependency
|
|
53
|
+
name: markly
|
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - "~>"
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0.16'
|
|
59
|
+
type: :runtime
|
|
60
|
+
prerelease: false
|
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - "~>"
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0.16'
|
|
56
66
|
executables: []
|
|
57
67
|
extensions: []
|
|
58
68
|
extra_rdoc_files: []
|
|
@@ -60,9 +70,19 @@ files:
|
|
|
60
70
|
- bake/agent/context.rb
|
|
61
71
|
- context/getting-started.md
|
|
62
72
|
- context/index.yaml
|
|
73
|
+
- context/usage.md
|
|
74
|
+
- design.md
|
|
63
75
|
- lib/agent/context.rb
|
|
64
|
-
- lib/agent/context/
|
|
76
|
+
- lib/agent/context/definition.rb
|
|
77
|
+
- lib/agent/context/document.rb
|
|
78
|
+
- lib/agent/context/errors.rb
|
|
79
|
+
- lib/agent/context/exclusion.rb
|
|
65
80
|
- lib/agent/context/installer.rb
|
|
81
|
+
- lib/agent/context/package.rb
|
|
82
|
+
- lib/agent/context/paths.rb
|
|
83
|
+
- lib/agent/context/skills.rb
|
|
84
|
+
- lib/agent/context/skills/definition.rb
|
|
85
|
+
- lib/agent/context/skills/ownership.rb
|
|
66
86
|
- lib/agent/context/version.rb
|
|
67
87
|
- license.md
|
|
68
88
|
- post.md
|
|
@@ -73,6 +93,8 @@ homepage: https://github.com/ioquatix/agent-context
|
|
|
73
93
|
licenses:
|
|
74
94
|
- MIT
|
|
75
95
|
metadata:
|
|
96
|
+
bug_tracker_uri: https://github.com/ioquatix/agent-context/issues
|
|
97
|
+
changelog_uri: https://github.com/ioquatix/agent-context/blob/main/releases.md
|
|
76
98
|
documentation_uri: https://ioquatix.github.io/agent-context/
|
|
77
99
|
funding_uri: https://github.com/sponsors/ioquatix/
|
|
78
100
|
source_code_uri: https://github.com/ioquatix/agent-context.git
|
|
@@ -83,14 +105,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
83
105
|
requirements:
|
|
84
106
|
- - ">="
|
|
85
107
|
- !ruby/object:Gem::Version
|
|
86
|
-
version: '3.
|
|
108
|
+
version: '3.3'
|
|
87
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
110
|
requirements:
|
|
89
111
|
- - ">="
|
|
90
112
|
- !ruby/object:Gem::Version
|
|
91
113
|
version: '0'
|
|
92
114
|
requirements: []
|
|
93
|
-
rubygems_version:
|
|
115
|
+
rubygems_version: 4.0.21
|
|
94
116
|
specification_version: 4
|
|
95
|
-
summary: Install and manage context files from Ruby gems.
|
|
117
|
+
summary: Install and manage context files and skills from Ruby gems.
|
|
96
118
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|
data/lib/agent/context/index.rb
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2025, by Samuel Williams.
|
|
5
|
-
# Copyright, 2025, by Shopify Inc.
|
|
6
|
-
|
|
7
|
-
require_relative "version"
|
|
8
|
-
require "fileutils"
|
|
9
|
-
require "yaml"
|
|
10
|
-
|
|
11
|
-
# @namespace
|
|
12
|
-
module Agent
|
|
13
|
-
# @namespace
|
|
14
|
-
module Context
|
|
15
|
-
# Represents an index for managing and generating agents.md files from context files.
|
|
16
|
-
#
|
|
17
|
-
# This class provides functionality to update or create AGENTS.md files following
|
|
18
|
-
# the AGENTS.md specification for agentic coding tools. It can parse existing
|
|
19
|
-
# agents.md files, update the context section, and generate new files when needed.
|
|
20
|
-
class Index
|
|
21
|
-
# Initialize a new index instance.
|
|
22
|
-
# @parameter context_path [String] The path to the context directory (default: ".context").
|
|
23
|
-
def initialize(context_path = ".context")
|
|
24
|
-
@context_path = context_path
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
attr :context_path
|
|
28
|
-
|
|
29
|
-
# Update or create an AGENTS.md file in the project root with context section
|
|
30
|
-
# This follows the AGENTS.md specification for agentic coding tools
|
|
31
|
-
def update_agents_md(agents_md_path = "agents.md")
|
|
32
|
-
context_content = generate_context_section
|
|
33
|
-
|
|
34
|
-
if File.exist?(agents_md_path)
|
|
35
|
-
update_existing_agents_md(agents_md_path, context_content)
|
|
36
|
-
else
|
|
37
|
-
create_new_agents_md(agents_md_path, context_content)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
Console.debug("Updated agents.md: #{agents_md_path}")
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Generate just the context section content (without top-level headers)
|
|
44
|
-
def generate_context_section
|
|
45
|
-
sections = []
|
|
46
|
-
|
|
47
|
-
sections << "This section provides links to documentation from installed packages. It is automatically generated and may be updated by running `bake agent:context:install`."
|
|
48
|
-
sections << ""
|
|
49
|
-
sections << "**Important:** Before performing any code, documentation, or analysis tasks, always read and apply the full content of any relevant documentation referenced in the following sections. These context files contain authoritative standards and best practices for documentation, code style, and project-specific workflows. **Do not proceed with any actions until you have read and incorporated the guidance from relevant context files.**"
|
|
50
|
-
sections << ""
|
|
51
|
-
sections << "**Setup Instructions:** If the referenced files are not present or if dependencies have been updated, run `bake agent:context:install` to install the latest context files."
|
|
52
|
-
sections << ""
|
|
53
|
-
|
|
54
|
-
gem_contexts = collect_gem_contexts
|
|
55
|
-
|
|
56
|
-
if gem_contexts.empty?
|
|
57
|
-
sections << "No context files found. Run `bake agent:context:install` to install context from gems."
|
|
58
|
-
sections << ""
|
|
59
|
-
else
|
|
60
|
-
gem_contexts.each do |gem_name, files|
|
|
61
|
-
sections << "### #{gem_name}"
|
|
62
|
-
sections << ""
|
|
63
|
-
|
|
64
|
-
# Get gem directory and load index
|
|
65
|
-
gem_directory = File.join(@context_path, gem_name)
|
|
66
|
-
index = load_gem_index(gem_name, gem_directory)
|
|
67
|
-
|
|
68
|
-
# Add gem description from index
|
|
69
|
-
if index["description"]
|
|
70
|
-
sections << index["description"]
|
|
71
|
-
sections << ""
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Use files from index if available, otherwise fall back to parsing
|
|
75
|
-
if index["files"] && !index["files"].empty?
|
|
76
|
-
index["files"].each do |file_info|
|
|
77
|
-
sections << "#### [#{file_info['title']}](.context/#{gem_name}/#{file_info['path']})"
|
|
78
|
-
sections << ""
|
|
79
|
-
sections << file_info["description"] if file_info["description"] && !file_info["description"].empty?
|
|
80
|
-
sections << ""
|
|
81
|
-
end
|
|
82
|
-
else
|
|
83
|
-
# Fallback to parsing files directly
|
|
84
|
-
files.each do |file_path|
|
|
85
|
-
if File.exist?(file_path)
|
|
86
|
-
title, description = extract_content(file_path)
|
|
87
|
-
relative_path = file_path.sub("#{@context_path}/", "")
|
|
88
|
-
sections << "#### [#{title}](.context/#{relative_path})"
|
|
89
|
-
sections << ""
|
|
90
|
-
sections << description if description && !description.empty?
|
|
91
|
-
sections << ""
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
sections.join("\n")
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
private
|
|
102
|
-
|
|
103
|
-
def update_existing_agents_md(agents_md_path, context_content)
|
|
104
|
-
content = File.read(agents_md_path)
|
|
105
|
-
|
|
106
|
-
# Find the # Agent heading
|
|
107
|
-
agent_heading_line = find_agent_heading_line(content)
|
|
108
|
-
|
|
109
|
-
if agent_heading_line
|
|
110
|
-
# Find or create the ## Context section
|
|
111
|
-
context_section = find_context_section_under_agent(content, agent_heading_line)
|
|
112
|
-
|
|
113
|
-
if context_section
|
|
114
|
-
# Replace existing context section
|
|
115
|
-
updated_content = replace_context_section(content, context_section, context_content)
|
|
116
|
-
else
|
|
117
|
-
# Insert new context section after agent heading
|
|
118
|
-
updated_content = insert_context_section_after_agent(content, agent_heading_line, context_content)
|
|
119
|
-
end
|
|
120
|
-
else
|
|
121
|
-
# No # Agent heading found, prepend it with context
|
|
122
|
-
updated_content = prepend_agent_with_context(content, context_content)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Write the updated content back to file
|
|
126
|
-
File.write(agents_md_path, updated_content)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def create_new_agents_md(agents_md_path, context_content)
|
|
130
|
-
content = [
|
|
131
|
-
"# Agent",
|
|
132
|
-
"",
|
|
133
|
-
"## Context",
|
|
134
|
-
"",
|
|
135
|
-
context_content,
|
|
136
|
-
].join("\n")
|
|
137
|
-
File.write(agents_md_path, content)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def find_agent_heading_line(content)
|
|
141
|
-
lines = content.lines
|
|
142
|
-
lines.each_with_index do |line, index|
|
|
143
|
-
if line.strip.start_with?("# ") && line.strip.downcase == "# agent"
|
|
144
|
-
return index
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
nil
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def find_context_section_under_agent(content, agent_line_index)
|
|
151
|
-
lines = content.lines
|
|
152
|
-
|
|
153
|
-
# Look for ## Context after the agent heading
|
|
154
|
-
(agent_line_index + 1).upto(lines.length - 1) do |index|
|
|
155
|
-
line = lines[index]
|
|
156
|
-
if line.strip == "## Context"
|
|
157
|
-
return index
|
|
158
|
-
elsif line.strip.start_with?("# ") && line.strip != "# Agent"
|
|
159
|
-
# We've hit another top-level heading, stop searching
|
|
160
|
-
break
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
nil
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def replace_context_section(content, context_line_index, context_content)
|
|
168
|
-
lines = content.lines
|
|
169
|
-
|
|
170
|
-
# Find the end of the context section
|
|
171
|
-
end_index = find_section_end(lines, context_line_index, 2)
|
|
172
|
-
|
|
173
|
-
# Build the new content
|
|
174
|
-
new_lines = []
|
|
175
|
-
new_lines.concat(lines[0...context_line_index])
|
|
176
|
-
new_lines << "## Context\n"
|
|
177
|
-
new_lines << "\n"
|
|
178
|
-
new_lines.concat(context_content.lines)
|
|
179
|
-
new_lines.concat(lines[end_index..-1])
|
|
180
|
-
|
|
181
|
-
new_lines.join
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def insert_context_section_after_agent(content, agent_line_index, context_content)
|
|
185
|
-
lines = content.lines
|
|
186
|
-
|
|
187
|
-
# Build the new content
|
|
188
|
-
new_lines = []
|
|
189
|
-
new_lines.concat(lines[0..agent_line_index])
|
|
190
|
-
new_lines << "\n"
|
|
191
|
-
new_lines << "## Context\n"
|
|
192
|
-
new_lines << "\n"
|
|
193
|
-
new_lines.concat(context_content.lines)
|
|
194
|
-
new_lines.concat(lines[agent_line_index + 1..-1])
|
|
195
|
-
|
|
196
|
-
new_lines.join
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def prepend_agent_with_context(content, context_content)
|
|
200
|
-
agent_context = [
|
|
201
|
-
"# Agent",
|
|
202
|
-
"",
|
|
203
|
-
"## Context",
|
|
204
|
-
"",
|
|
205
|
-
context_content,
|
|
206
|
-
""
|
|
207
|
-
].join("\n")
|
|
208
|
-
agent_context + content
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def find_section_end(lines, start_index, heading_level)
|
|
212
|
-
index = start_index + 1
|
|
213
|
-
|
|
214
|
-
while index < lines.length
|
|
215
|
-
line = lines[index]
|
|
216
|
-
|
|
217
|
-
if line.strip.start_with?("#")
|
|
218
|
-
level = line.strip.match(/^(#+)/)[1].length
|
|
219
|
-
if level <= heading_level
|
|
220
|
-
break
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
index += 1
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
index
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def collect_gem_contexts
|
|
231
|
-
gem_contexts = {}
|
|
232
|
-
|
|
233
|
-
return gem_contexts unless Dir.exist?(@context_path)
|
|
234
|
-
|
|
235
|
-
Dir.glob(File.join(@context_path, "*")).each do |gem_directory|
|
|
236
|
-
next unless File.directory?(gem_directory)
|
|
237
|
-
gem_name = File.basename(gem_directory)
|
|
238
|
-
|
|
239
|
-
markdown_files = Dir.glob(File.join(gem_directory, "**", "*.md")).sort
|
|
240
|
-
gem_contexts[gem_name] = markdown_files if markdown_files.any?
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
gem_contexts
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
# Load a gem's index file
|
|
247
|
-
def load_gem_index(gem_name, gem_directory)
|
|
248
|
-
index_path = File.join(gem_directory, "index.yaml")
|
|
249
|
-
|
|
250
|
-
if File.exist?(index_path)
|
|
251
|
-
YAML.load_file(index_path)
|
|
252
|
-
else
|
|
253
|
-
# Return a fallback index if no index.yaml exists
|
|
254
|
-
{
|
|
255
|
-
"description" => "Context files for #{gem_name}",
|
|
256
|
-
"files" => []
|
|
257
|
-
}
|
|
258
|
-
end
|
|
259
|
-
rescue => error
|
|
260
|
-
Console.debug("Error loading index for #{gem_name}: #{error.message}")
|
|
261
|
-
# Return a fallback index
|
|
262
|
-
{
|
|
263
|
-
"description" => "Context files for #{gem_name}",
|
|
264
|
-
"files" => []
|
|
265
|
-
}
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
def extract_content(file_path)
|
|
269
|
-
content = File.read(file_path)
|
|
270
|
-
lines = content.lines.map(&:strip)
|
|
271
|
-
|
|
272
|
-
title = extract_title(lines)
|
|
273
|
-
description = extract_description(lines)
|
|
274
|
-
|
|
275
|
-
[title, description]
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def extract_title(lines)
|
|
279
|
-
# Look for the first markdown header
|
|
280
|
-
header_line = lines.find {|line| line.start_with?("#")}
|
|
281
|
-
if header_line
|
|
282
|
-
# Remove markdown header syntax and clean up
|
|
283
|
-
header_line.sub(/^#+\s*/, "").strip
|
|
284
|
-
else
|
|
285
|
-
# If no header found, use a default
|
|
286
|
-
"Documentation"
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def extract_description(lines)
|
|
291
|
-
# Skip empty lines and headers to find the first paragraph
|
|
292
|
-
content_start = false
|
|
293
|
-
description_lines = []
|
|
294
|
-
|
|
295
|
-
lines.each do |line|
|
|
296
|
-
# Skip headers
|
|
297
|
-
next if line.start_with?("#")
|
|
298
|
-
|
|
299
|
-
# Skip empty lines until we find content
|
|
300
|
-
if !content_start && line.empty?
|
|
301
|
-
next
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
# Mark that we've found content
|
|
305
|
-
content_start = true
|
|
306
|
-
|
|
307
|
-
# If we hit an empty line after finding content, we've reached the end of the first paragraph
|
|
308
|
-
if line.empty?
|
|
309
|
-
break
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
description_lines << line
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
# Join the lines and truncate if too long
|
|
316
|
-
description = description_lines.join(" ").strip
|
|
317
|
-
if description.length > 197
|
|
318
|
-
description = description[0..196] + "..."
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
description
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
end
|