agents.yaml 0.2.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/dist/index.mjs +36 -9
- package/package.json +2 -1
- package/src/agents-file.ts +185 -170
- package/src/discover.test.ts +134 -95
- package/src/discover.ts +152 -123
- package/src/index.ts +4 -4
- package/src/paths.ts +13 -8
- package/src/run.ts +285 -196
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 { describeAgentDocument, 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
|
|
@@ -30,214 +45,288 @@ Usage:
|
|
|
30
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
)
|
|
149
177
|
}
|
|
150
178
|
|
|
151
179
|
function formatDocumentList(
|
|
152
|
-
|
|
180
|
+
documents: { path: string; description?: string | undefined }[],
|
|
153
181
|
): string {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
182
|
+
return documents
|
|
183
|
+
.map((doc) =>
|
|
184
|
+
doc.description ? `${doc.path}\n ${doc.description}` : doc.path,
|
|
185
|
+
)
|
|
186
|
+
.join("\n")
|
|
157
187
|
}
|
|
158
188
|
|
|
159
189
|
async function commandRemove(root: string, paths: string[]): Promise<void> {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
+
)
|
|
171
206
|
}
|
|
172
207
|
|
|
173
208
|
async function commandValidate(root: string, json: boolean): Promise<void> {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
}
|
|
192
229
|
}
|
|
193
230
|
|
|
194
231
|
async function interactive(root: string): Promise<void> {
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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}`
|
|
243
332
|
}
|