@agentskit/doc-bridge 1.7.45 → 1.9.0
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 +465 -0
- package/CONTRIBUTING.md +23 -0
- package/README.md +87 -40
- package/SECURITY.md +7 -0
- package/action.yml +1 -1
- package/bin/ak-docs.js +2 -2
- package/bin/ak-verify.js +13 -7
- package/dist/cli/program.d.ts +3 -1
- package/dist/cli/program.js +15861 -6223
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +86 -6
- package/dist/config/index.js.map +1 -1
- package/dist/{index-BUL0q7s8.d.ts → index-Beor6Yhi.d.ts} +133 -1
- package/dist/index.d.ts +9871 -1062
- package/dist/index.js +16126 -6125
- package/dist/index.js.map +1 -1
- package/docs/MARKETPLACE.md +1 -1
- package/docs/PRD-documentation-efficiency-study.md +406 -0
- package/docs/PRD-knowledge-retrieval-and-enrichment.md +466 -0
- package/docs/adr/0002-documentation-audit-boundary.md +22 -0
- package/docs/adr/0003-study-protocol-and-historical-evidence.md +40 -0
- package/docs/adr/0004-controlled-study-runner.md +25 -0
- package/docs/adr/0005-documentation-quality-and-criticality.md +20 -0
- package/docs/adr/0006-registry-semantic-grounding.md +20 -0
- package/docs/adr/0007-longitudinal-study-metrics.md +21 -0
- package/docs/adr/0008-study-verification-boundary.md +21 -0
- package/docs/adr/0009-study-provider-cli-adapter.md +25 -0
- package/docs/agent-corpus/INDEX.md +12 -1
- package/docs/agent-corpus/OVERVIEW.md +25 -0
- package/docs/agent-corpus/chat.md +5 -1
- package/docs/agent-corpus/cli.md +16 -0
- package/docs/agent-corpus/conformance.md +12 -0
- package/docs/agent-corpus/doc-bridge.md +47 -0
- package/docs/agent-corpus/doctor.md +8 -0
- package/docs/agent-corpus/gates.md +4 -0
- package/docs/agent-corpus/mcp.md +13 -0
- package/docs/agent-corpus/memory.md +4 -0
- package/docs/agent-corpus/query.md +33 -0
- package/docs/bench/README.md +122 -0
- package/docs/bench/retrieval-baseline-v1.json +28 -0
- package/docs/bench/retrieval-suite-v1.json +1033 -0
- package/docs/chat-and-rag.md +3 -2
- package/docs/for-agents.md +9 -1
- package/docs/getting-started.md +4 -11
- package/docs/guides/gate-ci.md +11 -1
- package/docs/guides/install-and-run.md +9 -65
- package/docs/index.md +22 -1
- package/docs/knowledge-engine-runbook.md +38 -3
- package/docs/landing/assets/context-payload-reduction.svg +21 -0
- package/docs/landing/assets/controlled-ab-comparison.svg +30 -0
- package/docs/landing/index.html +119 -5
- package/docs/loop-workflow.md +117 -0
- package/docs/mcp.md +6 -1
- package/docs/parity/public-claims-v1.json +145 -0
- package/docs/playbook/doc-bridge-pattern.md +1 -1
- package/docs/query.md +90 -2
- package/docs/recipes/index-pipeline.md +1 -1
- package/docs/schemas/agent-handoff-v1.md +15 -0
- package/docs/schemas/doc-bridge-index-v1.md +65 -0
- package/docs/spec/benchmark-v1.md +33 -1
- package/docs/spec/cli.md +30 -10
- package/docs/spec/config-v1.md +147 -8
- package/docs/spec/documentation-audit-v1.md +61 -0
- package/docs/spec/enrichment-overlay-v1.md +241 -0
- package/docs/spec/graph-signals-v1.md +92 -0
- package/docs/spec/incremental-scan-v1.md +102 -0
- package/docs/spec/markdown-analyzer-v1.md +73 -0
- package/docs/spec/mcp-knowledge-tools-v1.md +147 -0
- package/docs/spec/measured-enrichment-v1.md +229 -0
- package/docs/spec/public-parity-v1.md +119 -0
- package/docs/spec/registry-agents.md +6 -0
- package/docs/spec/render-v1.md +122 -0
- package/docs/spec/retrieval-index-v1.md +164 -0
- package/docs/spec/study-metrics-v1.md +58 -0
- package/docs/spec/study-protocol-v1.md +46 -0
- package/docs/spec/study-provider-cli-v1.md +116 -0
- package/docs/spec/study-runner-v1.md +35 -0
- package/docs/spec/study-task-suite-v1.md +41 -0
- package/docs/spec/study-verification-v1.md +40 -0
- package/docs/study/README.md +84 -0
- package/docs/study/ab-adjudicated-cost-analysis-v1.md +29 -0
- package/docs/study/ab-adjudicated-cost-plan-v1.json +33 -0
- package/docs/study/ab-adjudicated-cost-plan-v2-v1.json +33 -0
- package/docs/study/ab-adjudicated-cost-result-v1.json +80 -0
- package/docs/study/ab-baseline-analysis-v1.md +21 -0
- package/docs/study/ab-baseline-plan-v1.json +33 -0
- package/docs/study/ab-baseline-recovery-plan-v1.json +33 -0
- package/docs/study/ab-baseline-result-v1.json +79 -0
- package/docs/study/documentation-audit-round-2026-08-31.json +183 -0
- package/docs/study/historical-evidence-v1.json +252 -0
- package/docs/study/observation-ledger-v1.json +30632 -0
- package/docs/study/phase3-task-coverage-v1.json +34 -0
- package/docs/study/phase4-public-pilot-ledger-v1.json +1344 -0
- package/docs/study/phase4-public-pilot-result-v1.json +52 -0
- package/docs/study/phase4-public-pilot-run-plan-v1.json +26 -0
- package/docs/study/phase4-public-pilot-task-suite-v1.json +71 -0
- package/docs/study/pilot-round-2026-08-31.json +46 -0
- package/docs/study/protocol-v1.json +90 -0
- package/docs/study/publication-gate-v1.md +45 -0
- package/docs/study/quality-scorecard-cycle-plan.md +545 -0
- package/docs/study/quality-scorecard-v1.json +38 -0
- package/docs/study/round-1-adjudicated-smoke-v1.json +30642 -0
- package/docs/study/round-1-instrumentation-plan-v1.md +39 -0
- package/docs/study/round-2-expanded-adjudication-v1.json +91 -0
- package/docs/study/round-2-expanded-validation-v1.md +58 -0
- package/docs/study/round-3-evidence-contract-v1.json +75 -0
- package/docs/study/round-3-evidence-contract-v1.md +57 -0
- package/docs/study/round-4-confirmation-v1.json +75 -0
- package/docs/study/round-4-confirmation-v1.md +55 -0
- package/docs/study/run-plan-v1.json +33 -0
- package/docs/study/semantic-adjudication-cycle-8.md +20 -0
- package/docs/study/task-suite-v1.json +96 -0
- package/docs/study/token-efficiency-plan-v1.md +337 -0
- package/docs/study/token-efficiency-protocol-v2.json +62 -0
- package/docs/study/verification-binding-v1.json +27 -0
- package/docs/validation-cycle-plan.md +14 -0
- package/docs/verification-harness.md +11 -6
- package/ecosystem-claims.json +2 -2
- package/ecosystem-upstream.json +2 -2
- package/ecosystem.json +4 -4
- package/mcpb/manifest.json +9 -1
- package/package.json +25 -6
- package/scripts/check-ecosystem-upstream.mjs +34 -6
- package/skills/doc-bridge-handoff/fixtures/synthetic-repo/docs/for-agents/packages/payments.md +7 -0
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +192 -24
- package/src/audit/documentation.ts +513 -0
- package/src/bench/baseline.ts +198 -0
- package/src/bench/overlay-delta.ts +139 -0
- package/src/bench/retrieval.ts +319 -0
- package/src/budget/compile.ts +91 -0
- package/src/budget/sections.ts +70 -0
- package/src/cli/program.ts +684 -74
- package/src/cli/usage.ts +71 -0
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +4 -0
- package/src/config/schema.ts +117 -2
- package/src/conformance/documentation-standard-v1.ts +8 -6
- package/src/discovery/areas.ts +182 -0
- package/src/discovery/documentation.ts +211 -5
- package/src/discovery/identity.ts +24 -0
- package/src/discovery/incremental.ts +314 -0
- package/src/discovery/inputs.ts +110 -0
- package/src/discovery/markdown.ts +481 -0
- package/src/discovery/repository.ts +484 -101
- package/src/doctor/run-doctor.ts +244 -12
- package/src/enrich/approvals.ts +190 -0
- package/src/enrich/cache.ts +93 -0
- package/src/enrich/context-pack.ts +272 -0
- package/src/enrich/overlay.ts +255 -0
- package/src/enrich/review.ts +106 -0
- package/src/enrich/stage.ts +374 -0
- package/src/enrich/stats.ts +100 -0
- package/src/enrich/validate.ts +410 -0
- package/src/federation/llms.ts +2 -4
- package/src/findings/report.ts +103 -0
- package/src/graph/build.ts +356 -0
- package/src/graph/memory.ts +208 -0
- package/src/index-builder/build-handoffs.ts +22 -11
- package/src/index-builder/build-index.ts +132 -3
- package/src/index-builder/llms-txt.ts +48 -8
- package/src/index-builder/project-corpus.ts +111 -0
- package/src/index.ts +630 -2
- package/src/lib/fuzzy-match.ts +235 -0
- package/src/mcp/knowledge.ts +554 -0
- package/src/mcp/server.ts +113 -18
- package/src/metrics/benchmark.ts +21 -0
- package/src/parity/check.ts +309 -0
- package/src/parity/claims.ts +259 -0
- package/src/parity/resolve.ts +160 -0
- package/src/query/handoff.ts +326 -0
- package/src/query/load-index.ts +53 -1
- package/src/query/query.ts +92 -59
- package/src/query/search.ts +289 -92
- package/src/query/text.ts +155 -0
- package/src/reconciliation/reconcile.ts +101 -10
- package/src/render/data.ts +356 -0
- package/src/render/engine.ts +398 -0
- package/src/render/generated.ts +77 -0
- package/src/render/render.ts +209 -0
- package/src/render/template-source.ts +52 -0
- package/src/render/templates.ts +289 -0
- package/src/report/html.ts +2 -2
- package/src/retrieval/bm25.ts +161 -0
- package/src/retrieval/project.ts +495 -0
- package/src/retrieval/rank.ts +383 -0
- package/src/retrieval/weights.ts +39 -0
- package/src/retriever/doc-bridge-retriever.ts +100 -15
- package/src/rules/engine.ts +30 -10
- package/src/schemas/agent-handoff.ts +56 -0
- package/src/schemas/budget.ts +37 -0
- package/src/schemas/doc-bridge-index.ts +53 -2
- package/src/schemas/enrichment.ts +369 -0
- package/src/schemas/json-schemas.ts +39 -2
- package/src/schemas/knowledge.ts +14 -1
- package/src/schemas/retrieval-index.ts +152 -0
- package/src/shims/graphology.d.ts +91 -0
- package/src/study/adjudication.ts +196 -0
- package/src/study/execution.ts +350 -0
- package/src/study/expectations.ts +219 -0
- package/src/study/metrics.ts +467 -0
- package/src/study/protocol.ts +271 -0
- package/src/study/provider-cli.ts +115 -0
- package/src/study/provider-telemetry.ts +47 -0
- package/src/study/quality-scorecard.ts +164 -0
- package/src/study/runner.ts +461 -0
- package/src/study/task-suite.ts +321 -0
- package/src/study/verification.ts +134 -0
- package/src/validate.ts +1 -4
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +36 -11
- package/scripts/verification-harness.mjs +0 -482
package/src/cli/usage.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI's usage text, in its own module.
|
|
3
|
+
*
|
|
4
|
+
* `ak-docs parity` checks that a command this repository documents in public is a command the CLI
|
|
5
|
+
* actually offers, which means something other than the CLI has to be able to read the usage. The
|
|
6
|
+
* text lives here so that check does not import the program and create a cycle through it.
|
|
7
|
+
*/
|
|
8
|
+
export const CLI_COMMAND_USAGE = `ak-docs — human↔agent documentation bridge (@agentskit/doc-bridge)
|
|
9
|
+
|
|
10
|
+
Core (no API key):
|
|
11
|
+
ak-docs init [--demo] [--scaffold-workspaces]
|
|
12
|
+
ak-docs demo [--fixture example|monorepo] [--text] [--in-project]
|
|
13
|
+
ak-docs doctor [--text] [--badge] [--write-badge]
|
|
14
|
+
ak-docs index [--watch]
|
|
15
|
+
ak-docs discover [--text|--json]
|
|
16
|
+
ak-docs benchmark <fixture.json> <observation.json> [--text|--json]
|
|
17
|
+
ak-docs bench retrieval <suite.json> [--index <file>] [--baseline <file>] [--limit <n>] [--text|--json]
|
|
18
|
+
ak-docs bench retrieval <suite.json> --update-baseline --by <name> [--reason <text>]
|
|
19
|
+
ak-docs study protocol <protocol.json> [--text|--json]
|
|
20
|
+
ak-docs study history <registry.json> [--protocol <protocol.json>] [--text|--json]
|
|
21
|
+
ak-docs study tasks <task-suite.json> [--text|--json]
|
|
22
|
+
ak-docs study select <task-suite.json> [--text|--json]
|
|
23
|
+
ak-docs study plan <run-plan.json> [--text|--json]
|
|
24
|
+
ak-docs study providers <provider-cli.json> [--text|--json]
|
|
25
|
+
ak-docs study run <run-plan.json> <task-suite.json> --providers <provider-cli.json> --repositories <repositories.json> --ledger <ledger.json> [--round <id>] [--dry-run] [--text|--json]
|
|
26
|
+
ak-docs study adjudicate <observation-ledger.json> <task-suite.json> --adjudicator <provider-cli.json> --output <ledger.json> [--run-id <id>] [--offset <n>] [--limit <n>] [--text|--json]
|
|
27
|
+
ak-docs study ledger <observation-ledger.json> [--text|--json]
|
|
28
|
+
ak-docs study verification <binding.json> [--text|--json]
|
|
29
|
+
ak-docs study metrics <observation-ledger.json> [--baseline-round <id>] [--current-round <id>] [--baseline-run-id <id>] [--current-run-id <id>] [--allow-regressions] [--text|--json]
|
|
30
|
+
ak-docs scan | reconcile | check | map [--text|--json] [--html] [--report-threshold <bytes>]
|
|
31
|
+
ak-docs check --json --format finding emit diagnostics in the ecosystem Finding shape
|
|
32
|
+
ak-docs bench retrieval <suite> --overlay measure the suite with and without the accepted overlay
|
|
33
|
+
ak-docs enrich --retrieval-delta run the overlay through the golden suite after enriching
|
|
34
|
+
ak-docs study expectations <suite> --expectations <file> check the study's mechanical retrieval expectations
|
|
35
|
+
ak-docs check --enrich run the enrichment stage between reconcile and evaluate
|
|
36
|
+
ak-docs enrich [--json|--text] run the configured Registry roles over context packs
|
|
37
|
+
ak-docs enrich list | approve <proposalId> --by <name> | reject <proposalId> --by <name> [--reason <text>]
|
|
38
|
+
ak-docs fix propose links|normalize <artifact> [--output <file>]
|
|
39
|
+
ak-docs fix approve|apply <proposal.json> [--by <name>]
|
|
40
|
+
ak-docs suggest [--documentation] [--json|--text] run the configured Registry agent
|
|
41
|
+
ak-docs query [package|ownership|intent|change] <id> [--agent] [--text]
|
|
42
|
+
ak-docs search <term> [--agent] [--explain] [--mode=<mode>] [--context-budget=<tokens>] [--text]
|
|
43
|
+
ak-docs list <packages|intents|changes|knowledge> [--text]
|
|
44
|
+
ak-docs ask [question] local consult (no LLM)
|
|
45
|
+
ak-docs gate run [gate-id]
|
|
46
|
+
ak-docs rules run <report.json> [--preset default|recommended|strict] [--severity rule=level] [--ignore rule]
|
|
47
|
+
ak-docs conformance run documentation-standard-v1 [--text|--json]
|
|
48
|
+
ak-docs audit documentation [--text|--json]
|
|
49
|
+
ak-docs parity [--claims <file>] [--json|--text] check public claims against what the repository proves
|
|
50
|
+
ak-docs render <llms.txt|area|ownership|change-digest|overlay-review> [--data <artifact>] [--output <path>] [--print-template] [--json]
|
|
51
|
+
ak-docs mcp
|
|
52
|
+
ak-docs mcp install --cursor | --claude
|
|
53
|
+
ak-docs memory ingest|classify|promote [--pr] [--dry-run]
|
|
54
|
+
ak-docs bootstrap agent-docs
|
|
55
|
+
ak-docs validate-config | validate-handoff <file>
|
|
56
|
+
|
|
57
|
+
Intelligence (optional AgentsKit peers):
|
|
58
|
+
ak-docs rag ingest|search <query>
|
|
59
|
+
ak-docs chat terminal chat (Ink + RAG)
|
|
60
|
+
ak-docs ask <question> --chat one-shot grounded answer
|
|
61
|
+
|
|
62
|
+
Advanced / ecosystem:
|
|
63
|
+
ak-docs retrieve <query>
|
|
64
|
+
ak-docs registry topology
|
|
65
|
+
ak-docs playbook draft | pattern [--text]
|
|
66
|
+
|
|
67
|
+
Global flags:
|
|
68
|
+
-h, --help --version
|
|
69
|
+
--config <path> (project root = config file directory)
|
|
70
|
+
--agent --json --text --chat --demo
|
|
71
|
+
`
|
package/src/config/defaults.ts
CHANGED
|
@@ -57,6 +57,7 @@ export const applyConfigDefaults = (config: DocBridgeConfigV1): DocBridgeConfigV
|
|
|
57
57
|
'memory.classify', 'memory.promoteDraft', 'registry.topology',
|
|
58
58
|
'docbridge.snapshot', 'docbridge.report', 'docbridge.diagnostics',
|
|
59
59
|
'docbridge.relations', 'docbridge.run', 'docbridge.proposals',
|
|
60
|
+
'knowledge.search', 'knowledge.lookup',
|
|
60
61
|
],
|
|
61
62
|
transport: 'stdio',
|
|
62
63
|
...config.surfaces?.mcp,
|
package/src/config/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export {
|
|
|
8
8
|
} from './load-config.js'
|
|
9
9
|
export {
|
|
10
10
|
DocBridgeConfigV1Schema,
|
|
11
|
+
DocumentationAuditConfigSchema,
|
|
11
12
|
ReconciliationConfigSchema,
|
|
12
13
|
AgentCorpusConfigSchema,
|
|
13
14
|
HumanCorpusConfigSchema,
|
|
@@ -16,10 +17,13 @@ export {
|
|
|
16
17
|
EcosystemContractEvidenceSchema,
|
|
17
18
|
ConformanceConfigSchema,
|
|
18
19
|
ReportConfigSchema,
|
|
20
|
+
RetrievalConfigSchema,
|
|
19
21
|
type DocBridgeConfigV1,
|
|
22
|
+
type DocumentationAuditConfig,
|
|
20
23
|
type AgentCorpusConfig,
|
|
21
24
|
type DocumentationStandardRuleId,
|
|
22
25
|
type DocumentationStandardV1Config,
|
|
23
26
|
type ReconciliationConfig,
|
|
24
27
|
type ReportConfig,
|
|
28
|
+
type RetrievalConfig,
|
|
25
29
|
} from './schema.js'
|
package/src/config/schema.ts
CHANGED
|
@@ -182,7 +182,7 @@ export const RulesConfigSchema = z
|
|
|
182
182
|
export const ReconciliationConfigSchema = z
|
|
183
183
|
.object({
|
|
184
184
|
/** Semantic comparison level. Raw discovery always keeps file-level relations. */
|
|
185
|
-
scope: z.enum(['file', 'module', 'package']).optional(),
|
|
185
|
+
scope: z.enum(['file', 'module', 'area', 'package']).optional(),
|
|
186
186
|
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
187
187
|
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
|
|
188
188
|
/** Limit missing-declaration findings to relations whose endpoints are internal project entities. */
|
|
@@ -191,6 +191,27 @@ export const ReconciliationConfigSchema = z
|
|
|
191
191
|
})
|
|
192
192
|
.strict()
|
|
193
193
|
|
|
194
|
+
export const DocumentationAuditConfigSchema = z
|
|
195
|
+
.object({
|
|
196
|
+
/** Glob-like paths that should be compared for structure coverage. */
|
|
197
|
+
criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
198
|
+
/** Generated docs are checked for presence/freshness evidence only; content quality is skipped. */
|
|
199
|
+
generatedPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
200
|
+
exclude: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
201
|
+
minWords: z.number().int().nonnegative().max(100_000).optional(),
|
|
202
|
+
requiredSections: z.array(z.string().min(1).max(128)).max(32).optional(),
|
|
203
|
+
requireExamples: z.boolean().optional(),
|
|
204
|
+
exactDuplicates: z.boolean().optional(),
|
|
205
|
+
defaultTier: z.enum(['tier-0', 'tier-1', 'tier-2']).optional(),
|
|
206
|
+
tierRules: z.array(z.object({
|
|
207
|
+
pattern: z.string().min(1).max(512),
|
|
208
|
+
tier: z.enum(['tier-0', 'tier-1', 'tier-2']),
|
|
209
|
+
critical: z.boolean().optional(),
|
|
210
|
+
}).strict()).max(128).optional(),
|
|
211
|
+
requiredCriticalMetadata: z.array(z.enum(['owner', 'lifecycle', 'sourceOfTruth', 'validationPath'])).max(4).optional(),
|
|
212
|
+
})
|
|
213
|
+
.strict()
|
|
214
|
+
|
|
194
215
|
export const AnalysisConfigSchema = z
|
|
195
216
|
.object({
|
|
196
217
|
plugins: z
|
|
@@ -225,6 +246,18 @@ export const AnalysisConfigSchema = z
|
|
|
225
246
|
})
|
|
226
247
|
.strict()
|
|
227
248
|
.optional(),
|
|
249
|
+
/**
|
|
250
|
+
* How code areas are derived — the unit of architecture between a package and a file.
|
|
251
|
+
* `roots` names directories that contain areas rather than being one (`src` holds
|
|
252
|
+
* `src/query`); `depth` is how many levels below such a root an area sits.
|
|
253
|
+
*/
|
|
254
|
+
areas: z
|
|
255
|
+
.object({
|
|
256
|
+
depth: z.number().int().min(1).max(8).optional(),
|
|
257
|
+
roots: z.array(z.string().min(1).max(128)).max(32).optional(),
|
|
258
|
+
})
|
|
259
|
+
.strict()
|
|
260
|
+
.optional(),
|
|
228
261
|
})
|
|
229
262
|
.strict()
|
|
230
263
|
|
|
@@ -282,9 +315,11 @@ export const SurfacesConfigSchema = z
|
|
|
282
315
|
'docbridge.relations',
|
|
283
316
|
'docbridge.run',
|
|
284
317
|
'docbridge.proposals',
|
|
318
|
+
'knowledge.search',
|
|
319
|
+
'knowledge.lookup',
|
|
285
320
|
]),
|
|
286
321
|
)
|
|
287
|
-
.max(
|
|
322
|
+
.max(18)
|
|
288
323
|
.optional(),
|
|
289
324
|
transport: z.enum(['stdio', 'http']).optional(),
|
|
290
325
|
http: z
|
|
@@ -300,6 +335,16 @@ export const SurfacesConfigSchema = z
|
|
|
300
335
|
})
|
|
301
336
|
.strict()
|
|
302
337
|
|
|
338
|
+
/** One enrichment role: an installed Registry agent and the prompt version it is run with. */
|
|
339
|
+
export const EnrichmentRoleSchema = z
|
|
340
|
+
.object({
|
|
341
|
+
enabled: z.boolean().optional(),
|
|
342
|
+
/** Defaults to `intelligence.registry.agentId`. */
|
|
343
|
+
agentId: z.string().min(1).max(256).optional(),
|
|
344
|
+
promptVersion: z.string().min(1).max(64).optional(),
|
|
345
|
+
})
|
|
346
|
+
.strict()
|
|
347
|
+
|
|
303
348
|
export const IntelligenceConfigSchema = z
|
|
304
349
|
.object({
|
|
305
350
|
enabled: z.boolean().optional(),
|
|
@@ -359,11 +404,34 @@ export const IntelligenceConfigSchema = z
|
|
|
359
404
|
agentId: z.string().min(1).max(256).optional(),
|
|
360
405
|
agentRoot: z.string().min(1).max(512).optional(),
|
|
361
406
|
runnerModule: z.string().min(1).max(512).optional(),
|
|
407
|
+
cli: z
|
|
408
|
+
.object({
|
|
409
|
+
/** Executable name or absolute path. Arguments are passed without a shell. */
|
|
410
|
+
command: z.string().min(1).max(512),
|
|
411
|
+
args: z.array(z.string().max(2_048)).max(64).optional(),
|
|
412
|
+
})
|
|
413
|
+
.strict()
|
|
414
|
+
.optional(),
|
|
362
415
|
deterministic: z.boolean().optional(),
|
|
363
416
|
timeoutMs: z.number().int().positive().max(600_000).optional(),
|
|
417
|
+
maxInputBytes: z.number().int().positive().max(50_000_000).optional(),
|
|
364
418
|
maxTokens: z.number().int().positive().max(1_000_000).optional(),
|
|
365
419
|
maxResponseBytes: z.number().int().positive().max(10_000_000).optional(),
|
|
366
420
|
maxConcurrency: z.number().int().positive().max(64).optional(),
|
|
421
|
+
/** Byte budget of one enrichment context pack. Default 65536. */
|
|
422
|
+
maxPackBytes: z.number().int().min(4_096).max(4_000_000).optional(),
|
|
423
|
+
/**
|
|
424
|
+
* Which installed agent plays which enrichment role. The default is the configured agent
|
|
425
|
+
* as curator only. The adjudicator must be a different identity from both others.
|
|
426
|
+
*/
|
|
427
|
+
roles: z
|
|
428
|
+
.object({
|
|
429
|
+
curator: EnrichmentRoleSchema.optional(),
|
|
430
|
+
reviewer: EnrichmentRoleSchema.optional(),
|
|
431
|
+
adjudicator: EnrichmentRoleSchema.optional(),
|
|
432
|
+
})
|
|
433
|
+
.strict()
|
|
434
|
+
.optional(),
|
|
367
435
|
})
|
|
368
436
|
.strict()
|
|
369
437
|
.optional(),
|
|
@@ -470,12 +538,53 @@ export const DocumentationStandardV1ConfigSchema = z
|
|
|
470
538
|
}
|
|
471
539
|
})
|
|
472
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Retrieval tuning. Weights and parameters are configuration rather than code so a repository can
|
|
543
|
+
* change what its agents find first without a release, and so the change is recorded in the index.
|
|
544
|
+
*/
|
|
545
|
+
export const RetrievalConfigSchema = z
|
|
546
|
+
.object({
|
|
547
|
+
corpus: z
|
|
548
|
+
.object({
|
|
549
|
+
/** Project repository documents and modules into the index. On by default. */
|
|
550
|
+
enabled: z.boolean().optional(),
|
|
551
|
+
})
|
|
552
|
+
.strict()
|
|
553
|
+
.optional(),
|
|
554
|
+
weights: z.record(z.string().min(1).max(64), z.number().min(0).max(1_000)).optional(),
|
|
555
|
+
params: z
|
|
556
|
+
.object({ k1: z.number().min(0).max(100).optional(), b: z.number().min(0).max(1).optional() })
|
|
557
|
+
.strict()
|
|
558
|
+
.optional(),
|
|
559
|
+
benchmark: z
|
|
560
|
+
.object({
|
|
561
|
+
/** The golden retrieval suite the doctor measures hit@3 against. `docs/bench/retrieval-suite-v1.json` by default. */
|
|
562
|
+
suite: z.string().min(1).max(512).optional(),
|
|
563
|
+
})
|
|
564
|
+
.strict()
|
|
565
|
+
.optional(),
|
|
566
|
+
})
|
|
567
|
+
.strict()
|
|
568
|
+
|
|
473
569
|
export const ConformanceConfigSchema = z
|
|
474
570
|
.object({
|
|
475
571
|
documentationStandardV1: DocumentationStandardV1ConfigSchema.optional(),
|
|
476
572
|
})
|
|
477
573
|
.strict()
|
|
478
574
|
|
|
575
|
+
export const RenderTemplateNameSchema = z.enum(['llms.txt', 'area', 'ownership', 'change-digest', 'overlay-review'])
|
|
576
|
+
|
|
577
|
+
export const RenderConfigSchema = z
|
|
578
|
+
.object({
|
|
579
|
+
/**
|
|
580
|
+
* Project templates that replace the bundled ones, by template name, as paths relative to
|
|
581
|
+
* the project root. `ak-docs render <name> --print-template` prints the bundled template to
|
|
582
|
+
* start from.
|
|
583
|
+
*/
|
|
584
|
+
templates: z.partialRecord(RenderTemplateNameSchema, z.string().min(1).max(512)).optional(),
|
|
585
|
+
})
|
|
586
|
+
.strict()
|
|
587
|
+
|
|
479
588
|
export const DocBridgeConfigV1Schema = z
|
|
480
589
|
.object({
|
|
481
590
|
schemaVersion: z.literal(CONFIG_SCHEMA_VERSION),
|
|
@@ -496,6 +605,7 @@ export const DocBridgeConfigV1Schema = z
|
|
|
496
605
|
routing: RoutingConfigSchema.optional(),
|
|
497
606
|
gates: GatesConfigSchema.optional(),
|
|
498
607
|
reconciliation: ReconciliationConfigSchema.optional(),
|
|
608
|
+
audit: z.object({ documentation: DocumentationAuditConfigSchema.optional() }).strict().optional(),
|
|
499
609
|
analysis: AnalysisConfigSchema.optional(),
|
|
500
610
|
rules: RulesConfigSchema.optional(),
|
|
501
611
|
workflow: WorkflowConfigSchema.optional(),
|
|
@@ -505,15 +615,19 @@ export const DocBridgeConfigV1Schema = z
|
|
|
505
615
|
intelligence: IntelligenceConfigSchema.optional(),
|
|
506
616
|
federation: FederationConfigSchema.optional(),
|
|
507
617
|
conformance: ConformanceConfigSchema.optional(),
|
|
618
|
+
retrieval: RetrievalConfigSchema.optional(),
|
|
619
|
+
render: RenderConfigSchema.optional(),
|
|
508
620
|
})
|
|
509
621
|
.strict()
|
|
510
622
|
|
|
511
623
|
export type DocBridgeConfigV1 = z.infer<typeof DocBridgeConfigV1Schema>
|
|
624
|
+
export type RenderConfig = z.infer<typeof RenderConfigSchema>
|
|
512
625
|
export type AgentCorpusConfig = z.infer<typeof AgentCorpusConfigSchema>
|
|
513
626
|
export type HumanCorpusConfig = z.infer<typeof HumanCorpusConfigSchema>
|
|
514
627
|
export type DocumentationStandardV1Config = z.infer<typeof DocumentationStandardV1ConfigSchema>
|
|
515
628
|
export type DocumentationStandardRuleId = z.infer<typeof DocumentationStandardRuleIdSchema>
|
|
516
629
|
export type ReconciliationConfig = z.infer<typeof ReconciliationConfigSchema>
|
|
630
|
+
export type DocumentationAuditConfig = z.infer<typeof DocumentationAuditConfigSchema>
|
|
517
631
|
export type AnalysisConfig = z.infer<typeof AnalysisConfigSchema>
|
|
518
632
|
export type RuleId = z.infer<typeof RuleIdSchema>
|
|
519
633
|
export type RuleSeverity = z.infer<typeof RuleSeveritySchema>
|
|
@@ -521,3 +635,4 @@ export type RulesConfig = z.infer<typeof RulesConfigSchema>
|
|
|
521
635
|
export type WorkflowConfig = z.infer<typeof WorkflowConfigSchema>
|
|
522
636
|
export type RepositorySafetyConfig = z.infer<typeof RepositorySafetyConfigSchema>
|
|
523
637
|
export type ReportConfig = z.infer<typeof ReportConfigSchema>
|
|
638
|
+
export type RetrievalConfig = z.infer<typeof RetrievalConfigSchema>
|
|
@@ -11,6 +11,7 @@ import { buildDocBridgeIndex } from '../index-builder/build-index.js'
|
|
|
11
11
|
import { scanHumanDocRecords } from '../index-builder/human-adapters/index.js'
|
|
12
12
|
import { renderLlmsTxt } from '../index-builder/llms-txt.js'
|
|
13
13
|
import { toPosix } from '../lib/paths.js'
|
|
14
|
+
import type { DocBridgeIndexV1 } from '../schemas/doc-bridge-index.js'
|
|
14
15
|
|
|
15
16
|
export const DOCUMENTATION_STANDARD_V1_ID = 'documentation-standard-v1' as const
|
|
16
17
|
export const DOCUMENTATION_STANDARD_V1_STATUS = 'stable' as const
|
|
@@ -186,6 +187,7 @@ const llmsRule = (
|
|
|
186
187
|
root: string,
|
|
187
188
|
config: DocBridgeConfigV1,
|
|
188
189
|
options: DocumentationStandardV1Config,
|
|
190
|
+
generated: DocBridgeIndexV1,
|
|
189
191
|
): RuleDraft => {
|
|
190
192
|
const llmsPath = config.index?.llmsTxt?.outFile ?? 'llms.txt'
|
|
191
193
|
const llmsKey = safePath(root, llmsPath) ?? resolve(root, llmsPath)
|
|
@@ -196,8 +198,7 @@ const llmsRule = (
|
|
|
196
198
|
}
|
|
197
199
|
const paths = [llmsPath, ...rawSources.values()]
|
|
198
200
|
const evidence = paths.map((path) => fileEvidence(root, path))
|
|
199
|
-
const
|
|
200
|
-
const expectedLlms = renderLlmsTxt(config, generated.knowledge, generated.project?.name ?? 'project')
|
|
201
|
+
const expectedLlms = renderLlmsTxt(config, generated.knowledge, generated.project?.name ?? 'project', { root })
|
|
201
202
|
const llmsIsFresh = evidence[0]?.content === expectedLlms
|
|
202
203
|
if (evidence[0]?.exists) {
|
|
203
204
|
evidence[0] = {
|
|
@@ -284,8 +285,7 @@ const ecosystemContract = (
|
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
|
|
287
|
-
const handoffsRule = (
|
|
288
|
-
const index = buildDocBridgeIndex({ root, config, write: false }).index
|
|
288
|
+
const handoffsRule = (index: DocBridgeIndexV1): RuleDraft => {
|
|
289
289
|
const handoffs = Object.values(index.handoffs ?? {})
|
|
290
290
|
const ready = handoffs.filter(
|
|
291
291
|
(handoff) =>
|
|
@@ -456,10 +456,12 @@ export const runDocumentationStandardV1 = (
|
|
|
456
456
|
config: DocBridgeConfigV1,
|
|
457
457
|
): DocumentationConformanceReportV1 => {
|
|
458
458
|
const options = config.conformance?.documentationStandardV1 ?? {}
|
|
459
|
+
// Two rules need the index. Building it projects and hashes the whole repository, so build once.
|
|
460
|
+
const index = buildDocBridgeIndex({ root, config, write: false }).index
|
|
459
461
|
const drafts: RuleDraft[] = [
|
|
460
462
|
humanDocsRule(root, config),
|
|
461
|
-
llmsRule(root, config, options),
|
|
462
|
-
handoffsRule(
|
|
463
|
+
llmsRule(root, config, options, index),
|
|
464
|
+
handoffsRule(index),
|
|
463
465
|
contributionRule(root, options),
|
|
464
466
|
markersRule(root, options, 'metadata', 'required'),
|
|
465
467
|
linksRule(root, options),
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { toPosix } from '../lib/paths.js'
|
|
2
|
+
import { entityId } from './identity.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Code areas: the unit of architecture between a package and a file.
|
|
6
|
+
*
|
|
7
|
+
* Reconciliation could only compare at file, module or package level, and most repositories are a
|
|
8
|
+
* single package — so every internal relation aggregated into one self-loop, which the comparison
|
|
9
|
+
* skips. The result was a repository with a thousand observed relations reporting zero
|
|
10
|
+
* diagnostics, and a health score of 100 out of 100 saying nothing at all.
|
|
11
|
+
*
|
|
12
|
+
* An area is a directory that groups modules. It is derived, not declared: the first directory
|
|
13
|
+
* level under a package's source roots, plus any path an ownership record already names — because
|
|
14
|
+
* a configuration saying `path: "src/mcp"` is a human telling us that directory is a unit, and the
|
|
15
|
+
* graph had no entity for it.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const AREA_ANALYZER_VERSION = '1.0.0'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Directories that contain areas rather than being one.
|
|
22
|
+
*
|
|
23
|
+
* `src` is not an area in any useful sense; `src/query` is. When a package keeps its code under
|
|
24
|
+
* one of these, the area is the level below it.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_AREA_ROOTS = ['src', 'lib', 'app', 'source', 'server', 'client', 'packages', 'apps'] as const
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_AREA_DEPTH = 1
|
|
29
|
+
|
|
30
|
+
export type AreaModule = {
|
|
31
|
+
readonly moduleId: string
|
|
32
|
+
/** Repository-relative module path. */
|
|
33
|
+
readonly path: string
|
|
34
|
+
readonly packageId: string
|
|
35
|
+
/** Repository-relative package path; `.` for the root package. */
|
|
36
|
+
readonly packagePath: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type AreaOwnership = {
|
|
40
|
+
readonly id: string
|
|
41
|
+
readonly path: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type DerivedArea = {
|
|
45
|
+
readonly id: string
|
|
46
|
+
readonly path: string
|
|
47
|
+
readonly name: string
|
|
48
|
+
readonly packageId: string
|
|
49
|
+
/** The most specific area that encloses this one, when there is one. */
|
|
50
|
+
readonly parentId?: string
|
|
51
|
+
readonly moduleIds: readonly string[]
|
|
52
|
+
/** The ownership record that names exactly this path. */
|
|
53
|
+
readonly ownershipId?: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type DeriveAreasOptions = {
|
|
57
|
+
readonly modules: readonly AreaModule[]
|
|
58
|
+
readonly ownership?: readonly AreaOwnership[]
|
|
59
|
+
readonly depth?: number
|
|
60
|
+
readonly roots?: readonly string[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* Trailing separators are trimmed with a loop rather than /\/+$/: a quantified group anchored at
|
|
65
|
+
* the end backtracks quadratically on a path that is mostly separators, and a module path comes
|
|
66
|
+
* from the repository being scanned.
|
|
67
|
+
*/
|
|
68
|
+
const normalize = (path: string): string => {
|
|
69
|
+
const posix = toPosix(path).replace(/^\.\//, '')
|
|
70
|
+
let end = posix.length
|
|
71
|
+
while (end > 0 && posix[end - 1] === '/') end -= 1
|
|
72
|
+
return posix.slice(0, end)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const relativeToPackage = (modulePath: string, packagePath: string): string | undefined => {
|
|
76
|
+
if (packagePath === '.' || packagePath === '') return modulePath
|
|
77
|
+
const prefix = `${packagePath}/`
|
|
78
|
+
return modulePath.startsWith(prefix) ? modulePath.slice(prefix.length) : undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const join = (...parts: readonly string[]): string => parts.filter(Boolean).join('/')
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The area a module belongs to by convention: `depth` directory levels, counted below a source
|
|
85
|
+
* root when the module sits under one. A module directly in the package or in a bare source root
|
|
86
|
+
* has no conventional area — there is no directory grouping it with anything.
|
|
87
|
+
*/
|
|
88
|
+
export const conventionalAreaPath = (
|
|
89
|
+
module: AreaModule,
|
|
90
|
+
depth = DEFAULT_AREA_DEPTH,
|
|
91
|
+
roots: readonly string[] = DEFAULT_AREA_ROOTS,
|
|
92
|
+
): string | undefined => {
|
|
93
|
+
const relative = relativeToPackage(normalize(module.path), normalize(module.packagePath))
|
|
94
|
+
if (relative === undefined) return undefined
|
|
95
|
+
const segments = relative.split('/')
|
|
96
|
+
segments.pop()
|
|
97
|
+
if (!segments.length) return undefined
|
|
98
|
+
|
|
99
|
+
const rootPrefix: string[] = []
|
|
100
|
+
let rest = segments
|
|
101
|
+
if (segments[0] && roots.includes(segments[0])) {
|
|
102
|
+
rootPrefix.push(segments[0])
|
|
103
|
+
rest = segments.slice(1)
|
|
104
|
+
}
|
|
105
|
+
const taken = rest.slice(0, Math.max(1, depth))
|
|
106
|
+
if (!taken.length) return undefined
|
|
107
|
+
|
|
108
|
+
const packagePath = normalize(module.packagePath)
|
|
109
|
+
return join(packagePath === '.' ? '' : packagePath, ...rootPrefix, ...taken)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const encloses = (candidate: string, path: string): boolean => path === candidate || path.startsWith(`${candidate}/`)
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Derive the areas of a repository.
|
|
116
|
+
*
|
|
117
|
+
* Every module is assigned to exactly one area — the most specific one containing it — so the
|
|
118
|
+
* containment graph stays a tree and an aggregation at area scope has one answer per module.
|
|
119
|
+
* Nested areas keep their shape: `src` names the area that holds what is directly in `src`, and
|
|
120
|
+
* `src/query` its own, with `src` recorded as the parent.
|
|
121
|
+
*/
|
|
122
|
+
export const deriveAreas = (options: DeriveAreasOptions): readonly DerivedArea[] => {
|
|
123
|
+
const depth = options.depth ?? DEFAULT_AREA_DEPTH
|
|
124
|
+
const roots = options.roots ?? [...DEFAULT_AREA_ROOTS]
|
|
125
|
+
const ownership = (options.ownership ?? []).map((record) => ({ ...record, path: normalize(record.path) }))
|
|
126
|
+
|
|
127
|
+
/** Candidate path to the package it belongs to. */
|
|
128
|
+
const candidates = new Map<string, string>()
|
|
129
|
+
for (const module of options.modules) {
|
|
130
|
+
const path = conventionalAreaPath(module, depth, roots)
|
|
131
|
+
if (path && !candidates.has(path)) candidates.set(path, module.packageId)
|
|
132
|
+
}
|
|
133
|
+
// An ownership record naming a directory that holds observed code is an area by declaration.
|
|
134
|
+
for (const record of ownership) {
|
|
135
|
+
if (!record.path || candidates.has(record.path)) continue
|
|
136
|
+
const owner = options.modules.find((module) => encloses(record.path, normalize(module.path)))
|
|
137
|
+
if (owner) candidates.set(record.path, owner.packageId)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const paths = [...candidates.keys()].sort()
|
|
141
|
+
const byLength = [...paths].sort((a, b) => b.length - a.length || a.localeCompare(b))
|
|
142
|
+
const moduleIds = new Map<string, string[]>(paths.map((path) => [path, []]))
|
|
143
|
+
|
|
144
|
+
for (const module of options.modules) {
|
|
145
|
+
const modulePath = normalize(module.path)
|
|
146
|
+
const area = byLength.find((candidate) => encloses(candidate, modulePath))
|
|
147
|
+
if (area) moduleIds.get(area)?.push(module.moduleId)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const ownershipByPath = new Map(ownership.map((record) => [record.path, record.id]))
|
|
151
|
+
|
|
152
|
+
return paths.map((path) => {
|
|
153
|
+
const parent = byLength.find((candidate) => candidate !== path && encloses(candidate, path))
|
|
154
|
+
const ownershipId = ownershipByPath.get(path)
|
|
155
|
+
return {
|
|
156
|
+
id: entityId('area', path),
|
|
157
|
+
path,
|
|
158
|
+
name: path.split('/').pop() ?? path,
|
|
159
|
+
packageId: candidates.get(path) as string,
|
|
160
|
+
...(parent ? { parentId: entityId('area', parent) } : {}),
|
|
161
|
+
moduleIds: [...(moduleIds.get(path) ?? [])].sort(),
|
|
162
|
+
...(ownershipId ? { ownershipId } : {}),
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Ownership paths no observed entity lives under.
|
|
169
|
+
*
|
|
170
|
+
* A configured path that matches nothing is almost always a rename or a typo, and it is invisible
|
|
171
|
+
* until something looks: the handoff still resolves, it just points at a directory that no longer
|
|
172
|
+
* holds what it claims.
|
|
173
|
+
*/
|
|
174
|
+
export const unobservedOwnershipPaths = (
|
|
175
|
+
ownership: readonly AreaOwnership[],
|
|
176
|
+
observedPaths: readonly string[],
|
|
177
|
+
): readonly AreaOwnership[] => {
|
|
178
|
+
const paths = observedPaths.map(normalize)
|
|
179
|
+
return ownership
|
|
180
|
+
.map((record) => ({ ...record, path: normalize(record.path) }))
|
|
181
|
+
.filter((record) => record.path && !paths.some((path) => encloses(record.path, path)))
|
|
182
|
+
}
|