@agentskit/doc-bridge 0.1.0-alpha.3 → 1.0.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 +46 -0
- package/README.md +139 -57
- package/action.yml +78 -0
- package/dist/cli/program.js +987 -69
- package/dist/cli/program.js.map +1 -1
- package/dist/index.d.ts +238 -26
- package/dist/index.js +704 -22
- package/dist/index.js.map +1 -1
- package/docs/DOGFOOD-ROUND3.md +74 -0
- package/docs/POSITIONING.md +2 -0
- package/docs/RELEASE.md +5 -3
- package/docs/chat-and-rag.md +2 -0
- package/docs/getting-started.md +14 -1
- package/docs/landing/index.html +299 -0
- package/docs/mcp.md +10 -1
- package/docs/ollama-demo.md +64 -0
- package/docs/playbook/doc-bridge-pattern.md +114 -0
- package/docs/recipes/index-pipeline.md +89 -0
- package/docs/skills/doc-bridge.md +64 -0
- package/docs/spec/cli.md +6 -0
- package/docs/spec/playbook-feedback.md +12 -4
- package/examples/demo-example/doc-bridge.config.json +23 -0
- package/examples/demo-example/docs/for-agents/INDEX.md +3 -0
- package/examples/demo-example/docs/for-agents/packages/example.md +14 -0
- package/examples/demo-example/package.json +7 -0
- package/examples/demo-example/src/.gitkeep +0 -0
- package/examples/demo-monorepo/doc-bridge.config.json +37 -0
- package/examples/demo-monorepo/docs/for-agents/INDEX.md +4 -0
- package/examples/demo-monorepo/docs/for-agents/packages/auth.md +22 -0
- package/examples/demo-monorepo/docs/for-agents/packages/billing.md +19 -0
- package/examples/demo-monorepo/docs/human/guides/auth.md +7 -0
- package/examples/demo-monorepo/package.json +7 -0
- package/examples/demo-monorepo/packages/auth/package.json +8 -0
- package/examples/demo-monorepo/packages/billing/package.json +7 -0
- package/examples/demo-monorepo/pnpm-workspace.yaml +2 -0
- package/examples/ollama-chat.config.ts +42 -0
- package/package.json +5 -2
- package/src/cli/demo.ts +143 -0
- package/src/cli/program.ts +194 -14
- package/src/doctor/badge.ts +53 -0
- package/src/doctor/run-doctor.ts +286 -0
- package/src/index-builder/build-handoffs.ts +19 -1
- package/src/index-builder/watch-index.ts +94 -0
- package/src/index.ts +24 -0
- package/src/mcp/install.ts +84 -0
- package/src/memory/github-pr.ts +190 -0
- package/src/playbook/doc-bridge-pattern.ts +121 -0
- package/src/query/query.ts +19 -1
- package/src/schemas/agent-handoff.ts +11 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Index pipeline recipes
|
|
2
|
+
|
|
3
|
+
Keep `.doc-bridge/index.json` fresh during development and in CI.
|
|
4
|
+
|
|
5
|
+
## Watch mode (local dev)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
ak-docs index --watch
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Watches `docs/for-agents/` (and human corpus dirs when configured). Rebuilds on `.md`, `.mdx`, `.json`, `.yaml` changes.
|
|
12
|
+
|
|
13
|
+
Pair with MCP in Cursor — agents always resolve against a live index.
|
|
14
|
+
|
|
15
|
+
## Pre-commit hook
|
|
16
|
+
|
|
17
|
+
`.husky/pre-commit` or `.git/hooks/pre-commit`:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
#!/bin/sh
|
|
21
|
+
ak-docs index
|
|
22
|
+
ak-docs gate run index-freshness || {
|
|
23
|
+
echo "doc-bridge index is stale — run: ak-docs index"
|
|
24
|
+
exit 1
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or only index agent docs when they change:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
#!/bin/sh
|
|
32
|
+
if git diff --cached --name-only | grep -Eq 'docs/for-agents/|doc-bridge\.config'; then
|
|
33
|
+
ak-docs index
|
|
34
|
+
fi
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Turborepo task
|
|
38
|
+
|
|
39
|
+
`turbo.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"tasks": {
|
|
44
|
+
"ak-docs:index": {
|
|
45
|
+
"inputs": ["docs/for-agents/**", "doc-bridge.config.*"],
|
|
46
|
+
"outputs": [".doc-bridge/index.json", "llms.txt", ".doc-bridge/capabilities.json"]
|
|
47
|
+
},
|
|
48
|
+
"build": {
|
|
49
|
+
"dependsOn": ["ak-docs:index"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Root `package.json`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"scripts": {
|
|
60
|
+
"ak-docs:index": "ak-docs index",
|
|
61
|
+
"ak-docs:gate": "ak-docs gate run"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## CI (GitHub Action)
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
- uses: AgentsKit-io/doc-bridge@v1.0.0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or manual:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
- run: npm i -g @agentskit/doc-bridge && ak-docs index && ak-docs gate run
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Coverage badge in README
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
ak-docs doctor --badge
|
|
82
|
+
# paste markdown into README
|
|
83
|
+
|
|
84
|
+
# or persist for CI:
|
|
85
|
+
ak-docs doctor --write-badge
|
|
86
|
+
pnpm coverage:badge
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Paste the shields.io line from `.doc-bridge/coverage-badge.json` → `markdown` field.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Doc Bridge — agent routing skill
|
|
2
|
+
|
|
3
|
+
Use this skill in Cursor, Claude Code, or Codex so agents resolve ownership **before** editing packages.
|
|
4
|
+
|
|
5
|
+
## When to trigger
|
|
6
|
+
|
|
7
|
+
- User asks to change code under `packages/*`, `apps/*`, or any monorepo module
|
|
8
|
+
- Task mentions auth, billing, API, schema, or a package name
|
|
9
|
+
- Before creating or moving files in an unfamiliar area of the repo
|
|
10
|
+
|
|
11
|
+
## Required workflow
|
|
12
|
+
|
|
13
|
+
1. **Resolve handoff first** — call MCP tool `handoff.resolve` with the package id, or run:
|
|
14
|
+
```bash
|
|
15
|
+
ak-docs query ownership <id> --agent
|
|
16
|
+
```
|
|
17
|
+
2. **Read `startHere`** — open the agent doc path from the handoff
|
|
18
|
+
3. **Edit only `editRoots`** — do not touch sibling packages
|
|
19
|
+
4. **Run `checks`** from the handoff before claiming done
|
|
20
|
+
5. **Bridge to humans** — if `bridge.humanDoc` is `missing`, tell the user and suggest `ak-docs bootstrap agent-docs`; if `humanDoc` is set, link it in your summary
|
|
21
|
+
|
|
22
|
+
## MCP setup (one-time)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ak-docs mcp install --cursor
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or paste into `.cursor/mcp.json`:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"ak-docs": {
|
|
34
|
+
"command": "npx",
|
|
35
|
+
"args": ["ak-docs", "mcp"],
|
|
36
|
+
"cwd": "/absolute/path/to/repo"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Cursor rule (paste into `.cursor/rules/doc-bridge.mdc`)
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
---
|
|
46
|
+
description: Resolve doc-bridge handoff before editing packages
|
|
47
|
+
globs: packages/**/*
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
Before editing any file under packages/ or apps/:
|
|
51
|
+
|
|
52
|
+
1. Call MCP `handoff.resolve` for the owning package id
|
|
53
|
+
2. Read `startHere` from the handoff
|
|
54
|
+
3. Only modify paths under `editRoots`
|
|
55
|
+
4. Run every command in `checks` before finishing
|
|
56
|
+
5. If humanDoc is missing, surface `ak-docs bootstrap agent-docs` to the user
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick consult (no LLM)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
ak-docs ask "auth is broken in staging"
|
|
63
|
+
ak-docs doctor --text
|
|
64
|
+
```
|
package/docs/spec/cli.md
CHANGED
|
@@ -49,7 +49,10 @@ pnpm add -D @agentskit/doc-bridge
|
|
|
49
49
|
| `ak-docs init --scaffold-workspaces` | Also create draft `docs/for-agents/packages/*.md` from discovered pnpm workspaces; never overwrites existing docs |
|
|
50
50
|
| `ak-docs bootstrap agent-docs` | Create draft `docs/for-agents/human/*.md` from configured human-doc adapters; never overwrites existing docs |
|
|
51
51
|
| `ak-docs validate-config` | Zod-validate config file |
|
|
52
|
+
| `ak-docs demo [--fixture example\|monorepo] [--text]` | Bundled 60s wow path: handoff, gate red→green, MCP snippet |
|
|
53
|
+
| `ak-docs doctor [--text] [--badge] [--write-badge]` | Coverage score, gaps, gates, shields.io badge |
|
|
52
54
|
| `ak-docs index` | Build `DocBridgeIndex` + optional `llms.txt` |
|
|
55
|
+
| `ak-docs index --watch` | Debounced rebuild on agent/human doc changes |
|
|
53
56
|
| `ak-docs query <target> [--agent] [--text]` | Resolve package/module/intent/change → handoff JSON or text |
|
|
54
57
|
| `ak-docs search <term> [--agent] [--text]` | Full-text search over index |
|
|
55
58
|
| `ak-docs ask <question>` | Human-readable local consult mode: search + best match + next handoff commands; no LLM |
|
|
@@ -60,11 +63,14 @@ pnpm add -D @agentskit/doc-bridge
|
|
|
60
63
|
| `ak-docs memory ingest` | Normalize local memory files (`.agent-memory/**/*.md`, `.cursor/rules/*.mdc`) into `MemoryCandidate[]` |
|
|
61
64
|
| `ak-docs memory classify` | Deterministically route candidates to agent/human/playbook/discard |
|
|
62
65
|
| `ak-docs memory promote` | Build draft-only promotion body with safety scan; never auto-merges |
|
|
66
|
+
| `ak-docs memory promote --pr [--dry-run] [--force]` | Write draft + open GitHub draft PR via `gh` |
|
|
63
67
|
| `ak-docs registry topology` | Print the `doc-curator` topology for AgentsKit/Registry composition |
|
|
64
68
|
| `ak-docs playbook draft` | Build a draft Playbook feedback payload from local memory candidates |
|
|
69
|
+
| `ak-docs playbook pattern [--text]` | Export published Doc Bridge Playbook pattern (OKF markdown / JSON) |
|
|
65
70
|
| `ak-docs list <kind> [--text]` | List packages, apps, intents, … |
|
|
66
71
|
| `ak-docs gate run [index-freshness]` | Check generated index freshness |
|
|
67
72
|
| `ak-docs mcp` | Start MCP server (stdio default) |
|
|
73
|
+
| `ak-docs mcp install --cursor \| --claude` | Write MCP server config for Cursor or Claude Desktop |
|
|
68
74
|
|
|
69
75
|
### Layer 1 — optional AgentsKit peers (`intelligence.enabled`)
|
|
70
76
|
|
|
@@ -66,12 +66,20 @@ mergePolicy:
|
|
|
66
66
|
autoMerge: false
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
## Doc Bridge Pattern
|
|
69
|
+
## Doc Bridge Pattern (published)
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Full pattern: [`docs/playbook/doc-bridge-pattern.md`](../playbook/doc-bridge-pattern.md)
|
|
72
|
+
|
|
73
|
+
Export for Playbook PR:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ak-docs playbook pattern --text > doc-bridge-pattern.md
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The pattern describes the trilogy:
|
|
72
80
|
|
|
73
81
|
- `AgentHandoff`: precise editing route for coding agents.
|
|
74
82
|
- OKF docs: knowledge files that can be linted and indexed.
|
|
75
|
-
- Self-
|
|
83
|
+
- Self-describe artifacts: `llms.txt`, `capabilities.json`, and content hashes for discovery.
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
Memory-sourced findings still use `ak-docs memory promote --pr` — human review owns merge, licensing, and final wording.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"corpus": {
|
|
4
|
+
"agent": {
|
|
5
|
+
"root": "docs/for-agents"
|
|
6
|
+
}
|
|
7
|
+
},
|
|
8
|
+
"routing": {
|
|
9
|
+
"options": {
|
|
10
|
+
"ownership": {
|
|
11
|
+
"example": {
|
|
12
|
+
"path": "src",
|
|
13
|
+
"purpose": "Starter ownership target — replace with your real modules",
|
|
14
|
+
"checks": ["npm test"],
|
|
15
|
+
"agentDoc": "docs/for-agents/packages/example.md"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"gates": {
|
|
21
|
+
"preset": "minimal"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"corpus": {
|
|
4
|
+
"agent": {
|
|
5
|
+
"root": "docs/for-agents",
|
|
6
|
+
"index": "docs/for-agents/INDEX.md"
|
|
7
|
+
},
|
|
8
|
+
"human": {
|
|
9
|
+
"plugin": "plain-markdown",
|
|
10
|
+
"options": {
|
|
11
|
+
"contentDir": "docs/human",
|
|
12
|
+
"urlPrefix": "/docs"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"routing": {
|
|
17
|
+
"plugin": "pnpm-monorepo",
|
|
18
|
+
"options": {
|
|
19
|
+
"ownership": {
|
|
20
|
+
"auth": {
|
|
21
|
+
"path": "packages/auth",
|
|
22
|
+
"purpose": "Authentication, sessions, and token validation",
|
|
23
|
+
"checks": ["pnpm --filter @demo/auth test", "pnpm --filter @demo/auth lint"],
|
|
24
|
+
"humanDoc": "/docs/guides/auth"
|
|
25
|
+
},
|
|
26
|
+
"billing": {
|
|
27
|
+
"path": "packages/billing",
|
|
28
|
+
"purpose": "Subscriptions, invoices, and payment webhooks",
|
|
29
|
+
"checks": ["pnpm --filter @demo/billing test"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"gates": {
|
|
35
|
+
"preset": "minimal"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: package
|
|
3
|
+
package: auth
|
|
4
|
+
editRoot: packages/auth
|
|
5
|
+
checks:
|
|
6
|
+
- pnpm --filter @demo/auth test
|
|
7
|
+
- pnpm --filter @demo/auth lint
|
|
8
|
+
humanDoc: /docs/guides/auth
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# auth
|
|
12
|
+
|
|
13
|
+
Owns authentication, sessions, and token validation.
|
|
14
|
+
|
|
15
|
+
## Edit roots
|
|
16
|
+
|
|
17
|
+
- `packages/auth`
|
|
18
|
+
|
|
19
|
+
## Checks
|
|
20
|
+
|
|
21
|
+
- `pnpm --filter @demo/auth test`
|
|
22
|
+
- `pnpm --filter @demo/auth lint`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: package
|
|
3
|
+
package: billing
|
|
4
|
+
editRoot: packages/billing
|
|
5
|
+
checks:
|
|
6
|
+
- pnpm --filter @demo/billing test
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# billing
|
|
10
|
+
|
|
11
|
+
Owns subscriptions, invoices, and payment webhooks.
|
|
12
|
+
|
|
13
|
+
## Edit roots
|
|
14
|
+
|
|
15
|
+
- `packages/billing`
|
|
16
|
+
|
|
17
|
+
## Checks
|
|
18
|
+
|
|
19
|
+
- `pnpm --filter @demo/billing test`
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defineConfig } from '@agentskit/doc-bridge/config'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Local Ollama chat demo — zero cloud API key.
|
|
5
|
+
*
|
|
6
|
+
* Prerequisites:
|
|
7
|
+
* ollama serve
|
|
8
|
+
* ollama pull llama3.2
|
|
9
|
+
* ollama pull nomic-embed-text
|
|
10
|
+
* npm i -D @agentskit/rag @agentskit/ink @agentskit/adapters @agentskit/memory react
|
|
11
|
+
*
|
|
12
|
+
* Then:
|
|
13
|
+
* ak-docs index
|
|
14
|
+
* ak-docs rag ingest
|
|
15
|
+
* ak-docs ask "who owns auth?" --chat
|
|
16
|
+
* ak-docs chat
|
|
17
|
+
*/
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
schemaVersion: 1,
|
|
20
|
+
corpus: {
|
|
21
|
+
agent: { root: 'docs/for-agents' },
|
|
22
|
+
},
|
|
23
|
+
intelligence: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
adapter: {
|
|
26
|
+
provider: 'ollama',
|
|
27
|
+
model: 'llama3.2',
|
|
28
|
+
baseUrl: 'http://127.0.0.1:11434',
|
|
29
|
+
options: { embedModel: 'nomic-embed-text' },
|
|
30
|
+
},
|
|
31
|
+
chat: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
sources: ['agent'],
|
|
34
|
+
handoffFirst: true,
|
|
35
|
+
},
|
|
36
|
+
retriever: {
|
|
37
|
+
enabled: true,
|
|
38
|
+
mode: 'agentskit-rag',
|
|
39
|
+
},
|
|
40
|
+
runtime: 'agentskit',
|
|
41
|
+
},
|
|
42
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentskit/doc-bridge",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Human↔agent documentation bridge — deterministic handoffs, doc-site links, memory→docs, optional AgentsKit RAG/chat.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"bin",
|
|
24
|
+
"action.yml",
|
|
24
25
|
"scripts/prepare.mjs",
|
|
25
26
|
"docs",
|
|
26
27
|
"examples",
|
|
@@ -43,6 +44,8 @@
|
|
|
43
44
|
"smoke:packaged": "node scripts/smoke-packaged.mjs",
|
|
44
45
|
"smoke:docsites": "node scripts/smoke-docsites.mjs",
|
|
45
46
|
"smoke:real-docsites": "node scripts/smoke-real-docsites.mjs",
|
|
47
|
+
"smoke:ollama": "node scripts/smoke-ollama.mjs",
|
|
48
|
+
"coverage:badge": "node scripts/update-coverage-badge.mjs",
|
|
46
49
|
"changeset": "changeset",
|
|
47
50
|
"version-packages": "changeset version",
|
|
48
51
|
"release": "pnpm build && pnpm test && changeset publish",
|
|
@@ -119,5 +122,5 @@
|
|
|
119
122
|
"bugs": {
|
|
120
123
|
"url": "https://github.com/AgentsKit-io/doc-bridge/issues"
|
|
121
124
|
},
|
|
122
|
-
"homepage": "https://github.
|
|
125
|
+
"homepage": "https://agentskit-io.github.io/doc-bridge/"
|
|
123
126
|
}
|
package/src/cli/demo.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { dirname, join, resolve } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
import type { DocBridgeConfigV1 } from '../config/schema.js'
|
|
7
|
+
import { buildDocBridgeIndex } from '../index-builder/build-index.js'
|
|
8
|
+
import { runGates } from '../gates/run-gates.js'
|
|
9
|
+
import { mcpSnippet } from '../mcp/install.js'
|
|
10
|
+
import { loadDocBridgeIndex } from '../query/load-index.js'
|
|
11
|
+
import { runQuery } from '../query/query.js'
|
|
12
|
+
import type { AgentHandoffV1 } from '../schemas/agent-handoff.js'
|
|
13
|
+
|
|
14
|
+
export type DemoFixture = 'example' | 'monorepo'
|
|
15
|
+
|
|
16
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
|
|
17
|
+
|
|
18
|
+
const fixturePath = (fixture: DemoFixture): string => {
|
|
19
|
+
if (fixture === 'monorepo') {
|
|
20
|
+
return join(packageRoot, 'examples', 'demo-monorepo')
|
|
21
|
+
}
|
|
22
|
+
return join(packageRoot, 'examples', 'demo-example')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const formatHandoffText = (handoff: AgentHandoffV1): string[] => {
|
|
26
|
+
const bridge =
|
|
27
|
+
handoff.bridge?.humanDoc === 'missing'
|
|
28
|
+
? `human guide: missing → ${handoff.bridge.action ?? 'ak-docs bootstrap agent-docs'}`
|
|
29
|
+
: handoff.humanDoc
|
|
30
|
+
? `human guide: ${handoff.humanDoc}`
|
|
31
|
+
: 'human guide: (none)'
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
`target: ${handoff.target.id} (${handoff.target.path ?? 'n/a'})`,
|
|
35
|
+
`start: ${handoff.startHere}`,
|
|
36
|
+
`edit: ${handoff.editRoots.join(', ')}`,
|
|
37
|
+
`checks: ${handoff.checks.length ? handoff.checks.join(' · ') : '(none)'}`,
|
|
38
|
+
bridge,
|
|
39
|
+
...(handoff.notes.length ? [`notes: ${handoff.notes[0]}`] : []),
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type DemoResult = {
|
|
44
|
+
readonly ok: boolean
|
|
45
|
+
readonly fixture: DemoFixture
|
|
46
|
+
readonly handoff: AgentHandoffV1
|
|
47
|
+
readonly gateBefore: { readonly ok: boolean; readonly message: string }
|
|
48
|
+
readonly gateAfter: { readonly ok: boolean; readonly message: string }
|
|
49
|
+
readonly mcpSnippet: string
|
|
50
|
+
readonly nextCommands: readonly string[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const runDemo = (
|
|
54
|
+
root: string,
|
|
55
|
+
config: DocBridgeConfigV1,
|
|
56
|
+
fixture: DemoFixture = 'example',
|
|
57
|
+
options: { readonly copyFixture?: boolean } = {},
|
|
58
|
+
): DemoResult => {
|
|
59
|
+
const targetPackage = fixture === 'monorepo' ? 'auth' : 'example'
|
|
60
|
+
const fixtureDir = fixturePath(fixture)
|
|
61
|
+
|
|
62
|
+
if (options.copyFixture && existsSync(fixtureDir)) {
|
|
63
|
+
for (const rel of ['doc-bridge.config.json', 'docs', 'packages', 'pnpm-workspace.yaml', 'package.json']) {
|
|
64
|
+
const src = join(fixtureDir, rel)
|
|
65
|
+
if (!existsSync(src)) continue
|
|
66
|
+
const dest = join(root, rel)
|
|
67
|
+
cpSync(src, dest, { recursive: true })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const gateBeforeStale = runGates(root, config, ['index-freshness'])
|
|
72
|
+
const beforeGate = gateBeforeStale.results[0] ?? { ok: false, message: 'index-freshness unavailable' }
|
|
73
|
+
|
|
74
|
+
buildDocBridgeIndex({ root, config })
|
|
75
|
+
const index = loadDocBridgeIndex(root, config)
|
|
76
|
+
const handoff = runQuery(index, config, {
|
|
77
|
+
kind: 'package',
|
|
78
|
+
id: targetPackage,
|
|
79
|
+
agent: true,
|
|
80
|
+
}) as AgentHandoffV1
|
|
81
|
+
|
|
82
|
+
const gateAfter = runGates(root, config, ['index-freshness'])
|
|
83
|
+
const afterGate = gateAfter.results[0] ?? { ok: true, message: 'Index is fresh' }
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
ok: afterGate.ok,
|
|
87
|
+
fixture,
|
|
88
|
+
handoff,
|
|
89
|
+
gateBefore: { ok: beforeGate.ok, message: beforeGate.message },
|
|
90
|
+
gateAfter: { ok: afterGate.ok, message: afterGate.message },
|
|
91
|
+
mcpSnippet: mcpSnippet(root),
|
|
92
|
+
nextCommands: [
|
|
93
|
+
`ak-docs query package ${targetPackage} --agent`,
|
|
94
|
+
'ak-docs doctor --text',
|
|
95
|
+
'ak-docs mcp install --cursor',
|
|
96
|
+
'ak-docs ask "where do I change auth?"',
|
|
97
|
+
],
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const formatDemoText = (result: DemoResult): string[] => {
|
|
102
|
+
const lines = [
|
|
103
|
+
'ak-docs demo — AgentHandoff in 60s',
|
|
104
|
+
'═'.repeat(44),
|
|
105
|
+
'',
|
|
106
|
+
'Before (agent guesses package)',
|
|
107
|
+
' ✗ edits packages/billing when task mentions "auth"',
|
|
108
|
+
' ✗ runs repo-wide test instead of package checks',
|
|
109
|
+
'',
|
|
110
|
+
'After (handoff.resolve / query --agent)',
|
|
111
|
+
...formatHandoffText(result.handoff).map((line) => ` ✓ ${line}`),
|
|
112
|
+
'',
|
|
113
|
+
`Gate: ${result.gateBefore.ok ? 'green' : 'red'} → ${result.gateAfter.ok ? 'green' : 'red'}`,
|
|
114
|
+
` before: ${result.gateBefore.message}`,
|
|
115
|
+
` after: ${result.gateAfter.message}`,
|
|
116
|
+
'',
|
|
117
|
+
'MCP snippet (.cursor/mcp.json)',
|
|
118
|
+
...result.mcpSnippet.split('\n').map((line) => ` ${line}`),
|
|
119
|
+
'',
|
|
120
|
+
'Next',
|
|
121
|
+
...result.nextCommands.map((cmd) => ` → ${cmd}`),
|
|
122
|
+
]
|
|
123
|
+
return lines
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Ephemeral demo workspace for `ak-docs demo` without polluting cwd. */
|
|
127
|
+
export const withDemoWorkspace = (
|
|
128
|
+
fixture: DemoFixture,
|
|
129
|
+
fn: (root: string, config: DocBridgeConfigV1) => DemoResult,
|
|
130
|
+
): DemoResult => {
|
|
131
|
+
const dir = mkdtempSync(join(tmpdir(), 'ak-docs-demo-'))
|
|
132
|
+
try {
|
|
133
|
+
const fixtureDir = fixturePath(fixture)
|
|
134
|
+
if (!existsSync(fixtureDir)) {
|
|
135
|
+
throw new Error(`Demo fixture "${fixture}" not found at ${fixtureDir}`)
|
|
136
|
+
}
|
|
137
|
+
cpSync(fixtureDir, dir, { recursive: true })
|
|
138
|
+
const config = JSON.parse(readFileSync(join(dir, 'doc-bridge.config.json'), 'utf8')) as DocBridgeConfigV1
|
|
139
|
+
return fn(dir, config)
|
|
140
|
+
} finally {
|
|
141
|
+
rmSync(dir, { recursive: true, force: true })
|
|
142
|
+
}
|
|
143
|
+
}
|