agents.yaml 0.2.1 → 0.2.3
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/AGENTS.md +2 -0
- package/README.md +18 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +24 -15
- package/package.json +10 -9
- package/src/discover.bench.ts +219 -0
- package/src/discover.test.ts +20 -0
- package/src/discover.ts +14 -3
- package/src/paths.ts +2 -2
- package/src/run.ts +19 -8
package/AGENTS.md
CHANGED
|
@@ -19,3 +19,5 @@ Agents should treat paths listed in `documents` as promoted supplemental guidanc
|
|
|
19
19
|
The CLI can help discover package and local `AGENTS.md` files, add selected paths to `agents.yaml`, remove paths, initialize the root breadcrumb, and validate that referenced files still exist.
|
|
20
20
|
|
|
21
21
|
Discovery only considers direct dependencies under a project's `node_modules`; nested dependency `AGENTS.md` files are not automatically activated.
|
|
22
|
+
|
|
23
|
+
Discovery skips dot-prefixed directories by default. Use `agents discover --include-dot-directories` when hidden project directories should be scanned too.
|
package/README.md
CHANGED
|
@@ -17,12 +17,30 @@ pnpm run build
|
|
|
17
17
|
```sh
|
|
18
18
|
agents init
|
|
19
19
|
agents discover
|
|
20
|
+
agents discover --include-dot-directories
|
|
20
21
|
agents add ./node_modules/react/AGENTS.md
|
|
21
22
|
agents validate
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
Run `agents` with no command for the interactive flow.
|
|
25
26
|
|
|
27
|
+
Discovery skips dot-prefixed directories by default so local caches and tool
|
|
28
|
+
state do not dominate scan time. Use `--include-dot-directories` when you need
|
|
29
|
+
to search those directories too.
|
|
30
|
+
|
|
31
|
+
## Benchmark
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pnpm --filter agents.yaml bench
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The benchmark creates a temporary discovery fixture, compares default discovery
|
|
38
|
+
against `--include-dot-directories`, prints median/min/max timings, and removes
|
|
39
|
+
the fixture when it exits. Fixture size can be tuned with
|
|
40
|
+
`AGENTS_BENCH_HIDDEN_DIRS`, `AGENTS_BENCH_FILES_PER_HIDDEN_DIR`,
|
|
41
|
+
`AGENTS_BENCH_VISIBLE_PACKAGES`, `AGENTS_BENCH_ITERATIONS`, and
|
|
42
|
+
`AGENTS_BENCH_WARMUPS`.
|
|
43
|
+
|
|
26
44
|
## File Format
|
|
27
45
|
|
|
28
46
|
```yaml
|
package/dist/index.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/index.mjs
CHANGED
|
@@ -15,8 +15,8 @@ function resolveFromRoot(root, input) {
|
|
|
15
15
|
}
|
|
16
16
|
function formatProjectPath(root, target) {
|
|
17
17
|
const relative = path.relative(root, target).split(path.sep).join(path.posix.sep);
|
|
18
|
-
if (relative.startsWith("
|
|
19
|
-
return relative.startsWith("
|
|
18
|
+
if (relative === ".." || relative.startsWith("../")) return target;
|
|
19
|
+
return relative.startsWith("./") ? relative : `./${relative}`;
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/agents-file.ts
|
|
@@ -147,7 +147,8 @@ async function initProject(root, options) {
|
|
|
147
147
|
messages.push("AGENTS.md already mentions agents.yaml");
|
|
148
148
|
return { messages };
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
const next = source.trimEnd().length === 0 ? `# Project Instructions\n\n${breadcrumb}\n` : `${source.trimEnd()}\n\n${breadcrumb}\n`;
|
|
151
|
+
await writeFile(projectAgentsPath, next, "utf8");
|
|
151
152
|
messages.push("updated AGENTS.md");
|
|
152
153
|
} catch (error) {
|
|
153
154
|
if (!isNotFound(error)) throw error;
|
|
@@ -164,7 +165,7 @@ function isNotFound(error) {
|
|
|
164
165
|
}
|
|
165
166
|
//#endregion
|
|
166
167
|
//#region src/discover.ts
|
|
167
|
-
const skippedDirectories = new Set([
|
|
168
|
+
const skippedDirectories = /* @__PURE__ */ new Set([
|
|
168
169
|
".git",
|
|
169
170
|
".hg",
|
|
170
171
|
".svn",
|
|
@@ -174,16 +175,16 @@ const skippedDirectories = new Set([
|
|
|
174
175
|
"dist",
|
|
175
176
|
"build"
|
|
176
177
|
]);
|
|
177
|
-
async function discoverAgentDocuments(root) {
|
|
178
|
+
async function discoverAgentDocuments(root, options = {}) {
|
|
178
179
|
const found = [];
|
|
179
|
-
await walk(root, root, found);
|
|
180
|
+
await walk(root, root, found, options);
|
|
180
181
|
return found.filter((document) => document.path !== "./AGENTS.md").sort((left, right) => left.path.localeCompare(right.path));
|
|
181
182
|
}
|
|
182
183
|
async function describeAgentDocument(root, agentsDocumentPath) {
|
|
183
184
|
const absolutePath = resolveFromRoot(root, agentsDocumentPath);
|
|
184
185
|
return documentEntry(root, absolutePath, await readPackageDescription(path.dirname(absolutePath)));
|
|
185
186
|
}
|
|
186
|
-
async function walk(root, directory, found) {
|
|
187
|
+
async function walk(root, directory, found, options) {
|
|
187
188
|
let handle;
|
|
188
189
|
try {
|
|
189
190
|
handle = await opendir(directory);
|
|
@@ -197,12 +198,16 @@ async function walk(root, directory, found) {
|
|
|
197
198
|
await scanDirectNodeModules(root, absolutePath, found);
|
|
198
199
|
continue;
|
|
199
200
|
}
|
|
200
|
-
if (!
|
|
201
|
+
if (!shouldSkipDirectory(entry.name, options)) await walk(root, absolutePath, found, options);
|
|
201
202
|
continue;
|
|
202
203
|
}
|
|
203
204
|
if (entry.isFile() && entry.name === "AGENTS.md") found.push(await documentEntry(root, absolutePath, await readPackageDescription(path.dirname(absolutePath))));
|
|
204
205
|
}
|
|
205
206
|
}
|
|
207
|
+
function shouldSkipDirectory(name, options) {
|
|
208
|
+
if (skippedDirectories.has(name)) return true;
|
|
209
|
+
return !options.includeDotDirectories && name.startsWith(".");
|
|
210
|
+
}
|
|
206
211
|
async function scanDirectNodeModules(root, nodeModulesPath, found) {
|
|
207
212
|
let handle;
|
|
208
213
|
try {
|
|
@@ -263,7 +268,7 @@ const helpText = `agents
|
|
|
263
268
|
Usage:
|
|
264
269
|
agents
|
|
265
270
|
agents init [--force]
|
|
266
|
-
agents discover [--json]
|
|
271
|
+
agents discover [--json] [--include-dot-directories]
|
|
267
272
|
agents add <path...>
|
|
268
273
|
agents remove <path...>
|
|
269
274
|
agents validate [--json]
|
|
@@ -286,7 +291,10 @@ async function run(argv) {
|
|
|
286
291
|
await commandInit(root, parsed.flags.get("force") === true);
|
|
287
292
|
return;
|
|
288
293
|
case "discover":
|
|
289
|
-
await commandDiscover(root,
|
|
294
|
+
await commandDiscover(root, {
|
|
295
|
+
json: parsed.flags.get("json") === true,
|
|
296
|
+
includeDotDirectories: parsed.flags.get("include-dot-directories") === true
|
|
297
|
+
});
|
|
290
298
|
return;
|
|
291
299
|
case "add":
|
|
292
300
|
await commandAdd(root, parsed.values);
|
|
@@ -353,9 +361,9 @@ async function commandInit(root, force) {
|
|
|
353
361
|
clack.note(result.messages.join("\n"), "Updated");
|
|
354
362
|
clack.outro("Project breadcrumb is ready.");
|
|
355
363
|
}
|
|
356
|
-
async function commandDiscover(root,
|
|
357
|
-
const documents = await discoverAgentDocuments(root);
|
|
358
|
-
if (json) {
|
|
364
|
+
async function commandDiscover(root, options) {
|
|
365
|
+
const documents = await discoverAgentDocuments(root, { includeDotDirectories: options.includeDotDirectories });
|
|
366
|
+
if (options.json) {
|
|
359
367
|
console.log(JSON.stringify(documents, null, 2));
|
|
360
368
|
return;
|
|
361
369
|
}
|
|
@@ -444,8 +452,8 @@ async function interactive(root) {
|
|
|
444
452
|
}
|
|
445
453
|
await commandAdd(root, selected);
|
|
446
454
|
}
|
|
447
|
-
function chooseDocumentsToEnable(options) {
|
|
448
|
-
|
|
455
|
+
async function chooseDocumentsToEnable(options) {
|
|
456
|
+
const selected = await new MultiSelectPrompt({
|
|
449
457
|
options,
|
|
450
458
|
required: false,
|
|
451
459
|
render() {
|
|
@@ -467,6 +475,7 @@ ${styleText("cyan", clack.S_BAR_END)}
|
|
|
467
475
|
`;
|
|
468
476
|
}
|
|
469
477
|
}).prompt();
|
|
478
|
+
return typeof selected === "symbol" ? clack.CANCEL_SYMBOL : selected;
|
|
470
479
|
}
|
|
471
480
|
function styleDocumentOption(option, state) {
|
|
472
481
|
if (option.disabled) return `${styleText("gray", clack.S_CHECKBOX_INACTIVE)} ${styleText(["strikethrough", "gray"], option.label)}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents.yaml",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "A CLI for discovering and curating agent-readable documentation in agents.yaml.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,20 +17,21 @@
|
|
|
17
17
|
],
|
|
18
18
|
"type": "module",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@clack/core": "1.
|
|
21
|
-
"@clack/prompts": "1.
|
|
22
|
-
"yaml": "2.9.
|
|
23
|
-
"zod": "4.
|
|
20
|
+
"@clack/core": "1.5.1",
|
|
21
|
+
"@clack/prompts": "1.8.1",
|
|
22
|
+
"yaml": "2.9.1",
|
|
23
|
+
"zod": "4.6.5"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@types/node": "25.9.
|
|
27
|
-
"typescript": "
|
|
26
|
+
"@types/node": "25.9.6",
|
|
27
|
+
"typescript": "7.0.2"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": "26.
|
|
31
|
-
"pnpm": "
|
|
30
|
+
"node": "26.8.2",
|
|
31
|
+
"pnpm": "12.4.1"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
+
"bench": "node src/discover.bench.ts",
|
|
34
35
|
"build": "vp pack",
|
|
35
36
|
"dev": "src/index.ts",
|
|
36
37
|
"test": "vp test"
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"
|
|
2
|
+
import { tmpdir } from "node:os"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import { performance } from "node:perf_hooks"
|
|
5
|
+
import { discoverAgentDocuments, type DiscoverOptions } from "./discover.ts"
|
|
6
|
+
|
|
7
|
+
type BenchmarkCase = {
|
|
8
|
+
name: string
|
|
9
|
+
options?: DiscoverOptions
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type BenchmarkResult = {
|
|
13
|
+
name: string
|
|
14
|
+
docs: number
|
|
15
|
+
medianMs: number
|
|
16
|
+
minMs: number
|
|
17
|
+
maxMs: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const hiddenDirectories = readPositiveInteger("AGENTS_BENCH_HIDDEN_DIRS", 250)
|
|
21
|
+
const filesPerHiddenDirectory = readPositiveInteger(
|
|
22
|
+
"AGENTS_BENCH_FILES_PER_HIDDEN_DIR",
|
|
23
|
+
8,
|
|
24
|
+
)
|
|
25
|
+
const visiblePackages = readPositiveInteger("AGENTS_BENCH_VISIBLE_PACKAGES", 25)
|
|
26
|
+
const iterations = readPositiveInteger("AGENTS_BENCH_ITERATIONS", 7)
|
|
27
|
+
const warmups = readPositiveInteger("AGENTS_BENCH_WARMUPS", 1)
|
|
28
|
+
|
|
29
|
+
const cases: BenchmarkCase[] = [
|
|
30
|
+
{ name: "default" },
|
|
31
|
+
{
|
|
32
|
+
name: "include dot directories",
|
|
33
|
+
options: { includeDotDirectories: true },
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
const root = await mkdtemp(path.join(tmpdir(), "agents-yaml-bench-"))
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await createFixture(root)
|
|
41
|
+
const results: BenchmarkResult[] = []
|
|
42
|
+
|
|
43
|
+
for (const benchmarkCase of cases) {
|
|
44
|
+
for (let index = 0; index < warmups; index += 1) {
|
|
45
|
+
await discoverAgentDocuments(root, benchmarkCase.options)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
results.push(await runCase(root, benchmarkCase))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
printResults(results)
|
|
52
|
+
} finally {
|
|
53
|
+
await rm(root, { recursive: true, force: true })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function createFixture(root: string): Promise<void> {
|
|
57
|
+
await writeFile(
|
|
58
|
+
path.join(root, "agents.yaml"),
|
|
59
|
+
"version: 1\n\ndocuments: []\n",
|
|
60
|
+
"utf8",
|
|
61
|
+
)
|
|
62
|
+
await writeFile(path.join(root, "AGENTS.md"), "# Root guidance\n", "utf8")
|
|
63
|
+
|
|
64
|
+
await createDirectDependency(root)
|
|
65
|
+
await createVisibleProjectDocuments(root)
|
|
66
|
+
await createHiddenCache(root)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function createDirectDependency(root: string): Promise<void> {
|
|
70
|
+
const dependencyPath = path.join(root, "node_modules", "direct-lib")
|
|
71
|
+
await mkdir(dependencyPath, { recursive: true })
|
|
72
|
+
await writeFile(
|
|
73
|
+
path.join(dependencyPath, "AGENTS.md"),
|
|
74
|
+
"# Direct dependency guidance\n",
|
|
75
|
+
"utf8",
|
|
76
|
+
)
|
|
77
|
+
await writeFile(
|
|
78
|
+
path.join(dependencyPath, "package.json"),
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
name: "direct-lib",
|
|
81
|
+
description: "Direct fixture dependency.",
|
|
82
|
+
}),
|
|
83
|
+
"utf8",
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function createVisibleProjectDocuments(root: string): Promise<void> {
|
|
88
|
+
for (let index = 0; index < visiblePackages; index += 1) {
|
|
89
|
+
const packagePath = path.join(root, "packages", `visible-${index}`)
|
|
90
|
+
await mkdir(packagePath, { recursive: true })
|
|
91
|
+
await writeFile(
|
|
92
|
+
path.join(packagePath, "AGENTS.md"),
|
|
93
|
+
"# Visible project guidance\n",
|
|
94
|
+
"utf8",
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function createHiddenCache(root: string): Promise<void> {
|
|
100
|
+
for (let index = 0; index < hiddenDirectories; index += 1) {
|
|
101
|
+
const cachePath = path.join(root, ".cache", `entry-${index}`, "nested")
|
|
102
|
+
await mkdir(cachePath, { recursive: true })
|
|
103
|
+
await writeFile(
|
|
104
|
+
path.join(cachePath, "AGENTS.md"),
|
|
105
|
+
"# Hidden cache guidance\n",
|
|
106
|
+
"utf8",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
for (
|
|
110
|
+
let fileIndex = 0;
|
|
111
|
+
fileIndex < filesPerHiddenDirectory;
|
|
112
|
+
fileIndex += 1
|
|
113
|
+
) {
|
|
114
|
+
await writeFile(
|
|
115
|
+
path.join(cachePath, `file-${fileIndex}.txt`),
|
|
116
|
+
"x".repeat(100),
|
|
117
|
+
"utf8",
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function runCase(
|
|
124
|
+
root: string,
|
|
125
|
+
benchmarkCase: BenchmarkCase,
|
|
126
|
+
): Promise<BenchmarkResult> {
|
|
127
|
+
const durations: number[] = []
|
|
128
|
+
let docs = 0
|
|
129
|
+
|
|
130
|
+
for (let index = 0; index < iterations; index += 1) {
|
|
131
|
+
const start = performance.now()
|
|
132
|
+
const discovered = await discoverAgentDocuments(root, benchmarkCase.options)
|
|
133
|
+
const duration = performance.now() - start
|
|
134
|
+
|
|
135
|
+
docs = discovered.length
|
|
136
|
+
durations.push(duration)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sorted = [...durations].sort((left, right) => left - right)
|
|
140
|
+
const medianMs = sorted[Math.floor(sorted.length / 2)]
|
|
141
|
+
const minMs = sorted[0]
|
|
142
|
+
const maxMs = sorted[sorted.length - 1]
|
|
143
|
+
|
|
144
|
+
if (medianMs === undefined || minMs === undefined || maxMs === undefined) {
|
|
145
|
+
throw new Error("Benchmark did not record any durations")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
name: benchmarkCase.name,
|
|
150
|
+
docs,
|
|
151
|
+
medianMs,
|
|
152
|
+
minMs,
|
|
153
|
+
maxMs,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function printResults(results: BenchmarkResult[]): void {
|
|
158
|
+
console.log("agents discover benchmark")
|
|
159
|
+
console.log(
|
|
160
|
+
[
|
|
161
|
+
`fixture: hiddenDirectories=${hiddenDirectories}`,
|
|
162
|
+
`filesPerHiddenDirectory=${filesPerHiddenDirectory}`,
|
|
163
|
+
`visiblePackages=${visiblePackages}`,
|
|
164
|
+
`iterations=${iterations}`,
|
|
165
|
+
`warmups=${warmups}`,
|
|
166
|
+
].join(", "),
|
|
167
|
+
)
|
|
168
|
+
console.log("")
|
|
169
|
+
console.log(
|
|
170
|
+
[
|
|
171
|
+
pad("case", 24),
|
|
172
|
+
pad("docs", 8),
|
|
173
|
+
pad("median", 10),
|
|
174
|
+
pad("min", 10),
|
|
175
|
+
pad("max", 10),
|
|
176
|
+
].join(""),
|
|
177
|
+
)
|
|
178
|
+
console.log("-".repeat(62))
|
|
179
|
+
|
|
180
|
+
for (const result of results) {
|
|
181
|
+
console.log(
|
|
182
|
+
[
|
|
183
|
+
pad(result.name, 24),
|
|
184
|
+
pad(String(result.docs), 8),
|
|
185
|
+
pad(formatMs(result.medianMs), 10),
|
|
186
|
+
pad(formatMs(result.minMs), 10),
|
|
187
|
+
pad(formatMs(result.maxMs), 10),
|
|
188
|
+
].join(""),
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const defaultResult = results.find((result) => result.name === "default")
|
|
193
|
+
const includeDotResult = results.find(
|
|
194
|
+
(result) => result.name === "include dot directories",
|
|
195
|
+
)
|
|
196
|
+
if (defaultResult && includeDotResult && defaultResult.medianMs > 0) {
|
|
197
|
+
const ratio = includeDotResult.medianMs / defaultResult.medianMs
|
|
198
|
+
console.log("")
|
|
199
|
+
console.log(`include dot directories median: ${ratio.toFixed(1)}x default`)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function readPositiveInteger(name: string, fallback: number): number {
|
|
204
|
+
const raw = process.env[name]
|
|
205
|
+
if (!raw) return fallback
|
|
206
|
+
|
|
207
|
+
const value = Number.parseInt(raw, 10)
|
|
208
|
+
if (Number.isInteger(value) && value > 0) return value
|
|
209
|
+
|
|
210
|
+
throw new Error(`${name} must be a positive integer`)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function formatMs(value: number): string {
|
|
214
|
+
return `${value.toFixed(1)}ms`
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function pad(value: string, width: number): string {
|
|
218
|
+
return value.padEnd(width, " ")
|
|
219
|
+
}
|
package/src/discover.test.ts
CHANGED
|
@@ -132,6 +132,26 @@ describe("agents.yaml dependency discovery", () => {
|
|
|
132
132
|
},
|
|
133
133
|
])
|
|
134
134
|
})
|
|
135
|
+
|
|
136
|
+
it("skips dot-prefixed directories by default", async () => {
|
|
137
|
+
const root = await createTempProject()
|
|
138
|
+
const hiddenAgentsPath = path.join(root, ".cache", "AGENTS.md")
|
|
139
|
+
await mkdir(path.dirname(hiddenAgentsPath), { recursive: true })
|
|
140
|
+
await writeFile(hiddenAgentsPath, "# Hidden cache guidance\n", "utf8")
|
|
141
|
+
|
|
142
|
+
await expect(discoverAgentDocuments(root)).resolves.toEqual([])
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it("can include dot-prefixed directories when requested", async () => {
|
|
146
|
+
const root = await createTempProject()
|
|
147
|
+
const hiddenAgentsPath = path.join(root, ".cache", "AGENTS.md")
|
|
148
|
+
await mkdir(path.dirname(hiddenAgentsPath), { recursive: true })
|
|
149
|
+
await writeFile(hiddenAgentsPath, "# Hidden cache guidance\n", "utf8")
|
|
150
|
+
|
|
151
|
+
await expect(
|
|
152
|
+
discoverAgentDocuments(root, { includeDotDirectories: true }),
|
|
153
|
+
).resolves.toEqual([{ path: "./.cache/AGENTS.md" }])
|
|
154
|
+
})
|
|
135
155
|
})
|
|
136
156
|
|
|
137
157
|
async function createTempProject(): Promise<string> {
|
package/src/discover.ts
CHANGED
|
@@ -7,6 +7,10 @@ export type DiscoveredDocument = {
|
|
|
7
7
|
description?: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export type DiscoverOptions = {
|
|
11
|
+
includeDotDirectories?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
const skippedDirectories = new Set([
|
|
11
15
|
".git",
|
|
12
16
|
".hg",
|
|
@@ -20,9 +24,10 @@ const skippedDirectories = new Set([
|
|
|
20
24
|
|
|
21
25
|
export async function discoverAgentDocuments(
|
|
22
26
|
root: string,
|
|
27
|
+
options: DiscoverOptions = {},
|
|
23
28
|
): Promise<DiscoveredDocument[]> {
|
|
24
29
|
const found: DiscoveredDocument[] = []
|
|
25
|
-
await walk(root, root, found)
|
|
30
|
+
await walk(root, root, found, options)
|
|
26
31
|
return found
|
|
27
32
|
.filter((document) => document.path !== "./AGENTS.md")
|
|
28
33
|
.sort((left, right) => left.path.localeCompare(right.path))
|
|
@@ -44,6 +49,7 @@ async function walk(
|
|
|
44
49
|
root: string,
|
|
45
50
|
directory: string,
|
|
46
51
|
found: DiscoveredDocument[],
|
|
52
|
+
options: DiscoverOptions,
|
|
47
53
|
): Promise<void> {
|
|
48
54
|
let handle
|
|
49
55
|
try {
|
|
@@ -61,8 +67,8 @@ async function walk(
|
|
|
61
67
|
continue
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
if (!
|
|
65
|
-
await walk(root, absolutePath, found)
|
|
70
|
+
if (!shouldSkipDirectory(entry.name, options)) {
|
|
71
|
+
await walk(root, absolutePath, found, options)
|
|
66
72
|
}
|
|
67
73
|
continue
|
|
68
74
|
}
|
|
@@ -79,6 +85,11 @@ async function walk(
|
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
function shouldSkipDirectory(name: string, options: DiscoverOptions): boolean {
|
|
89
|
+
if (skippedDirectories.has(name)) return true
|
|
90
|
+
return !options.includeDotDirectories && name.startsWith(".")
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
async function scanDirectNodeModules(
|
|
83
94
|
root: string,
|
|
84
95
|
nodeModulesPath: string,
|
package/src/paths.ts
CHANGED
|
@@ -15,9 +15,9 @@ export function formatProjectPath(root: string, target: string): string {
|
|
|
15
15
|
.relative(root, target)
|
|
16
16
|
.split(path.sep)
|
|
17
17
|
.join(path.posix.sep)
|
|
18
|
-
if (relative.startsWith("
|
|
18
|
+
if (relative === ".." || relative.startsWith("../")) {
|
|
19
19
|
return target
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
return relative.startsWith("
|
|
22
|
+
return relative.startsWith("./") ? relative : `./${relative}`
|
|
23
23
|
}
|
package/src/run.ts
CHANGED
|
@@ -37,7 +37,7 @@ const helpText = `agents
|
|
|
37
37
|
Usage:
|
|
38
38
|
agents
|
|
39
39
|
agents init [--force]
|
|
40
|
-
agents discover [--json]
|
|
40
|
+
agents discover [--json] [--include-dot-directories]
|
|
41
41
|
agents add <path...>
|
|
42
42
|
agents remove <path...>
|
|
43
43
|
agents validate [--json]
|
|
@@ -62,7 +62,11 @@ export async function run(argv: string[]): Promise<void> {
|
|
|
62
62
|
await commandInit(root, parsed.flags.get("force") === true)
|
|
63
63
|
return
|
|
64
64
|
case "discover":
|
|
65
|
-
await commandDiscover(root,
|
|
65
|
+
await commandDiscover(root, {
|
|
66
|
+
json: parsed.flags.get("json") === true,
|
|
67
|
+
includeDotDirectories:
|
|
68
|
+
parsed.flags.get("include-dot-directories") === true,
|
|
69
|
+
})
|
|
66
70
|
return
|
|
67
71
|
case "add":
|
|
68
72
|
await commandAdd(root, parsed.values)
|
|
@@ -137,9 +141,14 @@ async function commandInit(root: string, force: boolean): Promise<void> {
|
|
|
137
141
|
clack.outro("Project breadcrumb is ready.")
|
|
138
142
|
}
|
|
139
143
|
|
|
140
|
-
async function commandDiscover(
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
async function commandDiscover(
|
|
145
|
+
root: string,
|
|
146
|
+
options: { json: boolean; includeDotDirectories: boolean },
|
|
147
|
+
): Promise<void> {
|
|
148
|
+
const documents = await discoverAgentDocuments(root, {
|
|
149
|
+
includeDotDirectories: options.includeDotDirectories,
|
|
150
|
+
})
|
|
151
|
+
if (options.json) {
|
|
143
152
|
console.log(JSON.stringify(documents, null, 2))
|
|
144
153
|
return
|
|
145
154
|
}
|
|
@@ -281,10 +290,10 @@ async function interactive(root: string): Promise<void> {
|
|
|
281
290
|
await commandAdd(root, selected)
|
|
282
291
|
}
|
|
283
292
|
|
|
284
|
-
function chooseDocumentsToEnable(
|
|
293
|
+
async function chooseDocumentsToEnable(
|
|
285
294
|
options: DocumentOption[],
|
|
286
|
-
): Promise<string[] |
|
|
287
|
-
|
|
295
|
+
): Promise<string[] | typeof clack.CANCEL_SYMBOL | undefined> {
|
|
296
|
+
const selected = await new MultiSelectPrompt<DocumentOption>({
|
|
288
297
|
options,
|
|
289
298
|
required: false,
|
|
290
299
|
render() {
|
|
@@ -308,6 +317,8 @@ ${styleText("cyan", clack.S_BAR_END)}
|
|
|
308
317
|
`
|
|
309
318
|
},
|
|
310
319
|
}).prompt()
|
|
320
|
+
|
|
321
|
+
return typeof selected === "symbol" ? clack.CANCEL_SYMBOL : selected
|
|
311
322
|
}
|
|
312
323
|
|
|
313
324
|
function styleDocumentOption(
|