agents.yaml 0.1.0 → 0.2.1
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 +3 -2
- package/README.md +4 -3
- package/dist/index.mjs +89 -24
- package/package.json +8 -10
- package/src/agents-file.ts +186 -163
- package/src/discover.test.ts +137 -55
- package/src/discover.ts +165 -93
- package/src/index.ts +4 -4
- package/src/paths.ts +13 -8
- package/src/run.ts +290 -191
package/src/run.ts
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { MultiSelectPrompt } from "@clack/core"
|
|
2
|
+
import * as clack from "@clack/prompts"
|
|
3
|
+
import { styleText } from "node:util"
|
|
2
4
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from
|
|
9
|
-
import { discoverAgentDocuments } from
|
|
10
|
-
import { cwd, formatProjectPath, resolveFromRoot } from
|
|
11
|
-
|
|
12
|
-
type Command =
|
|
5
|
+
addDocuments,
|
|
6
|
+
initProject,
|
|
7
|
+
loadAgentsFile,
|
|
8
|
+
removeDocuments,
|
|
9
|
+
validateAgentsFile,
|
|
10
|
+
} from "./agents-file.ts"
|
|
11
|
+
import { describeAgentDocument, discoverAgentDocuments } from "./discover.ts"
|
|
12
|
+
import { cwd, formatProjectPath, resolveFromRoot } from "./paths.ts"
|
|
13
|
+
|
|
14
|
+
type Command =
|
|
15
|
+
| "add"
|
|
16
|
+
| "discover"
|
|
17
|
+
| "help"
|
|
18
|
+
| "init"
|
|
19
|
+
| "remove"
|
|
20
|
+
| "validate"
|
|
21
|
+
| "version"
|
|
13
22
|
|
|
14
23
|
type ParsedArgs = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
command: Command | undefined
|
|
25
|
+
values: string[]
|
|
26
|
+
flags: Map<string, string | boolean>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type DocumentOption = {
|
|
30
|
+
value: string
|
|
31
|
+
label: string
|
|
32
|
+
disabled?: boolean
|
|
18
33
|
}
|
|
19
34
|
|
|
20
35
|
const helpText = `agents
|
|
@@ -27,207 +42,291 @@ Usage:
|
|
|
27
42
|
agents remove <path...>
|
|
28
43
|
agents validate [--json]
|
|
29
44
|
|
|
30
|
-
agents.yaml is a curated table of contents for
|
|
45
|
+
agents.yaml is a curated table of contents for promoted AGENTS.md guidance.`
|
|
31
46
|
|
|
32
47
|
export async function run(argv: string[]): Promise<void> {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
const parsed = parseArgs(argv)
|
|
49
|
+
const root = cwd()
|
|
50
|
+
|
|
51
|
+
switch (parsed.command) {
|
|
52
|
+
case undefined:
|
|
53
|
+
await interactive(root)
|
|
54
|
+
return
|
|
55
|
+
case "help":
|
|
56
|
+
console.log(helpText)
|
|
57
|
+
return
|
|
58
|
+
case "version":
|
|
59
|
+
console.log("0.1.0")
|
|
60
|
+
return
|
|
61
|
+
case "init":
|
|
62
|
+
await commandInit(root, parsed.flags.get("force") === true)
|
|
63
|
+
return
|
|
64
|
+
case "discover":
|
|
65
|
+
await commandDiscover(root, parsed.flags.get("json") === true)
|
|
66
|
+
return
|
|
67
|
+
case "add":
|
|
68
|
+
await commandAdd(root, parsed.values)
|
|
69
|
+
return
|
|
70
|
+
case "remove":
|
|
71
|
+
await commandRemove(root, parsed.values)
|
|
72
|
+
return
|
|
73
|
+
case "validate":
|
|
74
|
+
await commandValidate(root, parsed.flags.get("json") === true)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
function parseArgs(argv: string[]): ParsedArgs {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
80
|
+
const flags = new Map<string, string | boolean>()
|
|
81
|
+
const values: string[] = []
|
|
82
|
+
let command: Command | undefined
|
|
83
|
+
|
|
84
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
85
|
+
const arg = argv[index]
|
|
86
|
+
if (!arg) continue
|
|
87
|
+
|
|
88
|
+
if (arg === "--help" || arg === "-h") {
|
|
89
|
+
command = "help"
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (arg === "--version" || arg === "-v") {
|
|
94
|
+
command = "version"
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (arg.startsWith("--")) {
|
|
99
|
+
const [rawName, inlineValue] = arg.slice(2).split("=", 2)
|
|
100
|
+
if (!rawName) continue
|
|
101
|
+
if (inlineValue !== undefined) {
|
|
102
|
+
flags.set(rawName, inlineValue)
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
flags.set(rawName, true)
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!command && isCommand(arg)) {
|
|
111
|
+
command = arg
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
values.push(arg)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { command, values, flags }
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
function isCommand(value: string): value is Command {
|
|
107
|
-
|
|
122
|
+
return [
|
|
123
|
+
"add",
|
|
124
|
+
"discover",
|
|
125
|
+
"help",
|
|
126
|
+
"init",
|
|
127
|
+
"remove",
|
|
128
|
+
"validate",
|
|
129
|
+
"version",
|
|
130
|
+
].includes(value)
|
|
108
131
|
}
|
|
109
132
|
|
|
110
133
|
async function commandInit(root: string, force: boolean): Promise<void> {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
134
|
+
clack.intro("agents init")
|
|
135
|
+
const result = await initProject(root, { force })
|
|
136
|
+
clack.note(result.messages.join("\n"), "Updated")
|
|
137
|
+
clack.outro("Project breadcrumb is ready.")
|
|
115
138
|
}
|
|
116
139
|
|
|
117
140
|
async function commandDiscover(root: string, json: boolean): Promise<void> {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
const documents = await discoverAgentDocuments(root)
|
|
142
|
+
if (json) {
|
|
143
|
+
console.log(JSON.stringify(documents, null, 2))
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
clack.intro("agents discover")
|
|
148
|
+
if (documents.length === 0) {
|
|
149
|
+
clack.outro("No supplemental AGENTS.md files found.")
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
clack.note(formatDocumentList(documents), `Found ${documents.length}`)
|
|
154
|
+
clack.outro("Use agents add <path> to enable one.")
|
|
132
155
|
}
|
|
133
156
|
|
|
134
157
|
async function commandAdd(root: string, paths: string[]): Promise<void> {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
158
|
+
if (paths.length === 0) {
|
|
159
|
+
throw new Error("add requires at least one AGENTS.md path")
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const documents = await Promise.all(
|
|
163
|
+
paths.map((path) =>
|
|
164
|
+
describeAgentDocument(
|
|
165
|
+
root,
|
|
166
|
+
formatProjectPath(root, resolveFromRoot(root, path)),
|
|
167
|
+
),
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const file = await addDocuments(root, documents)
|
|
172
|
+
clack.intro("agents add")
|
|
173
|
+
clack.note(formatDocumentList(file.documents), "Promoted documents")
|
|
174
|
+
clack.outro(
|
|
175
|
+
`Added ${documents.length} document${documents.length === 1 ? "" : "s"}.`,
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function formatDocumentList(
|
|
180
|
+
documents: { path: string; description?: string | undefined }[],
|
|
181
|
+
): string {
|
|
182
|
+
return documents
|
|
183
|
+
.map((doc) =>
|
|
184
|
+
doc.description ? `${doc.path}\n ${doc.description}` : doc.path,
|
|
185
|
+
)
|
|
186
|
+
.join("\n")
|
|
147
187
|
}
|
|
148
188
|
|
|
149
189
|
async function commandRemove(root: string, paths: string[]): Promise<void> {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
190
|
+
if (paths.length === 0) {
|
|
191
|
+
throw new Error("remove requires at least one path")
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const normalizedPaths = paths.map((path) =>
|
|
195
|
+
formatProjectPath(root, resolveFromRoot(root, path)),
|
|
196
|
+
)
|
|
197
|
+
const result = await removeDocuments(root, normalizedPaths)
|
|
198
|
+
clack.intro("agents remove")
|
|
199
|
+
clack.note(
|
|
200
|
+
result.removed.join("\n") || "No matching documents were listed.",
|
|
201
|
+
"Removed",
|
|
202
|
+
)
|
|
203
|
+
clack.outro(
|
|
204
|
+
`agents.yaml now has ${result.file.documents.length} promoted document${result.file.documents.length === 1 ? "" : "s"}.`,
|
|
205
|
+
)
|
|
161
206
|
}
|
|
162
207
|
|
|
163
208
|
async function commandValidate(root: string, json: boolean): Promise<void> {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
209
|
+
const result = await validateAgentsFile(root)
|
|
210
|
+
if (json) {
|
|
211
|
+
console.log(JSON.stringify(result, null, 2))
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
clack.intro("agents validate")
|
|
216
|
+
if (result.errors.length > 0) {
|
|
217
|
+
clack.note(result.errors.join("\n"), "Errors")
|
|
218
|
+
}
|
|
219
|
+
if (result.warnings.length > 0) {
|
|
220
|
+
clack.note(result.warnings.join("\n"), "Warnings")
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
clack.outro(
|
|
224
|
+
result.ok ? "agents.yaml is valid." : "agents.yaml needs attention.",
|
|
225
|
+
)
|
|
226
|
+
if (!result.ok) {
|
|
227
|
+
process.exitCode = 1
|
|
228
|
+
}
|
|
182
229
|
}
|
|
183
230
|
|
|
184
231
|
async function interactive(root: string): Promise<void> {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
clack.intro("agents")
|
|
233
|
+
const action = await clack.select({
|
|
234
|
+
message: "What would you like to do?",
|
|
235
|
+
options: [
|
|
236
|
+
{ value: "discover", label: "Discover and enable AGENTS.md files" },
|
|
237
|
+
{ value: "validate", label: "Validate agents.yaml" },
|
|
238
|
+
{ value: "init", label: "Initialize breadcrumb files" },
|
|
239
|
+
],
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
if (clack.isCancel(action)) {
|
|
243
|
+
clack.cancel("Cancelled.")
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (action === "init") {
|
|
248
|
+
await commandInit(root, false)
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (action === "validate") {
|
|
253
|
+
await commandValidate(root, false)
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const existing = await loadAgentsFile(root)
|
|
258
|
+
const discovered = await discoverAgentDocuments(root)
|
|
259
|
+
const candidates = discovered.filter(
|
|
260
|
+
(doc) => !existing.documents.some((active) => active.path === doc.path),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if (candidates.length === 0) {
|
|
264
|
+
clack.outro("No unlisted supplemental AGENTS.md files found.")
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const selected = await chooseDocumentsToEnable(
|
|
269
|
+
candidates.map((doc) => ({ value: doc.path, label: doc.path })),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if (
|
|
273
|
+
clack.isCancel(selected) ||
|
|
274
|
+
selected === undefined ||
|
|
275
|
+
selected.length === 0
|
|
276
|
+
) {
|
|
277
|
+
clack.cancel("No documents selected.")
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
await commandAdd(root, selected)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function chooseDocumentsToEnable(
|
|
285
|
+
options: DocumentOption[],
|
|
286
|
+
): Promise<string[] | symbol | undefined> {
|
|
287
|
+
return new MultiSelectPrompt<DocumentOption>({
|
|
288
|
+
options,
|
|
289
|
+
required: false,
|
|
290
|
+
render() {
|
|
291
|
+
const prefix = `${styleText("cyan", clack.S_BAR)} `
|
|
292
|
+
const selected = this.value ?? []
|
|
293
|
+
|
|
294
|
+
return `${styleText("gray", clack.S_BAR)}
|
|
295
|
+
${clack.symbol(this.state)} Choose documents to enable
|
|
296
|
+
${prefix}${clack.limitOptions({
|
|
297
|
+
options: this.options,
|
|
298
|
+
cursor: this.cursor,
|
|
299
|
+
columnPadding: prefix.length,
|
|
300
|
+
style: (option, active) =>
|
|
301
|
+
styleDocumentOption(option, {
|
|
302
|
+
active,
|
|
303
|
+
selected: selected.includes(option.value),
|
|
304
|
+
}),
|
|
305
|
+
}).join(`
|
|
306
|
+
${prefix}`)}
|
|
307
|
+
${styleText("cyan", clack.S_BAR_END)}
|
|
308
|
+
`
|
|
309
|
+
},
|
|
310
|
+
}).prompt()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function styleDocumentOption(
|
|
314
|
+
option: DocumentOption,
|
|
315
|
+
state: { active: boolean; selected: boolean },
|
|
316
|
+
): string {
|
|
317
|
+
if (option.disabled) {
|
|
318
|
+
return `${styleText("gray", clack.S_CHECKBOX_INACTIVE)} ${styleText(
|
|
319
|
+
["strikethrough", "gray"],
|
|
320
|
+
option.label,
|
|
321
|
+
)}`
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const checkbox = state.selected
|
|
325
|
+
? styleText("green", clack.S_CHECKBOX_SELECTED)
|
|
326
|
+
: state.active
|
|
327
|
+
? styleText("cyan", clack.S_CHECKBOX_ACTIVE)
|
|
328
|
+
: styleText("dim", clack.S_CHECKBOX_INACTIVE)
|
|
329
|
+
const cursor = state.active ? styleText("cyan", ">") : " "
|
|
330
|
+
const label = state.active ? option.label : styleText("dim", option.label)
|
|
331
|
+
return `${cursor} ${checkbox} ${label}`
|
|
233
332
|
}
|