agents.yaml 0.2.0 → 0.2.2

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.
package/src/discover.ts CHANGED
@@ -1,158 +1,198 @@
1
- import { access, opendir, readFile } from 'node:fs/promises'
2
- import path from 'node:path'
3
- import { formatProjectPath, resolveFromRoot } from './paths.ts'
1
+ import { access, opendir, readFile } from "node:fs/promises"
2
+ import path from "node:path"
3
+ import { formatProjectPath, resolveFromRoot } from "./paths.ts"
4
4
 
5
5
  export type DiscoveredDocument = {
6
- path: string
7
- description?: string
6
+ path: string
7
+ description?: string
8
+ }
9
+
10
+ export type DiscoverOptions = {
11
+ includeDotDirectories?: boolean
8
12
  }
9
13
 
10
14
  const skippedDirectories = new Set([
11
- '.git',
12
- '.hg',
13
- '.svn',
14
- '.turbo',
15
- '.next',
16
- 'coverage',
17
- 'dist',
18
- 'build',
15
+ ".git",
16
+ ".hg",
17
+ ".svn",
18
+ ".turbo",
19
+ ".next",
20
+ "coverage",
21
+ "dist",
22
+ "build",
19
23
  ])
20
24
 
21
- export async function discoverAgentDocuments(root: string): Promise<DiscoveredDocument[]> {
22
- const found: DiscoveredDocument[] = []
23
- await walk(root, root, found)
24
- return found
25
- .filter((document) => document.path !== './AGENTS.md')
26
- .sort((left, right) => left.path.localeCompare(right.path))
25
+ export async function discoverAgentDocuments(
26
+ root: string,
27
+ options: DiscoverOptions = {},
28
+ ): Promise<DiscoveredDocument[]> {
29
+ const found: DiscoveredDocument[] = []
30
+ await walk(root, root, found, options)
31
+ return found
32
+ .filter((document) => document.path !== "./AGENTS.md")
33
+ .sort((left, right) => left.path.localeCompare(right.path))
27
34
  }
28
35
 
29
36
  export async function describeAgentDocument(
30
- root: string,
31
- agentsDocumentPath: string,
37
+ root: string,
38
+ agentsDocumentPath: string,
32
39
  ): Promise<DiscoveredDocument> {
33
- const absolutePath = resolveFromRoot(root, agentsDocumentPath)
34
- return documentEntry(root, absolutePath, await readPackageDescription(path.dirname(absolutePath)))
40
+ const absolutePath = resolveFromRoot(root, agentsDocumentPath)
41
+ return documentEntry(
42
+ root,
43
+ absolutePath,
44
+ await readPackageDescription(path.dirname(absolutePath)),
45
+ )
46
+ }
47
+
48
+ async function walk(
49
+ root: string,
50
+ directory: string,
51
+ found: DiscoveredDocument[],
52
+ options: DiscoverOptions,
53
+ ): Promise<void> {
54
+ let handle
55
+ try {
56
+ handle = await opendir(directory)
57
+ } catch {
58
+ return
59
+ }
60
+
61
+ for await (const entry of handle) {
62
+ const absolutePath = path.join(directory, entry.name)
63
+
64
+ if (entry.isDirectory()) {
65
+ if (entry.name === "node_modules") {
66
+ await scanDirectNodeModules(root, absolutePath, found)
67
+ continue
68
+ }
69
+
70
+ if (!shouldSkipDirectory(entry.name, options)) {
71
+ await walk(root, absolutePath, found, options)
72
+ }
73
+ continue
74
+ }
75
+
76
+ if (entry.isFile() && entry.name === "AGENTS.md") {
77
+ found.push(
78
+ await documentEntry(
79
+ root,
80
+ absolutePath,
81
+ await readPackageDescription(path.dirname(absolutePath)),
82
+ ),
83
+ )
84
+ }
85
+ }
35
86
  }
36
87
 
37
- async function walk(root: string, directory: string, found: DiscoveredDocument[]): Promise<void> {
38
- let handle
39
- try {
40
- handle = await opendir(directory)
41
- } catch {
42
- return
43
- }
44
-
45
- for await (const entry of handle) {
46
- const absolutePath = path.join(directory, entry.name)
47
-
48
- if (entry.isDirectory()) {
49
- if (entry.name === 'node_modules') {
50
- await scanDirectNodeModules(root, absolutePath, found)
51
- continue
52
- }
53
-
54
- if (!skippedDirectories.has(entry.name)) {
55
- await walk(root, absolutePath, found)
56
- }
57
- continue
58
- }
59
-
60
- if (entry.isFile() && entry.name === 'AGENTS.md') {
61
- found.push(
62
- await documentEntry(
63
- root,
64
- absolutePath,
65
- await readPackageDescription(path.dirname(absolutePath)),
66
- ),
67
- )
68
- }
69
- }
88
+ function shouldSkipDirectory(name: string, options: DiscoverOptions): boolean {
89
+ if (skippedDirectories.has(name)) return true
90
+ return !options.includeDotDirectories && name.startsWith(".")
70
91
  }
71
92
 
72
93
  async function scanDirectNodeModules(
73
- root: string,
74
- nodeModulesPath: string,
75
- found: DiscoveredDocument[],
94
+ root: string,
95
+ nodeModulesPath: string,
96
+ found: DiscoveredDocument[],
76
97
  ): Promise<void> {
77
- let handle
78
- try {
79
- handle = await opendir(nodeModulesPath)
80
- } catch {
81
- return
82
- }
83
-
84
- for await (const entry of handle) {
85
- if ((!entry.isDirectory() && !entry.isSymbolicLink()) || entry.name.startsWith('.')) {
86
- continue
87
- }
88
-
89
- const packagePath = path.join(nodeModulesPath, entry.name)
90
- if (entry.name.startsWith('@')) {
91
- await scanScopedPackages(root, packagePath, found)
92
- continue
93
- }
94
-
95
- await addPackageAgentsDocument(root, packagePath, found)
96
- }
98
+ let handle
99
+ try {
100
+ handle = await opendir(nodeModulesPath)
101
+ } catch {
102
+ return
103
+ }
104
+
105
+ for await (const entry of handle) {
106
+ if (
107
+ (!entry.isDirectory() && !entry.isSymbolicLink()) ||
108
+ entry.name.startsWith(".")
109
+ ) {
110
+ continue
111
+ }
112
+
113
+ const packagePath = path.join(nodeModulesPath, entry.name)
114
+ if (entry.name.startsWith("@")) {
115
+ await scanScopedPackages(root, packagePath, found)
116
+ continue
117
+ }
118
+
119
+ await addPackageAgentsDocument(root, packagePath, found)
120
+ }
97
121
  }
98
122
 
99
123
  async function scanScopedPackages(
100
- root: string,
101
- scopePath: string,
102
- found: DiscoveredDocument[],
124
+ root: string,
125
+ scopePath: string,
126
+ found: DiscoveredDocument[],
103
127
  ): Promise<void> {
104
- let handle
105
- try {
106
- handle = await opendir(scopePath)
107
- } catch {
108
- return
109
- }
110
-
111
- for await (const entry of handle) {
112
- if (entry.isDirectory() || entry.isSymbolicLink()) {
113
- await addPackageAgentsDocument(root, path.join(scopePath, entry.name), found)
114
- }
115
- }
128
+ let handle
129
+ try {
130
+ handle = await opendir(scopePath)
131
+ } catch {
132
+ return
133
+ }
134
+
135
+ for await (const entry of handle) {
136
+ if (entry.isDirectory() || entry.isSymbolicLink()) {
137
+ await addPackageAgentsDocument(
138
+ root,
139
+ path.join(scopePath, entry.name),
140
+ found,
141
+ )
142
+ }
143
+ }
116
144
  }
117
145
 
118
146
  async function addPackageAgentsDocument(
119
- root: string,
120
- packagePath: string,
121
- found: DiscoveredDocument[],
147
+ root: string,
148
+ packagePath: string,
149
+ found: DiscoveredDocument[],
122
150
  ): Promise<void> {
123
- const agentsPath = path.join(packagePath, 'AGENTS.md')
124
- try {
125
- await access(agentsPath)
126
- found.push(await documentEntry(root, agentsPath, await readPackageDescription(packagePath)))
127
- } catch {
128
- // Packages without AGENTS.md are simply not candidates.
129
- }
151
+ const agentsPath = path.join(packagePath, "AGENTS.md")
152
+ try {
153
+ await access(agentsPath)
154
+ found.push(
155
+ await documentEntry(
156
+ root,
157
+ agentsPath,
158
+ await readPackageDescription(packagePath),
159
+ ),
160
+ )
161
+ } catch {
162
+ // Packages without AGENTS.md are simply not candidates.
163
+ }
130
164
  }
131
165
 
132
166
  async function documentEntry(
133
- root: string,
134
- agentsPath: string,
135
- description: string | undefined,
167
+ root: string,
168
+ agentsPath: string,
169
+ description: string | undefined,
136
170
  ): Promise<DiscoveredDocument> {
137
- return {
138
- path: formatProjectPath(root, agentsPath),
139
- ...(description ? { description } : {}),
140
- }
171
+ return {
172
+ path: formatProjectPath(root, agentsPath),
173
+ ...(description ? { description } : {}),
174
+ }
141
175
  }
142
176
 
143
- async function readPackageDescription(packagePath: string): Promise<string | undefined> {
144
- try {
145
- const source = await readFile(path.join(packagePath, 'package.json'), 'utf8')
146
- const parsed = JSON.parse(source) as unknown
147
- if (!isPackageJson(parsed) || typeof parsed.description !== 'string') return undefined
148
-
149
- const description = parsed.description.trim()
150
- return description && description.length > 0 ? description : undefined
151
- } catch {
152
- return undefined
153
- }
177
+ async function readPackageDescription(
178
+ packagePath: string,
179
+ ): Promise<string | undefined> {
180
+ try {
181
+ const source = await readFile(
182
+ path.join(packagePath, "package.json"),
183
+ "utf8",
184
+ )
185
+ const parsed = JSON.parse(source) as unknown
186
+ if (!isPackageJson(parsed) || typeof parsed.description !== "string")
187
+ return undefined
188
+
189
+ const description = parsed.description.trim()
190
+ return description && description.length > 0 ? description : undefined
191
+ } catch {
192
+ return undefined
193
+ }
154
194
  }
155
195
 
156
196
  function isPackageJson(value: unknown): value is Record<string, unknown> {
157
- return typeof value === 'object' && value !== null
197
+ return typeof value === "object" && value !== null
158
198
  }
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { run } from './run.ts'
3
+ import { run } from "./run.ts"
4
4
 
5
5
  run(process.argv.slice(2)).catch((error: unknown) => {
6
- const message = error instanceof Error ? error.message : String(error)
7
- console.error(`agents: ${message}`)
8
- process.exitCode = 1
6
+ const message = error instanceof Error ? error.message : String(error)
7
+ console.error(`agents: ${message}`)
8
+ process.exitCode = 1
9
9
  })
package/src/paths.ts CHANGED
@@ -1,18 +1,23 @@
1
- import path from 'node:path'
1
+ import path from "node:path"
2
2
 
3
3
  export function cwd(): string {
4
- return process.cwd()
4
+ return process.cwd()
5
5
  }
6
6
 
7
7
  export function resolveFromRoot(root: string, input: string): string {
8
- return path.isAbsolute(input) ? path.normalize(input) : path.resolve(root, input)
8
+ return path.isAbsolute(input)
9
+ ? path.normalize(input)
10
+ : path.resolve(root, input)
9
11
  }
10
12
 
11
13
  export function formatProjectPath(root: string, target: string): string {
12
- const relative = path.relative(root, target).split(path.sep).join(path.posix.sep)
13
- if (relative.startsWith('..')) {
14
- return target
15
- }
14
+ const relative = path
15
+ .relative(root, target)
16
+ .split(path.sep)
17
+ .join(path.posix.sep)
18
+ if (relative === ".." || relative.startsWith("../")) {
19
+ return target
20
+ }
16
21
 
17
- return relative.startsWith('.') ? relative : `./${relative}`
22
+ return relative.startsWith("./") ? relative : `./${relative}`
18
23
  }