@agentskit/doc-bridge 1.0.1 → 1.1.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/CHANGELOG.md +36 -0
- package/CONTRIBUTING.md +7 -0
- package/README.md +81 -15
- package/SECURITY.md +1 -1
- package/action.yml +2 -2
- package/dist/cli/program.js +1248 -316
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +338 -13
- package/dist/config/index.js.map +1 -1
- package/dist/{index-CPUJbTbg.d.ts → index-DGI9TBLE.d.ts} +906 -11
- package/dist/index.d.ts +65 -11
- package/dist/index.js +1084 -171
- package/dist/index.js.map +1 -1
- package/docs/RELEASE.md +19 -21
- package/docs/getting-started.md +27 -2
- package/docs/landing/assets/doc-bridge-hero.webp +0 -0
- package/docs/landing/assets/doc-bridge-surfaces.webp +0 -0
- package/docs/landing/assets/doc-bridge-two-way.webp +0 -0
- package/docs/landing/index.html +70 -10
- package/docs/playbook/doc-bridge-pattern.md +2 -2
- package/docs/recipes/index-pipeline.md +2 -2
- package/docs/schemas/memory-candidate-v1.md +10 -1
- package/docs/spec/cli.md +1 -0
- package/docs/spec/config-v1.md +51 -6
- package/docs/spec/documentation-standard-v1.md +131 -0
- package/ecosystem-claims.json +187 -0
- package/ecosystem-upstream.json +9 -0
- package/ecosystem.json +231 -0
- package/package.json +14 -2
- package/scripts/check-ecosystem-upstream.mjs +50 -0
- package/src/cli/program.ts +43 -9
- package/src/config/index.ts +7 -1
- package/src/config/load-config.ts +4 -14
- package/src/config/schema.ts +91 -0
- package/src/conformance/documentation-standard-v1.ts +502 -0
- package/src/conformance/ecosystem-contract.ts +175 -0
- package/src/gates/run-gates.ts +33 -4
- package/src/index-builder/human-adapters/core.ts +12 -5
- package/src/index-builder/human-adapters/docusaurus.ts +29 -44
- package/src/index-builder/human-adapters/index.ts +15 -3
- package/src/index-builder/scan-corpus.ts +6 -6
- package/src/index.ts +17 -0
- package/src/lib/bounded-text.ts +25 -0
- package/src/lib/paths.ts +20 -2
- package/src/lib/static-js-literal.ts +261 -0
- package/src/lib/walk.ts +23 -4
- package/src/version.ts +1 -1
package/dist/cli/program.js
CHANGED
|
@@ -1,12 +1,271 @@
|
|
|
1
1
|
// src/cli/program.ts
|
|
2
|
-
import { existsSync as
|
|
3
|
-
import { dirname as dirname7, resolve as
|
|
2
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync16, writeFileSync as writeFileSync4 } from "fs";
|
|
3
|
+
import { dirname as dirname7, resolve as resolve12 } from "path";
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
5
|
|
|
6
6
|
// src/config/load-config.ts
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
8
8
|
import { basename, dirname, join, resolve } from "path";
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// src/lib/static-js-literal.ts
|
|
11
|
+
var stripComments = (source) => {
|
|
12
|
+
let out = "";
|
|
13
|
+
let index = 0;
|
|
14
|
+
let quote;
|
|
15
|
+
while (index < source.length) {
|
|
16
|
+
const char = source[index] ?? "";
|
|
17
|
+
const next = source[index + 1] ?? "";
|
|
18
|
+
if (quote) {
|
|
19
|
+
out += char;
|
|
20
|
+
if (char === "\\") {
|
|
21
|
+
out += next;
|
|
22
|
+
index += 2;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (char === quote) quote = void 0;
|
|
26
|
+
index += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
30
|
+
quote = char;
|
|
31
|
+
out += char;
|
|
32
|
+
index += 1;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (char === "/" && next === "/") {
|
|
36
|
+
while (index < source.length && source[index] !== "\n") index += 1;
|
|
37
|
+
out += "\n";
|
|
38
|
+
index += 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (char === "/" && next === "*") {
|
|
42
|
+
index += 2;
|
|
43
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) index += 1;
|
|
44
|
+
index += 2;
|
|
45
|
+
out += " ";
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
out += char;
|
|
49
|
+
index += 1;
|
|
50
|
+
}
|
|
51
|
+
if (quote) throw new Error("Unterminated string in static JavaScript literal.");
|
|
52
|
+
return out;
|
|
53
|
+
};
|
|
54
|
+
var LiteralParser = class {
|
|
55
|
+
constructor(source) {
|
|
56
|
+
this.source = source;
|
|
57
|
+
}
|
|
58
|
+
source;
|
|
59
|
+
index = 0;
|
|
60
|
+
parseAt(index) {
|
|
61
|
+
this.index = index;
|
|
62
|
+
const value = this.value();
|
|
63
|
+
this.space();
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
space() {
|
|
67
|
+
while (/\s/.test(this.source[this.index] ?? "")) this.index += 1;
|
|
68
|
+
}
|
|
69
|
+
value() {
|
|
70
|
+
this.space();
|
|
71
|
+
const char = this.source[this.index];
|
|
72
|
+
if (char === "{") return this.object();
|
|
73
|
+
if (char === "[") return this.array();
|
|
74
|
+
if (char === '"' || char === "'") return this.string();
|
|
75
|
+
const word = this.word();
|
|
76
|
+
if (word === "true") return true;
|
|
77
|
+
if (word === "false") return false;
|
|
78
|
+
if (word === "null") return null;
|
|
79
|
+
if (/^-?\d+(?:\.\d+)?$/.test(word)) return Number(word);
|
|
80
|
+
throw new Error(`Unsupported dynamic sidebar expression near "${word || char || "EOF"}".`);
|
|
81
|
+
}
|
|
82
|
+
object() {
|
|
83
|
+
const result = {};
|
|
84
|
+
this.index += 1;
|
|
85
|
+
this.space();
|
|
86
|
+
while (this.source[this.index] !== "}") {
|
|
87
|
+
const key = this.source[this.index] === '"' || this.source[this.index] === "'" ? this.string() : this.word();
|
|
88
|
+
if (!key) throw new Error("Static sidebar object key is required.");
|
|
89
|
+
this.space();
|
|
90
|
+
if (this.source[this.index] !== ":") throw new Error(`Static sidebar key "${key}" requires a literal value.`);
|
|
91
|
+
this.index += 1;
|
|
92
|
+
result[key] = this.value();
|
|
93
|
+
this.space();
|
|
94
|
+
if (this.source[this.index] === ",") {
|
|
95
|
+
this.index += 1;
|
|
96
|
+
this.space();
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (this.source[this.index] !== "}") throw new Error("Expected comma or closing brace in static sidebar.");
|
|
100
|
+
}
|
|
101
|
+
this.index += 1;
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
array() {
|
|
105
|
+
const result = [];
|
|
106
|
+
this.index += 1;
|
|
107
|
+
this.space();
|
|
108
|
+
while (this.source[this.index] !== "]") {
|
|
109
|
+
result.push(this.value());
|
|
110
|
+
this.space();
|
|
111
|
+
if (this.source[this.index] === ",") {
|
|
112
|
+
this.index += 1;
|
|
113
|
+
this.space();
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (this.source[this.index] !== "]") throw new Error("Expected comma or closing bracket in static sidebar.");
|
|
117
|
+
}
|
|
118
|
+
this.index += 1;
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
string() {
|
|
122
|
+
const quote = this.source[this.index] ?? "";
|
|
123
|
+
this.index += 1;
|
|
124
|
+
let result = "";
|
|
125
|
+
while (this.index < this.source.length) {
|
|
126
|
+
const char = this.source[this.index] ?? "";
|
|
127
|
+
if (char === quote) {
|
|
128
|
+
this.index += 1;
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
if (char === "\\") {
|
|
132
|
+
const escaped = this.source[this.index + 1];
|
|
133
|
+
if (escaped === void 0) break;
|
|
134
|
+
const escapes = { n: "\n", r: "\r", t: " " };
|
|
135
|
+
result += escapes[escaped] ?? escaped;
|
|
136
|
+
this.index += 2;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
result += char;
|
|
140
|
+
this.index += 1;
|
|
141
|
+
}
|
|
142
|
+
throw new Error("Unterminated string in static sidebar.");
|
|
143
|
+
}
|
|
144
|
+
word() {
|
|
145
|
+
this.space();
|
|
146
|
+
const start = this.index;
|
|
147
|
+
while (/[A-Za-z0-9_$.-]/.test(this.source[this.index] ?? "")) this.index += 1;
|
|
148
|
+
return this.source.slice(start, this.index);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var maskStrings = (source) => {
|
|
152
|
+
const chars = [...source];
|
|
153
|
+
let quote;
|
|
154
|
+
let previousToken;
|
|
155
|
+
const regexPrefixKeywords = /* @__PURE__ */ new Set([
|
|
156
|
+
"await",
|
|
157
|
+
"case",
|
|
158
|
+
"delete",
|
|
159
|
+
"do",
|
|
160
|
+
"else",
|
|
161
|
+
"in",
|
|
162
|
+
"instanceof",
|
|
163
|
+
"new",
|
|
164
|
+
"of",
|
|
165
|
+
"return",
|
|
166
|
+
"throw",
|
|
167
|
+
"typeof",
|
|
168
|
+
"void",
|
|
169
|
+
"yield"
|
|
170
|
+
]);
|
|
171
|
+
const canStartRegex = () => previousToken === void 0 || "=(:,[!&|?{};".includes(previousToken) || previousToken === "=>" || regexPrefixKeywords.has(previousToken);
|
|
172
|
+
for (let index = 0; index < chars.length; index += 1) {
|
|
173
|
+
const char = chars[index] ?? "";
|
|
174
|
+
if (quote) {
|
|
175
|
+
chars[index] = " ";
|
|
176
|
+
if (char === "\\") {
|
|
177
|
+
if (index + 1 < chars.length) chars[index + 1] = " ";
|
|
178
|
+
index += 1;
|
|
179
|
+
} else if (char === quote) {
|
|
180
|
+
quote = void 0;
|
|
181
|
+
previousToken = "literal";
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
186
|
+
quote = char;
|
|
187
|
+
chars[index] = " ";
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (char === "/" && canStartRegex()) {
|
|
191
|
+
chars[index] = " ";
|
|
192
|
+
let inClass = false;
|
|
193
|
+
for (index += 1; index < chars.length; index += 1) {
|
|
194
|
+
const regexChar = chars[index] ?? "";
|
|
195
|
+
chars[index] = " ";
|
|
196
|
+
if (regexChar === "\\") {
|
|
197
|
+
if (index + 1 < chars.length) chars[index + 1] = " ";
|
|
198
|
+
index += 1;
|
|
199
|
+
} else if (regexChar === "[") {
|
|
200
|
+
inClass = true;
|
|
201
|
+
} else if (regexChar === "]") {
|
|
202
|
+
inClass = false;
|
|
203
|
+
} else if (regexChar === "/" && !inClass) {
|
|
204
|
+
while (/[A-Za-z]/.test(chars[index + 1] ?? "")) {
|
|
205
|
+
index += 1;
|
|
206
|
+
chars[index] = " ";
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
previousToken = "literal";
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (/[A-Za-z_$]/.test(char)) {
|
|
215
|
+
const start = index;
|
|
216
|
+
while (/[A-Za-z0-9_$]/.test(chars[index + 1] ?? "")) index += 1;
|
|
217
|
+
previousToken = chars.slice(start, index + 1).join("");
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (char === "=" && chars[index + 1] === ">") {
|
|
221
|
+
previousToken = "=>";
|
|
222
|
+
index += 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (!/\s/.test(char)) previousToken = char;
|
|
226
|
+
}
|
|
227
|
+
return chars.join("");
|
|
228
|
+
};
|
|
229
|
+
var nonSpace = (source, start) => {
|
|
230
|
+
let index = start;
|
|
231
|
+
while (/\s/.test(source[index] ?? "")) index += 1;
|
|
232
|
+
return index;
|
|
233
|
+
};
|
|
234
|
+
var escapedRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
235
|
+
var parseStaticJsObject = (source) => {
|
|
236
|
+
const clean = stripComments(source);
|
|
237
|
+
const mask = maskStrings(clean);
|
|
238
|
+
const parseBinding = (identifier) => {
|
|
239
|
+
const declaration = new RegExp(`\\b(?:const|let|var)\\s+${escapedRegex(identifier)}(?:\\s*:[^=;]+)?\\s*=`).exec(mask);
|
|
240
|
+
if (!declaration) throw new Error(`Exported binding "${identifier}" must reference a static object declaration.`);
|
|
241
|
+
const start = nonSpace(mask, declaration.index + declaration[0].length);
|
|
242
|
+
if (mask[start] !== "{") throw new Error(`Exported binding "${identifier}" must be a static object literal.`);
|
|
243
|
+
return new LiteralParser(clean).parseAt(start);
|
|
244
|
+
};
|
|
245
|
+
const commonJs = /\bmodule\.exports\s*=/.exec(mask);
|
|
246
|
+
if (commonJs) {
|
|
247
|
+
const start = nonSpace(mask, commonJs.index + commonJs[0].length);
|
|
248
|
+
if (mask[start] === "{") return new LiteralParser(clean).parseAt(start);
|
|
249
|
+
const identifier = /^[A-Za-z_$][\w$]*/.exec(mask.slice(start))?.[0];
|
|
250
|
+
if (identifier) return parseBinding(identifier);
|
|
251
|
+
throw new Error("module.exports must be assigned a static object literal or binding.");
|
|
252
|
+
}
|
|
253
|
+
const exported = /\bexport\s+default\b/.exec(mask);
|
|
254
|
+
if (exported) {
|
|
255
|
+
let start = nonSpace(mask, exported.index + exported[0].length);
|
|
256
|
+
if (mask.slice(start).startsWith("defineConfig")) {
|
|
257
|
+
start = nonSpace(mask, start + "defineConfig".length);
|
|
258
|
+
if (mask[start] !== "(") throw new Error("defineConfig must be called with a static object literal.");
|
|
259
|
+
start = nonSpace(mask, start + 1);
|
|
260
|
+
if (mask[start] !== "{") throw new Error("defineConfig must be called with a static object literal.");
|
|
261
|
+
return new LiteralParser(clean).parseAt(start);
|
|
262
|
+
}
|
|
263
|
+
if (mask[start] === "{") return new LiteralParser(clean).parseAt(start);
|
|
264
|
+
const identifier = /^[A-Za-z_$][\w$]*/.exec(mask.slice(start))?.[0];
|
|
265
|
+
if (identifier) return parseBinding(identifier);
|
|
266
|
+
}
|
|
267
|
+
throw new Error("Docusaurus sidebar must export a static object or assign one to a const.");
|
|
268
|
+
};
|
|
10
269
|
|
|
11
270
|
// src/config/defaults.ts
|
|
12
271
|
var DEFAULT_AGENT_EXCLUDE = ["**/node_modules/**", "**/.git/**"];
|
|
@@ -147,7 +406,8 @@ var GatesConfigSchema = z.object({
|
|
|
147
406
|
"okf-type",
|
|
148
407
|
"docs-style",
|
|
149
408
|
"routing-currency",
|
|
150
|
-
"bootstrap-size"
|
|
409
|
+
"bootstrap-size",
|
|
410
|
+
"documentation-standard-v1"
|
|
151
411
|
])
|
|
152
412
|
).max(16).optional(),
|
|
153
413
|
exclude: z.array(
|
|
@@ -158,7 +418,8 @@ var GatesConfigSchema = z.object({
|
|
|
158
418
|
"okf-type",
|
|
159
419
|
"docs-style",
|
|
160
420
|
"routing-currency",
|
|
161
|
-
"bootstrap-size"
|
|
421
|
+
"bootstrap-size",
|
|
422
|
+
"documentation-standard-v1"
|
|
162
423
|
])
|
|
163
424
|
).max(16).optional(),
|
|
164
425
|
options: z.record(z.string(), z.unknown()).optional()
|
|
@@ -237,6 +498,69 @@ var FederationConfigSchema = z.object({
|
|
|
237
498
|
}).strict()
|
|
238
499
|
).max(16).optional()
|
|
239
500
|
}).strict();
|
|
501
|
+
var DocumentationEvidenceFileSchema = z.object({
|
|
502
|
+
path: z.string().min(1).max(512),
|
|
503
|
+
contains: z.array(z.string().min(1).max(512)).min(1).max(32)
|
|
504
|
+
}).strict();
|
|
505
|
+
var DocumentationLinkEvidenceSchema = z.object({
|
|
506
|
+
url: z.string().url().max(512),
|
|
507
|
+
paths: z.array(z.string().min(1).max(512)).min(1).max(32)
|
|
508
|
+
}).strict();
|
|
509
|
+
var DocumentationQuickstartEvidenceSchema = z.object({
|
|
510
|
+
id: z.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
|
|
511
|
+
doc: z.string().min(1).max(512),
|
|
512
|
+
test: z.string().min(1).max(512),
|
|
513
|
+
command: z.string().min(1).max(512),
|
|
514
|
+
testContains: z.array(z.string().min(1).max(256)).min(1).max(16)
|
|
515
|
+
}).strict();
|
|
516
|
+
var EcosystemContractEvidenceSchema = z.object({
|
|
517
|
+
manifest: z.string().min(1).max(512),
|
|
518
|
+
claims: z.string().min(1).max(512),
|
|
519
|
+
productId: z.string().regex(/^[a-z][a-z0-9-]*$/).max(128)
|
|
520
|
+
}).strict();
|
|
521
|
+
var DocumentationStandardRuleIdSchema = z.enum([
|
|
522
|
+
"human-docs",
|
|
523
|
+
"llms-and-raw-source",
|
|
524
|
+
"agent-handoffs",
|
|
525
|
+
"contribution",
|
|
526
|
+
"metadata",
|
|
527
|
+
"cross-links",
|
|
528
|
+
"tested-quickstarts",
|
|
529
|
+
"visual-explanations",
|
|
530
|
+
"structured-diagrams"
|
|
531
|
+
]);
|
|
532
|
+
var DocumentationStandardExceptionSchema = z.object({
|
|
533
|
+
ruleId: DocumentationStandardRuleIdSchema,
|
|
534
|
+
reason: z.string().min(10).max(1024),
|
|
535
|
+
approvedBy: z.string().min(1).max(256),
|
|
536
|
+
trackingUrl: z.string().url().max(512)
|
|
537
|
+
}).strict();
|
|
538
|
+
var DocumentationStandardV1ConfigSchema = z.object({
|
|
539
|
+
rawSources: z.array(z.string().min(1).max(512)).max(64).optional(),
|
|
540
|
+
contributionPaths: z.array(z.string().min(1).max(512)).max(16).optional(),
|
|
541
|
+
metadata: z.array(DocumentationEvidenceFileSchema).max(32).optional(),
|
|
542
|
+
links: z.array(DocumentationLinkEvidenceSchema).max(64).optional(),
|
|
543
|
+
quickstarts: z.array(DocumentationQuickstartEvidenceSchema).max(32).optional(),
|
|
544
|
+
visuals: z.array(z.string().min(1).max(512)).max(64).optional(),
|
|
545
|
+
diagrams: z.array(DocumentationEvidenceFileSchema).max(32).optional(),
|
|
546
|
+
ecosystemContract: EcosystemContractEvidenceSchema.optional(),
|
|
547
|
+
exceptions: z.array(DocumentationStandardExceptionSchema).max(32).optional()
|
|
548
|
+
}).strict().superRefine((value, context) => {
|
|
549
|
+
const seen = /* @__PURE__ */ new Set();
|
|
550
|
+
for (const [index, exception] of (value.exceptions ?? []).entries()) {
|
|
551
|
+
if (seen.has(exception.ruleId)) {
|
|
552
|
+
context.addIssue({
|
|
553
|
+
code: "custom",
|
|
554
|
+
path: ["exceptions", index, "ruleId"],
|
|
555
|
+
message: `Duplicate exception for rule ${exception.ruleId}`
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
seen.add(exception.ruleId);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
var ConformanceConfigSchema = z.object({
|
|
562
|
+
documentationStandardV1: DocumentationStandardV1ConfigSchema.optional()
|
|
563
|
+
}).strict();
|
|
240
564
|
var DocBridgeConfigV1Schema = z.object({
|
|
241
565
|
schemaVersion: z.literal(CONFIG_SCHEMA_VERSION),
|
|
242
566
|
project: z.object({
|
|
@@ -252,7 +576,8 @@ var DocBridgeConfigV1Schema = z.object({
|
|
|
252
576
|
gates: GatesConfigSchema.optional(),
|
|
253
577
|
surfaces: SurfacesConfigSchema.optional(),
|
|
254
578
|
intelligence: IntelligenceConfigSchema.optional(),
|
|
255
|
-
federation: FederationConfigSchema.optional()
|
|
579
|
+
federation: FederationConfigSchema.optional(),
|
|
580
|
+
conformance: ConformanceConfigSchema.optional()
|
|
256
581
|
}).strict();
|
|
257
582
|
|
|
258
583
|
// src/config/load-config.ts
|
|
@@ -299,22 +624,16 @@ var parseJsonConfig = (raw, path) => {
|
|
|
299
624
|
}
|
|
300
625
|
};
|
|
301
626
|
var parseCodeConfig = (raw, path) => {
|
|
302
|
-
const
|
|
303
|
-
if (/\bimport\b/.test(
|
|
627
|
+
const unsupportedImports = raw.replace(/import\s+type\s+[\s\S]*?;?\n/g, "").replace(/import\s+\{\s*defineConfig\s*\}\s+from\s+['"]@agentskit\/doc-bridge(?:\/config)?['"];?\n?/g, "");
|
|
628
|
+
if (/\bimport\b/.test(unsupportedImports)) {
|
|
304
629
|
throw new Error(`Unsupported import in ${path}. Static config only supports defineConfig imports.`);
|
|
305
630
|
}
|
|
306
|
-
const sandbox = {
|
|
307
|
-
module: { exports: {} },
|
|
308
|
-
exports: {},
|
|
309
|
-
defineConfig: (config) => config
|
|
310
|
-
};
|
|
311
631
|
try {
|
|
312
|
-
|
|
632
|
+
return parseStaticJsObject(raw);
|
|
313
633
|
} catch (error) {
|
|
314
634
|
const message = error instanceof Error ? error.message : String(error);
|
|
315
635
|
throw new Error(`Failed to load config at ${path}: ${message}`);
|
|
316
636
|
}
|
|
317
|
-
return sandbox.module.exports.default;
|
|
318
637
|
};
|
|
319
638
|
var parseConfig = (input) => {
|
|
320
639
|
const result = DocBridgeConfigV1Schema.safeParse(input);
|
|
@@ -345,24 +664,197 @@ var projectRootFromConfigPath = (configFilePath, projectRootField) => {
|
|
|
345
664
|
return configDir;
|
|
346
665
|
};
|
|
347
666
|
|
|
667
|
+
// src/conformance/documentation-standard-v1.ts
|
|
668
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, realpathSync as realpathSync5, statSync as statSync3 } from "fs";
|
|
669
|
+
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
|
|
670
|
+
|
|
671
|
+
// src/conformance/ecosystem-contract.ts
|
|
672
|
+
import { z as z2 } from "zod";
|
|
673
|
+
var NonEmptyStringSchema = z2.string().refine((value) => value.trim().length > 0, "must be non-empty");
|
|
674
|
+
var HttpsUrlSchema = z2.string().url().refine((value) => value.startsWith("https://"), "must use https");
|
|
675
|
+
var RepoSchema = z2.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/);
|
|
676
|
+
var SlugSchema = z2.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
677
|
+
var SurfaceSchema = z2.object({
|
|
678
|
+
home: HttpsUrlSchema.optional(),
|
|
679
|
+
docs: HttpsUrlSchema.optional(),
|
|
680
|
+
llms: HttpsUrlSchema.optional(),
|
|
681
|
+
stats: HttpsUrlSchema.optional(),
|
|
682
|
+
documentation: z2.enum(["fumadocs", "repository"]),
|
|
683
|
+
chat: z2.enum(["agentschat", "custom", "none"])
|
|
684
|
+
}).passthrough();
|
|
685
|
+
var ProductSchema = z2.object({
|
|
686
|
+
id: SlugSchema,
|
|
687
|
+
name: NonEmptyStringSchema,
|
|
688
|
+
shortName: NonEmptyStringSchema,
|
|
689
|
+
kind: NonEmptyStringSchema,
|
|
690
|
+
role: NonEmptyStringSchema,
|
|
691
|
+
promise: NonEmptyStringSchema,
|
|
692
|
+
maturity: z2.enum(["planning", "alpha", "beta", "stable", "deprecated"]),
|
|
693
|
+
repo: RepoSchema,
|
|
694
|
+
accent: z2.string().regex(/^#[0-9A-Fa-f]{6}$/),
|
|
695
|
+
surfaces: SurfaceSchema,
|
|
696
|
+
navigation: z2.object({
|
|
697
|
+
showInBar: z2.boolean(),
|
|
698
|
+
order: z2.number().int().nonnegative().optional(),
|
|
699
|
+
next: z2.array(SlugSchema)
|
|
700
|
+
}).passthrough()
|
|
701
|
+
}).passthrough();
|
|
702
|
+
var LegacyPropertySchema = z2.object({
|
|
703
|
+
id: SlugSchema,
|
|
704
|
+
name: NonEmptyStringSchema,
|
|
705
|
+
barLabel: NonEmptyStringSchema,
|
|
706
|
+
domain: NonEmptyStringSchema,
|
|
707
|
+
url: HttpsUrlSchema,
|
|
708
|
+
repo: RepoSchema,
|
|
709
|
+
tagline: NonEmptyStringSchema,
|
|
710
|
+
kind: NonEmptyStringSchema,
|
|
711
|
+
accent: z2.string().regex(/^#[0-9A-Fa-f]{6}$/),
|
|
712
|
+
llms: HttpsUrlSchema.optional(),
|
|
713
|
+
stats: HttpsUrlSchema.optional()
|
|
714
|
+
}).passthrough();
|
|
715
|
+
var ManifestSchema = z2.object({
|
|
716
|
+
schemaVersion: z2.literal(2),
|
|
717
|
+
parentBrand: z2.object({ id: NonEmptyStringSchema, name: NonEmptyStringSchema }).passthrough(),
|
|
718
|
+
products: z2.array(ProductSchema).min(1),
|
|
719
|
+
properties: z2.array(LegacyPropertySchema).length(4),
|
|
720
|
+
builder: z2.object({ id: NonEmptyStringSchema, name: NonEmptyStringSchema, url: HttpsUrlSchema }).passthrough().optional()
|
|
721
|
+
}).passthrough();
|
|
722
|
+
var EvidenceSchema = z2.discriminatedUnion("type", [
|
|
723
|
+
z2.object({
|
|
724
|
+
type: z2.literal("repository-derivation"),
|
|
725
|
+
repo: RepoSchema,
|
|
726
|
+
path: NonEmptyStringSchema,
|
|
727
|
+
summary: NonEmptyStringSchema
|
|
728
|
+
}).passthrough(),
|
|
729
|
+
z2.object({ type: z2.literal("endpoint"), url: HttpsUrlSchema, summary: NonEmptyStringSchema }).passthrough()
|
|
730
|
+
]);
|
|
731
|
+
var ClaimSchema = z2.object({
|
|
732
|
+
id: NonEmptyStringSchema,
|
|
733
|
+
value: z2.number().finite().nonnegative(),
|
|
734
|
+
noun: NonEmptyStringSchema,
|
|
735
|
+
conservativeFloor: z2.number().int().nonnegative().optional(),
|
|
736
|
+
evidence: EvidenceSchema
|
|
737
|
+
}).passthrough();
|
|
738
|
+
var ClaimProductSchema = z2.object({
|
|
739
|
+
productId: SlugSchema,
|
|
740
|
+
source: z2.discriminatedUnion("type", [
|
|
741
|
+
z2.object({ type: z2.literal("endpoint"), url: HttpsUrlSchema }).passthrough(),
|
|
742
|
+
z2.object({ type: z2.literal("repository"), repo: RepoSchema }).passthrough()
|
|
743
|
+
]),
|
|
744
|
+
verification: z2.enum(["verified", "declared"]),
|
|
745
|
+
claims: z2.array(ClaimSchema)
|
|
746
|
+
}).passthrough();
|
|
747
|
+
var ClaimsSchema = z2.object({
|
|
748
|
+
schemaVersion: z2.literal(1),
|
|
749
|
+
manifestSchemaVersion: z2.literal(2),
|
|
750
|
+
products: z2.array(ClaimProductSchema)
|
|
751
|
+
}).passthrough();
|
|
752
|
+
var LEGACY_PRODUCT_IDS = ["agentskit", "akos", "playbook", "registry"];
|
|
753
|
+
var parseCanonicalEcosystemContract = (manifestInput, claimsInput) => {
|
|
754
|
+
const manifest = ManifestSchema.parse(manifestInput);
|
|
755
|
+
const claims = ClaimsSchema.parse(claimsInput);
|
|
756
|
+
const products = new Map(manifest.products.map((product) => [product.id, product]));
|
|
757
|
+
if (products.size !== manifest.products.length) throw new Error("Manifest product IDs must be unique.");
|
|
758
|
+
const navigationOrders = /* @__PURE__ */ new Set();
|
|
759
|
+
for (const product of manifest.products) {
|
|
760
|
+
if (product.surfaces.documentation === "fumadocs" && !product.surfaces.docs) {
|
|
761
|
+
throw new Error(`Product ${product.id} requires a docs surface for Fumadocs.`);
|
|
762
|
+
}
|
|
763
|
+
if (product.navigation.showInBar) {
|
|
764
|
+
if (!product.surfaces.home || product.navigation.order === void 0) {
|
|
765
|
+
throw new Error(`Product ${product.id} requires home and order for shared navigation.`);
|
|
766
|
+
}
|
|
767
|
+
if (navigationOrders.has(product.navigation.order)) throw new Error("Navigation orders must be unique.");
|
|
768
|
+
navigationOrders.add(product.navigation.order);
|
|
769
|
+
}
|
|
770
|
+
const next = new Set(product.navigation.next);
|
|
771
|
+
if (next.size !== product.navigation.next.length || next.has(product.id)) {
|
|
772
|
+
throw new Error(`Product ${product.id} has duplicate or self-referential navigation.`);
|
|
773
|
+
}
|
|
774
|
+
for (const nextId of next) {
|
|
775
|
+
if (!products.has(nextId)) throw new Error(`Product ${product.id} references unknown product ${nextId}.`);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
for (const [index, id] of LEGACY_PRODUCT_IDS.entries()) {
|
|
779
|
+
const legacy = manifest.properties[index];
|
|
780
|
+
const product = products.get(id);
|
|
781
|
+
if (!legacy || !product || legacy.id !== id || !product.surfaces.home) {
|
|
782
|
+
throw new Error(`Legacy property ${index} must project product ${id}.`);
|
|
783
|
+
}
|
|
784
|
+
const expected = {
|
|
785
|
+
name: product.name,
|
|
786
|
+
barLabel: product.shortName,
|
|
787
|
+
domain: new URL(product.surfaces.home).host,
|
|
788
|
+
url: product.surfaces.home,
|
|
789
|
+
repo: product.repo,
|
|
790
|
+
tagline: product.promise,
|
|
791
|
+
kind: product.kind,
|
|
792
|
+
accent: product.accent,
|
|
793
|
+
llms: product.surfaces.llms,
|
|
794
|
+
stats: product.surfaces.stats
|
|
795
|
+
};
|
|
796
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
797
|
+
if (legacy[key] !== value) throw new Error(`Legacy ${id}.${key} must match v2.`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const claimProducts = new Map(claims.products.map((product) => [product.productId, product]));
|
|
801
|
+
if (claimProducts.size !== claims.products.length || claimProducts.size !== products.size) {
|
|
802
|
+
throw new Error("Claims must include every manifest product exactly once.");
|
|
803
|
+
}
|
|
804
|
+
for (const [productId, product] of products) {
|
|
805
|
+
const claimProduct = claimProducts.get(productId);
|
|
806
|
+
if (!claimProduct) throw new Error(`Claims are missing product ${productId}.`);
|
|
807
|
+
if (claimProduct.source.type === "endpoint") {
|
|
808
|
+
if (claimProduct.source.url !== product.surfaces.stats) throw new Error(`Claims source for ${productId} must match stats.`);
|
|
809
|
+
} else if (claimProduct.source.repo !== product.repo) {
|
|
810
|
+
throw new Error(`Claims source for ${productId} must match repo.`);
|
|
811
|
+
}
|
|
812
|
+
if (claimProduct.verification === "declared" && claimProduct.claims.length > 0) {
|
|
813
|
+
throw new Error(`Declared product ${productId} cannot publish claims.`);
|
|
814
|
+
}
|
|
815
|
+
const claimIds = /* @__PURE__ */ new Set();
|
|
816
|
+
for (const claim of claimProduct.claims) {
|
|
817
|
+
if (claimIds.has(claim.id)) throw new Error(`Product ${productId} has duplicate claim ${claim.id}.`);
|
|
818
|
+
claimIds.add(claim.id);
|
|
819
|
+
if (claim.conservativeFloor !== void 0 && claim.conservativeFloor > claim.value) {
|
|
820
|
+
throw new Error(`Claim ${productId}:${claim.id} has a floor above its value.`);
|
|
821
|
+
}
|
|
822
|
+
if (claim.evidence.type === "repository-derivation" && claim.evidence.repo !== product.repo) {
|
|
823
|
+
throw new Error(`Claim ${productId}:${claim.id} evidence must match the product repo.`);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return { manifest, claims };
|
|
828
|
+
};
|
|
829
|
+
|
|
348
830
|
// src/index-builder/build-index.ts
|
|
349
|
-
import { mkdirSync, readFileSync as
|
|
350
|
-
import { dirname as dirname3, join as
|
|
831
|
+
import { mkdirSync, readFileSync as readFileSync6, writeFileSync } from "fs";
|
|
832
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
351
833
|
|
|
352
834
|
// src/lib/paths.ts
|
|
353
|
-
import {
|
|
835
|
+
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
836
|
+
import { isAbsolute, relative, resolve as resolve2, sep } from "path";
|
|
354
837
|
var toPosix = (value) => value.split("\\").join("/");
|
|
838
|
+
var containedProjectPath = (root, path) => {
|
|
839
|
+
const projectRoot = realpathSync.native(resolve2(root));
|
|
840
|
+
const unresolved = resolve2(projectRoot, path);
|
|
841
|
+
const unresolvedRelative = relative(projectRoot, unresolved);
|
|
842
|
+
if (isAbsolute(unresolvedRelative) || unresolvedRelative === ".." || unresolvedRelative.startsWith(`..${sep}`)) return void 0;
|
|
843
|
+
const canonical = existsSync2(unresolved) ? realpathSync.native(unresolved) : unresolved;
|
|
844
|
+
const canonicalRelative = relative(projectRoot, canonical);
|
|
845
|
+
return isAbsolute(canonicalRelative) || canonicalRelative === ".." || canonicalRelative.startsWith(`..${sep}`) ? void 0 : canonical;
|
|
846
|
+
};
|
|
355
847
|
|
|
356
848
|
// src/lib/package-manager.ts
|
|
357
|
-
import { existsSync as
|
|
849
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
358
850
|
import { join as join2 } from "path";
|
|
359
851
|
var detectPackageManager = (root) => {
|
|
360
|
-
if (
|
|
852
|
+
if (existsSync3(join2(root, "pnpm-lock.yaml")) || existsSync3(join2(root, "pnpm-workspace.yaml"))) {
|
|
361
853
|
return "pnpm";
|
|
362
854
|
}
|
|
363
|
-
if (
|
|
364
|
-
if (
|
|
365
|
-
if (
|
|
855
|
+
if (existsSync3(join2(root, "yarn.lock"))) return "yarn";
|
|
856
|
+
if (existsSync3(join2(root, "bun.lockb")) || existsSync3(join2(root, "bun.lock"))) return "bun";
|
|
857
|
+
if (existsSync3(join2(root, "package-lock.json"))) return "npm";
|
|
366
858
|
try {
|
|
367
859
|
const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
|
|
368
860
|
const pm = pkg.packageManager?.split("@")[0];
|
|
@@ -383,7 +875,7 @@ var defaultChecksForTarget = (root, opts) => {
|
|
|
383
875
|
} catch {
|
|
384
876
|
}
|
|
385
877
|
}
|
|
386
|
-
const isWorkspace =
|
|
878
|
+
const isWorkspace = existsSync3(join2(root, "pnpm-workspace.yaml")) || existsSync3(join2(root, "lerna.json")) || Boolean(
|
|
387
879
|
(() => {
|
|
388
880
|
try {
|
|
389
881
|
const pkg = JSON.parse(readFileSync2(join2(root, "package.json"), "utf8"));
|
|
@@ -417,9 +909,24 @@ var defaultChecksForTarget = (root, opts) => {
|
|
|
417
909
|
return opts.strict ? ["npm test", "npm run lint"] : ["npm test"];
|
|
418
910
|
};
|
|
419
911
|
|
|
420
|
-
// src/
|
|
421
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
422
|
-
|
|
912
|
+
// src/lib/bounded-text.ts
|
|
913
|
+
import { readFileSync as readFileSync3, statSync } from "fs";
|
|
914
|
+
var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
915
|
+
var MAX_CORPUS_BYTES = 64 * 1024 * 1024;
|
|
916
|
+
var readBoundedText = (path, budget, limits) => {
|
|
917
|
+
const maxFileBytes = limits?.maxFileBytes ?? MAX_DOCUMENT_BYTES;
|
|
918
|
+
const maxCorpusBytes = limits?.maxCorpusBytes ?? MAX_CORPUS_BYTES;
|
|
919
|
+
const stat = statSync(path);
|
|
920
|
+
if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
|
|
921
|
+
if (stat.size > maxFileBytes) {
|
|
922
|
+
throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
|
|
923
|
+
}
|
|
924
|
+
if (budget.used + stat.size > maxCorpusBytes) {
|
|
925
|
+
throw new Error(`Documentation corpus exceeds the ${maxCorpusBytes} byte read budget.`);
|
|
926
|
+
}
|
|
927
|
+
budget.used += stat.size;
|
|
928
|
+
return readFileSync3(path, "utf8");
|
|
929
|
+
};
|
|
423
930
|
|
|
424
931
|
// src/lib/markdown.ts
|
|
425
932
|
var parseFrontmatter = (markdown) => {
|
|
@@ -510,14 +1017,24 @@ var slugFromPath = (relPath) => {
|
|
|
510
1017
|
};
|
|
511
1018
|
|
|
512
1019
|
// src/lib/walk.ts
|
|
513
|
-
import { readdirSync,
|
|
1020
|
+
import { lstatSync, readdirSync, realpathSync as realpathSync2 } from "fs";
|
|
514
1021
|
import { join as join3 } from "path";
|
|
515
1022
|
var DEFAULT_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "coverage", ".doc-bridge"]);
|
|
1023
|
+
var DEFAULT_MAX_FILES = 1e4;
|
|
516
1024
|
var walkFiles = (root, opts) => {
|
|
517
1025
|
const extensions = opts?.extensions ?? [".md"];
|
|
518
1026
|
const skip = opts?.skipDirs ?? DEFAULT_SKIP;
|
|
519
1027
|
const out = [];
|
|
1028
|
+
const visited = /* @__PURE__ */ new Set();
|
|
520
1029
|
const visit = (dir) => {
|
|
1030
|
+
let canonicalDir;
|
|
1031
|
+
try {
|
|
1032
|
+
canonicalDir = realpathSync2.native(dir);
|
|
1033
|
+
} catch {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
if (visited.has(canonicalDir)) return;
|
|
1037
|
+
visited.add(canonicalDir);
|
|
521
1038
|
let entries = [];
|
|
522
1039
|
try {
|
|
523
1040
|
entries = readdirSync(dir);
|
|
@@ -528,10 +1045,11 @@ var walkFiles = (root, opts) => {
|
|
|
528
1045
|
const abs = join3(dir, name);
|
|
529
1046
|
let st;
|
|
530
1047
|
try {
|
|
531
|
-
st =
|
|
1048
|
+
st = lstatSync(abs);
|
|
532
1049
|
} catch {
|
|
533
1050
|
continue;
|
|
534
1051
|
}
|
|
1052
|
+
if (st.isSymbolicLink()) continue;
|
|
535
1053
|
if (st.isDirectory()) {
|
|
536
1054
|
if (skip.has(name)) continue;
|
|
537
1055
|
visit(abs);
|
|
@@ -539,6 +1057,9 @@ var walkFiles = (root, opts) => {
|
|
|
539
1057
|
}
|
|
540
1058
|
if (!st.isFile()) continue;
|
|
541
1059
|
if (extensions.some((ext) => name.endsWith(ext))) {
|
|
1060
|
+
if (out.length >= (opts?.maxFiles ?? DEFAULT_MAX_FILES)) {
|
|
1061
|
+
throw new Error(`Documentation corpus exceeds the ${opts?.maxFiles ?? DEFAULT_MAX_FILES} file limit.`);
|
|
1062
|
+
}
|
|
542
1063
|
out.push(toPosix(abs));
|
|
543
1064
|
}
|
|
544
1065
|
}
|
|
@@ -549,13 +1070,15 @@ var walkFiles = (root, opts) => {
|
|
|
549
1070
|
|
|
550
1071
|
// src/index-builder/scan-corpus.ts
|
|
551
1072
|
var scanAgentCorpus = (root, config) => {
|
|
552
|
-
const agentRoot =
|
|
1073
|
+
const agentRoot = containedProjectPath(root, config.corpus.agent.root);
|
|
1074
|
+
if (!agentRoot) throw new Error("Agent corpus root escapes the project root.");
|
|
553
1075
|
const files = walkFiles(agentRoot, { extensions: [".md", ".mdx"] });
|
|
554
1076
|
const corpusRelRoot = toPosix(config.corpus.agent.root);
|
|
1077
|
+
const budget = { used: 0 };
|
|
555
1078
|
return files.map((abs) => {
|
|
556
1079
|
const relToCorpus = toPosix(abs.replace(`${toPosix(agentRoot)}/`, ""));
|
|
557
1080
|
const relPath = `${corpusRelRoot}/${relToCorpus}`;
|
|
558
|
-
const raw =
|
|
1081
|
+
const raw = readBoundedText(abs, budget);
|
|
559
1082
|
const { data: frontmatter } = parseFrontmatter(raw);
|
|
560
1083
|
const id = frontmatterString(frontmatter, "id") ?? frontmatterString(frontmatter, "package") ?? slugFromPath(relToCorpus);
|
|
561
1084
|
const title = firstHeading(raw) ?? id;
|
|
@@ -811,7 +1334,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
811
1334
|
};
|
|
812
1335
|
|
|
813
1336
|
// src/version.ts
|
|
814
|
-
var PACKAGE_VERSION = "1.
|
|
1337
|
+
var PACKAGE_VERSION = "1.1.1";
|
|
815
1338
|
|
|
816
1339
|
// src/index-builder/capabilities.ts
|
|
817
1340
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -879,14 +1402,16 @@ ${lines.join("\n")}
|
|
|
879
1402
|
`;
|
|
880
1403
|
};
|
|
881
1404
|
|
|
1405
|
+
// src/index-builder/human-adapters/index.ts
|
|
1406
|
+
import { realpathSync as realpathSync4 } from "fs";
|
|
1407
|
+
import { resolve as resolve4, sep as sep3 } from "path";
|
|
1408
|
+
|
|
882
1409
|
// src/index-builder/human-adapters/docusaurus.ts
|
|
883
|
-
import { existsSync as
|
|
884
|
-
import { join as join6 } from "path";
|
|
885
|
-
import vm2 from "vm";
|
|
1410
|
+
import { existsSync as existsSync4 } from "fs";
|
|
886
1411
|
|
|
887
1412
|
// src/index-builder/human-adapters/core.ts
|
|
888
|
-
import {
|
|
889
|
-
import {
|
|
1413
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
1414
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
|
|
890
1415
|
var optionString = (options, keys) => {
|
|
891
1416
|
for (const key of keys) {
|
|
892
1417
|
const value = options?.[key];
|
|
@@ -917,10 +1442,16 @@ var humanUrl = (slug2, urlPrefix) => {
|
|
|
917
1442
|
};
|
|
918
1443
|
var scanMarkdownDocs = (root, humanRoot, options) => {
|
|
919
1444
|
const out = [];
|
|
920
|
-
const
|
|
1445
|
+
const projectRoot = realpathSync3.native(resolve3(root));
|
|
1446
|
+
const absRoot = containedProjectPath(root, humanRoot);
|
|
1447
|
+
if (!absRoot) return out;
|
|
1448
|
+
const budget = { used: 0 };
|
|
921
1449
|
for (const abs of walkFiles(absRoot, { extensions: [".md", ".mdx"] })) {
|
|
1450
|
+
const canonical = realpathSync3.native(abs);
|
|
1451
|
+
const fileRelative = relative2(projectRoot, canonical);
|
|
1452
|
+
if (isAbsolute2(fileRelative) || fileRelative === ".." || fileRelative.startsWith(`..${sep2}`)) continue;
|
|
922
1453
|
const relToHumanRoot = toPosix(abs.replace(`${toPosix(absRoot)}/`, ""));
|
|
923
|
-
const raw =
|
|
1454
|
+
const raw = readBoundedText(abs, budget);
|
|
924
1455
|
if (options?.includeRelPath && !options.includeRelPath(relToHumanRoot, raw)) continue;
|
|
925
1456
|
out.push({
|
|
926
1457
|
id: options?.idForDoc?.(relToHumanRoot, raw) ?? docId(relToHumanRoot, raw),
|
|
@@ -958,41 +1489,31 @@ var docusaurusRecordId = (relPath, raw) => {
|
|
|
958
1489
|
const frontmatter = parseFrontmatter2(raw);
|
|
959
1490
|
return frontmatter.package ?? frontmatter.module ?? docusaurusSidebarId(relPath, raw);
|
|
960
1491
|
};
|
|
961
|
-
var
|
|
962
|
-
if (!existsSync3(file)) return void 0;
|
|
963
|
-
const raw = readFileSync5(file, "utf8").replace(/import\s+type\s+[\s\S]*?;?\n/g, "").replace(/:\s*[A-Za-z0-9_.$<>{}\[\],\s]+(?=\s*=)/g, "").replace(/\s+satisfies\s+[A-Za-z0-9_.$<>{}\[\],\s]+(?=\s*(?:;|\n|$))/g, "").replace(/export\s+default/, "module.exports =");
|
|
964
|
-
const sandbox = { module: { exports: {} }, exports: {} };
|
|
965
|
-
vm2.runInNewContext(raw, sandbox, { timeout: 250 });
|
|
966
|
-
return sandbox.module.exports;
|
|
967
|
-
};
|
|
968
|
-
var visitSidebar = (value, filter) => {
|
|
969
|
-
if (typeof value === "string") {
|
|
970
|
-
filter.ids.add(value);
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
if (Array.isArray(value)) {
|
|
974
|
-
for (const item2 of value) visitSidebar(item2, filter);
|
|
975
|
-
return;
|
|
976
|
-
}
|
|
977
|
-
if (!value || typeof value !== "object") return;
|
|
978
|
-
const item = value;
|
|
979
|
-
if ((item.type === "doc" || item.type === "ref") && typeof item.id === "string") {
|
|
980
|
-
filter.ids.add(item.id);
|
|
981
|
-
}
|
|
982
|
-
if (item.type === "autogenerated" && typeof item.dirName === "string") {
|
|
983
|
-
filter.autogenDirs.push(item.dirName);
|
|
984
|
-
}
|
|
985
|
-
if (Array.isArray(item.items)) visitSidebar(item.items, filter);
|
|
986
|
-
if (item.link && typeof item.link === "object") visitSidebar(item.link, filter);
|
|
987
|
-
for (const child of Object.values(item)) {
|
|
988
|
-
if (Array.isArray(child)) visitSidebar(child, filter);
|
|
989
|
-
}
|
|
990
|
-
};
|
|
991
|
-
var readSidebars = (root, sidebarsFile) => {
|
|
1492
|
+
var readSidebars = (sidebarsFile) => {
|
|
992
1493
|
if (!sidebarsFile) return { enabled: false, ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
|
|
993
|
-
|
|
1494
|
+
if (!existsSync4(sidebarsFile)) return { enabled: false, ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
|
|
1495
|
+
const raw = readBoundedText(sidebarsFile, { used: 0 }, { maxFileBytes: 1048576, maxCorpusBytes: 1048576 });
|
|
994
1496
|
const filter = { ids: /* @__PURE__ */ new Set(), autogenDirs: [] };
|
|
995
|
-
|
|
1497
|
+
const visit = (value) => {
|
|
1498
|
+
if (typeof value === "string") {
|
|
1499
|
+
filter.ids.add(value);
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
if (Array.isArray(value)) {
|
|
1503
|
+
for (const item2 of value) visit(item2);
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
if (!value || typeof value !== "object") return;
|
|
1507
|
+
const item = value;
|
|
1508
|
+
if ((item.type === "doc" || item.type === "ref") && typeof item.id === "string") filter.ids.add(item.id);
|
|
1509
|
+
if (item.type === "autogenerated" && typeof item.dirName === "string") filter.autogenDirs.push(item.dirName);
|
|
1510
|
+
if (Array.isArray(item.items)) visit(item.items);
|
|
1511
|
+
if (item.link && typeof item.link === "object") visit(item.link);
|
|
1512
|
+
for (const child of Object.values(item)) {
|
|
1513
|
+
if (Array.isArray(child)) visit(child);
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
visit(parseStaticJsObject(raw));
|
|
996
1517
|
return { enabled: true, ...filter };
|
|
997
1518
|
};
|
|
998
1519
|
var isIncludedBySidebar = (filter, id) => {
|
|
@@ -1008,7 +1529,8 @@ var docusaurusAdapter = {
|
|
|
1008
1529
|
scan: ({ root, config }) => {
|
|
1009
1530
|
const docsDir = optionString(config.options, ["docsDir", "root"]);
|
|
1010
1531
|
if (!docsDir) return [];
|
|
1011
|
-
const
|
|
1532
|
+
const sidebarsFile = optionString(config.options, ["sidebarsFile"]);
|
|
1533
|
+
const sidebarFilter = readSidebars(sidebarsFile ? containedProjectPath(root, sidebarsFile) : void 0);
|
|
1012
1534
|
return scanMarkdownDocs(root, docsDir, {
|
|
1013
1535
|
includeRelPath: (relPath, raw) => isIncludedBySidebar(sidebarFilter, docusaurusSidebarId(relPath, raw)),
|
|
1014
1536
|
idForDoc: docusaurusRecordId,
|
|
@@ -1019,13 +1541,13 @@ var docusaurusAdapter = {
|
|
|
1019
1541
|
};
|
|
1020
1542
|
|
|
1021
1543
|
// src/index-builder/human-adapters/fumadocs.ts
|
|
1022
|
-
import { existsSync as
|
|
1023
|
-
import { join as
|
|
1544
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1545
|
+
import { join as join4 } from "path";
|
|
1024
1546
|
var readMetaPages = (dir) => {
|
|
1025
|
-
const file =
|
|
1026
|
-
if (!
|
|
1547
|
+
const file = join4(dir, "meta.json");
|
|
1548
|
+
if (!existsSync5(file)) return void 0;
|
|
1027
1549
|
try {
|
|
1028
|
-
const meta = JSON.parse(
|
|
1550
|
+
const meta = JSON.parse(readFileSync4(file, "utf8"));
|
|
1029
1551
|
return Array.isArray(meta.pages) ? meta.pages.filter((page) => typeof page === "string") : void 0;
|
|
1030
1552
|
} catch {
|
|
1031
1553
|
return void 0;
|
|
@@ -1040,7 +1562,7 @@ var isListedByMeta = (contentRoot, relPath) => {
|
|
|
1040
1562
|
if (isDotFile(relPath)) return false;
|
|
1041
1563
|
const parts = relPath.split("/");
|
|
1042
1564
|
for (let i = 0; i < parts.length; i += 1) {
|
|
1043
|
-
const dir =
|
|
1565
|
+
const dir = join4(contentRoot, ...parts.slice(0, i));
|
|
1044
1566
|
const pages = readMetaPages(dir);
|
|
1045
1567
|
if (!pages?.length || pages.includes("...")) continue;
|
|
1046
1568
|
const key = pageKey(parts[i] ?? "");
|
|
@@ -1053,7 +1575,7 @@ var fumadocsAdapter = {
|
|
|
1053
1575
|
scan: ({ root, config }) => {
|
|
1054
1576
|
const contentDir = optionString(config.options, ["contentDir", "root"]);
|
|
1055
1577
|
if (!contentDir) return [];
|
|
1056
|
-
const contentRoot =
|
|
1578
|
+
const contentRoot = join4(root, contentDir);
|
|
1057
1579
|
const excludePrefixes = Array.isArray(config.options?.excludePrefixes) ? config.options.excludePrefixes.filter((v) => typeof v === "string") : typeof config.options?.excludePrefix === "string" ? [config.options.excludePrefix] : [];
|
|
1058
1580
|
if (!excludePrefixes.includes("for-agents")) excludePrefixes.push("for-agents");
|
|
1059
1581
|
return scanMarkdownDocs(root, contentDir, {
|
|
@@ -1089,15 +1611,23 @@ var humanConfigs = (config) => {
|
|
|
1089
1611
|
if (!human) return [];
|
|
1090
1612
|
return Array.isArray(human) ? human : [human];
|
|
1091
1613
|
};
|
|
1614
|
+
var canonicalPath = (path) => {
|
|
1615
|
+
try {
|
|
1616
|
+
return realpathSync4.native(path);
|
|
1617
|
+
} catch {
|
|
1618
|
+
return resolve4(path);
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1092
1621
|
var scanHumanDocRecords = (root, config) => {
|
|
1093
1622
|
const out = [];
|
|
1094
1623
|
const seen = /* @__PURE__ */ new Set();
|
|
1095
|
-
const agentRoot = config.corpus.agent.root
|
|
1624
|
+
const agentRoot = canonicalPath(resolve4(root, config.corpus.agent.root));
|
|
1096
1625
|
for (const human of humanConfigs(config)) {
|
|
1097
1626
|
const adapter = ADAPTERS.find((candidate) => candidate.plugin === human.plugin);
|
|
1098
1627
|
if (!adapter) continue;
|
|
1099
1628
|
for (const record of adapter.scan({ root, config: human })) {
|
|
1100
|
-
|
|
1629
|
+
const recordPath = canonicalPath(record.path);
|
|
1630
|
+
if (recordPath === agentRoot || recordPath.startsWith(`${agentRoot}${sep3}`) || record.path.includes("/for-agents/") || record.path.endsWith("/for-agents")) {
|
|
1101
1631
|
continue;
|
|
1102
1632
|
}
|
|
1103
1633
|
if (seen.has(record.id)) continue;
|
|
@@ -1110,26 +1640,26 @@ var scanHumanDocRecords = (root, config) => {
|
|
|
1110
1640
|
var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(root, config).map((doc) => [doc.id, doc.url]));
|
|
1111
1641
|
|
|
1112
1642
|
// src/index-builder/plugins/pnpm-monorepo.ts
|
|
1113
|
-
import { existsSync as
|
|
1114
|
-
import { basename as basename2, join as
|
|
1643
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
1644
|
+
import { basename as basename2, join as join6 } from "path";
|
|
1115
1645
|
|
|
1116
1646
|
// src/lib/glob-expand.ts
|
|
1117
|
-
import { existsSync as
|
|
1118
|
-
import { join as
|
|
1647
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1648
|
+
import { join as join5 } from "path";
|
|
1119
1649
|
var expandWorkspaceGlobs = (root, patterns) => {
|
|
1120
1650
|
const dirs = /* @__PURE__ */ new Set();
|
|
1121
1651
|
for (const pattern of patterns) {
|
|
1122
1652
|
const normalized = toPosix(pattern).replace(/\/$/, "");
|
|
1123
1653
|
if (!normalized.includes("*")) {
|
|
1124
|
-
const abs =
|
|
1125
|
-
if (
|
|
1654
|
+
const abs = join5(root, normalized);
|
|
1655
|
+
if (existsSync6(abs)) dirs.add(toPosix(abs));
|
|
1126
1656
|
continue;
|
|
1127
1657
|
}
|
|
1128
1658
|
const star = normalized.indexOf("*");
|
|
1129
|
-
const base =
|
|
1130
|
-
if (!
|
|
1659
|
+
const base = join5(root, normalized.slice(0, star).replace(/\/$/, ""));
|
|
1660
|
+
if (!existsSync6(base)) continue;
|
|
1131
1661
|
for (const name of readdirSync2(base)) {
|
|
1132
|
-
const abs =
|
|
1662
|
+
const abs = join5(base, name);
|
|
1133
1663
|
try {
|
|
1134
1664
|
if (statSync2(abs).isDirectory()) dirs.add(toPosix(abs));
|
|
1135
1665
|
} catch {
|
|
@@ -1161,10 +1691,10 @@ var parsePnpmWorkspace = (yaml) => {
|
|
|
1161
1691
|
return patterns;
|
|
1162
1692
|
};
|
|
1163
1693
|
var readPackageJson = (dir) => {
|
|
1164
|
-
const file =
|
|
1165
|
-
if (!
|
|
1694
|
+
const file = join6(dir, "package.json");
|
|
1695
|
+
if (!existsSync7(file)) return null;
|
|
1166
1696
|
try {
|
|
1167
|
-
return JSON.parse(
|
|
1697
|
+
return JSON.parse(readFileSync5(file, "utf8"));
|
|
1168
1698
|
} catch {
|
|
1169
1699
|
return null;
|
|
1170
1700
|
}
|
|
@@ -1173,9 +1703,9 @@ var discoverPnpmPackages = (root, config) => {
|
|
|
1173
1703
|
const explicit = config.routing?.options?.packages;
|
|
1174
1704
|
let patterns = explicit;
|
|
1175
1705
|
if (!patterns?.length) {
|
|
1176
|
-
const workspaceFile =
|
|
1177
|
-
if (
|
|
1178
|
-
patterns = parsePnpmWorkspace(
|
|
1706
|
+
const workspaceFile = join6(root, "pnpm-workspace.yaml");
|
|
1707
|
+
if (existsSync7(workspaceFile)) {
|
|
1708
|
+
patterns = parsePnpmWorkspace(readFileSync5(workspaceFile, "utf8"));
|
|
1179
1709
|
}
|
|
1180
1710
|
}
|
|
1181
1711
|
if (!patterns?.length) return [];
|
|
@@ -1196,7 +1726,7 @@ var discoverPnpmPackages = (root, config) => {
|
|
|
1196
1726
|
var projectName = (root, config) => {
|
|
1197
1727
|
if (config.project?.name) return config.project.name;
|
|
1198
1728
|
try {
|
|
1199
|
-
const pkg = JSON.parse(
|
|
1729
|
+
const pkg = JSON.parse(readFileSync6(join7(root, "package.json"), "utf8"));
|
|
1200
1730
|
if (pkg.name) return pkg.name;
|
|
1201
1731
|
} catch {
|
|
1202
1732
|
}
|
|
@@ -1204,7 +1734,7 @@ var projectName = (root, config) => {
|
|
|
1204
1734
|
};
|
|
1205
1735
|
var existingGeneratedAt = (indexPath, contentHash) => {
|
|
1206
1736
|
try {
|
|
1207
|
-
const index = JSON.parse(
|
|
1737
|
+
const index = JSON.parse(readFileSync6(indexPath, "utf8"));
|
|
1208
1738
|
return index.contentHash === contentHash && typeof index.generatedAt === "string" ? index.generatedAt : void 0;
|
|
1209
1739
|
} catch {
|
|
1210
1740
|
return void 0;
|
|
@@ -1215,7 +1745,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1215
1745
|
const config = opts.config;
|
|
1216
1746
|
const write = opts.write ?? true;
|
|
1217
1747
|
const outFile = config.index?.outFile ?? ".doc-bridge/index.json";
|
|
1218
|
-
const indexPath =
|
|
1748
|
+
const indexPath = join7(root, outFile);
|
|
1219
1749
|
const corpus = scanAgentCorpus(root, config);
|
|
1220
1750
|
const knowledge = corpus.map(({ absPath: _a, relPath: _r, frontmatter: _f, ...entry }) => entry);
|
|
1221
1751
|
const shouldDiscover = config.routing?.plugin === "pnpm-monorepo" || Boolean(config.routing?.options?.packages?.length) || config.routing?.plugin === "npm-workspaces" || config.routing?.plugin === "yarn-workspaces";
|
|
@@ -1250,7 +1780,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1250
1780
|
if (config.index?.llmsTxt?.enabled !== false) {
|
|
1251
1781
|
const llmsOut = config.index?.llmsTxt?.outFile ?? "llms.txt";
|
|
1252
1782
|
llmsTxtRelPath = toPosix(llmsOut);
|
|
1253
|
-
llmsTxtPath =
|
|
1783
|
+
llmsTxtPath = join7(root, llmsOut);
|
|
1254
1784
|
if (write) {
|
|
1255
1785
|
writeFileSync(
|
|
1256
1786
|
llmsTxtPath,
|
|
@@ -1262,7 +1792,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1262
1792
|
let capabilitiesPath;
|
|
1263
1793
|
if (config.index?.capabilities?.enabled !== false) {
|
|
1264
1794
|
const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
|
|
1265
|
-
capabilitiesPath =
|
|
1795
|
+
capabilitiesPath = join7(root, capabilitiesOut);
|
|
1266
1796
|
if (write) {
|
|
1267
1797
|
mkdirSync(dirname3(capabilitiesPath), { recursive: true });
|
|
1268
1798
|
writeFileSync(
|
|
@@ -1283,9 +1813,366 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
1283
1813
|
};
|
|
1284
1814
|
};
|
|
1285
1815
|
|
|
1816
|
+
// src/conformance/documentation-standard-v1.ts
|
|
1817
|
+
var DOCUMENTATION_STANDARD_V1_ID = "documentation-standard-v1";
|
|
1818
|
+
var DOCUMENTATION_STANDARD_V1_STATUS = "stable";
|
|
1819
|
+
var MAX_TEXT_EVIDENCE_BYTES = 4 * 1024 * 1024;
|
|
1820
|
+
var safePath = (root, path) => {
|
|
1821
|
+
const rootAbs = realpathSync5.native(resolve5(root));
|
|
1822
|
+
const unresolved = resolve5(rootAbs, path);
|
|
1823
|
+
const unresolvedRel = relative3(rootAbs, unresolved);
|
|
1824
|
+
if (isAbsolute3(unresolvedRel) || unresolvedRel === ".." || unresolvedRel.startsWith(`..${sep4}`)) return void 0;
|
|
1825
|
+
if (!existsSync8(unresolved)) return unresolved;
|
|
1826
|
+
try {
|
|
1827
|
+
const abs = realpathSync5.native(unresolved);
|
|
1828
|
+
const rel = relative3(rootAbs, abs);
|
|
1829
|
+
return !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep4}`) ? abs : void 0;
|
|
1830
|
+
} catch {
|
|
1831
|
+
return void 0;
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
var fileEvidence = (root, path, options) => {
|
|
1835
|
+
const abs = safePath(root, path);
|
|
1836
|
+
if (!abs) {
|
|
1837
|
+
return {
|
|
1838
|
+
exists: false,
|
|
1839
|
+
content: "",
|
|
1840
|
+
evidence: { path, detail: "Path escapes the project root." }
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
if (!existsSync8(abs)) {
|
|
1844
|
+
return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
|
|
1845
|
+
}
|
|
1846
|
+
try {
|
|
1847
|
+
const stat = statSync3(abs);
|
|
1848
|
+
if (!stat.isFile()) {
|
|
1849
|
+
return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
|
|
1850
|
+
}
|
|
1851
|
+
if (stat.size === 0) {
|
|
1852
|
+
return { exists: false, content: "", evidence: { path, detail: "File is empty." } };
|
|
1853
|
+
}
|
|
1854
|
+
if (options?.readContent === false) {
|
|
1855
|
+
return {
|
|
1856
|
+
exists: true,
|
|
1857
|
+
content: "",
|
|
1858
|
+
evidence: { path: toPosix(relative3(resolve5(root), abs)) || ".", detail: "File exists and is non-empty." }
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
if (stat.size > MAX_TEXT_EVIDENCE_BYTES) {
|
|
1862
|
+
return {
|
|
1863
|
+
exists: false,
|
|
1864
|
+
content: "",
|
|
1865
|
+
evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
const content = readFileSync7(abs, "utf8");
|
|
1869
|
+
return {
|
|
1870
|
+
exists: content.trim().length > 0,
|
|
1871
|
+
content,
|
|
1872
|
+
evidence: {
|
|
1873
|
+
path: toPosix(relative3(resolve5(root), abs)) || ".",
|
|
1874
|
+
detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
} catch {
|
|
1878
|
+
return { exists: false, content: "", evidence: { path, detail: "File is not readable text." } };
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
var resultWithException = (draft, options) => {
|
|
1882
|
+
if (draft.passed) {
|
|
1883
|
+
const { passed: _passed2, ...result2 } = draft;
|
|
1884
|
+
return { ...result2, status: "pass", ok: true };
|
|
1885
|
+
}
|
|
1886
|
+
const exception = options.exceptions?.find((candidate) => candidate.ruleId === draft.id);
|
|
1887
|
+
const { passed: _passed, ...result } = draft;
|
|
1888
|
+
if (!exception) return { ...result, status: "fail", ok: false };
|
|
1889
|
+
return {
|
|
1890
|
+
...result,
|
|
1891
|
+
status: "excepted",
|
|
1892
|
+
ok: true,
|
|
1893
|
+
exception: {
|
|
1894
|
+
reason: exception.reason,
|
|
1895
|
+
approvedBy: exception.approvedBy,
|
|
1896
|
+
trackingUrl: exception.trackingUrl
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
};
|
|
1900
|
+
var humanDocsRule = (root, config) => {
|
|
1901
|
+
const docs = scanHumanDocRecords(root, config);
|
|
1902
|
+
return {
|
|
1903
|
+
id: "human-docs",
|
|
1904
|
+
level: "required",
|
|
1905
|
+
passed: docs.length > 0,
|
|
1906
|
+
message: docs.length > 0 ? `Found ${docs.length} human document(s).` : "No human documentation was discovered.",
|
|
1907
|
+
evidence: docs.slice(0, 10).map((doc) => ({
|
|
1908
|
+
path: toPosix(relative3(resolve5(root), doc.path)),
|
|
1909
|
+
detail: `Human route: ${doc.url}`
|
|
1910
|
+
})),
|
|
1911
|
+
remediation: {
|
|
1912
|
+
command: "edit doc-bridge.config.json",
|
|
1913
|
+
detail: "Configure corpus.human with a supported adapter and a non-agent documentation root."
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
};
|
|
1917
|
+
var llmsRule = (root, config, options) => {
|
|
1918
|
+
const llmsPath = config.index?.llmsTxt?.outFile ?? "llms.txt";
|
|
1919
|
+
const llmsKey = safePath(root, llmsPath) ?? resolve5(root, llmsPath);
|
|
1920
|
+
const rawSources = /* @__PURE__ */ new Map();
|
|
1921
|
+
for (const path of options.rawSources ?? []) {
|
|
1922
|
+
const key = safePath(root, path) ?? resolve5(root, path);
|
|
1923
|
+
if (key !== llmsKey && !rawSources.has(key)) rawSources.set(key, path);
|
|
1924
|
+
}
|
|
1925
|
+
const paths = [llmsPath, ...rawSources.values()];
|
|
1926
|
+
const evidence = paths.map((path) => fileEvidence(root, path));
|
|
1927
|
+
const generated = buildDocBridgeIndex({ root, config, write: false }).index;
|
|
1928
|
+
const expectedLlms = renderLlmsTxt(config, generated.knowledge, generated.project?.name ?? "project");
|
|
1929
|
+
const llmsIsFresh = evidence[0]?.content === expectedLlms;
|
|
1930
|
+
if (evidence[0]?.exists) {
|
|
1931
|
+
evidence[0] = {
|
|
1932
|
+
...evidence[0],
|
|
1933
|
+
evidence: {
|
|
1934
|
+
...evidence[0].evidence,
|
|
1935
|
+
detail: llmsIsFresh ? "File matches the deterministic ak-docs output." : "File is stale or was not generated by the current ak-docs inputs."
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
const passed = config.index?.llmsTxt?.enabled !== false && paths.length > 1 && llmsIsFresh && evidence.every((item) => item.exists);
|
|
1940
|
+
return {
|
|
1941
|
+
id: "llms-and-raw-source",
|
|
1942
|
+
level: "required",
|
|
1943
|
+
passed,
|
|
1944
|
+
message: passed ? `Resolved llms.txt and ${paths.length - 1} raw source(s).` : "llms.txt must be enabled, current, and accompanied by at least one readable raw source.",
|
|
1945
|
+
evidence: evidence.map((item) => item.evidence),
|
|
1946
|
+
remediation: {
|
|
1947
|
+
command: "ak-docs index",
|
|
1948
|
+
detail: "Generate llms.txt and configure conformance.documentationStandardV1.rawSources."
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
};
|
|
1952
|
+
var normalizedUrl = (value) => value.replace(/\/$/, "");
|
|
1953
|
+
var ecosystemContract = (root, options) => {
|
|
1954
|
+
const declaration = options.ecosystemContract;
|
|
1955
|
+
if (!declaration) {
|
|
1956
|
+
return {
|
|
1957
|
+
passed: false,
|
|
1958
|
+
urls: /* @__PURE__ */ new Set(),
|
|
1959
|
+
evidence: [{ path: "doc-bridge.config.json", detail: "Canonical ecosystem contract evidence is not declared." }]
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
const manifestFile = fileEvidence(root, declaration.manifest);
|
|
1963
|
+
const claimsFile = fileEvidence(root, declaration.claims);
|
|
1964
|
+
const evidence = [manifestFile.evidence, claimsFile.evidence];
|
|
1965
|
+
if (!manifestFile.exists || !claimsFile.exists) return { passed: false, urls: /* @__PURE__ */ new Set(), evidence };
|
|
1966
|
+
try {
|
|
1967
|
+
const manifest = JSON.parse(manifestFile.content);
|
|
1968
|
+
const claims = JSON.parse(claimsFile.content);
|
|
1969
|
+
const contract = parseCanonicalEcosystemContract(manifest, claims);
|
|
1970
|
+
const productIds = contract.manifest.products.map((product) => product.id);
|
|
1971
|
+
if (!productIds.includes(declaration.productId)) throw new Error(`Manifest is missing product ${declaration.productId}.`);
|
|
1972
|
+
const urls = /* @__PURE__ */ new Set();
|
|
1973
|
+
for (const product of contract.manifest.products) {
|
|
1974
|
+
for (const value of [
|
|
1975
|
+
product.surfaces.home,
|
|
1976
|
+
product.surfaces.docs,
|
|
1977
|
+
product.surfaces.llms,
|
|
1978
|
+
product.surfaces.stats
|
|
1979
|
+
]) {
|
|
1980
|
+
if (typeof value === "string" && /^https:\/\//.test(value)) urls.add(normalizedUrl(value));
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
evidence[0] = { path: declaration.manifest, detail: `Validated ${productIds.length} canonical product(s), including ${declaration.productId}.` };
|
|
1984
|
+
evidence[1] = { path: declaration.claims, detail: `Validated claim-ledger identity for ${contract.claims.products.length} product(s).` };
|
|
1985
|
+
return { passed: urls.size > 0, urls, evidence };
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
evidence.push({
|
|
1988
|
+
path: `${declaration.manifest}, ${declaration.claims}`,
|
|
1989
|
+
detail: error instanceof Error ? error.message : "Canonical ecosystem contract is invalid."
|
|
1990
|
+
});
|
|
1991
|
+
return { passed: false, urls: /* @__PURE__ */ new Set(), evidence };
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
var handoffsRule = (root, config) => {
|
|
1995
|
+
const index = buildDocBridgeIndex({ root, config, write: false }).index;
|
|
1996
|
+
const handoffs = Object.values(index.handoffs ?? {});
|
|
1997
|
+
const ready = handoffs.filter(
|
|
1998
|
+
(handoff) => handoff.startHere.length > 0 && handoff.editRoots.length > 0 && handoff.checks.length > 0 && (handoff.bridge?.humanDoc === "linked" || handoff.bridge?.humanDoc === "external")
|
|
1999
|
+
);
|
|
2000
|
+
return {
|
|
2001
|
+
id: "agent-handoffs",
|
|
2002
|
+
level: "required",
|
|
2003
|
+
passed: ready.length > 0 && ready.length === handoffs.length,
|
|
2004
|
+
message: ready.length > 0 && ready.length === handoffs.length ? `${ready.length} handoff(s) are action-ready and human-linked.` : `${ready.length}/${handoffs.length} handoff(s) are action-ready and human-linked.`,
|
|
2005
|
+
evidence: handoffs.map((handoff) => ({
|
|
2006
|
+
path: handoff.startHere,
|
|
2007
|
+
detail: `${handoff.target.id}: ${handoff.editRoots.length} edit root(s), ${handoff.checks.length} check(s), bridge=${handoff.bridge?.humanDoc ?? "none"}`
|
|
2008
|
+
})),
|
|
2009
|
+
remediation: {
|
|
2010
|
+
command: "ak-docs bootstrap agent-docs && ak-docs index",
|
|
2011
|
+
detail: "Add ownership checks and a resolvable humanDoc for every handoff."
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
};
|
|
2015
|
+
var contributionRule = (root, options) => {
|
|
2016
|
+
const paths = options.contributionPaths?.length ? options.contributionPaths : ["CONTRIBUTING.md"];
|
|
2017
|
+
const evidence = paths.map((path) => fileEvidence(root, path));
|
|
2018
|
+
return {
|
|
2019
|
+
id: "contribution",
|
|
2020
|
+
level: "required",
|
|
2021
|
+
passed: evidence.some((item) => item.exists),
|
|
2022
|
+
message: evidence.some((item) => item.exists) ? "Contribution guidance is available." : "Contribution guidance is missing.",
|
|
2023
|
+
evidence: evidence.map((item) => item.evidence),
|
|
2024
|
+
remediation: {
|
|
2025
|
+
command: "edit CONTRIBUTING.md",
|
|
2026
|
+
detail: "Document setup, validation commands, and the pull-request workflow."
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
};
|
|
2030
|
+
var markersRule = (root, options, kind, level) => {
|
|
2031
|
+
const declarations = options[kind === "metadata" ? "metadata" : "diagrams"] ?? [];
|
|
2032
|
+
const evidence = [];
|
|
2033
|
+
let passed = declarations.length > 0;
|
|
2034
|
+
for (const declaration of declarations) {
|
|
2035
|
+
const file = fileEvidence(root, declaration.path);
|
|
2036
|
+
const missing = declaration.contains.filter((marker) => !file.content.includes(marker));
|
|
2037
|
+
if (!file.exists || missing.length > 0) passed = false;
|
|
2038
|
+
evidence.push({
|
|
2039
|
+
path: declaration.path,
|
|
2040
|
+
detail: !file.exists ? file.evidence.detail : missing.length ? `Missing marker(s): ${missing.join(", ")}` : `Found marker(s): ${declaration.contains.join(", ")}`
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
return {
|
|
2044
|
+
id: kind,
|
|
2045
|
+
level,
|
|
2046
|
+
passed,
|
|
2047
|
+
message: passed ? `${kind} evidence is complete.` : `${kind} evidence is incomplete.`,
|
|
2048
|
+
evidence,
|
|
2049
|
+
remediation: {
|
|
2050
|
+
command: "edit doc-bridge.config.json",
|
|
2051
|
+
detail: `Declare ${kind} evidence paths and markers that exist in the repository.`
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
};
|
|
2055
|
+
var linksRule = (root, options) => {
|
|
2056
|
+
const links = options.links ?? [];
|
|
2057
|
+
const contract = ecosystemContract(root, options);
|
|
2058
|
+
const evidence = [...contract.evidence];
|
|
2059
|
+
let passed = links.length > 0 && contract.passed;
|
|
2060
|
+
for (const link of links) {
|
|
2061
|
+
const sources = link.paths.map((path) => ({ path, file: fileEvidence(root, path) }));
|
|
2062
|
+
const matches = sources.filter(({ file }) => file.exists && file.content.includes(link.url));
|
|
2063
|
+
const canonical = contract.urls.has(normalizedUrl(link.url));
|
|
2064
|
+
if (matches.length === 0 || !canonical) passed = false;
|
|
2065
|
+
evidence.push({
|
|
2066
|
+
path: sources.map((source) => source.path).join(", "),
|
|
2067
|
+
detail: matches.length === 0 ? `Missing ${link.url}` : canonical ? `Found canonical ecosystem URL ${link.url}` : `Found ${link.url}, but it is absent from the canonical ecosystem manifest.`
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
return {
|
|
2071
|
+
id: "cross-links",
|
|
2072
|
+
level: "required",
|
|
2073
|
+
passed,
|
|
2074
|
+
message: passed ? `${links.length} required ecosystem link(s) resolve in source.` : "One or more required ecosystem links are missing from source.",
|
|
2075
|
+
evidence,
|
|
2076
|
+
remediation: {
|
|
2077
|
+
command: "edit README.md",
|
|
2078
|
+
detail: "Sync the canonical ecosystem snapshots and add each configured canonical URL to a declared documentation source."
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
};
|
|
2082
|
+
var quickstartsRule = (root, options) => {
|
|
2083
|
+
const quickstarts = options.quickstarts ?? [];
|
|
2084
|
+
const evidence = [];
|
|
2085
|
+
let passed = quickstarts.length > 0;
|
|
2086
|
+
for (const quickstart of quickstarts) {
|
|
2087
|
+
const doc = fileEvidence(root, quickstart.doc);
|
|
2088
|
+
const test = fileEvidence(root, quickstart.test);
|
|
2089
|
+
const missingMarkers = quickstart.testContains.filter((marker) => !test.content.includes(marker));
|
|
2090
|
+
if (!doc.exists || !test.exists || missingMarkers.length > 0 || !quickstart.command.trim()) passed = false;
|
|
2091
|
+
evidence.push(
|
|
2092
|
+
{ path: quickstart.doc, detail: `${quickstart.id}: ${doc.evidence.detail}` },
|
|
2093
|
+
{
|
|
2094
|
+
path: quickstart.test,
|
|
2095
|
+
detail: missingMarkers.length ? `${quickstart.id}: missing test marker(s): ${missingMarkers.join(", ")}` : `${quickstart.id}: test evidence; CI command: ${quickstart.command}`
|
|
2096
|
+
}
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
return {
|
|
2100
|
+
id: "tested-quickstarts",
|
|
2101
|
+
level: "required",
|
|
2102
|
+
passed,
|
|
2103
|
+
message: passed ? `${quickstarts.length} quickstart(s) have executable test evidence.` : "Quickstart test evidence is incomplete.",
|
|
2104
|
+
evidence,
|
|
2105
|
+
remediation: {
|
|
2106
|
+
command: "pnpm test",
|
|
2107
|
+
detail: "Map every quickstart to a documentation path, test file, identifying marker, and CI command."
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
2110
|
+
};
|
|
2111
|
+
var visualsRule = (root, options) => {
|
|
2112
|
+
const visuals = options.visuals ?? [];
|
|
2113
|
+
const evidence = visuals.map((path) => fileEvidence(root, path, { readContent: false }));
|
|
2114
|
+
return {
|
|
2115
|
+
id: "visual-explanations",
|
|
2116
|
+
level: "recommended",
|
|
2117
|
+
passed: visuals.length > 0 && evidence.every((item) => item.exists),
|
|
2118
|
+
message: visuals.length > 0 && evidence.every((item) => item.exists) ? `${visuals.length} visual asset(s) found.` : "Visual explanation evidence is incomplete.",
|
|
2119
|
+
evidence: evidence.map((item) => item.evidence),
|
|
2120
|
+
remediation: {
|
|
2121
|
+
command: "edit doc-bridge.config.json",
|
|
2122
|
+
detail: "Declare the images or animations that explain the product workflow."
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
};
|
|
2126
|
+
var count = (results, level, status) => results.filter((result) => result.level === level && result.status === status).length;
|
|
2127
|
+
var runDocumentationStandardV1 = (root, config) => {
|
|
2128
|
+
const options = config.conformance?.documentationStandardV1 ?? {};
|
|
2129
|
+
const drafts = [
|
|
2130
|
+
humanDocsRule(root, config),
|
|
2131
|
+
llmsRule(root, config, options),
|
|
2132
|
+
handoffsRule(root, config),
|
|
2133
|
+
contributionRule(root, options),
|
|
2134
|
+
markersRule(root, options, "metadata", "required"),
|
|
2135
|
+
linksRule(root, options),
|
|
2136
|
+
quickstartsRule(root, options),
|
|
2137
|
+
visualsRule(root, options),
|
|
2138
|
+
markersRule(root, options, "structured-diagrams", "recommended")
|
|
2139
|
+
];
|
|
2140
|
+
const results = drafts.map((draft) => resultWithException(draft, options));
|
|
2141
|
+
return {
|
|
2142
|
+
schemaVersion: 1,
|
|
2143
|
+
profile: { id: DOCUMENTATION_STANDARD_V1_ID, version: 1, status: DOCUMENTATION_STANDARD_V1_STATUS },
|
|
2144
|
+
ok: results.filter((result) => result.level === "required").every((result) => result.ok),
|
|
2145
|
+
recommendedOk: results.filter((result) => result.level === "recommended").every((result) => result.ok),
|
|
2146
|
+
summary: {
|
|
2147
|
+
required: {
|
|
2148
|
+
passed: count(results, "required", "pass"),
|
|
2149
|
+
failed: count(results, "required", "fail"),
|
|
2150
|
+
excepted: count(results, "required", "excepted")
|
|
2151
|
+
},
|
|
2152
|
+
recommended: {
|
|
2153
|
+
passed: count(results, "recommended", "pass"),
|
|
2154
|
+
failed: count(results, "recommended", "fail"),
|
|
2155
|
+
excepted: count(results, "recommended", "excepted")
|
|
2156
|
+
}
|
|
2157
|
+
},
|
|
2158
|
+
results
|
|
2159
|
+
};
|
|
2160
|
+
};
|
|
2161
|
+
var formatDocumentationStandardText = (report) => [
|
|
2162
|
+
`Documentation Standard v1 (${report.profile.status})`,
|
|
2163
|
+
`Required: ${report.summary.required.passed} passed \xB7 ${report.summary.required.failed} failed \xB7 ${report.summary.required.excepted} excepted`,
|
|
2164
|
+
`Recommended: ${report.summary.recommended.passed} passed \xB7 ${report.summary.recommended.failed} failed \xB7 ${report.summary.recommended.excepted} excepted`,
|
|
2165
|
+
"",
|
|
2166
|
+
...report.results.flatMap((result) => [
|
|
2167
|
+
`${result.status === "pass" ? "PASS" : result.status === "excepted" ? "EXCEPTED" : "FAIL"} [${result.level}] ${result.id}: ${result.message}`,
|
|
2168
|
+
...result.evidence.map((evidence) => ` evidence: ${evidence.path} \u2014 ${evidence.detail}`),
|
|
2169
|
+
...result.exception ? [` exception: ${result.exception.reason} \u2014 ${result.exception.approvedBy} (${result.exception.trackingUrl})`] : result.ok ? [] : [` fix: ${result.remediation.command} \u2014 ${result.remediation.detail}`]
|
|
2170
|
+
])
|
|
2171
|
+
];
|
|
2172
|
+
|
|
1286
2173
|
// src/federation/llms.ts
|
|
1287
|
-
import { existsSync as
|
|
1288
|
-
import { resolve as
|
|
2174
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
2175
|
+
import { resolve as resolve6 } from "path";
|
|
1289
2176
|
|
|
1290
2177
|
// src/query/search.ts
|
|
1291
2178
|
var tokenize = (value) => value.toLowerCase().split(/[^a-z0-9@/_-]+/).filter((t) => t.length >= 2);
|
|
@@ -1414,9 +2301,9 @@ var defaultFetchText = async (url) => {
|
|
|
1414
2301
|
var sourceText = async (root, source, fetchText) => {
|
|
1415
2302
|
try {
|
|
1416
2303
|
if (/^https?:\/\//.test(source)) return await fetchText(source);
|
|
1417
|
-
const path =
|
|
1418
|
-
if (!
|
|
1419
|
-
return
|
|
2304
|
+
const path = resolve6(root, source);
|
|
2305
|
+
if (!existsSync9(path)) return null;
|
|
2306
|
+
return readFileSync8(path, "utf8");
|
|
1420
2307
|
} catch {
|
|
1421
2308
|
return null;
|
|
1422
2309
|
}
|
|
@@ -1501,16 +2388,16 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
|
|
|
1501
2388
|
};
|
|
1502
2389
|
|
|
1503
2390
|
// src/gates/run-gates.ts
|
|
1504
|
-
import { readFileSync as
|
|
2391
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
1505
2392
|
|
|
1506
2393
|
// src/query/load-index.ts
|
|
1507
|
-
import { existsSync as
|
|
1508
|
-
import { join as
|
|
2394
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
2395
|
+
import { join as join8, resolve as resolve7 } from "path";
|
|
1509
2396
|
|
|
1510
2397
|
// src/schemas/agent-handoff.ts
|
|
1511
|
-
import { z as
|
|
2398
|
+
import { z as z3 } from "zod";
|
|
1512
2399
|
var HANDOFF_SCHEMA_VERSION = 1;
|
|
1513
|
-
var HandoffTargetTypeSchema =
|
|
2400
|
+
var HandoffTargetTypeSchema = z3.enum([
|
|
1514
2401
|
"package",
|
|
1515
2402
|
"module",
|
|
1516
2403
|
"app",
|
|
@@ -1521,133 +2408,133 @@ var HandoffTargetTypeSchema = z2.enum([
|
|
|
1521
2408
|
"change",
|
|
1522
2409
|
"search"
|
|
1523
2410
|
]);
|
|
1524
|
-
var HandoffTargetSchema =
|
|
2411
|
+
var HandoffTargetSchema = z3.object({
|
|
1525
2412
|
type: HandoffTargetTypeSchema,
|
|
1526
|
-
id:
|
|
1527
|
-
path:
|
|
1528
|
-
group:
|
|
1529
|
-
layer:
|
|
2413
|
+
id: z3.string().min(1).max(256),
|
|
2414
|
+
path: z3.string().min(1).max(512).optional(),
|
|
2415
|
+
group: z3.string().min(1).max(128).optional(),
|
|
2416
|
+
layer: z3.string().min(1).max(32).optional()
|
|
1530
2417
|
}).strict();
|
|
1531
|
-
var HandoffBridgeSchema =
|
|
1532
|
-
humanDoc:
|
|
1533
|
-
action:
|
|
1534
|
-
bootstrap:
|
|
2418
|
+
var HandoffBridgeSchema = z3.object({
|
|
2419
|
+
humanDoc: z3.enum(["linked", "missing", "external"]),
|
|
2420
|
+
action: z3.string().min(1).max(256).optional(),
|
|
2421
|
+
bootstrap: z3.string().min(1).max(256).optional()
|
|
1535
2422
|
}).strict();
|
|
1536
|
-
var AgentHandoffV1Schema =
|
|
1537
|
-
type:
|
|
1538
|
-
schemaVersion:
|
|
1539
|
-
source:
|
|
2423
|
+
var AgentHandoffV1Schema = z3.object({
|
|
2424
|
+
type: z3.literal("agent-handoff"),
|
|
2425
|
+
schemaVersion: z3.literal(HANDOFF_SCHEMA_VERSION).default(HANDOFF_SCHEMA_VERSION),
|
|
2426
|
+
source: z3.string().min(1).max(512),
|
|
1540
2427
|
target: HandoffTargetSchema,
|
|
1541
|
-
startHere:
|
|
1542
|
-
readBeforeEditing:
|
|
1543
|
-
editRoots:
|
|
1544
|
-
checks:
|
|
1545
|
-
humanDoc:
|
|
2428
|
+
startHere: z3.string().min(1).max(512),
|
|
2429
|
+
readBeforeEditing: z3.array(z3.string().min(1).max(512)).max(64),
|
|
2430
|
+
editRoots: z3.array(z3.string().min(1).max(512)).max(32),
|
|
2431
|
+
checks: z3.array(z3.string().min(1).max(256)).max(32),
|
|
2432
|
+
humanDoc: z3.string().min(1).max(512).nullable().optional(),
|
|
1546
2433
|
bridge: HandoffBridgeSchema.optional(),
|
|
1547
|
-
playbookPatterns:
|
|
1548
|
-
notes:
|
|
2434
|
+
playbookPatterns: z3.array(z3.string().url()).max(16).optional(),
|
|
2435
|
+
notes: z3.array(z3.string().min(1).max(1024)).max(16)
|
|
1549
2436
|
}).strict();
|
|
1550
2437
|
var AgentHandoffLegacySchema = AgentHandoffV1Schema.omit({ schemaVersion: true }).extend({
|
|
1551
|
-
schemaVersion:
|
|
2438
|
+
schemaVersion: z3.literal(HANDOFF_SCHEMA_VERSION).optional()
|
|
1552
2439
|
});
|
|
1553
|
-
var AgentSearchMatchSchema =
|
|
1554
|
-
type:
|
|
1555
|
-
id:
|
|
1556
|
-
path:
|
|
1557
|
-
summary:
|
|
1558
|
-
score:
|
|
1559
|
-
refs:
|
|
2440
|
+
var AgentSearchMatchSchema = z3.object({
|
|
2441
|
+
type: z3.string().min(1).max(64),
|
|
2442
|
+
id: z3.string().min(1).max(256),
|
|
2443
|
+
path: z3.string().min(1).max(512),
|
|
2444
|
+
summary: z3.string().max(2048).optional(),
|
|
2445
|
+
score: z3.number().optional(),
|
|
2446
|
+
refs: z3.number().int().nonnegative().optional()
|
|
1560
2447
|
}).strict();
|
|
1561
|
-
var AgentSearchV1Schema =
|
|
1562
|
-
type:
|
|
1563
|
-
schemaVersion:
|
|
1564
|
-
source:
|
|
1565
|
-
term:
|
|
1566
|
-
count:
|
|
2448
|
+
var AgentSearchV1Schema = z3.object({
|
|
2449
|
+
type: z3.literal("agent-search"),
|
|
2450
|
+
schemaVersion: z3.literal(HANDOFF_SCHEMA_VERSION).default(HANDOFF_SCHEMA_VERSION),
|
|
2451
|
+
source: z3.string().min(1).max(512),
|
|
2452
|
+
term: z3.string().min(1).max(512),
|
|
2453
|
+
count: z3.number().int().nonnegative(),
|
|
1567
2454
|
bestMatch: AgentSearchMatchSchema.nullable(),
|
|
1568
|
-
matches:
|
|
1569
|
-
nextCommands:
|
|
2455
|
+
matches: z3.array(AgentSearchMatchSchema).max(32),
|
|
2456
|
+
nextCommands: z3.array(z3.string().min(1).max(512)).max(16)
|
|
1570
2457
|
}).strict();
|
|
1571
2458
|
var normalizeAgentHandoff = (input) => AgentHandoffV1Schema.parse(AgentHandoffLegacySchema.parse(input));
|
|
1572
2459
|
|
|
1573
2460
|
// src/schemas/doc-bridge-index.ts
|
|
1574
|
-
import { z as
|
|
2461
|
+
import { z as z4 } from "zod";
|
|
1575
2462
|
var INDEX_SCHEMA_VERSION = 1;
|
|
1576
|
-
var ContentHashAlgoSchema =
|
|
1577
|
-
var EcosystemPropertySchema =
|
|
1578
|
-
id:
|
|
1579
|
-
name:
|
|
1580
|
-
url:
|
|
1581
|
-
llms:
|
|
2463
|
+
var ContentHashAlgoSchema = z4.literal("sha256-normalized-v1");
|
|
2464
|
+
var EcosystemPropertySchema = z4.object({
|
|
2465
|
+
id: z4.string().min(1).max(64),
|
|
2466
|
+
name: z4.string().min(1).max(128),
|
|
2467
|
+
url: z4.string().url().optional(),
|
|
2468
|
+
llms: z4.string().url().optional()
|
|
1582
2469
|
}).strict();
|
|
1583
|
-
var KnowledgeEntrySchema =
|
|
1584
|
-
id:
|
|
1585
|
-
type:
|
|
1586
|
-
title:
|
|
1587
|
-
path:
|
|
1588
|
-
description:
|
|
2470
|
+
var KnowledgeEntrySchema = z4.object({
|
|
2471
|
+
id: z4.string().min(1).max(256),
|
|
2472
|
+
type: z4.string().min(1).max(128),
|
|
2473
|
+
title: z4.string().min(1).max(256),
|
|
2474
|
+
path: z4.string().min(1).max(512),
|
|
2475
|
+
description: z4.string().max(2048).optional(),
|
|
1589
2476
|
/** Flattened body excerpt for full-text search (not for display). */
|
|
1590
|
-
body:
|
|
1591
|
-
links:
|
|
1592
|
-
tags:
|
|
2477
|
+
body: z4.string().max(8e3).optional(),
|
|
2478
|
+
links: z4.array(z4.string().min(1).max(512)).max(64).optional(),
|
|
2479
|
+
tags: z4.array(z4.string().min(1).max(64)).max(32).optional()
|
|
1593
2480
|
}).strict();
|
|
1594
|
-
var CapabilityRefSchema =
|
|
1595
|
-
id:
|
|
1596
|
-
kind:
|
|
1597
|
-
description:
|
|
2481
|
+
var CapabilityRefSchema = z4.object({
|
|
2482
|
+
id: z4.string().min(1).max(256),
|
|
2483
|
+
kind: z4.string().min(1).max(64),
|
|
2484
|
+
description: z4.string().max(512).optional()
|
|
1598
2485
|
}).strict();
|
|
1599
|
-
var OwnershipRecordSchema =
|
|
1600
|
-
id:
|
|
1601
|
-
path:
|
|
1602
|
-
group:
|
|
1603
|
-
layer:
|
|
1604
|
-
purpose:
|
|
1605
|
-
checks:
|
|
1606
|
-
agentDoc:
|
|
1607
|
-
humanDoc:
|
|
1608
|
-
readme:
|
|
2486
|
+
var OwnershipRecordSchema = z4.object({
|
|
2487
|
+
id: z4.string().min(1).max(256),
|
|
2488
|
+
path: z4.string().min(1).max(512),
|
|
2489
|
+
group: z4.string().min(1).max(128).optional(),
|
|
2490
|
+
layer: z4.string().min(1).max(32).optional(),
|
|
2491
|
+
purpose: z4.string().max(1024).optional(),
|
|
2492
|
+
checks: z4.array(z4.string().min(1).max(256)).max(32),
|
|
2493
|
+
agentDoc: z4.string().min(1).max(512).optional(),
|
|
2494
|
+
humanDoc: z4.string().min(1).max(512).optional(),
|
|
2495
|
+
readme: z4.string().min(1).max(512).optional()
|
|
1609
2496
|
}).strict();
|
|
1610
|
-
var IndexLookupSchema =
|
|
1611
|
-
packages:
|
|
1612
|
-
ownership:
|
|
1613
|
-
intents:
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
id:
|
|
1617
|
-
title:
|
|
1618
|
-
paths:
|
|
2497
|
+
var IndexLookupSchema = z4.object({
|
|
2498
|
+
packages: z4.array(z4.string().min(1).max(256)).max(2e3),
|
|
2499
|
+
ownership: z4.record(z4.string().min(1).max(256), OwnershipRecordSchema).optional(),
|
|
2500
|
+
intents: z4.record(
|
|
2501
|
+
z4.string().min(1).max(128),
|
|
2502
|
+
z4.object({
|
|
2503
|
+
id: z4.string().min(1).max(128),
|
|
2504
|
+
title: z4.string().min(1).max(256),
|
|
2505
|
+
paths: z4.array(z4.string().min(1).max(512)).max(32)
|
|
1619
2506
|
}).strict()
|
|
1620
2507
|
).optional(),
|
|
1621
|
-
changes:
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
id:
|
|
1625
|
-
title:
|
|
1626
|
-
startHere:
|
|
1627
|
-
relatedPackages:
|
|
2508
|
+
changes: z4.record(
|
|
2509
|
+
z4.string().min(1).max(128),
|
|
2510
|
+
z4.object({
|
|
2511
|
+
id: z4.string().min(1).max(128),
|
|
2512
|
+
title: z4.string().min(1).max(256),
|
|
2513
|
+
startHere: z4.string().min(1).max(512),
|
|
2514
|
+
relatedPackages: z4.array(z4.string().min(1).max(256)).max(32).optional()
|
|
1628
2515
|
}).strict()
|
|
1629
2516
|
).optional()
|
|
1630
2517
|
}).strict();
|
|
1631
|
-
var DocBridgeIndexV1Schema =
|
|
1632
|
-
schemaVersion:
|
|
1633
|
-
contentHash:
|
|
2518
|
+
var DocBridgeIndexV1Schema = z4.object({
|
|
2519
|
+
schemaVersion: z4.literal(INDEX_SCHEMA_VERSION),
|
|
2520
|
+
contentHash: z4.string().regex(/^[a-f0-9]{64}$/),
|
|
1634
2521
|
contentHashAlgo: ContentHashAlgoSchema,
|
|
1635
|
-
generatedAt:
|
|
1636
|
-
project:
|
|
1637
|
-
name:
|
|
1638
|
-
root:
|
|
2522
|
+
generatedAt: z4.string().datetime().optional(),
|
|
2523
|
+
project: z4.object({
|
|
2524
|
+
name: z4.string().min(1).max(128),
|
|
2525
|
+
root: z4.string().min(1).max(512).optional()
|
|
1639
2526
|
}).strict().optional(),
|
|
1640
|
-
properties:
|
|
1641
|
-
knowledge:
|
|
1642
|
-
capabilities:
|
|
1643
|
-
handoffs:
|
|
2527
|
+
properties: z4.array(EcosystemPropertySchema).max(16).optional(),
|
|
2528
|
+
knowledge: z4.array(KnowledgeEntrySchema).max(1e4),
|
|
2529
|
+
capabilities: z4.array(CapabilityRefSchema).max(5e3).optional(),
|
|
2530
|
+
handoffs: z4.record(z4.string().min(1).max(256), AgentHandoffLegacySchema).optional(),
|
|
1644
2531
|
lookup: IndexLookupSchema.optional()
|
|
1645
2532
|
}).strict();
|
|
1646
2533
|
|
|
1647
2534
|
// src/schemas/memory-candidate.ts
|
|
1648
|
-
import { z as
|
|
2535
|
+
import { z as z5 } from "zod";
|
|
1649
2536
|
var MEMORY_CANDIDATE_SCHEMA_VERSION = 1;
|
|
1650
|
-
var MemorySourceSchema =
|
|
2537
|
+
var MemorySourceSchema = z5.enum([
|
|
1651
2538
|
"cursor",
|
|
1652
2539
|
"claude",
|
|
1653
2540
|
"codex",
|
|
@@ -1655,24 +2542,24 @@ var MemorySourceSchema = z4.enum([
|
|
|
1655
2542
|
"agent-memory",
|
|
1656
2543
|
"manual"
|
|
1657
2544
|
]);
|
|
1658
|
-
var MemorySuggestedTypeSchema =
|
|
2545
|
+
var MemorySuggestedTypeSchema = z5.enum([
|
|
1659
2546
|
"user",
|
|
1660
2547
|
"feedback",
|
|
1661
2548
|
"project",
|
|
1662
2549
|
"reference",
|
|
1663
2550
|
"unknown"
|
|
1664
2551
|
]);
|
|
1665
|
-
var MemoryCandidateV1Schema =
|
|
1666
|
-
schemaVersion:
|
|
1667
|
-
id:
|
|
2552
|
+
var MemoryCandidateV1Schema = z5.object({
|
|
2553
|
+
schemaVersion: z5.literal(MEMORY_CANDIDATE_SCHEMA_VERSION),
|
|
2554
|
+
id: z5.string().min(1).max(256),
|
|
1668
2555
|
source: MemorySourceSchema,
|
|
1669
|
-
rawPath:
|
|
1670
|
-
fact:
|
|
1671
|
-
why:
|
|
1672
|
-
howToApply:
|
|
2556
|
+
rawPath: z5.string().min(1).max(512).optional(),
|
|
2557
|
+
fact: z5.string().min(1).max(8e3),
|
|
2558
|
+
why: z5.string().max(4e3).optional(),
|
|
2559
|
+
howToApply: z5.string().max(4e3).optional(),
|
|
1673
2560
|
suggestedType: MemorySuggestedTypeSchema,
|
|
1674
|
-
confidence:
|
|
1675
|
-
references:
|
|
2561
|
+
confidence: z5.number().min(0).max(1),
|
|
2562
|
+
references: z5.array(z5.string().min(1).max(512)).max(64)
|
|
1676
2563
|
}).strict();
|
|
1677
2564
|
|
|
1678
2565
|
// src/validate.ts
|
|
@@ -1718,17 +2605,28 @@ var IndexNotFoundError = class extends Error {
|
|
|
1718
2605
|
}
|
|
1719
2606
|
path;
|
|
1720
2607
|
};
|
|
1721
|
-
var indexFilePath = (root, config) =>
|
|
2608
|
+
var indexFilePath = (root, config) => join8(root, config.index?.outFile ?? ".doc-bridge/index.json");
|
|
1722
2609
|
var loadDocBridgeIndex = (root, config) => {
|
|
1723
2610
|
const path = indexFilePath(root, config);
|
|
1724
|
-
if (!
|
|
1725
|
-
const raw = JSON.parse(
|
|
2611
|
+
if (!existsSync10(path)) throw new IndexNotFoundError(path);
|
|
2612
|
+
const raw = JSON.parse(readFileSync9(path, "utf8"));
|
|
1726
2613
|
return parseDocBridgeIndex(raw);
|
|
1727
2614
|
};
|
|
1728
2615
|
|
|
1729
2616
|
// src/gates/run-gates.ts
|
|
1730
|
-
var
|
|
2617
|
+
var RESERVED_GATE_IDS = /* @__PURE__ */ new Set(["link-rot", "routing-currency", "bootstrap-size"]);
|
|
1731
2618
|
var runGate = (root, config, id) => {
|
|
2619
|
+
if (id === "documentation-standard-v1") {
|
|
2620
|
+
const details = runDocumentationStandardV1(root, config);
|
|
2621
|
+
return {
|
|
2622
|
+
id,
|
|
2623
|
+
ok: details.ok,
|
|
2624
|
+
message: details.ok ? "Documentation Standard v1 required rules pass" : `${details.summary.required.failed} Documentation Standard v1 required rule(s) failed`,
|
|
2625
|
+
expected: "all required rules pass or have approved exceptions",
|
|
2626
|
+
actual: `${details.summary.required.passed} passed, ${details.summary.required.failed} failed, ${details.summary.required.excepted} excepted`,
|
|
2627
|
+
details
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
1732
2630
|
if (id === "human-guide-links") return runHumanGuideLinksGate(root, config);
|
|
1733
2631
|
if (id === "okf-type") return runOkfTypeGate(root, config);
|
|
1734
2632
|
if (id === "docs-style") return runDocsStyleGate(root, config);
|
|
@@ -1786,7 +2684,7 @@ var runOkfTypeGate = (root, config) => {
|
|
|
1786
2684
|
const required = config.corpus.agent.okf?.requireType ?? config.gates?.preset === "strict";
|
|
1787
2685
|
if (!required) return { id: "okf-type", ok: true, message: "OKF type frontmatter not required" };
|
|
1788
2686
|
const allowed = config.corpus.agent.okf?.allowedTypes;
|
|
1789
|
-
const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(
|
|
2687
|
+
const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({ path: doc.path, type: frontmatterType(readFileSync10(doc.absPath, "utf8")) })).filter((doc) => !doc.type || allowed && !allowed.includes(doc.type));
|
|
1790
2688
|
if (bad.length) {
|
|
1791
2689
|
return {
|
|
1792
2690
|
id: "okf-type",
|
|
@@ -1860,7 +2758,7 @@ var runDocsStyleGate = (root, config) => {
|
|
|
1860
2758
|
}
|
|
1861
2759
|
const bad = scanAgentCorpus(root, config).filter((doc) => doc.path !== config.corpus.agent.index).map((doc) => ({
|
|
1862
2760
|
path: doc.path,
|
|
1863
|
-
missing: missingStyleRules(
|
|
2761
|
+
missing: missingStyleRules(readFileSync10(doc.absPath, "utf8"), required)
|
|
1864
2762
|
})).filter((doc) => doc.missing.length > 0);
|
|
1865
2763
|
if (bad.length) {
|
|
1866
2764
|
return {
|
|
@@ -1890,10 +2788,16 @@ var resolveGateIds = (config) => {
|
|
|
1890
2788
|
) : ["index-freshness", "human-guide-links"]
|
|
1891
2789
|
);
|
|
1892
2790
|
for (const id of config.gates?.include ?? []) {
|
|
1893
|
-
if (
|
|
2791
|
+
if (RESERVED_GATE_IDS.has(id)) {
|
|
2792
|
+
process.emitWarning(`Gate "${id}" is reserved and has no runtime implementation; it was not executed.`, {
|
|
2793
|
+
code: "AK_DOCS_RESERVED_GATE"
|
|
2794
|
+
});
|
|
2795
|
+
continue;
|
|
2796
|
+
}
|
|
2797
|
+
ids.add(id);
|
|
1894
2798
|
}
|
|
1895
2799
|
for (const id of config.gates?.exclude ?? []) {
|
|
1896
|
-
if (
|
|
2800
|
+
if (!RESERVED_GATE_IDS.has(id)) ids.delete(id);
|
|
1897
2801
|
}
|
|
1898
2802
|
return [...ids];
|
|
1899
2803
|
};
|
|
@@ -2009,7 +2913,7 @@ var runQuery = (index, config, req) => {
|
|
|
2009
2913
|
};
|
|
2010
2914
|
|
|
2011
2915
|
// src/intelligence/adapter.ts
|
|
2012
|
-
import { join as
|
|
2916
|
+
import { join as join9 } from "path";
|
|
2013
2917
|
|
|
2014
2918
|
// src/intelligence/peers.ts
|
|
2015
2919
|
var PeerMissingError = class extends Error {
|
|
@@ -2136,20 +3040,20 @@ var resolveIntelligenceRuntime = async (config) => {
|
|
|
2136
3040
|
}
|
|
2137
3041
|
return { adapter, embed, provider, ...model ? { model } : {} };
|
|
2138
3042
|
};
|
|
2139
|
-
var defaultVectorStorePath = (root) =>
|
|
3043
|
+
var defaultVectorStorePath = (root) => join9(root, ".doc-bridge", "vectors");
|
|
2140
3044
|
|
|
2141
3045
|
// src/intelligence/rag.ts
|
|
2142
|
-
import { readFileSync as
|
|
2143
|
-
import { join as
|
|
3046
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
3047
|
+
import { join as join10 } from "path";
|
|
2144
3048
|
var loadDocuments = (root, index, sources) => {
|
|
2145
3049
|
const includeAgent = sources.includes("agent") || sources.length === 0;
|
|
2146
3050
|
const docs = [];
|
|
2147
3051
|
if (includeAgent) {
|
|
2148
3052
|
for (const entry of index.knowledge) {
|
|
2149
|
-
const abs =
|
|
3053
|
+
const abs = join10(root, entry.path);
|
|
2150
3054
|
let content = "";
|
|
2151
3055
|
try {
|
|
2152
|
-
content =
|
|
3056
|
+
content = readFileSync11(abs, "utf8");
|
|
2153
3057
|
} catch {
|
|
2154
3058
|
content = [entry.title, entry.description].filter(Boolean).join("\n\n");
|
|
2155
3059
|
}
|
|
@@ -2167,7 +3071,7 @@ var createDocBridgeRag = async (root, config, index) => {
|
|
|
2167
3071
|
const { embed } = await resolveIntelligenceRuntime(config);
|
|
2168
3072
|
const ragMod = await importPeer("@agentskit/rag");
|
|
2169
3073
|
const memoryMod = await importPeer("@agentskit/memory");
|
|
2170
|
-
const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ?
|
|
3074
|
+
const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join10(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
|
|
2171
3075
|
const store = memoryMod.fileVectorMemory({ path: storePath });
|
|
2172
3076
|
const rag = ragMod.createRAG({
|
|
2173
3077
|
embed,
|
|
@@ -2302,15 +3206,15 @@ var startInkChat = async (root, config, index) => {
|
|
|
2302
3206
|
};
|
|
2303
3207
|
|
|
2304
3208
|
// src/memory/ingest.ts
|
|
2305
|
-
import { existsSync as
|
|
2306
|
-
import { join as
|
|
3209
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
|
|
3210
|
+
import { join as join11 } from "path";
|
|
2307
3211
|
var memoryFact = (raw, id) => firstParagraph(raw) ?? firstHeading(raw) ?? id;
|
|
2308
3212
|
var relativePath = (root, abs) => toPosix(abs).replace(`${toPosix(root)}/`, "");
|
|
2309
3213
|
var ingestMarkdownDir = (root, dir, source, confidence) => {
|
|
2310
|
-
if (!
|
|
3214
|
+
if (!existsSync11(dir)) return [];
|
|
2311
3215
|
return walkFiles(dir, { extensions: [".md", ".mdc"] }).map((abs) => {
|
|
2312
3216
|
const rel = relativePath(root, abs);
|
|
2313
|
-
const raw =
|
|
3217
|
+
const raw = readFileSync12(abs, "utf8");
|
|
2314
3218
|
const id = slugFromPath(rel);
|
|
2315
3219
|
return {
|
|
2316
3220
|
schemaVersion: 1,
|
|
@@ -2325,10 +3229,10 @@ var ingestMarkdownDir = (root, dir, source, confidence) => {
|
|
|
2325
3229
|
});
|
|
2326
3230
|
};
|
|
2327
3231
|
var ingestCursorRules = (root) => {
|
|
2328
|
-
const dir =
|
|
3232
|
+
const dir = join11(root, ".cursor", "rules");
|
|
2329
3233
|
return ingestMarkdownDir(root, dir, "cursor", 0.6);
|
|
2330
3234
|
};
|
|
2331
|
-
var ingestAgentMemory = (root) => ingestMarkdownDir(root,
|
|
3235
|
+
var ingestAgentMemory = (root) => ingestMarkdownDir(root, join11(root, ".agent-memory"), "agent-memory", 0.7);
|
|
2332
3236
|
var ingestMemoryCandidates = (root) => [
|
|
2333
3237
|
...ingestAgentMemory(root),
|
|
2334
3238
|
...ingestCursorRules(root)
|
|
@@ -2415,8 +3319,8 @@ var draftMemoryPromotion = (classifications) => {
|
|
|
2415
3319
|
|
|
2416
3320
|
// src/memory/github-pr.ts
|
|
2417
3321
|
import { execFileSync, spawnSync } from "child_process";
|
|
2418
|
-
import { existsSync as
|
|
2419
|
-
import { join as
|
|
3322
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3323
|
+
import { join as join12 } from "path";
|
|
2420
3324
|
var run = (cmd, args, cwd) => {
|
|
2421
3325
|
const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
|
|
2422
3326
|
const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
@@ -2424,10 +3328,10 @@ var run = (cmd, args, cwd) => {
|
|
|
2424
3328
|
};
|
|
2425
3329
|
var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
|
|
2426
3330
|
var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
2427
|
-
var defaultPromotionDraftPath = (root) =>
|
|
3331
|
+
var defaultPromotionDraftPath = (root) => join12(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
|
|
2428
3332
|
var writePromotionDraft = (root, draft, path) => {
|
|
2429
3333
|
const draftPath = path ?? defaultPromotionDraftPath(root);
|
|
2430
|
-
mkdirSync2(
|
|
3334
|
+
mkdirSync2(join12(root, ".doc-bridge", "drafts"), { recursive: true });
|
|
2431
3335
|
writeFileSync2(draftPath, `${draft.body}
|
|
2432
3336
|
`, "utf8");
|
|
2433
3337
|
return draftPath;
|
|
@@ -2463,7 +3367,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
|
|
|
2463
3367
|
message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
|
|
2464
3368
|
};
|
|
2465
3369
|
}
|
|
2466
|
-
if (!
|
|
3370
|
+
if (!existsSync12(join12(root, ".git"))) {
|
|
2467
3371
|
return {
|
|
2468
3372
|
ok: false,
|
|
2469
3373
|
dryRun: false,
|
|
@@ -2566,22 +3470,22 @@ ${auth.out}`
|
|
|
2566
3470
|
};
|
|
2567
3471
|
|
|
2568
3472
|
// src/index-builder/watch-index.ts
|
|
2569
|
-
import { existsSync as
|
|
2570
|
-
import { dirname as dirname4, resolve as
|
|
3473
|
+
import { existsSync as existsSync13, watch } from "fs";
|
|
3474
|
+
import { dirname as dirname4, resolve as resolve8 } from "path";
|
|
2571
3475
|
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
2572
3476
|
var collectWatchRoots = (root, config, configPath) => {
|
|
2573
3477
|
const roots = /* @__PURE__ */ new Set();
|
|
2574
|
-
roots.add(
|
|
3478
|
+
roots.add(resolve8(root, config.corpus.agent.root));
|
|
2575
3479
|
const humanSources = config.corpus.human ? Array.isArray(config.corpus.human) ? config.corpus.human : [config.corpus.human] : [];
|
|
2576
3480
|
for (const source of humanSources) {
|
|
2577
3481
|
const humanOpts = source.options ?? {};
|
|
2578
3482
|
for (const key of ["contentDir", "docsDir", "root"]) {
|
|
2579
3483
|
const value = humanOpts[key];
|
|
2580
|
-
if (typeof value === "string" && value.length) roots.add(
|
|
3484
|
+
if (typeof value === "string" && value.length) roots.add(resolve8(root, value));
|
|
2581
3485
|
}
|
|
2582
3486
|
}
|
|
2583
|
-
if (configPath) roots.add(dirname4(
|
|
2584
|
-
return [...roots].filter((dir) =>
|
|
3487
|
+
if (configPath) roots.add(dirname4(resolve8(configPath)));
|
|
3488
|
+
return [...roots].filter((dir) => existsSync13(dir));
|
|
2585
3489
|
};
|
|
2586
3490
|
var watchDocBridgeIndex = (opts) => {
|
|
2587
3491
|
const debounceMs = opts.debounceMs ?? 350;
|
|
@@ -2622,8 +3526,8 @@ var watchDocBridgeIndex = (opts) => {
|
|
|
2622
3526
|
rebuild();
|
|
2623
3527
|
});
|
|
2624
3528
|
}
|
|
2625
|
-
const configDir =
|
|
2626
|
-
if (
|
|
3529
|
+
const configDir = resolve8(opts.root);
|
|
3530
|
+
if (existsSync13(configDir)) {
|
|
2627
3531
|
watch(configDir, (_event, filename) => {
|
|
2628
3532
|
if (!filename || !/doc-bridge\.config/.test(filename)) return;
|
|
2629
3533
|
rebuild();
|
|
@@ -2799,15 +3703,15 @@ var docBridgePatternPayload = () => ({
|
|
|
2799
3703
|
});
|
|
2800
3704
|
|
|
2801
3705
|
// src/cli/demo.ts
|
|
2802
|
-
import { cpSync, existsSync as
|
|
3706
|
+
import { cpSync, existsSync as existsSync15, mkdtempSync, readFileSync as readFileSync14, rmSync } from "fs";
|
|
2803
3707
|
import { tmpdir } from "os";
|
|
2804
|
-
import { dirname as dirname6, join as
|
|
3708
|
+
import { dirname as dirname6, join as join15, resolve as resolve10 } from "path";
|
|
2805
3709
|
import { fileURLToPath } from "url";
|
|
2806
3710
|
|
|
2807
3711
|
// src/mcp/install.ts
|
|
2808
|
-
import { existsSync as
|
|
3712
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync3 } from "fs";
|
|
2809
3713
|
import { homedir } from "os";
|
|
2810
|
-
import { dirname as dirname5, join as
|
|
3714
|
+
import { dirname as dirname5, join as join14, resolve as resolve9 } from "path";
|
|
2811
3715
|
var SERVER_NAME = "ak-docs";
|
|
2812
3716
|
var mcpServerEntry = (root) => ({
|
|
2813
3717
|
command: "npx",
|
|
@@ -2815,9 +3719,9 @@ var mcpServerEntry = (root) => ({
|
|
|
2815
3719
|
cwd: root
|
|
2816
3720
|
});
|
|
2817
3721
|
var readJson = (path) => {
|
|
2818
|
-
if (!
|
|
3722
|
+
if (!existsSync14(path)) return {};
|
|
2819
3723
|
try {
|
|
2820
|
-
const parsed = JSON.parse(
|
|
3724
|
+
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
2821
3725
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
2822
3726
|
} catch {
|
|
2823
3727
|
return {};
|
|
@@ -2829,12 +3733,12 @@ var writeJson = (path, value) => {
|
|
|
2829
3733
|
`, "utf8");
|
|
2830
3734
|
};
|
|
2831
3735
|
var resolveTargetPath = (target, root) => {
|
|
2832
|
-
if (target === "cursor") return
|
|
2833
|
-
return
|
|
3736
|
+
if (target === "cursor") return resolve9(root, ".cursor", "mcp.json");
|
|
3737
|
+
return join14(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
2834
3738
|
};
|
|
2835
3739
|
var installMcpConfig = (root, target) => {
|
|
2836
3740
|
const configPath = resolveTargetPath(target, root);
|
|
2837
|
-
const created = !
|
|
3741
|
+
const created = !existsSync14(configPath);
|
|
2838
3742
|
const existing = readJson(configPath);
|
|
2839
3743
|
const servers = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? { ...existing.mcpServers } : {};
|
|
2840
3744
|
servers[SERVER_NAME] = mcpServerEntry(root);
|
|
@@ -2859,12 +3763,12 @@ var installMcpConfig = (root, target) => {
|
|
|
2859
3763
|
var mcpSnippet = (root) => JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpServerEntry(root) } }, null, 2);
|
|
2860
3764
|
|
|
2861
3765
|
// src/cli/demo.ts
|
|
2862
|
-
var packageRoot =
|
|
3766
|
+
var packageRoot = resolve10(dirname6(fileURLToPath(import.meta.url)), "..", "..");
|
|
2863
3767
|
var fixturePath = (fixture) => {
|
|
2864
3768
|
if (fixture === "monorepo") {
|
|
2865
|
-
return
|
|
3769
|
+
return join15(packageRoot, "examples", "demo-monorepo");
|
|
2866
3770
|
}
|
|
2867
|
-
return
|
|
3771
|
+
return join15(packageRoot, "examples", "demo-example");
|
|
2868
3772
|
};
|
|
2869
3773
|
var formatHandoffText = (handoff) => {
|
|
2870
3774
|
const bridge = handoff.bridge?.humanDoc === "missing" ? `human guide: missing \u2192 ${handoff.bridge.action ?? "ak-docs bootstrap agent-docs"}` : handoff.humanDoc ? `human guide: ${handoff.humanDoc}` : "human guide: (none)";
|
|
@@ -2880,11 +3784,11 @@ var formatHandoffText = (handoff) => {
|
|
|
2880
3784
|
var runDemo = (root, config, fixture = "example", options = {}) => {
|
|
2881
3785
|
const targetPackage = fixture === "monorepo" ? "auth" : "example";
|
|
2882
3786
|
const fixtureDir = fixturePath(fixture);
|
|
2883
|
-
if (options.copyFixture &&
|
|
3787
|
+
if (options.copyFixture && existsSync15(fixtureDir)) {
|
|
2884
3788
|
for (const rel of ["doc-bridge.config.json", "docs", "packages", "pnpm-workspace.yaml", "package.json"]) {
|
|
2885
|
-
const src =
|
|
2886
|
-
if (!
|
|
2887
|
-
const dest =
|
|
3789
|
+
const src = join15(fixtureDir, rel);
|
|
3790
|
+
if (!existsSync15(src)) continue;
|
|
3791
|
+
const dest = join15(root, rel);
|
|
2888
3792
|
cpSync(src, dest, { recursive: true });
|
|
2889
3793
|
}
|
|
2890
3794
|
}
|
|
@@ -2939,14 +3843,14 @@ var formatDemoText = (result) => {
|
|
|
2939
3843
|
return lines;
|
|
2940
3844
|
};
|
|
2941
3845
|
var withDemoWorkspace = (fixture, fn) => {
|
|
2942
|
-
const dir = mkdtempSync(
|
|
3846
|
+
const dir = mkdtempSync(join15(tmpdir(), "ak-docs-demo-"));
|
|
2943
3847
|
try {
|
|
2944
3848
|
const fixtureDir = fixturePath(fixture);
|
|
2945
|
-
if (!
|
|
3849
|
+
if (!existsSync15(fixtureDir)) {
|
|
2946
3850
|
throw new Error(`Demo fixture "${fixture}" not found at ${fixtureDir}`);
|
|
2947
3851
|
}
|
|
2948
3852
|
cpSync(fixtureDir, dir, { recursive: true });
|
|
2949
|
-
const config = JSON.parse(
|
|
3853
|
+
const config = JSON.parse(readFileSync14(join15(dir, "doc-bridge.config.json"), "utf8"));
|
|
2950
3854
|
return fn(dir, config);
|
|
2951
3855
|
} finally {
|
|
2952
3856
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -3157,9 +4061,9 @@ var formatDoctorText = (report) => {
|
|
|
3157
4061
|
};
|
|
3158
4062
|
|
|
3159
4063
|
// src/mcp/server.ts
|
|
3160
|
-
import { readFileSync as
|
|
3161
|
-
import { relative, resolve as
|
|
3162
|
-
import { z as
|
|
4064
|
+
import { readFileSync as readFileSync15, realpathSync as realpathSync6 } from "fs";
|
|
4065
|
+
import { relative as relative4, resolve as resolve11 } from "path";
|
|
4066
|
+
import { z as z6, ZodError } from "zod";
|
|
3163
4067
|
var MCP_TOOLS = [
|
|
3164
4068
|
{
|
|
3165
4069
|
name: "handoff.resolve",
|
|
@@ -3218,21 +4122,21 @@ var MCP_TOOLS = [
|
|
|
3218
4122
|
}
|
|
3219
4123
|
];
|
|
3220
4124
|
var asRecord = (value) => value && typeof value === "object" ? value : {};
|
|
3221
|
-
var HandoffResolveArgsSchema =
|
|
3222
|
-
id:
|
|
3223
|
-
kind:
|
|
4125
|
+
var HandoffResolveArgsSchema = z6.object({
|
|
4126
|
+
id: z6.string().min(1),
|
|
4127
|
+
kind: z6.enum(["package", "ownership"]).optional()
|
|
3224
4128
|
});
|
|
3225
|
-
var DocSearchArgsSchema =
|
|
3226
|
-
term:
|
|
3227
|
-
limit:
|
|
4129
|
+
var DocSearchArgsSchema = z6.object({
|
|
4130
|
+
term: z6.string().min(1),
|
|
4131
|
+
limit: z6.number().int().positive().max(100).optional()
|
|
3228
4132
|
});
|
|
3229
|
-
var RetrieverQueryArgsSchema =
|
|
3230
|
-
query:
|
|
3231
|
-
limit:
|
|
4133
|
+
var RetrieverQueryArgsSchema = z6.object({
|
|
4134
|
+
query: z6.string().min(1),
|
|
4135
|
+
limit: z6.number().int().positive().max(100).optional()
|
|
3232
4136
|
});
|
|
3233
|
-
var DocGetArgsSchema =
|
|
3234
|
-
id:
|
|
3235
|
-
path:
|
|
4137
|
+
var DocGetArgsSchema = z6.object({
|
|
4138
|
+
id: z6.string().min(1).optional(),
|
|
4139
|
+
path: z6.string().min(1).optional()
|
|
3236
4140
|
}).refine((args) => args.id || args.path, "doc.get requires id or path");
|
|
3237
4141
|
var parseToolArgs = (tool, schema, value) => {
|
|
3238
4142
|
try {
|
|
@@ -3264,12 +4168,12 @@ var findDocPath = (index, args) => {
|
|
|
3264
4168
|
return doc.path;
|
|
3265
4169
|
};
|
|
3266
4170
|
var resolveDocPath = (root, relPath) => {
|
|
3267
|
-
const rootAbs =
|
|
3268
|
-
const unresolved =
|
|
3269
|
-
const unresolvedRel =
|
|
4171
|
+
const rootAbs = realpathSync6.native(root);
|
|
4172
|
+
const unresolved = resolve11(rootAbs, relPath);
|
|
4173
|
+
const unresolvedRel = relative4(rootAbs, unresolved);
|
|
3270
4174
|
if (unresolvedRel.startsWith("..")) throw new Error("doc.get path escapes project root");
|
|
3271
|
-
const abs =
|
|
3272
|
-
const rel =
|
|
4175
|
+
const abs = realpathSync6.native(unresolved);
|
|
4176
|
+
const rel = relative4(rootAbs, abs);
|
|
3273
4177
|
if (rel.startsWith("..")) throw new Error("doc.get path escapes project root");
|
|
3274
4178
|
return abs;
|
|
3275
4179
|
};
|
|
@@ -3303,7 +4207,7 @@ var handleMcpRequest = (ctx, request) => {
|
|
|
3303
4207
|
}
|
|
3304
4208
|
if (name === "doc.get") {
|
|
3305
4209
|
const relPath = findDocPath(index(), parseToolArgs("doc.get", DocGetArgsSchema, args));
|
|
3306
|
-
return textResult(
|
|
4210
|
+
return textResult(readFileSync15(resolveDocPath(ctx.root, relPath), "utf8"));
|
|
3307
4211
|
}
|
|
3308
4212
|
if (name === "gate.status") return textResult(runGates(ctx.root, ctx.config));
|
|
3309
4213
|
if (name === "retriever.query") {
|
|
@@ -3388,11 +4292,12 @@ Core (no API key):
|
|
|
3388
4292
|
ak-docs demo [--fixture example|monorepo] [--text] [--in-project]
|
|
3389
4293
|
ak-docs doctor [--text] [--badge] [--write-badge]
|
|
3390
4294
|
ak-docs index [--watch]
|
|
3391
|
-
ak-docs query
|
|
4295
|
+
ak-docs query [package|ownership|intent|change] <id> [--agent] [--text]
|
|
3392
4296
|
ak-docs search <term> [--agent] [--text]
|
|
3393
4297
|
ak-docs list <packages|intents|changes|knowledge> [--text]
|
|
3394
4298
|
ak-docs ask [question] local consult (no LLM)
|
|
3395
4299
|
ak-docs gate run [gate-id]
|
|
4300
|
+
ak-docs conformance run documentation-standard-v1 [--text|--json]
|
|
3396
4301
|
ak-docs mcp
|
|
3397
4302
|
ak-docs mcp install --cursor | --claude
|
|
3398
4303
|
ak-docs memory ingest|classify|promote [--pr] [--dry-run]
|
|
@@ -3416,7 +4321,13 @@ Global flags:
|
|
|
3416
4321
|
`;
|
|
3417
4322
|
var QUERY_KINDS = /* @__PURE__ */ new Set(["package", "ownership", "intent", "change", "search"]);
|
|
3418
4323
|
var LIST_KINDS = /* @__PURE__ */ new Set(["packages", "intents", "changes", "knowledge"]);
|
|
3419
|
-
var GATE_IDS = /* @__PURE__ */ new Set([
|
|
4324
|
+
var GATE_IDS = /* @__PURE__ */ new Set([
|
|
4325
|
+
"index-freshness",
|
|
4326
|
+
"human-guide-links",
|
|
4327
|
+
"okf-type",
|
|
4328
|
+
"docs-style",
|
|
4329
|
+
"documentation-standard-v1"
|
|
4330
|
+
]);
|
|
3420
4331
|
var parseArgs = (argv) => {
|
|
3421
4332
|
const flags = /* @__PURE__ */ new Set();
|
|
3422
4333
|
let configPath;
|
|
@@ -3457,6 +4368,7 @@ var parseArgs = (argv) => {
|
|
|
3457
4368
|
else if (positional[0] === "chat") command = "chat";
|
|
3458
4369
|
else if (positional[0] === "rag") command = "rag";
|
|
3459
4370
|
else if (positional[0] === "list") command = "list";
|
|
4371
|
+
else if (positional[0] === "conformance") command = "conformance";
|
|
3460
4372
|
return { command, flags, configPath, positional };
|
|
3461
4373
|
};
|
|
3462
4374
|
var writeJson2 = (payload) => {
|
|
@@ -3550,12 +4462,12 @@ var readIndexedDoc = (root, config, idOrPath) => {
|
|
|
3550
4462
|
const index = loadDocBridgeIndex(root, config);
|
|
3551
4463
|
const entry = index.knowledge.find((doc) => doc.id === idOrPath || doc.path === idOrPath);
|
|
3552
4464
|
if (!entry) throw new Error(`Unknown indexed doc "${idOrPath}". Try: search ${idOrPath}`);
|
|
3553
|
-
const abs =
|
|
3554
|
-
const rootAbs =
|
|
4465
|
+
const abs = resolve12(root, entry.path);
|
|
4466
|
+
const rootAbs = resolve12(root);
|
|
3555
4467
|
if (abs !== rootAbs && !abs.startsWith(`${rootAbs}/`)) {
|
|
3556
4468
|
throw new Error(`Indexed doc escapes project root: ${entry.path}`);
|
|
3557
4469
|
}
|
|
3558
|
-
return
|
|
4470
|
+
return readFileSync16(abs, "utf8");
|
|
3559
4471
|
};
|
|
3560
4472
|
var runAskRepl = async (root, config) => {
|
|
3561
4473
|
const index = loadDocBridgeIndex(root, config);
|
|
@@ -3631,7 +4543,7 @@ var diagnosticNextCommands = (config, result) => {
|
|
|
3631
4543
|
];
|
|
3632
4544
|
};
|
|
3633
4545
|
var writeIfMissing = (path, contents) => {
|
|
3634
|
-
if (
|
|
4546
|
+
if (existsSync16(path)) return false;
|
|
3635
4547
|
mkdirSync5(dirname7(path), { recursive: true });
|
|
3636
4548
|
writeFileSync4(path, contents, "utf8");
|
|
3637
4549
|
return true;
|
|
@@ -3736,7 +4648,7 @@ var scaffoldWorkspaceDocs = (root, config) => {
|
|
|
3736
4648
|
const created = [];
|
|
3737
4649
|
const skipped = [];
|
|
3738
4650
|
for (const pkg of discoverPnpmPackages(root, config)) {
|
|
3739
|
-
const path =
|
|
4651
|
+
const path = resolve12(root, config.corpus.agent.root, "packages", `${pkg.id}.md`);
|
|
3740
4652
|
if (writeIfMissing(path, workspaceDocDraft(pkg.id, pkg.path))) created.push(path);
|
|
3741
4653
|
else skipped.push(path);
|
|
3742
4654
|
}
|
|
@@ -3746,11 +4658,11 @@ var bootstrapAgentDocs = (root, config) => {
|
|
|
3746
4658
|
const created = [];
|
|
3747
4659
|
const skipped = [];
|
|
3748
4660
|
for (const doc of scanHumanDocRecords(root, config)) {
|
|
3749
|
-
const raw =
|
|
4661
|
+
const raw = readFileSync16(doc.path, "utf8");
|
|
3750
4662
|
const body = raw.replace(/^---\n[\s\S]*?\n---\n?/, "");
|
|
3751
4663
|
const title = firstHeading(body) ?? doc.id;
|
|
3752
4664
|
const description = firstParagraph(body);
|
|
3753
|
-
const draftPath =
|
|
4665
|
+
const draftPath = resolve12(root, config.corpus.agent.root, "human", `${doc.id}.md`);
|
|
3754
4666
|
const draft = [
|
|
3755
4667
|
"---",
|
|
3756
4668
|
"type: knowledge",
|
|
@@ -3814,8 +4726,8 @@ var runCli = (argv) => {
|
|
|
3814
4726
|
return 1;
|
|
3815
4727
|
}
|
|
3816
4728
|
try {
|
|
3817
|
-
const abs =
|
|
3818
|
-
const raw =
|
|
4729
|
+
const abs = resolve12(file);
|
|
4730
|
+
const raw = readFileSync16(abs, "utf8");
|
|
3819
4731
|
const handoff = parseAgentHandoff(JSON.parse(raw));
|
|
3820
4732
|
writeJson2({ ok: true, handoff });
|
|
3821
4733
|
return 0;
|
|
@@ -3828,10 +4740,10 @@ var runCli = (argv) => {
|
|
|
3828
4740
|
if (command === "init") {
|
|
3829
4741
|
const root = process.cwd();
|
|
3830
4742
|
const withDemo = flags.has("--demo") || !flags.has("--no-demo");
|
|
3831
|
-
const configFile =
|
|
3832
|
-
const docsIndex =
|
|
3833
|
-
const exampleDoc =
|
|
3834
|
-
const agentsMd =
|
|
4743
|
+
const configFile = resolve12(root, configPath ?? "doc-bridge.config.json");
|
|
4744
|
+
const docsIndex = resolve12(root, "docs/for-agents/INDEX.md");
|
|
4745
|
+
const exampleDoc = resolve12(root, "docs/for-agents/packages/example.md");
|
|
4746
|
+
const agentsMd = resolve12(root, "AGENTS.md");
|
|
3835
4747
|
const configWritten = writeIfMissing(configFile, initConfigContents(configFile, withDemo));
|
|
3836
4748
|
const indexWritten = writeIfMissing(
|
|
3837
4749
|
docsIndex,
|
|
@@ -3839,7 +4751,7 @@ var runCli = (argv) => {
|
|
|
3839
4751
|
);
|
|
3840
4752
|
const exampleWritten = withDemo ? writeIfMissing(exampleDoc, exampleAgentDoc) : false;
|
|
3841
4753
|
const agentsWritten = writeIfMissing(agentsMd, agentsMdSnippet);
|
|
3842
|
-
if (withDemo) writeIfMissing(
|
|
4754
|
+
if (withDemo) writeIfMissing(resolve12(root, "src/.gitkeep"), "");
|
|
3843
4755
|
const scaffold = flags.has("--scaffold-workspaces") ? scaffoldWorkspaceDocs(root, loadProject(configFile).config) : void 0;
|
|
3844
4756
|
writeJson2({
|
|
3845
4757
|
ok: true,
|
|
@@ -3997,12 +4909,12 @@ var runCli = (argv) => {
|
|
|
3997
4909
|
const action = positional[1];
|
|
3998
4910
|
const gateId = positional[2];
|
|
3999
4911
|
if (action !== "run") {
|
|
4000
|
-
process.stderr.write("Usage: ak-docs gate run [
|
|
4912
|
+
process.stderr.write("Usage: ak-docs gate run [gate-id]\n");
|
|
4001
4913
|
return 1;
|
|
4002
4914
|
}
|
|
4003
4915
|
if (gateId && !GATE_IDS.has(gateId)) {
|
|
4004
4916
|
process.stderr.write(
|
|
4005
|
-
`Unsupported gate "${gateId}". Supported gates:
|
|
4917
|
+
`Unsupported gate "${gateId}". Supported gates: ${[...GATE_IDS].join(", ")}
|
|
4006
4918
|
`
|
|
4007
4919
|
);
|
|
4008
4920
|
return 1;
|
|
@@ -4014,6 +4926,26 @@ var runCli = (argv) => {
|
|
|
4014
4926
|
return result.ok ? 0 : 1;
|
|
4015
4927
|
} catch (error) {
|
|
4016
4928
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
4929
|
+
`);
|
|
4930
|
+
return 1;
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
if (command === "conformance") {
|
|
4934
|
+
const action = positional[1];
|
|
4935
|
+
const profile = positional[2];
|
|
4936
|
+
if (action !== "run" || profile !== DOCUMENTATION_STANDARD_V1_ID) {
|
|
4937
|
+
process.stderr.write(`Usage: ak-docs conformance run ${DOCUMENTATION_STANDARD_V1_ID} [--text|--json]
|
|
4938
|
+
`);
|
|
4939
|
+
return 1;
|
|
4940
|
+
}
|
|
4941
|
+
try {
|
|
4942
|
+
const { config, root } = loadProject(configPath);
|
|
4943
|
+
const report = runDocumentationStandardV1(root, config);
|
|
4944
|
+
if (wantsTextOutput(flags, config)) writeLines(formatDocumentationStandardText(report));
|
|
4945
|
+
else writeJson2(report);
|
|
4946
|
+
return report.ok ? 0 : 1;
|
|
4947
|
+
} catch (error) {
|
|
4948
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
4017
4949
|
`);
|
|
4018
4950
|
return 1;
|
|
4019
4951
|
}
|
|
@@ -4062,7 +4994,7 @@ var runCli = (argv) => {
|
|
|
4062
4994
|
const { config, root } = loadProject(configPath);
|
|
4063
4995
|
const report = runDoctor(root, config);
|
|
4064
4996
|
if (flags.has("--write-badge")) {
|
|
4065
|
-
const badgePath =
|
|
4997
|
+
const badgePath = resolve12(root, ".doc-bridge", "coverage-badge.json");
|
|
4066
4998
|
mkdirSync5(dirname7(badgePath), { recursive: true });
|
|
4067
4999
|
writeFileSync4(badgePath, `${formatDoctorBadgeJson(report.badge)}
|
|
4068
5000
|
`, "utf8");
|
|
@@ -4116,15 +5048,15 @@ var runCli = (argv) => {
|
|
|
4116
5048
|
}
|
|
4117
5049
|
}
|
|
4118
5050
|
if (command === "query") {
|
|
4119
|
-
const
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
process.stderr.write("Usage: ak-docs query <package|ownership|intent|change> <id> [--agent]\n");
|
|
5051
|
+
const maybeKind = positional[1];
|
|
5052
|
+
if (maybeKind === "search") {
|
|
5053
|
+
process.stderr.write("Usage: ak-docs query [package|ownership|intent|change] <id> [--agent]\n");
|
|
4123
5054
|
return 1;
|
|
4124
5055
|
}
|
|
5056
|
+
const kind = maybeKind && QUERY_KINDS.has(maybeKind) ? maybeKind : "package";
|
|
5057
|
+
const id = kind === maybeKind ? positional[2] : positional[1];
|
|
4125
5058
|
if (!id) {
|
|
4126
|
-
process.stderr.write(
|
|
4127
|
-
`);
|
|
5059
|
+
process.stderr.write("Usage: ak-docs query [package|ownership|intent|change] <id> [--agent]\n");
|
|
4128
5060
|
return 1;
|
|
4129
5061
|
}
|
|
4130
5062
|
try {
|